---
read_when:
    - คุณต้องการงานเบื้องหลังหรืองานแบบขนานผ่านเอเจนต์
    - คุณกำลังเปลี่ยนแปลงนโยบายเครื่องมือ sessions_spawn หรือเอเจนต์ย่อย
    - คุณกำลังนำไปใช้หรือแก้ไขปัญหาเซสชันซับเอเจนต์ที่ผูกกับเธรด
sidebarTitle: Sub-agents
summary: สร้างการรันเอเจนต์เบื้องหลังแบบแยกกัน ซึ่งแจ้งผลลัพธ์กลับไปยังแชตของผู้ร้องขอ
title: เอเจนต์ย่อย
x-i18n:
    generated_at: "2026-05-11T20:40:26Z"
    model: gpt-5.5
    provider: openai
    source_hash: 02b03bdfd5cddf5618fddf0804f017400c36751095166dac18fa35fa3bfd4c6e
    source_path: tools/subagents.md
    workflow: 16
---

เอเจนต์ย่อยคือการรันเอเจนต์เบื้องหลังที่สร้างจากการรันเอเจนต์ที่มีอยู่
เอเจนต์ย่อยจะทำงานในเซสชันของตนเอง (`agent:<agentId>:subagent:<uuid>`) และ
เมื่อเสร็จแล้วจะ**ประกาศ**ผลลัพธ์กลับไปยังช่องแชตของผู้ร้องขอ
การรันเอเจนต์ย่อยแต่ละครั้งจะถูกติดตามเป็น
[งานเบื้องหลัง](/th/automation/tasks)

เป้าหมายหลัก:

- ทำงาน "วิจัย / งานยาว / เครื่องมือช้า" แบบขนานโดยไม่บล็อกการรันหลัก
- แยกเอเจนต์ย่อยออกจากกันโดยค่าเริ่มต้น (การแยกเซสชัน + การ sandbox แบบเลือกใช้ได้)
- ทำให้พื้นผิวเครื่องมือใช้งานผิดได้ยาก: เอเจนต์ย่อยจะ**ไม่ได้**รับเครื่องมือเซสชันโดยค่าเริ่มต้น
- รองรับความลึกของการซ้อนที่กำหนดค่าได้สำหรับรูปแบบตัวประสานงาน

<Note>
**หมายเหตุเรื่องค่าใช้จ่าย:** เอเจนต์ย่อยแต่ละตัวมีบริบทและการใช้โทเค็นของตนเองโดย
ค่าเริ่มต้น สำหรับงานหนักหรืองานที่ทำซ้ำ ให้ตั้งค่าโมเดลที่ถูกกว่าสำหรับเอเจนต์ย่อย
และให้เอเจนต์หลักใช้โมเดลคุณภาพสูงกว่า กำหนดค่าผ่าน
`agents.defaults.subagents.model` หรือการแทนที่รายเอเจนต์ เมื่อเอเจนต์ลูก
    จำเป็นต้องใช้ทรานสคริปต์ปัจจุบันของผู้ร้องขอจริงๆ เอเจนต์สามารถร้องขอ
    `context: "fork"` สำหรับการสร้างครั้งนั้นได้ เซสชันเอเจนต์ย่อยที่ผูกกับเธรดจะมีค่าเริ่มต้นเป็น
    `context: "fork"` เพราะเซสชันเหล่านั้นแตกสาขาการสนทนาปัจจุบันไปยัง
    เธรดติดตามผล
</Note>

## คำสั่งสแลช

ใช้ `/subagents` เพื่อตรวจสอบหรือควบคุมการรันเอเจนต์ย่อยสำหรับ**เซสชันปัจจุบัน**:

```text
/subagents list
/subagents kill <id|#|all>
/subagents log <id|#> [limit] [tools]
/subagents info <id|#>
/subagents send <id|#> <message>
/subagents steer <id|#> <message>
/subagents spawn <agentId> <task> [--model <model>] [--thinking <level>]
```

ใช้ [`/steer <message>`](/th/tools/steer) ระดับบนสุดเพื่อบังคับทิศทางการรันที่ใช้งานอยู่ของเซสชันผู้ร้องขอปัจจุบัน ใช้ `/subagents steer <id|#> <message>` เมื่อเป้าหมายเป็นการรันลูก

`/subagents info` แสดงเมตาดาต้าการรัน (สถานะ, ประทับเวลา, id เซสชัน,
เส้นทางทรานสคริปต์, การล้างข้อมูล) ใช้ `sessions_history` สำหรับมุมมองการเรียกคืนแบบมีขอบเขต
และกรองความปลอดภัยแล้ว; ตรวจสอบเส้นทางทรานสคริปต์บนดิสก์เมื่อคุณ
ต้องการทรานสคริปต์เต็มดิบ

### การควบคุมการผูกเธรด

คำสั่งเหล่านี้ทำงานบนช่องทางที่รองรับการผูกเธรดแบบคงอยู่
ดู [ช่องทางที่รองรับเธรด](#thread-supporting-channels) ด้านล่าง

```text
/focus <subagent-label|session-key|session-id|session-label>
/unfocus
/agents
/session idle <duration|off>
/session max-age <duration|off>
```

### พฤติกรรมการสร้าง

`/subagents spawn` เริ่มเอเจนต์ย่อยเบื้องหลังในฐานะคำสั่งผู้ใช้ (ไม่ใช่
รีเลย์ภายใน) และส่งการอัปเดตการเสร็จสิ้นสุดท้ายหนึ่งครั้งกลับไปยัง
แชตของผู้ร้องขอเมื่อการรันเสร็จสิ้น

<AccordionGroup>
  <Accordion title="Non-blocking, push-based completion">
    - คำสั่งสร้างเป็นแบบไม่บล็อก; คำสั่งจะคืน id การรันทันที
    - เมื่อเสร็จสิ้น เอเจนต์ย่อยจะประกาศข้อความสรุป/ผลลัพธ์กลับไปยังช่องแชตของผู้ร้องขอ
    - เทิร์นของเอเจนต์ที่ต้องใช้ผลลัพธ์จากลูกควรเรียก `sessions_yield` หลังจากสร้างงานที่จำเป็นแล้ว การทำเช่นนั้นจะจบเทิร์นปัจจุบันและให้เหตุการณ์การเสร็จสิ้นมาถึงเป็นข้อความถัดไปที่โมเดลมองเห็นได้
    - การเสร็จสิ้นเป็นแบบพุช เมื่อสร้างแล้ว **อย่า**โพล `/subagents list`, `sessions_list` หรือ `sessions_history` ในลูปเพียงเพื่อรอให้เสร็จ; ตรวจสอบสถานะเฉพาะเมื่อจำเป็นสำหรับการดีบักหรือการแทรกแซงเท่านั้น
    - เอาต์พุตของลูกคือรายงาน/หลักฐานให้เอเจนต์ผู้ร้องขอสังเคราะห์ ไม่ใช่ข้อความคำสั่งที่ผู้ใช้เขียน และไม่สามารถแทนที่นโยบายระบบ ผู้พัฒนา หรือผู้ใช้ได้
    - เมื่อเสร็จสิ้น OpenClaw จะพยายามอย่างดีที่สุดเพื่อปิดแท็บ/กระบวนการเบราว์เซอร์ที่ติดตามไว้ซึ่งเปิดโดยเซสชันเอเจนต์ย่อยนั้น ก่อนที่โฟลว์ล้างข้อมูลการประกาศจะดำเนินต่อ

  </Accordion>
  <Accordion title="Manual-spawn delivery resilience">
    - OpenClaw ส่งการเสร็จสิ้นกลับไปยังเซสชันผู้ร้องขอผ่านเทิร์น `agent` พร้อมคีย์ idempotency ที่เสถียร
    - หากการรันของผู้ร้องขอยังทำงานอยู่ OpenClaw จะพยายามปลุก/บังคับทิศทางการรันนั้นก่อน แทนที่จะเริ่มเส้นทางตอบกลับที่มองเห็นได้เส้นที่สอง
    - หากการส่งมอบการเสร็จสิ้นให้เอเจนต์ผู้ร้องขอล้มเหลวหรือไม่มีเอาต์พุตที่มองเห็นได้ OpenClaw จะถือว่าการส่งมอบล้มเหลวและถอยกลับไปใช้การกำหนดเส้นทาง/ลองใหม่ผ่านคิว โดยจะไม่ส่งผลลัพธ์ดิบของลูกโดยตรงไปยังแชตภายนอก
    - หากไม่สามารถใช้การส่งมอบโดยตรงได้ จะถอยกลับไปใช้การกำหนดเส้นทางผ่านคิว
    - หากการกำหนดเส้นทางผ่านคิวยังไม่พร้อมใช้งาน การประกาศจะถูกลองใหม่ด้วย exponential backoff สั้นๆ ก่อนยอมแพ้ในที่สุด
    - การส่งมอบการเสร็จสิ้นจะคงเส้นทางผู้ร้องขอที่แก้ไขแล้วไว้: เส้นทางการเสร็จสิ้นที่ผูกกับเธรดหรือผูกกับการสนทนาจะชนะเมื่อพร้อมใช้งาน; หากต้นทางการเสร็จสิ้นให้มาเพียงช่องทาง OpenClaw จะเติมเป้าหมาย/บัญชีที่ขาดหายจากเส้นทางที่แก้ไขแล้วของเซสชันผู้ร้องขอ (`lastChannel` / `lastTo` / `lastAccountId`) เพื่อให้การส่งมอบโดยตรงยังทำงานได้

  </Accordion>
  <Accordion title="Completion handoff metadata">
    การส่งต่อการเสร็จสิ้นไปยังเซสชันผู้ร้องขอเป็นบริบทภายในที่สร้างโดยรันไทม์
    (ไม่ใช่ข้อความที่ผู้ใช้เขียน) และประกอบด้วย:

    - `Result` — ข้อความตอบกลับ `assistant` ที่มองเห็นได้ล่าสุด หรือมิฉะนั้นเป็นข้อความเครื่องมือ/toolResult ล่าสุดที่ทำให้ปลอดภัยแล้ว การรันที่ล้มเหลวแบบสิ้นสุดจะไม่นำข้อความตอบกลับที่จับไว้กลับมาใช้
    - `Status` — `completed successfully` / `failed` / `timed out` / `unknown`
    - สถิติรันไทม์/โทเค็นแบบย่อ
    - คำสั่งการส่งมอบที่บอกเอเจนต์ผู้ร้องขอให้เขียนใหม่ด้วยเสียงผู้ช่วยปกติ (ไม่ส่งต่อเมตาดาต้าภายในดิบ)

  </Accordion>
  <Accordion title="Modes and ACP runtime">
    - `--model` และ `--thinking` แทนที่ค่าเริ่มต้นสำหรับการรันนั้นโดยเฉพาะ
    - ใช้ `info`/`log` เพื่อตรวจสอบรายละเอียดและเอาต์พุตหลังเสร็จสิ้น
    - `/subagents spawn` เป็นโหมดครั้งเดียว (`mode: "run"`) สำหรับเซสชันแบบคงอยู่ที่ผูกกับเธรด ให้ใช้ `sessions_spawn` พร้อม `thread: true` และ `mode: "session"`
    - สำหรับเซสชันชุดทดสอบ ACP (Claude Code, Gemini CLI, OpenCode หรือ Codex ACP/acpx แบบชัดเจน) ให้ใช้ `sessions_spawn` พร้อม `runtime: "acp"` เมื่อเครื่องมือประกาศรันไทม์นั้น ดู [โมเดลการส่งมอบ ACP](/th/tools/acp-agents#delivery-model) เมื่อดีบักการเสร็จสิ้นหรือลูปเอเจนต์ต่อเอเจนต์ เมื่อเปิดใช้งาน Plugin `codex` การควบคุมแชต/เธรดของ Codex ควรเลือกใช้ `/codex ...` แทน ACP เว้นแต่ผู้ใช้จะขอ ACP/acpx อย่างชัดเจน
    - OpenClaw ซ่อน `runtime: "acp"` จนกว่าจะเปิดใช้งาน ACP, ผู้ร้องขอไม่ได้อยู่ใน sandbox และมีการโหลด Plugin แบ็กเอนด์ เช่น `acpx` แล้ว `runtime: "acp"` คาดหวัง id ชุดทดสอบ ACP ภายนอก หรือรายการ `agents.list[]` ที่มี `runtime.type="acp"`; ใช้รันไทม์เอเจนต์ย่อยเริ่มต้นสำหรับเอเจนต์การกำหนดค่า OpenClaw ปกติจาก `agents_list`

  </Accordion>
</AccordionGroup>

## โหมดบริบท

เอเจนต์ย่อยแบบเนทีฟจะเริ่มแบบแยกส่วน เว้นแต่ผู้เรียกจะขอ fork
ทรานสคริปต์ปัจจุบันอย่างชัดเจน

| โหมด       | เมื่อใดควรใช้                                                                                                                         | พฤติกรรม                                                                          |
| ---------- | -------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
| `isolated` | การวิจัยใหม่, การใช้งานอิสระ, งานเครื่องมือช้า หรือสิ่งใดก็ตามที่สามารถบรีฟในข้อความงานได้                           | สร้างทรานสคริปต์ลูกที่สะอาด นี่เป็นค่าเริ่มต้นและช่วยลดการใช้โทเค็น  |
| `fork`     | งานที่ขึ้นอยู่กับการสนทนาปัจจุบัน, ผลลัพธ์เครื่องมือก่อนหน้า หรือคำสั่งที่มีความละเอียดอ่อนซึ่งมีอยู่แล้วในทรานสคริปต์ของผู้ร้องขอ | แตกสาขาทรานสคริปต์ของผู้ร้องขอเข้าไปยังเซสชันลูกก่อนที่ลูกจะเริ่ม |

ใช้ `fork` เท่าที่จำเป็น ใช้สำหรับการมอบหมายงานที่ไวต่อบริบท ไม่ใช่
สิ่งทดแทนการเขียนพรอมต์งานให้ชัดเจน

## เครื่องมือ: `sessions_spawn`

เริ่มการรันเอเจนต์ย่อยด้วย `deliver: false` บนเลน `subagent` ส่วนกลาง
จากนั้นรันขั้นตอนประกาศและโพสต์คำตอบประกาศไปยังช่องแชตของผู้ร้องขอ

ความพร้อมใช้งานขึ้นอยู่กับนโยบายเครื่องมือที่มีผลจริงของผู้เรียก โปรไฟล์ `coding` และ
`full` เปิดเผย `sessions_spawn` โดยค่าเริ่มต้น โปรไฟล์ `messaging`
ไม่เปิดเผย; เพิ่ม `tools.alsoAllow: ["sessions_spawn", "sessions_yield",
"subagents"]` หรือใช้ `tools.profile: "coding"` สำหรับเอเจนต์ที่ควรมอบหมาย
งาน นโยบายอนุญาต/ปฏิเสธของช่องทาง/กลุ่ม, provider, sandbox และรายเอเจนต์
ยังสามารถลบเครื่องมือหลังขั้นตอนโปรไฟล์ได้ ใช้ `/tools` จาก
เซสชันเดียวกันเพื่อยืนยันรายการเครื่องมือที่มีผลจริง

**ค่าเริ่มต้น:**

- **โมเดล:** สืบทอดจากผู้เรียก เว้นแต่คุณตั้งค่า `agents.defaults.subagents.model` (หรือ `agents.list[].subagents.model` รายเอเจนต์); `sessions_spawn.model` ที่ระบุอย่างชัดเจนยังคงมีผลเหนือกว่า
- **Thinking:** สืบทอดจากผู้เรียก เว้นแต่คุณตั้งค่า `agents.defaults.subagents.thinking` (หรือ `agents.list[].subagents.thinking` รายเอเจนต์); `sessions_spawn.thinking` ที่ระบุอย่างชัดเจนยังคงมีผลเหนือกว่า
- **หมดเวลาการรัน:** หากละ `sessions_spawn.runTimeoutSeconds` ไว้ OpenClaw จะใช้ `agents.defaults.subagents.runTimeoutSeconds` เมื่อมีการตั้งค่า; มิฉะนั้นจะถอยกลับเป็น `0` (ไม่หมดเวลา)

### โหมดพรอมต์การมอบหมายงาน

`agents.defaults.subagents.delegationMode` ควบคุมเฉพาะแนวทางพรอมต์เท่านั้น; ไม่เปลี่ยนนโยบายเครื่องมือหรือบังคับใช้การมอบหมายงาน

- `suggest` (ค่าเริ่มต้น): คงการกระตุ้นในพรอมต์มาตรฐานให้ใช้เอเจนต์ย่อยสำหรับงานที่ใหญ่กว่าหรือช้ากว่า
- `prefer`: บอกเอเจนต์หลักให้ตอบสนองต่อเนื่องและมอบหมายสิ่งใดก็ตามที่ซับซ้อนกว่าการตอบกลับโดยตรงผ่าน `sessions_spawn`

การแทนที่รายเอเจนต์ใช้ `agents.list[].subagents.delegationMode`

```json5
{
  agents: {
    defaults: {
      subagents: {
        delegationMode: "prefer",
        maxConcurrent: 4,
      },
    },
    list: [
      {
        id: "coordinator",
        subagents: { delegationMode: "prefer" },
      },
    ],
  },
}
```

### พารามิเตอร์เครื่องมือ

<ParamField path="task" type="string" required>
  คำอธิบายงานสำหรับเอเจนต์ย่อย
</ParamField>
<ParamField path="taskName" type="string">
  ตัวระบุเสถียรแบบเลือกได้สำหรับการกำหนดเป้าหมาย `subagents` ในภายหลัง ต้องตรงกับ `[a-z][a-z0-9_]{0,63}` และต้องไม่เป็นเป้าหมายที่สงวนไว้ เช่น `last` หรือ `all` ควรใช้เมื่อผู้ประสานงานอาจต้องควบคุม หยุด หรือระบุ child เฉพาะหลังจากสร้าง children หลายตัว
</ParamField>
<ParamField path="label" type="string">
  ป้ายกำกับแบบมนุษย์อ่านได้ที่ระบุได้ตามต้องการ
</ParamField>
<ParamField path="agentId" type="string">
  สร้างภายใต้ id ของเอเจนต์อื่นเมื่อ `subagents.allowAgents` อนุญาต
</ParamField>
<ParamField path="runtime" type='"subagent" | "acp"' default="subagent">
  `acp` ใช้เฉพาะสำหรับ ACP harness ภายนอก (`claude`, `droid`, `gemini`, `opencode` หรือ Codex ACP/acpx ที่ร้องขออย่างชัดเจน) และสำหรับรายการ `agents.list[]` ที่ `runtime.type` เป็น `acp`
</ParamField>
<ParamField path="resumeSessionId" type="string">
  เฉพาะ ACP ดำเนินการต่อจากเซสชัน ACP harness ที่มีอยู่เมื่อ `runtime: "acp"`; จะถูกละเว้นสำหรับการสร้างเอเจนต์ย่อยแบบเนทีฟ
</ParamField>
<ParamField path="streamTo" type='"parent"'>
  เฉพาะ ACP สตรีมเอาต์พุตการรัน ACP ไปยังเซสชัน parent เมื่อ `runtime: "acp"`; ละไว้สำหรับการสร้างเอเจนต์ย่อยแบบเนทีฟ
</ParamField>
<ParamField path="model" type="string">
  แทนที่โมเดลของเอเจนต์ย่อย ค่าที่ไม่ถูกต้องจะถูกข้าม และเอเจนต์ย่อยจะรันบนโมเดลเริ่มต้นพร้อมคำเตือนในผลลัพธ์ของเครื่องมือ
</ParamField>
<ParamField path="thinking" type="string">
  แทนที่ระดับ thinking สำหรับการรันเอเจนต์ย่อย
</ParamField>
<ParamField path="runTimeoutSeconds" type="number">
  ค่าเริ่มต้นเป็น `agents.defaults.subagents.runTimeoutSeconds` เมื่อมีการตั้งค่า มิฉะนั้นเป็น `0` เมื่อมีการตั้งค่า การรันเอเจนต์ย่อยจะถูกยกเลิกหลังจาก N วินาที
</ParamField>
<ParamField path="thread" type="boolean" default="false">
  เมื่อเป็น `true` จะร้องขอการผูก thread ของช่องทางสำหรับเซสชันเอเจนต์ย่อยนี้
</ParamField>
<ParamField path="mode" type='"run" | "session"' default="run">
  หากละ `mode` และ `thread: true` ค่าเริ่มต้นจะกลายเป็น `session` `mode: "session"` ต้องใช้ `thread: true`
</ParamField>
<ParamField path="cleanup" type='"delete" | "keep"' default="keep">
  `"delete"` จะเก็บถาวรทันทีหลังจากประกาศ (ยังคงเก็บทรานสคริปต์ไว้ผ่านการเปลี่ยนชื่อ)
</ParamField>
<ParamField path="sandbox" type='"inherit" | "require"' default="inherit">
  `require` จะปฏิเสธการสร้าง เว้นแต่รันไทม์ child เป้าหมายจะอยู่ใน sandbox
</ParamField>
<ParamField path="context" type='"isolated" | "fork"' default="isolated">
  `fork` จะแยกทรานสคริปต์ปัจจุบันของผู้ร้องขอไปยังเซสชัน child เฉพาะเอเจนต์ย่อยแบบเนทีฟ การสร้างที่ผูกกับ thread มีค่าเริ่มต้นเป็น `fork`; การสร้างที่ไม่ใช่ thread มีค่าเริ่มต้นเป็น `isolated`
</ParamField>

<Warning>
`sessions_spawn` **ไม่** รับพารามิเตอร์การส่งผ่านช่องทาง (`target`,
`channel`, `to`, `threadId`, `replyTo`, `transport`) สำหรับการส่ง ให้ใช้
`message`/`sessions_send` จากการรันที่สร้างขึ้น
</Warning>

### ชื่องานและการกำหนดเป้าหมาย

`taskName` เป็นตัวระบุที่โมเดลเห็นสำหรับการจัดการ orchestration ไม่ใช่คีย์เซสชัน
ใช้สำหรับชื่อ child ที่เสถียร เช่น `review_subagents`,
`linux_validation` หรือ `docs_update` เมื่อผู้ประสานงานอาจต้องควบคุม
หรือหยุด child นั้นในภายหลัง

การแก้เป้าหมายยอมรับการตรงกันแบบชัดเจนกับ `taskName` และ prefix
ที่ไม่กำกวม การจับคู่ถูกจำกัดอยู่ในหน้าต่างเป้าหมาย active/recent เดียวกับที่ใช้
โดยเป้าหมาย `/subagents` แบบหมายเลข ดังนั้น child ที่เสร็จสิ้นไปนานแล้วจะไม่ทำให้
ตัวระบุที่นำกลับมาใช้ใหม่กำกวม หาก children ที่ active หรือ recent สองตัวใช้
`taskName` เดียวกัน เป้าหมายจะกำกวม ให้ใช้ดัชนีรายการ คีย์เซสชัน หรือ
run id แทน

เป้าหมายที่สงวนไว้ `last` และ `all` ไม่ใช่ค่า `taskName` ที่ถูกต้อง
เพราะมีความหมายด้านการควบคุมอยู่แล้ว

## เครื่องมือ: `sessions_yield`

สิ้นสุดเทิร์นโมเดลปัจจุบันและรออีเวนต์รันไทม์ โดยหลักคืออีเวนต์
การเสร็จสิ้นของเอเจนต์ย่อย ให้มาถึงเป็นข้อความถัดไป ใช้หลังจาก
สร้างงาน child ที่จำเป็นเมื่อผู้ร้องขอไม่สามารถให้คำตอบสุดท้าย
ได้จนกว่า completion เหล่านั้นจะมาถึง

`sessions_yield` เป็น primitive สำหรับการรอ อย่าแทนที่ด้วยลูป polling
ผ่าน `subagents`, `sessions_list`, `sessions_history`, shell
`sleep` หรือการ polling process เพียงเพื่อตรวจจับการเสร็จสิ้นของ child

ใช้ `sessions_yield` เฉพาะเมื่อรายการเครื่องมือที่มีผลของเซสชันมีเครื่องมือนี้
โปรไฟล์เครื่องมือแบบ minimal หรือ custom บางรายการอาจเปิดเผย `sessions_spawn` และ
`subagents` โดยไม่เปิดเผย `sessions_yield`; ในกรณีนั้น อย่าประดิษฐ์
ลูป polling เพียงเพื่อรอการเสร็จสิ้น

เมื่อมี children ที่ active อยู่ OpenClaw จะใส่บล็อกพรอมป์
`Active Subagents` ขนาดกะทัดรัดที่รันไทม์สร้างขึ้นลงในเทิร์นปกติ เพื่อให้ผู้ร้องขอเห็น
เซสชัน child ปัจจุบัน, run ids, สถานะ, ป้ายกำกับ, งาน และ
นามแฝง `taskName` โดยไม่ต้อง polling ฟิลด์งานและป้ายกำกับใน
บล็อกนั้นถูก quote เป็นข้อมูล ไม่ใช่คำสั่ง เพราะอาจมีต้นทาง
จากอาร์กิวเมนต์การสร้างที่ผู้ใช้/โมเดลให้มา

## เครื่องมือ: `subagents`

แสดงรายการ ควบคุม หรือหยุดการรันเอเจนต์ย่อยที่ถูกสร้างและเป็นของเซสชัน
ผู้ร้องขอ ขอบเขตจำกัดอยู่ที่ผู้ร้องขอปัจจุบัน; child จะเห็น/ควบคุมได้เฉพาะ
children ที่ตัวเองควบคุมอยู่เท่านั้น

ใช้ `subagents` สำหรับสถานะตามคำขอ การดีบัก การควบคุม หรือการหยุด
ใช้ `sessions_yield` เพื่อรออีเวนต์การเสร็จสิ้น

## เซสชันที่ผูกกับ thread

เมื่อเปิดใช้การผูก thread สำหรับช่องทาง เอเจนต์ย่อยสามารถคงการผูก
กับ thread เพื่อให้ข้อความติดตามผลของผู้ใช้ใน thread นั้นยังคง route ไปยัง
เซสชันเอเจนต์ย่อยเดียวกัน

### ช่องทางที่รองรับ thread

**Discord** เป็นช่องทางเดียวที่รองรับอยู่ในขณะนี้ รองรับ
เซสชันเอเจนต์ย่อยที่ผูกกับ thread แบบถาวร (`sessions_spawn` พร้อม
`thread: true`), การควบคุม thread แบบ manual (`/focus`, `/unfocus`, `/agents`,
`/session idle`, `/session max-age`) และคีย์ adapter
`channels.discord.threadBindings.enabled`,
`channels.discord.threadBindings.idleHours`,
`channels.discord.threadBindings.maxAgeHours` และ
`channels.discord.threadBindings.spawnSessions`

### ลำดับอย่างย่อ

<Steps>
  <Step title="สร้าง">
    `sessions_spawn` พร้อม `thread: true` (และเลือกใส่ `mode: "session"` ได้)
  </Step>
  <Step title="ผูก">
    OpenClaw สร้างหรือผูก thread กับเป้าหมายเซสชันนั้นในช่องทางที่ active
  </Step>
  <Step title="Route ข้อความติดตามผล">
    การตอบกลับและข้อความติดตามผลใน thread นั้นจะ route ไปยังเซสชันที่ผูกไว้
  </Step>
  <Step title="ตรวจสอบ timeout">
    ใช้ `/session idle` เพื่อตรวจสอบ/อัปเดต auto-unfocus เมื่อไม่มีการใช้งาน และ
    `/session max-age` เพื่อควบคุมเพดานแบบ hard cap
  </Step>
  <Step title="แยกออก">
    ใช้ `/unfocus` เพื่อแยกออกด้วยตนเอง
  </Step>
</Steps>

### การควบคุมแบบ manual

| คำสั่ง            | ผลลัพธ์                                                                |
| ------------------ | --------------------------------------------------------------------- |
| `/focus <target>`  | ผูก thread ปัจจุบัน (หรือสร้างใหม่) กับเป้าหมายเอเจนต์ย่อย/เซสชัน |
| `/unfocus`         | ลบการผูกสำหรับ thread ปัจจุบันที่ถูกผูกไว้                       |
| `/agents`          | แสดงรายการการรันที่ active และสถานะการผูก (`thread:<id>` หรือ `unbound`)       |
| `/session idle`    | ตรวจสอบ/อัปเดต auto-unfocus เมื่อ idle (เฉพาะ thread ที่ผูกและโฟกัสอยู่)         |
| `/session max-age` | ตรวจสอบ/อัปเดต hard cap (เฉพาะ thread ที่ผูกและโฟกัสอยู่)                  |

### สวิตช์การกำหนดค่า

- **ค่าเริ่มต้นแบบ global:** `session.threadBindings.enabled`, `session.threadBindings.idleHours`, `session.threadBindings.maxAgeHours`
- **คีย์ override ของช่องทางและ auto-bind เมื่อสร้าง** เป็นแบบเฉพาะ adapter ดู [ช่องทางที่รองรับ thread](#thread-supporting-channels) ด้านบน

ดู [เอกสารอ้างอิงการกำหนดค่า](/th/gateway/configuration-reference) และ
[คำสั่ง slash](/th/tools/slash-commands) สำหรับรายละเอียด adapter ปัจจุบัน

### Allowlist

<ParamField path="agents.list[].subagents.allowAgents" type="string[]">
  รายการ id ของเอเจนต์ที่สามารถกำหนดเป้าหมายผ่าน `agentId` แบบชัดเจน (`["*"]` อนุญาตทุกตัว) ค่าเริ่มต้น: เฉพาะเอเจนต์ผู้ร้องขอ หากคุณตั้งค่ารายการและยังต้องการให้ผู้ร้องขอสร้างตัวเองด้วย `agentId` ให้ใส่ id ของผู้ร้องขอไว้ในรายการ
</ParamField>
<ParamField path="agents.defaults.subagents.allowAgents" type="string[]">
  allowlist ของเอเจนต์เป้าหมายเริ่มต้นที่ใช้เมื่อเอเจนต์ผู้ร้องขอไม่ได้ตั้งค่า `subagents.allowAgents` ของตัวเอง
</ParamField>
<ParamField path="agents.defaults.subagents.requireAgentId" type="boolean" default="false">
  บล็อกการเรียก `sessions_spawn` ที่ละ `agentId` (บังคับให้เลือกโปรไฟล์อย่างชัดเจน) การ override รายเอเจนต์: `agents.list[].subagents.requireAgentId`
</ParamField>
<ParamField path="agents.defaults.subagents.announceTimeoutMs" type="number" default="120000">
  timeout ต่อการเรียกสำหรับความพยายามส่งประกาศ `agent` ของ Gateway ค่าเป็นจำนวนเต็มบวกในหน่วยมิลลิวินาที และถูก clamp ให้อยู่ในค่าสูงสุดของ timer ที่ปลอดภัยต่อแพลตฟอร์ม การลองใหม่แบบ transient อาจทำให้เวลารอประกาศทั้งหมดนานกว่า timeout ที่กำหนดไว้หนึ่งครั้ง
</ParamField>

หากเซสชันผู้ร้องขออยู่ใน sandbox, `sessions_spawn` จะปฏิเสธเป้าหมาย
ที่จะรันโดยไม่อยู่ใน sandbox

### การค้นพบ

ใช้ `agents_list` เพื่อดูว่า id ของเอเจนต์ใดได้รับอนุญาตสำหรับ
`sessions_spawn` อยู่ในขณะนี้ การตอบกลับรวมโมเดลที่มีผลของเอเจนต์แต่ละตัวที่แสดง
และ metadata รันไทม์ที่ฝังไว้ เพื่อให้ผู้เรียกแยกความแตกต่างระหว่าง PI, Codex
app-server และรันไทม์เนทีฟอื่น ๆ ที่กำหนดค่าไว้ได้

### การเก็บถาวรอัตโนมัติ

- เซสชันเอเจนต์ย่อยจะถูกเก็บถาวรโดยอัตโนมัติหลังจาก `agents.defaults.subagents.archiveAfterMinutes` (ค่าเริ่มต้น `60`)
- การเก็บถาวรใช้ `sessions.delete` และเปลี่ยนชื่อทรานสคริปต์เป็น `*.deleted.<timestamp>` (โฟลเดอร์เดียวกัน)
- `cleanup: "delete"` จะเก็บถาวรทันทีหลังจากประกาศ (ยังคงเก็บทรานสคริปต์ไว้ผ่านการเปลี่ยนชื่อ)
- การเก็บถาวรอัตโนมัติเป็นแบบ best-effort; timer ที่ค้างอยู่จะหายไปหาก Gateway รีสตาร์ท
- `runTimeoutSeconds` **ไม่** เก็บถาวรอัตโนมัติ; มันเพียงหยุดการรัน เซสชันยังคงอยู่จนกว่าจะถูกเก็บถาวรอัตโนมัติ
- การเก็บถาวรอัตโนมัติใช้กับเซสชัน depth-1 และ depth-2 เท่า ๆ กัน
- การล้างข้อมูลเบราว์เซอร์แยกจากการล้างข้อมูลเก็บถาวร: แท็บ/กระบวนการเบราว์เซอร์ที่ติดตามจะถูกปิดแบบ best-effort เมื่อการรันเสร็จสิ้น แม้ว่าจะเก็บระเบียนทรานสคริปต์/เซสชันไว้ก็ตาม

## เอเจนต์ย่อยแบบซ้อน

โดยค่าเริ่มต้น เอเจนต์ย่อยไม่สามารถสร้างเอเจนต์ย่อยของตัวเองได้
(`maxSpawnDepth: 1`) ตั้งค่า `maxSpawnDepth: 2` เพื่อเปิดใช้การซ้อนหนึ่งระดับ
ซึ่งคือ **รูปแบบ orchestrator**: main → orchestrator sub-agent →
worker sub-sub-agents

```json5
{
  agents: {
    defaults: {
      subagents: {
        maxSpawnDepth: 2, // allow sub-agents to spawn children (default: 1)
        maxChildrenPerAgent: 5, // max active children per agent session (default: 5)
        maxConcurrent: 8, // global concurrency lane cap (default: 8)
        runTimeoutSeconds: 900, // default timeout for sessions_spawn when omitted (0 = no timeout)
        announceTimeoutMs: 120000, // per-call gateway announce timeout
      },
    },
  },
}
```

### ระดับความลึก

| ความลึก | รูปแบบคีย์เซสชัน                            | บทบาท                                          | สร้างได้หรือไม่                   |
| ----- | -------------------------------------------- | --------------------------------------------- | ---------------------------- |
| 0     | `agent:<id>:main`                            | เอเจนต์หลัก                                    | เสมอ                       |
| 1     | `agent:<id>:subagent:<uuid>`                 | เอเจนต์ย่อย (orchestrator เมื่ออนุญาต depth 2) | เฉพาะเมื่อ `maxSpawnDepth >= 2` |
| 2     | `agent:<id>:subagent:<uuid>:subagent:<uuid>` | เอเจนต์ย่อยของเอเจนต์ย่อย (leaf worker)                   | ไม่เคย                        |

### สายประกาศ

ผลลัพธ์ไหลย้อนกลับขึ้นไปตามสาย:

1. worker depth-2 เสร็จสิ้น → ประกาศไปยัง parent ของมัน (orchestrator depth-1)
2. orchestrator depth-1 ได้รับประกาศ สังเคราะห์ผลลัพธ์ เสร็จสิ้น → ประกาศไปยัง main
3. เอเจนต์ main ได้รับประกาศและส่งต่อให้ผู้ใช้

แต่ละระดับจะเห็นเฉพาะประกาศจาก children โดยตรงของตัวเอง

<Note>
**คำแนะนำด้านการปฏิบัติงาน:** เริ่มงานลูกหนึ่งครั้งแล้วรอเหตุการณ์การเสร็จสิ้น แทนที่จะสร้างลูป polling รอบ `sessions_list`, `sessions_history`, `/subagents list` หรือคำสั่ง `exec` sleep
`sessions_list` และ `/subagents list` รักษาความสัมพันธ์ของเซสชันลูกให้มุ่งเน้นที่งานที่ยังทำงานอยู่ — ลูกที่ยังทำงานอยู่จะยังคงแนบอยู่ ลูกที่สิ้นสุดแล้วจะยังมองเห็นได้ในหน้าต่างเวลาล่าสุดช่วงสั้น ๆ และลิงก์ลูกแบบ store-only ที่ค้างจะถูกละเว้นหลังพ้นหน้าต่างความสดใหม่ วิธีนี้ป้องกันไม่ให้เมตาดาต้า `spawnedBy` / `parentSessionKey` เก่าปลุกลูกเงาขึ้นมาใหม่หลังรีสตาร์ท หากเหตุการณ์การเสร็จสิ้นของลูกมาถึงหลังจากคุณส่งคำตอบสุดท้ายไปแล้ว การติดตามผลที่ถูกต้องคือโทเค็นเงียบแบบตรงตัว `NO_REPLY` / `no_reply`
</Note>

### นโยบายเครื่องมือตามความลึก

- บทบาทและขอบเขตการควบคุมจะถูกเขียนลงในเมตาดาต้าเซสชันตอน spawn ซึ่งช่วยป้องกันไม่ให้คีย์เซสชันแบบแบนหรือที่กู้คืนมาได้รับสิทธิ์ orchestrator กลับมาโดยไม่ตั้งใจ
- **ความลึก 1 (orchestrator, เมื่อ `maxSpawnDepth >= 2`):** ได้รับ `sessions_spawn`, `subagents`, `sessions_list`, `sessions_history` เพื่อให้จัดการลูกของตนได้ เครื่องมือเซสชัน/ระบบอื่น ๆ ยังคงถูกปฏิเสธ
- **ความลึก 1 (leaf, เมื่อ `maxSpawnDepth == 1`):** ไม่มีเครื่องมือเซสชัน (พฤติกรรมเริ่มต้นปัจจุบัน)
- **ความลึก 2 (leaf worker):** ไม่มีเครื่องมือเซสชัน — `sessions_spawn` จะถูกปฏิเสธเสมอที่ความลึก 2 ไม่สามารถ spawn ลูกเพิ่มเติมได้

### ขีดจำกัดการ spawn ต่อเอเจนต์

แต่ละเซสชันเอเจนต์ (ที่ความลึกใดก็ได้) สามารถมีลูกที่ทำงานอยู่ได้สูงสุด `maxChildrenPerAgent`
(ค่าเริ่มต้น `5`) ต่อครั้ง วิธีนี้ป้องกันการกระจายงานแบบ runaway
จาก orchestrator ตัวเดียว

### การหยุดแบบ cascade

การหยุด orchestrator ความลึก 1 จะหยุดลูกความลึก 2 ทั้งหมดของมันโดยอัตโนมัติ:

- `/stop` ในแชตหลักจะหยุดเอเจนต์ความลึก 1 ทั้งหมดและ cascade ไปยังลูกความลึก 2 ของพวกมัน
- `/subagents kill <id>` หยุดซับเอเจนต์ที่ระบุและ cascade ไปยังลูกของมัน
- `/subagents kill all` หยุดซับเอเจนต์ทั้งหมดของผู้ร้องขอและ cascade

## การยืนยันตัวตน

การยืนยันตัวตนของซับเอเจนต์ถูก resolve ด้วย **รหัสเอเจนต์** ไม่ใช่ด้วยประเภทเซสชัน:

- คีย์เซสชันซับเอเจนต์คือ `agent:<agentId>:subagent:<uuid>`
- auth store ถูกโหลดจาก `agentDir` ของเอเจนต์นั้น
- โปรไฟล์ auth ของเอเจนต์หลักจะถูกผสานเข้าเป็น **fallback**; โปรไฟล์เอเจนต์จะ override โปรไฟล์หลักเมื่อมีความขัดแย้ง

การผสานเป็นแบบเพิ่มเข้าไป ดังนั้นโปรไฟล์หลักจึงพร้อมใช้งานเป็น
fallback เสมอ การยืนยันตัวตนที่แยกขาดอย่างสมบูรณ์ต่อเอเจนต์ยังไม่รองรับ

## ประกาศ

ซับเอเจนต์รายงานกลับผ่านขั้นตอนประกาศ:

- ขั้นตอนประกาศทำงานภายในเซสชันซับเอเจนต์ (ไม่ใช่เซสชันผู้ร้องขอ)
- หากซับเอเจนต์ตอบกลับว่า `ANNOUNCE_SKIP` แบบตรงตัว จะไม่มีการโพสต์สิ่งใด
- หากข้อความ assistant ล่าสุดเป็นโทเค็นเงียบแบบตรงตัว `NO_REPLY` / `no_reply` เอาต์พุตประกาศจะถูกระงับ แม้เคยมีความคืบหน้าที่มองเห็นได้ก่อนหน้า

การส่งมอบขึ้นอยู่กับความลึกของผู้ร้องขอ:

- เซสชันผู้ร้องขอระดับบนใช้การเรียก `agent` แบบ follow-up พร้อมการส่งมอบภายนอก (`deliver=true`)
- เซสชันซับเอเจนต์ผู้ร้องขอที่ซ้อนกันจะได้รับการฉีด follow-up ภายใน (`deliver=false`) เพื่อให้ orchestrator สามารถสังเคราะห์ผลลัพธ์ลูกภายในเซสชันได้
- หากเซสชันซับเอเจนต์ผู้ร้องขอที่ซ้อนกันหายไป OpenClaw จะ fallback ไปยังผู้ร้องขอของเซสชันนั้นเมื่อพร้อมใช้งาน

สำหรับเซสชันผู้ร้องขอระดับบน การส่งมอบโดยตรงในโหมดเสร็จสิ้นจะ resolve
เส้นทาง conversation/thread ที่ผูกไว้และ hook override ก่อน จากนั้นเติม
ฟิลด์ channel-target ที่ขาดจากเส้นทางที่เก็บไว้ของเซสชันผู้ร้องขอ
วิธีนี้ทำให้การเสร็จสิ้นไปถึงแชต/หัวข้อที่ถูกต้อง แม้ต้นทางการเสร็จสิ้น
จะระบุเพียงช่องทางเท่านั้น

การรวมผลการเสร็จสิ้นของลูกถูกจำกัดขอบเขตไว้ที่รันของผู้ร้องขอปัจจุบันเมื่อ
สร้าง findings การเสร็จสิ้นแบบซ้อน ป้องกันไม่ให้เอาต์พุตลูกจากรันก่อนหน้า
ที่ค้างรั่วเข้าสู่ประกาศปัจจุบัน การตอบกลับประกาศจะรักษา routing
ของ thread/topic เมื่อมีอยู่บน channel adapters

### บริบทประกาศ

บริบทประกาศถูกทำให้เป็นมาตรฐานเป็นบล็อกเหตุการณ์ภายในที่เสถียร:

| ฟิลด์          | แหล่งที่มา                                                                                                        |
| -------------- | ------------------------------------------------------------------------------------------------------------- |
| แหล่งที่มา         | `subagent` หรือ `cron`                                                                                          |
| รหัสเซสชัน    | คีย์/รหัสเซสชันลูก                                                                                          |
| ประเภท           | ประเภทประกาศ + ป้ายกำกับงาน                                                                                    |
| สถานะ         | ได้มาจากผลลัพธ์รันไทม์ (`success`, `error`, `timeout` หรือ `unknown`) — **ไม่ได้** อนุมานจากข้อความโมเดล |
| เนื้อหาผลลัพธ์ | ข้อความ assistant ที่มองเห็นได้ล่าสุด มิฉะนั้นเป็นข้อความ tool/toolResult ล่าสุดที่ผ่านการ sanitize                                |
| การติดตามผล      | คำแนะนำที่อธิบายว่าเมื่อใดควรตอบกลับและเมื่อใดควรเงียบ                                                           |

รันที่ล้มเหลวแบบ terminal จะรายงานสถานะล้มเหลวโดยไม่เล่นซ้ำข้อความตอบกลับ
ที่จับไว้ เมื่อ timeout หากลูกผ่านได้เพียงการเรียกเครื่องมือ ประกาศสามารถ
ยุบประวัตินั้นให้เป็นสรุปความคืบหน้าบางส่วนแบบสั้น แทนที่จะเล่นซ้ำเอาต์พุต
เครื่องมือดิบ

### บรรทัดสถิติ

payload ประกาศมีบรรทัดสถิติที่ท้ายสุด (แม้เมื่อถูก wrap):

- รันไทม์ (เช่น `runtime 5m12s`)
- การใช้โทเค็น (input/output/total)
- ค่าใช้จ่ายโดยประมาณเมื่อมีการตั้งค่าราคาโมเดล (`models.providers.*.models[].cost`)
- `sessionKey`, `sessionId` และเส้นทาง transcript เพื่อให้เอเจนต์หลักดึงประวัติผ่าน `sessions_history` หรือตรวจไฟล์บนดิสก์ได้

เมตาดาต้าภายในมีไว้สำหรับ orchestration เท่านั้น; การตอบกลับที่ผู้ใช้เห็น
ควรถูกเขียนใหม่ด้วยน้ำเสียง assistant ปกติ

### เหตุผลที่ควรเลือก `sessions_history`

`sessions_history` เป็นเส้นทาง orchestration ที่ปลอดภัยกว่า:

- การเรียกคืนของ assistant ถูกทำให้เป็นมาตรฐานก่อน: ลบ thinking tags; ลบ scaffolding `<relevant-memories>` / `<relevant_memories>`; ลบบล็อก payload XML การเรียกเครื่องมือแบบข้อความล้วน (`<tool_call>`, `<function_call>`, `<tool_calls>`, `<function_calls>`) รวมถึง payload ที่ถูกตัดและปิดไม่สมบูรณ์; ลบ scaffolding การเรียก/ผลลัพธ์เครื่องมือที่ถูกลดระดับและ marker บริบทประวัติ; ลบโทเค็นควบคุมโมเดลที่รั่ว (`<|assistant|>`, ASCII `<|...|>` อื่น ๆ, แบบ full-width `<｜...｜>`); ลบ XML การเรียกเครื่องมือ MiniMax ที่ malformed
- ข้อความที่คล้าย credential/token จะถูก redact
- บล็อกยาวสามารถถูกตัดให้สั้นลงได้
- ประวัติขนาดใหญ่มากสามารถทิ้งแถวเก่ากว่าหรือแทนที่แถวที่ใหญ่เกินด้วย `[sessions_history omitted: message too large]`
- การตรวจ transcript ดิบบนดิสก์เป็น fallback เมื่อคุณต้องการ transcript แบบ byte-for-byte เต็มรูปแบบ

## นโยบายเครื่องมือ

ซับเอเจนต์ใช้โปรไฟล์และ pipeline นโยบายเครื่องมือเดียวกับพาเรนต์หรือ
เอเจนต์เป้าหมายก่อน หลังจากนั้น OpenClaw จะใช้ชั้นข้อจำกัดของซับเอเจนต์

เมื่อไม่มี `tools.profile` ที่จำกัด ซับเอเจนต์จะได้รับ **เครื่องมือทั้งหมด ยกเว้น
เครื่องมือเซสชัน** และเครื่องมือระบบ:

- `sessions_list`
- `sessions_history`
- `sessions_send`
- `sessions_spawn`

`sessions_history` ยังคงเป็นมุมมองการเรียกคืนที่มีขอบเขตและผ่านการ sanitize ที่นี่ด้วย —
ไม่ใช่การ dump transcript ดิบ

เมื่อ `maxSpawnDepth >= 2` ซับเอเจนต์ orchestrator ความลึก 1 จะได้รับ
`sessions_spawn`, `subagents`, `sessions_list` และ
`sessions_history` เพิ่มเติม เพื่อให้จัดการลูกของตนได้

### Override ผ่าน config

```json5
{
  agents: {
    defaults: {
      subagents: {
        maxConcurrent: 1,
      },
    },
  },
  tools: {
    subagents: {
      tools: {
        // deny wins
        deny: ["gateway", "cron"],
        // if allow is set, it becomes allow-only (deny still wins)
        // allow: ["read", "exec", "process"]
      },
    },
  },
}
```

`tools.subagents.tools.allow` เป็นตัวกรอง allow-only ขั้นสุดท้าย มันสามารถจำกัด
ชุดเครื่องมือที่ resolve แล้วให้แคบลง แต่ไม่สามารถ **เพิ่มกลับ** เครื่องมือที่ถูกลบ
โดย `tools.profile` ได้ ตัวอย่างเช่น `tools.profile: "coding"` มี
`web_search`/`web_fetch` แต่ไม่มีเครื่องมือ `browser` หากต้องการให้
ซับเอเจนต์โปรไฟล์ coding ใช้ browser automation ได้ ให้เพิ่ม browser ที่
ขั้นโปรไฟล์:

```json5
{
  tools: {
    profile: "coding",
    alsoAllow: ["browser"],
  },
}
```

ใช้ `agents.list[].tools.alsoAllow: ["browser"]` ต่อเอเจนต์ เมื่อมีเพียงเอเจนต์เดียว
ที่ควรได้รับ browser automation

## Concurrency

ซับเอเจนต์ใช้ queue lane เฉพาะภายใน process:

- **ชื่อ lane:** `subagent`
- **Concurrency:** `agents.defaults.subagents.maxConcurrent` (ค่าเริ่มต้น `8`)

## Liveness และการกู้คืน

OpenClaw ไม่ถือว่าการไม่มี `endedAt` เป็นหลักฐานถาวรว่าซับเอเจนต์
ยังมีชีวิตอยู่ รันที่ยังไม่จบซึ่งเก่ากว่า stale-run window จะหยุดถูกนับ
เป็น active/pending ใน `/subagents list`, สรุปสถานะ,
descendant completion gating และการตรวจ concurrency ต่อเซสชัน

หลังจาก Gateway รีสตาร์ท รันที่กู้คืนมาแบบค้างและยังไม่จบจะถูกตัดออก เว้นแต่
เซสชันลูกของมันถูกทำเครื่องหมาย `abortedLastRun: true` เซสชันลูกที่ถูก
ยกเลิกจากการรีสตาร์ทเหล่านั้นยังคงกู้คืนได้ผ่าน flow การกู้คืน orphan ของซับเอเจนต์
ซึ่งจะส่งข้อความ resume สังเคราะห์ก่อนล้าง marker ที่ถูกยกเลิก

การกู้คืนอัตโนมัติหลังรีสตาร์ทมีขอบเขตต่อเซสชันลูก หากลูกซับเอเจนต์เดียวกัน
ถูกยอมรับให้กู้คืน orphan ซ้ำ ๆ ภายใน rapid re-wedge window
OpenClaw จะคง tombstone การกู้คืนไว้บนเซสชันนั้นและหยุด auto-resume
ในการรีสตาร์ทครั้งต่อ ๆ ไป เรียกใช้
`openclaw tasks maintenance --apply` เพื่อ reconcile task record หรือ
`openclaw doctor --fix` เพื่อล้างแฟล็กการกู้คืนที่ถูกยกเลิกและค้างบน
เซสชันที่ถูก tombstone

<Note>
หากการ spawn ซับเอเจนต์ล้มเหลวด้วย Gateway `PAIRING_REQUIRED` /
`scope-upgrade` ให้ตรวจ RPC caller ก่อนแก้ไขสถานะ pairing
การประสานงาน `sessions_spawn` ภายในควรเชื่อมต่อเป็น
`client.id: "gateway-client"` พร้อม `client.mode: "backend"` ผ่าน
auth แบบ direct loopback shared-token/password; เส้นทางนั้นไม่ขึ้นกับ
scope baseline ของอุปกรณ์ที่จับคู่ของ CLI ผู้เรียกจากระยะไกล,
`deviceIdentity` แบบชัดเจน, เส้นทาง device-token แบบชัดเจน และไคลเอนต์ browser/node
ยังคงต้องได้รับการอนุมัติอุปกรณ์ตามปกติสำหรับ scope upgrades
</Note>

## การหยุด

- การส่ง `/stop` ในแชตผู้ร้องขอจะ abort เซสชันผู้ร้องขอและหยุดรันซับเอเจนต์ที่ยังทำงานอยู่ซึ่ง spawn จากเซสชันนั้น พร้อม cascade ไปยังลูกที่ซ้อนอยู่
- `/subagents kill <id>` หยุดซับเอเจนต์ที่ระบุและ cascade ไปยังลูกของมัน

## ข้อจำกัด

- การประกาศของซับเอเจนต์เป็นแบบ **best-effort** หาก gateway รีสตาร์ท งาน "announce back" ที่ pending จะหายไป
- ซับเอเจนต์ยังคงใช้ทรัพยากร process ของ gateway เดียวกัน; ให้ถือว่า `maxConcurrent` เป็นวาล์วนิรภัย
- `sessions_spawn` เป็นแบบ non-blocking เสมอ: ส่งคืน `{ status: "accepted", runId, childSessionKey }` ทันที
- บริบทซับเอเจนต์ฉีดเฉพาะ `AGENTS.md`, `TOOLS.md`, `SOUL.md`, `IDENTITY.md` และ `USER.md` (ไม่มี `MEMORY.md`, `HEARTBEAT.md` หรือ `BOOTSTRAP.md`)
- ความลึกการซ้อนสูงสุดคือ 5 (`maxSpawnDepth` range: 1–5) แนะนำให้ใช้ความลึก 2 สำหรับกรณีใช้งานส่วนใหญ่
- `maxChildrenPerAgent` จำกัดจำนวนลูกที่ทำงานอยู่ต่อเซสชัน (ค่าเริ่มต้น `5`, range `1–20`)

## ที่เกี่ยวข้อง

- [เอเจนต์ ACP](/th/tools/acp-agents)
- [ส่งเอเจนต์](/th/tools/agent-send)
- [งานเบื้องหลัง](/th/automation/tasks)
- [เครื่องมือแซนด์บ็อกซ์หลายเอเจนต์](/th/tools/multi-agent-sandbox-tools)
