---
title: 工作階段執行緒
url: https://platform.claude.com/docs/zh-TW/managed-agents/session-threads
description: 列出、中斷和封存多代理工作階段的執行緒，讀取其事件，並處理跨執行緒的工具權限。
featureMetadata:
  topic:
    title: Managed Agents
    url: https://platform.claude.com/docs/en/managed-agents/overview
  status: beta
  betaHeader: managed-agents-2026-04-01
---

在[多代理工作階段](https://platform.claude.com/docs/zh-TW/managed-agents/multiagent-orchestration)中，每個代理都在自己的 **session thread**（工作階段執行緒）中運作。本頁說明如何列出、中斷和封存執行緒、執行緒傳送的事件，以及工具權限如何在執行緒之間運作。[工作流程執行](https://platform.claude.com/docs/zh-TW/managed-agents/workflow-runs)也會建立工作階段執行緒。

## 主要執行緒與工作階段執行緒

**工作階段層級事件串流**（`/v1/sessions/{session_id}/events/stream`）被視為 **primary thread**（主要執行緒），其中包含所有執行緒中所有活動的精簡檢視。您不會看到[子代理](https://platform.claude.com/docs/zh-TW/managed-agents/multiagent-orchestration#delegate-to-subagents)的完整活動，但您會看到其工作的開始與結束，以及工具權限請求等阻塞事件。

**工作階段執行緒**是您深入查看特定代理活動的地方。

工作階段的 `status` (csharp, go: `Status`) 是所有代理活動的彙總；如果至少有一個執行緒處於 `running`，則整體工作階段狀態也會是 `running`。正在執行的[工作流程執行](https://platform.claude.com/docs/zh-TW/managed-agents/workflow-runs)也可以讓工作階段保持 `running`，即使其執行緒都沒有在工作。當沒有執行緒在工作，且有執行緒在等待您的用戶端時，工作階段為 `idle`；請參閱[得知工作何時完成](https://platform.claude.com/docs/zh-TW/managed-agents/workflow-runs#know-when-the-work-is-done)。

[工作階段預算](https://platform.claude.com/docs/zh-TW/managed-agents/budgets)是工作階段所有執行緒共用的單一上限。當達到上限時，各執行緒會獨立暫停，且每個執行緒的成本會依該執行緒本身所使用的模型計價。

<Note>
  一個工作階段同時最多可以有 25 個子執行緒。閒置的執行緒在您封存之前都會計入，而主要執行緒不計入。主要執行緒的代理可以呼叫單一子代理的多個副本，從而建立與同一個 `agent` 相關聯的多個執行緒。[顧問](https://platform.claude.com/docs/zh-TW/managed-agents/multiagent-orchestration#give-the-session-an-advisor)諮詢執行緒不受此限制。[工作流程執行](https://platform.claude.com/docs/zh-TW/managed-agents/workflow-runs)的執行緒也不受此限制。
</Note>

## 列出執行緒

依下列方式列出與工作階段相關聯的所有執行緒：

<CodeGroup>
  ```bash cURL
  curl -fsS "https://api.anthropic.com/v1/sessions/$SESSION_ID/threads" \
    -H "x-api-key: $ANTHROPIC_API_KEY" \
    -H "anthropic-version: 2023-06-01" \
    -H "anthropic-beta: managed-agents-2026-04-01"
  ```

  ```bash CLI
  ant beta:sessions:threads list --session-id "$SESSION_ID"
  ```

  ```python Python
  for thread in client.beta.sessions.threads.list(session.id):
      agent = thread.agent
      label = agent.type if agent.type == "advisor" else agent.name
      print(f"[{label}] {thread.status}")
  ```

  ```typescript TypeScript
  for await (const thread of client.beta.sessions.threads.list(session.id)) {
    const label = thread.agent.type === "advisor" ? thread.agent.type : thread.agent.name;
    console.log(`[${label}] ${thread.status}`);
  }
  ```

  ```csharp C#
  await foreach (var sessionThread in (await client.Beta.Sessions.Threads.List(session.ID)).Paginate())
  {
      var label = sessionThread.Agent.Name ?? sessionThread.Agent.Json.GetProperty("type").GetString();
      Console.WriteLine($"[{label}] {sessionThread.Status.Raw()}");
  }
  ```

  ```go Go
  threads := client.Beta.Sessions.Threads.ListAutoPaging(ctx, session.ID, anthropic.BetaSessionThreadListParams{})
  for threads.Next() {
  	thread := threads.Current()
  	label := cmp.Or(thread.Agent.Name, thread.Agent.Type)
  	fmt.Printf("[%s] %s\n", label, thread.Status)
  }
  if err := threads.Err(); err != nil {
  	panic(err)
  }
  ```

  ```java Java
  for (var thread : client.beta().sessions().threads().list(session.id()).autoPager()) {
      var agent = thread.agent();
      var label = agent.isAgent() ? agent.asAgent().name() : agent.type().asString();
      IO.println("[" + label + "] " + thread.status());
  }
  ```

  ```php PHP
  foreach ($client->beta->sessions->threads->list($session->id)->pagingEachItem() as $thread) {
      $label = $thread->agent instanceof \Anthropic\Beta\Agents\BetaManagedAgentsAdvisor
          ? $thread->agent->type
          : $thread->agent->name;
      echo "[{$label}] {$thread->status}\n";
  }
  ```

  ```ruby Ruby
  client.beta.sessions.threads.list(session.id).auto_paging_each do |thread|
    agent = thread.agent
    label = agent.type == :advisor ? agent.type : agent.name
    puts "[#{label}] #{thread.status}"
  end
  ```
</CodeGroup>

完整清單包含主要執行緒。主要執行緒的 `parent_thread_id` (java: `parentThreadId`; php: `parentThreadID`; csharp, go: `ParentThreadID`) 為 `null` (python: `None`; ruby: `nil`; java: `Optional.empty()`; go: `""`)。其他每個執行緒都是子執行緒。除了[執行的執行緒](https://platform.claude.com/docs/zh-TW/managed-agents/workflow-runs#a-runs-threads)之外，`workflow_run_id` (java: `workflowRunId`; php: `workflowRunID`; csharp, go: `WorkflowRunID`) 皆為 `null` (python: `None`; ruby: `nil`; java: `Optional.empty()`; go: `""`)。

若只要列出具有特定狀態的執行緒，請在請求中加入 `statuses[]`，並重複該參數以指定多個狀態，例如 `?statuses[]=running&statuses[]=idle`。省略此參數則會傳回所有狀態的執行緒。

## 中斷工作階段執行緒

傳送帶有 `session_thread_id` (java: `sessionThreadId`; php: `sessionThreadID`; csharp, go: `SessionThreadID`) 的 `user.interrupt` 以停止特定執行緒。省略 `session_thread_id` (java: `sessionThreadId`; php: `sessionThreadID`; csharp, go: `SessionThreadID`) 會中斷工作階段中所有未封存的執行緒，包括主要執行緒。在具有動態工作流程的工作階段中，中斷不會結束任何執行，而指定某個執行之執行緒的中斷則不會停止任何東西。中斷會關閉其他子執行緒的待處理工具呼叫，但請勿依賴它來關閉執行之執行緒的待處理工具呼叫。請參閱[在有開啟的執行時中斷工作階段](https://platform.claude.com/docs/zh-TW/managed-agents/workflow-runs#interrupt-a-session-with-runs-open)。

<CodeGroup>
  ```bash cURL
  curl -fsS "https://api.anthropic.com/v1/sessions/$SESSION_ID/events?beta=true" \
    -H "x-api-key: $ANTHROPIC_API_KEY" \
    -H "anthropic-version: 2023-06-01" \
    -H "anthropic-beta: managed-agents-2026-04-01" \
    -H "content-type: application/json" \
    -d '{"events": [{"type": "user.interrupt", "session_thread_id": "'"$THREAD_ID"'"}]}'
  ```

  ```bash CLI
  ant beta:sessions:events send \
    --session-id "$SESSION_ID" \
    --event "{type: user.interrupt, session_thread_id: $THREAD_ID}"
  ```

  ```python Python
  client.beta.sessions.events.send(
      session.id,
      events=[{"type": "user.interrupt", "session_thread_id": thread.id}],
  )
  ```

  ```typescript TypeScript
  await client.beta.sessions.events.send(session.id, {
    events: [{ type: "user.interrupt", session_thread_id: thread.id }]
  });
  ```

  ```csharp C#
  await client.Beta.Sessions.Events.Send(session.ID, new()
  {
      Events =
      [
          new BetaManagedAgentsUserInterruptEventParams
          {
              Type = BetaManagedAgentsUserInterruptEventParamsType.UserInterrupt,
              SessionThreadID = thread.ID,
          },
      ],
  });
  ```

  ```go Go
  if _, err := client.Beta.Sessions.Events.Send(ctx, session.ID, anthropic.BetaSessionEventSendParams{
  	Events: []anthropic.BetaManagedAgentsEventParamsUnion{{
  		OfUserInterrupt: &anthropic.BetaManagedAgentsUserInterruptEventParams{
  			Type:            anthropic.BetaManagedAgentsUserInterruptEventParamsTypeUserInterrupt,
  			SessionThreadID: anthropic.String(thread.ID),
  		},
  	}},
  }); err != nil {
  	panic(err)
  }
  ```

  ```java Java
  client.beta().sessions().events().send(
      session.id(),
      EventSendParams.builder()
          .addEvent(BetaManagedAgentsUserInterruptEventParams.builder()
              .type(BetaManagedAgentsUserInterruptEventParams.Type.USER_INTERRUPT)
              .sessionThreadId(thread.id())
              .build())
          .build());
  ```

  ```php PHP
  $client->beta->sessions->events->send(
      $session->id,
      events: [
          ['type' => 'user.interrupt', 'session_thread_id' => $thread->id],
      ],
  );
  ```

  ```ruby Ruby
  client.beta.sessions.events.send_(
    session.id,
    events: [{type: "user.interrupt", session_thread_id: thread.id}]
  )
  ```
</CodeGroup>

對於因 `requires_action` 而阻塞的子代理執行緒，中斷會以錯誤工具結果（"Tool execution was interrupted before completion. Please retry."）關閉每個待處理的工具呼叫，並直接重新發出帶有 `stop_reason: end_turn` 的 `session.thread_status_idle`；模型不會被取樣。對於以 `end_turn` 或 `budget_reached` 處於閒置狀態的子執行緒，中斷不會產生任何作用。指定已終止執行緒的中斷會傳回 400 錯誤。被中斷的子執行緒不會向主要執行緒的代理傳送它在回合結束時會傳送的報告。當該代理正在等待子執行緒時，在有其他東西到達之前（例如 `user.message` 或另一個執行緒的報告），它不會開始另一個回合。

## 封存工作階段執行緒

您可以選擇在工作階段執行緒完成工作後將其封存。封存執行緒會釋放其在 25 個子執行緒限制中所佔的名額。伺服器會自行封存[工作流程執行](https://platform.claude.com/docs/zh-TW/managed-agents/workflow-runs)的執行緒。您不需要封存它們，而且在執行開啟期間您也無法封存。

<CodeGroup>
  ```bash cURL
  curl -fsS -X POST "https://api.anthropic.com/v1/sessions/$SESSION_ID/threads/$THREAD_ID/archive" \
    -H "x-api-key: $ANTHROPIC_API_KEY" \
    -H "anthropic-version: 2023-06-01" \
    -H "anthropic-beta: managed-agents-2026-04-01"
  ```

  ```bash CLI
  ant beta:sessions:threads archive \
    --session-id "$SESSION_ID" \
    --thread-id "$THREAD_ID"
  ```

  ```python Python
  archived = client.beta.sessions.threads.archive(thread.id, session_id=session.id)
  print(archived.status, archived.archived_at)
  ```

  ```typescript TypeScript
  const archived = await client.beta.sessions.threads.archive(thread.id, {
    session_id: session.id
  });
  console.log(archived.status, archived.archived_at);
  ```

  ```csharp C#
  var archived = await client.Beta.Sessions.Threads.Archive(thread.ID, new() { SessionID = session.ID });
  Console.WriteLine($"{archived.Status} {archived.ArchivedAt}");
  ```

  ```go Go
  archived, err := client.Beta.Sessions.Threads.Archive(ctx, thread.ID, anthropic.BetaSessionThreadArchiveParams{
  	SessionID: session.ID,
  })
  if err != nil {
  	panic(err)
  }
  fmt.Println(archived.Status, archived.ArchivedAt)
  ```

  ```java Java
  var archived = client.beta().sessions().threads().archive(
      thread.id(),
      ThreadArchiveParams.builder()
          .sessionId(session.id())
          .build());
  IO.println(archived.status() + " " + archived.archivedAt().orElseThrow());
  ```

  ```php PHP
  $archived = $client->beta->sessions->threads->archive($thread->id, sessionID: $session->id);
  echo "{$archived->status} {$archived->archivedAt->format(DATE_ATOM)}\n";
  ```

  ```ruby Ruby
  archived = client.beta.sessions.threads.archive(thread.id, session_id: session.id)
  puts "#{archived.status} #{archived.archived_at}"
  ```
</CodeGroup>

只有在執行緒處於 `idle` 時，封存才會成功。停留在 `requires_action` 的執行緒視為閒置，可以直接封存；只有正在執行的執行緒必須先中斷：

<CodeGroup>
  ```bash cURL
  # 中斷執行緒，然後將其封存
  curl -fsS "https://api.anthropic.com/v1/sessions/$SESSION_ID/events?beta=true" \
    -H "x-api-key: $ANTHROPIC_API_KEY" \
    -H "anthropic-version: 2023-06-01" \
    -H "anthropic-beta: managed-agents-2026-04-01" \
    -H "content-type: application/json" \
    -d '{"events": [{"type": "user.interrupt", "session_thread_id": "'"$THREAD_ID"'"}]}'

  curl -fsS -X POST "https://api.anthropic.com/v1/sessions/$SESSION_ID/threads/$THREAD_ID/archive" \
    -H "x-api-key: $ANTHROPIC_API_KEY" \
    -H "anthropic-version: 2023-06-01" \
    -H "anthropic-beta: managed-agents-2026-04-01"
  ```

  ```bash CLI
  ant beta:sessions:events send \
    --session-id "$SESSION_ID" \
    --event "{type: user.interrupt, session_thread_id: $THREAD_ID}"

  ant beta:sessions:threads archive \
    --session-id "$SESSION_ID" \
    --thread-id "$THREAD_ID"
  ```

  ```python Python
  client.beta.sessions.events.send(
      session.id,
      events=[{"type": "user.interrupt", "session_thread_id": thread.id}],
  )
  archived = client.beta.sessions.threads.archive(thread.id, session_id=session.id)
  print(archived.status, archived.archived_at)
  ```

  ```typescript TypeScript
  await client.beta.sessions.events.send(session.id, {
    events: [{ type: "user.interrupt", session_thread_id: thread.id }]
  });
  const archived = await client.beta.sessions.threads.archive(thread.id, {
    session_id: session.id
  });
  console.log(archived.status, archived.archived_at);
  ```

  ```csharp C#
  await client.Beta.Sessions.Events.Send(session.ID, new()
  {
      Events =
      [
          new BetaManagedAgentsUserInterruptEventParams
          {
              Type = BetaManagedAgentsUserInterruptEventParamsType.UserInterrupt,
              SessionThreadID = thread.ID,
          },
      ],
  });
  archived = await client.Beta.Sessions.Threads.Archive(thread.ID, new() { SessionID = session.ID });
  Console.WriteLine($"{archived.Status} {archived.ArchivedAt}");
  ```

  ```go Go
  if _, err := client.Beta.Sessions.Events.Send(ctx, session.ID, anthropic.BetaSessionEventSendParams{
  	Events: []anthropic.BetaManagedAgentsEventParamsUnion{{
  		OfUserInterrupt: &anthropic.BetaManagedAgentsUserInterruptEventParams{
  			Type:            anthropic.BetaManagedAgentsUserInterruptEventParamsTypeUserInterrupt,
  			SessionThreadID: anthropic.String(thread.ID),
  		},
  	}},
  }); err != nil {
  	panic(err)
  }

  archived, err := client.Beta.Sessions.Threads.Archive(ctx, thread.ID, anthropic.BetaSessionThreadArchiveParams{
  	SessionID: session.ID,
  })
  if err != nil {
  	panic(err)
  }
  fmt.Println(archived.Status, archived.ArchivedAt)
  ```

  ```java Java
  client.beta().sessions().events().send(
      session.id(),
      EventSendParams.builder()
          .addEvent(BetaManagedAgentsUserInterruptEventParams.builder()
              .type(BetaManagedAgentsUserInterruptEventParams.Type.USER_INTERRUPT)
              .sessionThreadId(thread.id())
              .build())
          .build());

  archived = client.beta().sessions().threads().archive(
      thread.id(),
      ThreadArchiveParams.builder()
          .sessionId(session.id())
          .build());
  IO.println(archived.status() + " " + archived.archivedAt().orElseThrow());
  ```

  ```php PHP
  $client->beta->sessions->events->send(
      $session->id,
      events: [['type' => 'user.interrupt', 'session_thread_id' => $thread->id]],
  );
  $archived = $client->beta->sessions->threads->archive($thread->id, sessionID: $session->id);
  echo "{$archived->status} {$archived->archivedAt->format(DATE_ATOM)}\n";
  ```

  ```ruby Ruby
  client.beta.sessions.events.send_(
    session.id,
    events: [{type: "user.interrupt", session_thread_id: thread.id}]
  )
  archived = client.beta.sessions.threads.archive(thread.id, session_id: session.id)
  puts "#{archived.status} #{archived.archived_at}"
  ```
</CodeGroup>

## 主要執行緒事件

這些事件會在位於 `/v1/sessions/{session_id}/events/stream` 的主要執行緒上呈現多代理活動。訊息方向事件的命名是相對於其所出現串流的執行緒而言：`agent.thread_message_received` 表示有訊息從另一個執行緒抵達此執行緒，而 `agent.thread_message_sent` 表示此執行緒傳送了一則訊息。例如，主要執行緒的代理所委派的任務，會以 `agent.thread_message_received` 事件的形式抵達子執行緒自己的串流。

| 類型                                 | 說明                                                                                                                                                                                                                                                                     |
| ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `session.thread_created`           | 已建立執行緒。包含 `session_thread_id` (java: `sessionThreadId`; php: `sessionThreadID`; csharp, go: `SessionThreadID`) 和 `agent_name` (java, php: `agentName`; csharp, go: `AgentName`)。                                                                                       |
| `session.thread_status_running`    | 某個執行緒開始活動。                                                                                                                                                                                                                                                             |
| `session.thread_status_idle`       | 與執行緒相關聯的代理正在等待輸入。包含指出代理停止原因的 `stop_reason` (java, php: `stopReason`; csharp, go: `StopReason`)。                                                                                                                                                                        |
| `session.thread_status_terminated` | 某個執行緒已終止且不再接受任何輸入，例如因為它已被封存或遇到無法復原的錯誤。顧問執行緒也會在其諮詢結束時終止。                                                                                                                                                                                                                |
| `agent.thread_message_received`    | 在主要執行緒上，子代理向主要執行緒的代理傳送了報告或問題。包含 `from_session_thread_id` (java: `fromSessionThreadId`; php: `fromSessionThreadID`; csharp, go: `FromSessionThreadID`)、`from_agent_name` (java, php: `fromAgentName`; csharp, go: `FromAgentName`) 和 `content` (csharp, go: `Content`)。 |
| `agent.thread_message_sent`        | 在主要執行緒上，主要執行緒的代理向子代理傳送了任務或後續訊息。包含 `to_session_thread_id` (java: `toSessionThreadId`; php: `toSessionThreadID`; csharp, go: `ToSessionThreadID`)、`to_agent_name` (java, php: `toAgentName`; csharp, go: `ToAgentName`) 和 `content` (csharp, go: `Content`)。             |

顧問諮詢會以保留名稱 `anthropic.advisor` 發出這些相同的執行緒事件（在執行緒生命週期事件上作為 `agent_name` (java, php: `agentName`; csharp, go: `AgentName`)，在建議傳遞上作為 `from_agent_name` (java, php: `fromAgentName`; csharp, go: `FromAgentName`)）；有關順序，請參閱[為工作階段提供顧問](https://platform.claude.com/docs/zh-TW/managed-agents/multiagent-orchestration#give-the-session-an-advisor)。

[工作流程執行](https://platform.claude.com/docs/zh-TW/managed-agents/workflow-runs)的執行緒在主要串流上的呈現方式如下：

* **生命週期事件：**&#x57F7;行的每個執行緒都會傳送帶有該執行之 `workflow_run_id` (java: `workflowRunId`; php: `workflowRunID`; csharp, go: `WorkflowRunID`) 的 `session.thread_created`，以及其 `session.thread_status_running`、`session.thread_status_idle` 和 `session.thread_status_terminated` 事件。
* **訊息事件：**&#x57F7;行之執行緒的提示（一個 `agent.thread_message_received` 事件）會保留在其自己的串流上。
* **執行事件：**`workflow_run.*` 事件也會抵達此串流；請參閱[執行事件](https://platform.claude.com/docs/zh-TW/managed-agents/workflow-runs#run-events)。
* **等待您處理的工具呼叫：**&#x57F7;行之執行緒中需要您用戶端處理的工具呼叫，會如同任何子執行緒一樣交叉發布到此串流。請參閱[工具權限與自訂工具](https://platform.claude.com/docs/zh-TW/managed-agents/session-threads#tool-permissions-and-custom-tools)。

## 工作階段執行緒事件

關鍵事件會被代理轉送到主要執行緒。不過，您可能仍想調查特定代理的推理和工具呼叫。若要這麼做，請串流或列出相關工作階段執行緒的事件。

每個工作階段執行緒在 `/v1/sessions/{session_id}/threads/{thread_id}/stream` 都有自己的事件串流，且它接受與工作階段層級串流相同的 `event_deltas[]` 參數，因此您可以在模型生成子代理的文字時預覽該文字。一個連線只會預覽它正在讀取的執行緒：子執行緒的預覽永遠不會出現在工作階段層級串流上，因此若要即時觀看子代理，請開啟其自己的執行緒串流。關於選擇加入、累積和調和預覽，請參閱[預覽工作階段執行緒事件](https://platform.claude.com/docs/zh-TW/managed-agents/event-deltas#preview-session-thread-events)。

在工作流程執行中，伺服器會執行一個工作流程：由主要執行緒的代理所撰寫的程式。在該執行的每個執行緒上，第一個 `agent.thread_message_received` 是工作流程所撰寫的提示。其 `from_session_thread_id` (java: `fromSessionThreadId`; php: `fromSessionThreadID`; csharp, go: `FromSessionThreadID`) 是主要執行緒的 ID，且 API 不會設定其 `from_agent_name` (java, php: `fromAgentName`; csharp, go: `FromAgentName`)。API 不保證提示的文字內容，因此請勿剖析它。該執行緒在主要執行緒串流上的 `session.thread_status_terminated` 事件會告訴您該執行緒已完成。沒有任何事件會記錄它傳回給工作流程的結果。

執行緒的串流不會重播先前的事件。緊接在 `session.thread_created` 之後，執行之執行緒的事件清單可能是空的，因為伺服器會在其之後才寫入該執行緒的第一個事件。因此，請先開啟執行緒的串流，然後列出執行緒的事件，並略過每個 `id` (csharp, go: `ID`) 已由清單傳回的串流事件。

<Tabs>
  <Tab title="串流工作階段執行緒事件">
    <CodeGroup>
      ```bash cURL
      curl -fsSN "https://api.anthropic.com/v1/sessions/$SESSION_ID/threads/$THREAD_ID/stream?beta=true" \
        -H "x-api-key: $ANTHROPIC_API_KEY" \
        -H "anthropic-version: 2023-06-01" \
        -H "anthropic-beta: managed-agents-2026-04-01"
      ```

      ```bash CLI
      ant beta:sessions:threads:events stream \
        --session-id "$SESSION_ID" \
        --thread-id "$THREAD_ID"
      ```

      ```python Python
      with client.beta.sessions.threads.events.stream(
          thread.id,
          session_id=session.id,
      ) as stream:
          for event in stream:
              match event.type:
                  case "agent.message":
                      for block in event.content:
                          if block.type == "text":
                              print(block.text, end="")
                  case "session.thread_status_idle":
                      break
      ```

      ```typescript TypeScript
      const stream = await client.beta.sessions.threads.events.stream(thread.id, {
        session_id: session.id
      });

      loop: for await (const event of stream) {
        switch (event.type) {
          case "agent.message":
            for (const block of event.content) {
              if (block.type === "text") {
                process.stdout.write(block.text);
              }
            }
            break;
          case "session.thread_status_idle":
            break loop;
        }
      }
      ```

      ```csharp C#
      await foreach (var evt in client.Beta.Sessions.Threads.Events.StreamStreaming(thread.ID, new() { SessionID = session.ID }))
      {
          if (evt.Value is BetaManagedAgentsAgentMessageEvent message)
          {
              foreach (var block in message.Content)
              {
                  if (block.Value is BetaManagedAgentsTextBlock textBlock)
                  {
                      Console.Write(textBlock.Text);
                  }
              }
          }
          else if (evt.Value is BetaManagedAgentsSessionThreadStatusIdleEvent)
          {
              break;
          }
      }
      ```

      ```go Go
      	stream := client.Beta.Sessions.Threads.Events.StreamEvents(ctx, thread.ID, anthropic.BetaSessionThreadEventStreamParams{
      		SessionID: session.ID,
      	})
      	defer stream.Close()

      loop:
      	for stream.Next() {
      		event := stream.Current()
      		switch event.Type {
      		case "agent.message":
      			for _, block := range event.AsAgentMessage().Content {
      				if block.Type == "text" {
      					fmt.Print(block.Text)
      				}
      			}
      		case "session.thread_status_idle":
      			break loop
      		}
      	}
      	if err := stream.Err(); err != nil {
      		panic(err)
      	}
      ```

      ```java Java
      try (var streamResponse = client.beta().sessions().threads().events().streamStreaming(
          thread.id(),
          EventStreamParams.builder().sessionId(session.id()).build()
      )) {
          loop:
          for (var event : (Iterable<BetaManagedAgentsStreamSessionThreadEvents>) streamResponse.stream()::iterator) {
              switch (event.type().value()) {
                  case AGENT_MESSAGE -> {
                      for (var block : event.asAgentMessage().content()) {
                          block.text().ifPresent(textBlock -> IO.print(textBlock.text()));
                      }
                  }
                  case SESSION_THREAD_STATUS_IDLE -> {
                      break loop;
                  }
              }
          }
      }
      ```

      ```php PHP
      $stream = $client->beta->sessions->threads->events->streamStream(
          $thread->id,
          sessionID: $session->id,
      );

      foreach ($stream as $event) {
          switch (true) {
              case $event instanceof \Anthropic\Beta\Sessions\Events\ManagedAgentsAgentMessageEvent:
                  foreach ($event->content as $block) {
                      if ($block instanceof \Anthropic\Beta\Sessions\Events\ManagedAgentsTextBlock) {
                          echo $block->text;
                      }
                  }
                  break;
              case $event instanceof \Anthropic\Beta\Sessions\Events\ManagedAgentsSessionThreadStatusIdleEvent:
                  break 2;
          }
      }
      ```

      ```ruby Ruby
      client.beta.sessions.threads.events.stream_events(thread.id, session_id: session.id).each do |event|
        case event
        when Anthropic::Beta::Sessions::BetaManagedAgentsAgentMessageEvent
          event.content.each do |block|
            print block.text if block.is_a?(Anthropic::Beta::Sessions::BetaManagedAgentsTextBlock)
          end
        when Anthropic::Beta::Sessions::BetaManagedAgentsSessionThreadStatusIdleEvent
          break
        end
      end
      ```
    </CodeGroup>
  </Tab>

  <Tab title="列出工作階段執行緒事件">
    列出所有過去的工作階段執行緒事件，以取得完整的歷史記錄。

    <CodeGroup>
      ```bash cURL
      curl -fsS "https://api.anthropic.com/v1/sessions/$SESSION_ID/threads/$THREAD_ID/events" \
        -H "x-api-key: $ANTHROPIC_API_KEY" \
        -H "anthropic-version: 2023-06-01" \
        -H "anthropic-beta: managed-agents-2026-04-01"
      ```

      ```bash CLI
      ant beta:sessions:threads:events list \
        --session-id "$SESSION_ID" \
        --thread-id "$THREAD_ID"
      ```

      ```python Python
      for event in client.beta.sessions.threads.events.list(
          thread.id,
          session_id=session.id,
      ):
          print(f"[{event.type}] {event.processed_at}")
      ```

      ```typescript TypeScript
      for await (const event of client.beta.sessions.threads.events.list(thread.id, {
        session_id: session.id
      })) {
        console.log(`[${event.type}] ${event.processed_at}`);
      }
      ```

      ```csharp C#
      var page = await client.Beta.Sessions.Threads.Events.List(thread.ID, new() { SessionID = session.ID });
      await foreach (var evt in page.Paginate())
      {
          Console.WriteLine($"[{evt.Json.GetProperty("type").GetString()}] {evt.ProcessedAt}");
      }
      ```

      ```go Go
      pager := client.Beta.Sessions.Threads.Events.ListAutoPaging(ctx, thread.ID, anthropic.BetaSessionThreadEventListParams{
      	SessionID: session.ID,
      })
      for pager.Next() {
      	event := pager.Current()
      	fmt.Printf("[%s] %s\n", event.Type, event.ProcessedAt)
      }
      if err := pager.Err(); err != nil {
      	panic(err)
      }
      ```

      ```java Java
      for (var event : client.beta().sessions().threads().events().list(
              thread.id(),
              EventListParams.builder().sessionId(session.id()).build()
          ).autoPager()) {
          var type = event._json().orElseThrow() instanceof JsonObject json
              ? json.values().get("type").asStringOrThrow()
              : "unknown";
          var processedAt = event.processedAt().map(OffsetDateTime::toString).orElse("pending");
          IO.println("[" + type + "] " + processedAt);
      }
      ```

      ```php PHP
      foreach (
          $client->beta->sessions->threads->events->list(
              $thread->id,
              sessionID: $session->id,
          )->pagingEachItem() as $event
      ) {
          echo "[{$event->type}] {$event->processedAt->format(DATE_RFC3339)}\n";
      }
      ```

      ```ruby Ruby
      client.beta.sessions.threads.events.list(
        thread.id,
        session_id: session.id
      ).auto_paging_each do |event|
        puts "[#{event.type}] #{event.processed_at}"
      end
      ```
    </CodeGroup>
  </Tab>
</Tabs>

## 工具權限與自訂工具

如果子代理需要您的用戶端提供某些東西，例如執行工具呼叫的[權限](https://platform.claude.com/docs/zh-TW/managed-agents/events-and-streaming#tool-confirmation)或[自訂工具的結果](https://platform.claude.com/docs/zh-TW/managed-agents/events-and-streaming#handling-custom-tool-calls)，該事件會交叉發布到**主要執行緒**，並以 `session_thread_id` 識別其來源工作階段執行緒。在 `always_ask` 下，或在 [`auto`](https://platform.claude.com/docs/zh-TW/managed-agents/permission-policies#let-the-server-evaluate-each-call-with-auto) 下伺服器無法做出判定時，工具呼叫需要您的權限。

```json
{
  "type": "session.thread_status_idle",
  "id": "sevt_01ABC...",
  "session_thread_id": "sthr_01DEF...",
  "agent_name": "code-reviewer",
  "stop_reason": {
    "type": "requires_action",
    "event_ids": ["sevt_01XYZ..."]
  }
}
```

發布 `user.tool_confirmation`（帶有 `tool_use_id` (java: `toolUseId`; php: `toolUseID`; csharp, go: `ToolUseID`)）或 `user.custom_tool_result`（帶有 `custom_tool_use_id` (java: `customToolUseId`; php: `customToolUseID`; csharp, go: `CustomToolUseID`)）；伺服器會自動將回應路由到正確的執行緒。回應可能會以不同的 `id` (csharp, go: `ID`) 值出現在主要執行緒和子代理的執行緒上。若要比對這兩個副本，請比較 `type` (csharp, go: `Type`) 和 `tool_use_id` (java: `toolUseId`; php: `toolUseID`; csharp, go: `ToolUseID`)（或 `custom_tool_use_id` (java: `customToolUseId`; php: `customToolUseID`; csharp, go: `CustomToolUseID`)），而不是 `id` (csharp, go: `ID`)。

只有在沒有任何執行緒處於 `running` 時，工作階段才會變為 `idle`，因此 `session.status_idle` 可能會在子代理的呼叫之後很久才抵達。您不必等待它：一旦交叉發布的 `agent.custom_tool_use` 事件抵達，就立即傳送 `user.custom_tool_result`。

在 `auto` 下，您的 `user.message` 事件可能會讓伺服器允許原本會拒絕的呼叫。子代理執行緒中的任何內容都不會被視為您的意圖。您的用戶端不會在那裡發布任何訊息，而主要執行緒的代理傳送給子代理的訊息也不算數。當伺服器在 `auto` 下拒絕某個呼叫時，不會有任何內容被交叉發布：該事件和錯誤工具結果只會出現在子代理自己的[執行緒串流](https://platform.claude.com/docs/zh-TW/managed-agents/session-threads#session-thread-events)上，且子代理會繼續執行。

以下範例位於[工具確認處理常式](https://platform.claude.com/docs/zh-TW/managed-agents/events-and-streaming#tool-confirmation)的事件迴圈內。對於 `stop_reason.event_ids` 中的每個 ID，它會傳送允許該呼叫的 `user.tool_confirmation`。相同的模式也適用於 `user.custom_tool_result`，您也可以在其 `agent.custom_tool_use` 事件抵達時立即傳送。

<CodeGroup exclude="shell:cURL, shell:CLI">
  <CodeGroupItem language="cURL">
    此工作流程需要的邏輯超出單一 shell 命令所能處理的範圍。請使用 [Claude SDK](https://platform.claude.com/docs/zh-TW/cli-sdks-libraries/overview) 來執行它。
  </CodeGroupItem>

  <CodeGroupItem language="CLI">
    此工作流程需要的邏輯超出單一 shell 命令所能處理的範圍。請使用 [Claude SDK](https://platform.claude.com/docs/zh-TW/cli-sdks-libraries/overview) 來執行它。
  </CodeGroupItem>

  ```python Python
  for event_id in stop.event_ids:
      client.beta.sessions.events.send(
          session.id,
          events=[
              {
                  "type": "user.tool_confirmation",
                  "tool_use_id": event_id,
                  "result": "allow",
              }
          ],
      )
  ```

  ```typescript TypeScript
  for (const eventId of stop.event_ids) {
    await client.beta.sessions.events.send(session.id, {
      events: [
        {
          type: "user.tool_confirmation",
          tool_use_id: eventId,
          result: "allow"
        }
      ]
    });
  }
  ```

  ```csharp C#
  foreach (var eventId in requiresAction.EventIds)
  {
      await client.Beta.Sessions.Events.Send(session.ID, new()
      {
          Events =
          [
              new BetaManagedAgentsUserToolConfirmationEventParams
              {
                  Type = BetaManagedAgentsUserToolConfirmationEventParamsType.UserToolConfirmation,
                  ToolUseID = eventId,
                  Result = BetaManagedAgentsUserToolConfirmationEventParamsResult.Allow,
              },
          ],
      });
  }
  ```

  ```go Go
  for _, eventID := range stopReason.EventIDs {
  	params := anthropic.BetaManagedAgentsUserToolConfirmationEventParams{
  		Type:      anthropic.BetaManagedAgentsUserToolConfirmationEventParamsTypeUserToolConfirmation,
  		ToolUseID: eventID,
  		Result:    anthropic.BetaManagedAgentsUserToolConfirmationEventParamsResultAllow,
  	}
  	if _, err := client.Beta.Sessions.Events.Send(ctx, session.ID, anthropic.BetaSessionEventSendParams{
  		Events: []anthropic.BetaManagedAgentsEventParamsUnion{{OfUserToolConfirmation: &params}},
  	}); err != nil {
  		panic(err)
  	}
  }
  ```

  ```java Java
  for (var eventId : pendingToolUseIds) {
      client.beta().sessions().events().send(
          session.id(),
          EventSendParams.builder()
              .addEvent(BetaManagedAgentsUserToolConfirmationEventParams.builder()
                  .type(BetaManagedAgentsUserToolConfirmationEventParams.Type.USER_TOOL_CONFIRMATION)
                  .toolUseId(eventId)
                  .result(BetaManagedAgentsUserToolConfirmationEventParams.Result.ALLOW)
                  .build())
              .build()
      );
  }
  ```

  ```php PHP
  foreach ($event->stopReason->eventIDs as $eventId) {
      $client->beta->sessions->events->send($session->id, events: [[
          'type' => 'user.tool_confirmation',
          'tool_use_id' => $eventId,
          'result' => 'allow',
      ]]);
  }
  ```

  ```ruby Ruby
  event_ids.each do |event_id|
    client.beta.sessions.events.send_(session.id, events: [{
      type: "user.tool_confirmation",
      tool_use_id: event_id,
      result: "allow"
    }])
  end
  ```
</CodeGroup>

上述模式會回應閒置事件所列出的呼叫。在主要串流上，子代理的 `session.thread_status_idle` 事件可能會在其 `stop_reason.event_ids` 所列出的 `agent.tool_use` 或 `agent.mcp_tool_use` 事件之前抵達。針對事件尚未抵達之呼叫的 `user.tool_confirmation` 可能會傳回 400。為避免這種情況，請在每個 [`evaluated_permission` (java, php: `evaluatedPermission`; csharp, go: `EvaluatedPermission`)](https://platform.claude.com/docs/zh-TW/managed-agents/permission-policies#see-how-each-call-was-evaluated) 為 `ask` 的呼叫其自身事件抵達主要串流時，再回應該呼叫。
