Plugin maintainer reference

โครงสร้างภายในสถาปัตยกรรม Plugin

สำหรับโมเดลความสามารถสาธารณะ รูปแบบ Plugin และสัญญาความเป็นเจ้าของ/การดำเนินการ โปรดดู สถาปัตยกรรม Plugin หน้านี้ครอบคลุมกลไกภายใน ได้แก่ ไปป์ไลน์การโหลด รีจิสทรี ฮุกขณะรัน เส้นทาง HTTP ของ Gateway พาธการนำเข้า และตารางสคีมา

ไปป์ไลน์การโหลด

เมื่อเริ่มทำงาน OpenClaw จะดำเนินการโดยคร่าว ๆ ดังนี้:

  1. ค้นหารูท Plugin ที่อาจเป็นไปได้
  2. อ่านแมนิเฟสต์บันเดิลแบบเนทีฟหรือที่เข้ากันได้และข้อมูลเมตาของแพ็กเกจ
  3. ปฏิเสธรายการที่ไม่ปลอดภัย
  4. ปรับการกำหนดค่า Plugin ให้เป็นมาตรฐาน (plugins.enabled, allow, deny, entries, slots, load.paths)
  5. ตัดสินใจว่าจะเปิดใช้งานแต่ละรายการหรือไม่
  6. โหลดโมดูลเนทีฟที่เปิดใช้งาน: โมดูลที่บันเดิลและสร้างไว้แล้วใช้ตัวโหลดเนทีฟ; ซอร์ส TypeScript ในเครื่องจากบุคคลที่สามใช้ Jiti เป็นทางเลือกฉุกเฉิน
  7. เรียกฮุก register(api) แบบเนทีฟและรวบรวมการลงทะเบียนไว้ในรีจิสทรี Plugin
  8. เปิดให้คำสั่งและพื้นผิวขณะรันเข้าถึงรีจิสทรี

ด่านตรวจความปลอดภัยทำงาน ก่อน การดำเนินการขณะรัน การค้นหาจะบล็อกรายการเมื่อ:

  • จุดเข้าใช้งานที่แก้ไขพาธแล้วออกนอกรูท Plugin
  • พาธ (หรือไดเรกทอรีรูท) เปิดให้ทุกคนเขียนได้
  • สำหรับ Plugin ที่ไม่ได้บันเดิล ความเป็นเจ้าของพาธไม่ตรงกับ uid ปัจจุบัน (หรือ root)

สำหรับไดเรกทอรีที่บันเดิลและเปิดให้ทุกคนเขียนได้ ระบบจะลองซ่อมแซมแบบแทนที่ด้วย chmod ก่อน (การติดตั้ง npm/แบบส่วนกลางอาจจัดส่งไดเรกทอรีแพ็กเกจที่ 0777) แล้วจึงตรวจสอบด่านอีกครั้ง; ส่วนการตรวจสอบความเป็นเจ้าของจะถูกข้ามทั้งหมดสำหรับต้นทางแบบบันเดิล

รายการที่ถูกบล็อกยังคงมี id ของ Plugin อยู่ในข้อมูลวินิจฉัยที่ปล่อยออกมาเมื่อ ทราบ id ดังกล่าว (รวมถึง id ที่หาได้จากแมนิเฟสต์ภายในไดเรกทอรี ที่ถูกปฏิเสธด้วยเหตุผลอื่น) ดังนั้นการกำหนดค่าที่อ้างอิง id นั้นจะเห็น Plugin ที่ถูกบล็อกซึ่งเชื่อมโยงกับคำเตือนด้านความปลอดภัยของพาธ แทนข้อผิดพลาด "Plugin ที่ไม่รู้จัก" ซึ่งไม่เกี่ยวข้อง

ลักษณะการทำงานที่ใช้แมนิเฟสต์ก่อน

แมนิเฟสต์เป็นแหล่งข้อมูลจริงของระนาบควบคุม OpenClaw ใช้แมนิเฟสต์เพื่อ:

  • ระบุ Plugin
  • ค้นหาช่องทาง/Skills/สคีมาการกำหนดค่าหรือความสามารถของบันเดิลที่ประกาศไว้
  • ตรวจสอบ plugins.entries.<id>.config
  • เสริมป้ายกำกับ/ข้อความตัวอย่างในช่องกรอกของ Control UI
  • แสดงข้อมูลเมตาการติดตั้ง/แค็ตตาล็อก
  • เก็บตัวบ่งชี้การเปิดใช้งานและการตั้งค่าที่มีต้นทุนต่ำโดยไม่โหลดรันไทม์ของ Plugin

สำหรับ Plugin แบบเนทีฟ โมดูลรันไทม์เป็นส่วนของระนาบข้อมูล โดยลงทะเบียน ลักษณะการทำงานจริง เช่น ฮุก เครื่องมือ คำสั่ง หรือโฟลว์ของผู้ให้บริการ

บล็อก activation และ setup ที่เป็นตัวเลือกในแมนิเฟสต์จะยังอยู่ในระนาบควบคุม บล็อกเหล่านี้เป็นเพียงตัวบ่งชี้ข้อมูลเมตาสำหรับการวางแผนเปิดใช้งานและการค้นหาการตั้งค่า; ไม่ได้ใช้แทนการลงทะเบียนขณะรัน register(...) หรือ setupEntry ผู้ใช้ข้อมูลการเปิดใช้งานแบบสดจะใช้คำสั่ง ช่องทาง และคำใบ้ผู้ให้บริการจากแมนิเฟสต์เพื่อ จำกัดการโหลด Plugin ก่อนสร้างรีจิสทรีในวงกว้าง:

  • การโหลด CLI จำกัดเฉพาะ Plugin ที่เป็นเจ้าของคำสั่งหลักที่ร้องขอ
  • การตั้งค่าช่องทาง/การแก้ไข Plugin จำกัดเฉพาะ Plugin ที่เป็นเจ้าของ id ช่องทางที่ร้องขอ
  • การตั้งค่า/การแก้ไขขณะรันของผู้ให้บริการที่ระบุชัดเจน จำกัดเฉพาะ Plugin ที่เป็นเจ้าของ id ผู้ให้บริการที่ร้องขอ
  • การวางแผนเริ่มต้น Gateway ใช้ activation.onStartup สำหรับการนำเข้าเมื่อเริ่มต้น ที่ระบุชัดเจน; Plugin ที่ไม่มีข้อมูลเมตาการเริ่มต้นจะโหลดผ่านทริกเกอร์ การเปิดใช้งานที่แคบกว่าเท่านั้น

ตัววางแผนการเปิดใช้งานมีทั้ง API ที่ส่งคืนเฉพาะ id สำหรับผู้เรียกเดิม และ API แผนสำหรับการวินิจฉัย รายการในแผนรายงานเหตุผลที่เลือก Plugin โดยแยกคำใบ้ activation.* ที่ระบุชัดเจนออกจากทางเลือกสำรองตามความเป็นเจ้าของในแมนิเฟสต์:

เหตุผล (จากคำใบ้ activation.*) เหตุผล (จากความเป็นเจ้าของในแมนิเฟสต์)
activation-agent-harness-hint
activation-capability-hint
activation-channel-hint manifest-channel-owner (channels)
activation-command-hint manifest-command-alias (commandAliases)
activation-provider-hint manifest-provider-owner (providers), manifest-setup-provider-owner (setup.providers)
activation-route-hint
— (ทริกเกอร์ฮุกไม่มีรูปแบบคำใบ้) manifest-hook-owner (hooks), manifest-tool-contract (contracts.tools)

การแยกเหตุผลนี้คือขอบเขตความเข้ากันได้: ข้อมูลเมตา Plugin เดิม ยังคงทำงาน ขณะที่โค้ดใหม่ตรวจจับคำใบ้แบบกว้างหรือพฤติกรรมทางเลือกสำรองได้ โดยไม่เปลี่ยนความหมายของการโหลดขณะรัน

การโหลดรันไทม์ล่วงหน้าขณะประมวลผลคำขอที่ขอสโคป all แบบกว้าง ยังคงหา ชุด id Plugin ที่มีผลอย่างชัดเจนจากการกำหนดค่า การวางแผนเริ่มต้น ช่องทาง ที่กำหนดค่าไว้ สล็อต และกฎการเปิดใช้งานอัตโนมัติ (resolveEffectivePluginIds ใน src/plugins/effective-plugin-ids.ts) หาก ชุดที่หาได้นั้นว่างเปล่า OpenClaw จะคงสโคปให้ว่าง แทนที่จะขยายไปยัง ทุก Plugin ที่ค้นพบได้

การค้นหาการตั้งค่าจะเลือก id ที่ตัวบ่งชี้เป็นเจ้าของ เช่น setup.providers และ setup.cliBackends ก่อน เพื่อจำกัด Plugin ที่อาจเป็นไปได้ แล้วจึงใช้ setup-api เป็นทางเลือกสำรองสำหรับ Plugin ที่ยังต้องใช้ฮุกขณะรันในขั้นตอนการตั้งค่า รายการ การตั้งค่าผู้ให้บริการใช้ providerAuthChoices จากแมนิเฟสต์ ตัวเลือกการตั้งค่า ที่ได้จากตัวบ่งชี้ และข้อมูลเมตาแค็ตตาล็อกการติดตั้ง โดยไม่โหลดรันไทม์ผู้ให้บริการ ค่า setup.requiresRuntime: false ที่ระบุชัดเจนเป็นจุดตัดที่ใช้เฉพาะตัวบ่งชี้; หากละเว้น requiresRuntime ระบบจะเก็บทางเลือกสำรองของ API การตั้งค่าแบบเดิมไว้เพื่อความเข้ากันได้ หาก Plugin ที่ค้นพบมากกว่าหนึ่งรายการอ้างความเป็นเจ้าของผู้ให้บริการตั้งค่าหรือ id แบ็กเอนด์ CLI เดียวกันหลังปรับเป็นมาตรฐาน การค้นหาการตั้งค่าจะปฏิเสธเจ้าของที่กำกวม แทนการอาศัย ลำดับการค้นหา เมื่อรันไทม์การตั้งค่าทำงานจริง ข้อมูลวินิจฉัยของรีจิสทรีจะรายงาน ความคลาดเคลื่อนระหว่าง setup.providers / setup.cliBackends กับผู้ให้บริการหรือแบ็กเอนด์ CLI ที่ API การตั้งค่าลงทะเบียนจริง โดยไม่บล็อก Plugin แบบเดิม

ขอบเขตแคชของ Plugin

OpenClaw ไม่แคชผลการค้นหา Plugin หรือข้อมูลรีจิสทรีแมนิเฟสต์โดยตรง ไว้เบื้องหลังช่วงเวลาตามนาฬิกา การติดตั้ง การแก้ไขแมนิเฟสต์ และการเปลี่ยนพาธโหลด ต้องปรากฏให้เห็นในการอ่านข้อมูลเมตาหรือการสร้างสแนปช็อตใหม่แบบระบุชัดเจนครั้งถัดไป ตัวแยกวิเคราะห์ไฟล์แมนิเฟสต์เก็บแคชลายเซ็นไฟล์แบบจำกัดขนาด ซึ่งใช้พาธ แมนิเฟสต์ที่เปิดร่วมกับอุปกรณ์/inode ขนาด และ mtime/ctime เป็นคีย์; แคชดังกล่าวมีไว้เพียง หลีกเลี่ยงการแยกวิเคราะห์ไบต์ที่ไม่เปลี่ยนแปลงซ้ำ และต้องไม่แคชคำตอบเกี่ยวกับการค้นหา รีจิสทรี เจ้าของ หรือนโยบาย

เส้นทางด่วนสำหรับข้อมูลเมตาที่ปลอดภัยคือความเป็นเจ้าของออบเจ็กต์อย่างชัดเจน ไม่ใช่แคชที่ซ่อนอยู่ ฮอตพาธการเริ่มต้น Gateway ควรส่ง PluginMetadataSnapshot ปัจจุบัน PluginLookUpTable ที่หาได้ หรือรีจิสทรีแมนิเฟสต์ที่ระบุชัดเจนผ่านสายการเรียก การตรวจสอบการกำหนดค่า การเปิดใช้งานอัตโนมัติเมื่อเริ่มต้น การบูตสแตรป Plugin และการเลือก ผู้ให้บริการสามารถใช้ออบเจ็กต์เหล่านั้นซ้ำได้ขณะที่ออบเจ็กต์ยังแทนการกำหนดค่าและ รายการ Plugin ปัจจุบัน การค้นหาการตั้งค่ายังคงสร้างข้อมูลเมตาแมนิเฟสต์ใหม่ตามต้องการ เว้นแต่เส้นทางการตั้งค่าเฉพาะจะได้รับรีจิสทรีแมนิเฟสต์อย่างชัดเจน; ให้คง สิ่งนี้เป็นทางเลือกสำรองของโคลด์พาธ แทนการเพิ่มแคชการค้นหาที่ซ่อนอยู่ เมื่อ อินพุตเปลี่ยน ให้สร้างและแทนที่สแนปช็อตใหม่ แทนการแก้ไขสแนปช็อตเดิมหรือ เก็บสำเนาย้อนหลัง วิวเหนือรีจิสทรี Plugin ที่ใช้งานอยู่และตัวช่วยบูตสแตรป ช่องทางแบบบันเดิลควรถูกคำนวณใหม่จากรีจิสทรี/รูทปัจจุบัน แมปอายุสั้น ใช้ได้ภายในการเรียกหนึ่งครั้งเพื่อขจัดงานซ้ำหรือป้องกันการกลับเข้าใหม่; แต่ต้องไม่กลายเป็น แคชข้อมูลเมตาระดับโพรเซส

สำหรับการโหลด Plugin ชั้นแคชถาวรคือการโหลดรันไทม์ ซึ่งอาจใช้ สถานะตัวโหลดซ้ำเมื่อมีการโหลดโค้ดหรืออาร์ติแฟกต์ที่ติดตั้งจริง เช่น:

  • PluginLoaderCacheState และรีจิสทรีรันไทม์ที่ใช้งานอยู่ซึ่งเข้ากันได้
  • แคช jiti/โมดูลและแคชตัวโหลดพื้นผิวสาธารณะที่ใช้เพื่อหลีกเลี่ยงการนำเข้า พื้นผิวรันไทม์เดียวกันซ้ำ
  • แคชระบบไฟล์สำหรับอาร์ติแฟกต์ Plugin ที่ติดตั้ง
  • แมปอายุสั้นต่อการเรียกสำหรับการปรับพาธเป็นมาตรฐานหรือการแก้ไขรายการซ้ำ

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

ห้ามเพิ่มแคชแบบถาวรหรือตามช่วงเวลานาฬิกาสำหรับ:

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

ผู้เรียกที่สร้างข้อมูลเมตาแมนิเฟสต์ใหม่จากดัชนี Plugin ที่ติดตั้งและจัดเก็บถาวร จะสร้างรีจิสทรีนั้นใหม่ตามต้องการ ดัชนีที่ติดตั้งเป็นสถานะระนาบต้นทาง แบบคงทน; ไม่ใช่แคชข้อมูลเมตาภายในโพรเซสที่ซ่อนอยู่

โมเดลรีจิสทรี

Plugin ที่โหลดแล้วจะไม่แก้ไขโกลบอลของคอร์แบบสุ่มโดยตรง แต่จะลงทะเบียนเข้าสู่ รีจิสทรี Plugin ส่วนกลาง (PluginRegistry ใน src/plugins/registry-types.ts) ซึ่งติดตามระเบียน Plugin (ข้อมูลระบุตัวตน แหล่งที่มา ต้นทาง สถานะ ข้อมูลวินิจฉัย) พร้อมอาร์เรย์สำหรับทุกความสามารถ ได้แก่ เครื่องมือ ฮุกแบบเดิมและฮุกที่มีชนิด ช่องทาง ผู้ให้บริการ ตัวจัดการ RPC ของ Gateway เส้นทาง HTTP ตัวลงทะเบียน CLI บริการเบื้องหลัง คำสั่งที่ Plugin เป็นเจ้าของ และกลุ่มผู้ให้บริการที่มีชนิดอีกหลายสิบประเภท (เสียงพูด การฝังเวกเตอร์ การสร้างภาพ/วิดีโอ/เพลง การดึงข้อมูล/ค้นหาเว็บ ชุดทดสอบตัวแทน การดำเนินการกับเซสชัน และอื่น ๆ)

จากนั้นฟีเจอร์ของคอร์จะอ่านจากรีจิสทรีนั้น แทนการสื่อสารกับโมดูล Plugin โดยตรง ซึ่งทำให้การโหลดเป็นทิศทางเดียว:

  • โมดูล Plugin -> การลงทะเบียนรีจิสทรี
  • รันไทม์ของคอร์ -> การใช้รีจิสทรี

การแยกนี้สำคัญต่อความสะดวกในการบำรุงรักษา เพราะหมายความว่าพื้นผิวคอร์ส่วนใหญ่ ต้องการจุดผสานรวมเพียงจุดเดียว: "อ่านรีจิสทรี" ไม่ใช่ "จัดการโมดูล Plugin แต่ละรายการเป็นกรณีพิเศษ"

คอลแบ็กการผูกการสนทนา

Plugin ที่ผูกการสนทนาสามารถตอบสนองเมื่อการอนุมัติได้รับการตัดสินแล้ว

ใช้ api.onConversationBindingResolved(...) เพื่อรับคอลแบ็กหลังคำขอผูก ได้รับการอนุมัติหรือปฏิเสธ:

ts
export default {  id: "my-plugin",  register(api) {    api.onConversationBindingResolved(async (event) => {      if (event.status === "approved") {        // ขณะนี้มีการผูกสำหรับ Plugin นี้ + การสนทนาแล้ว        console.log(event.binding?.conversationId);        return;      }       // คำขอถูกปฏิเสธ; ล้างสถานะรอดำเนินการในเครื่อง      console.log(event.request.conversation.conversationId);    });  },};

ฟิลด์เพย์โหลดของคอลแบ็ก:

  • status: "approved" หรือ "denied"
  • decision: "allow-once", "allow-always" หรือ "deny"
  • binding: การผูกที่แก้ไขแล้วสำหรับคำขอที่ได้รับอนุมัติ
  • request: สรุปคำขอเดิม คำใบ้การยกเลิกการเชื่อมโยง id ผู้ส่ง และ ข้อมูลเมตาการสนทนา

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

ฮุกรันไทม์ของผู้ให้บริการ

Plugin ผู้ให้บริการมีสามชั้น:

  • ข้อมูลเมตาแมนิเฟสต์ สำหรับการค้นหาก่อนรันไทม์ที่มีต้นทุนต่ำ: setup.providers[].envVars, providerAuthAliases, providerAuthChoices และ channelConfigs
  • ฮุกในช่วงกำหนดค่า: catalog รวมถึง applyConfigDefaults
  • ฮุกขณะรัน: ฮุกเสริมมากกว่า 40 รายการ ครอบคลุมการยืนยันตัวตน การแก้ไขโมเดล การห่อสตรีม ระดับการคิด นโยบายการเล่นซ้ำ และเอนด์พอยต์การใช้งาน โปรดดู ลำดับฮุกและการใช้งาน

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

ใช้ setup.providers[].envVars ของ manifest เมื่อผู้ให้บริการมี ข้อมูลประจำตัวที่อิงตามตัวแปรสภาพแวดล้อม ซึ่งเส้นทางทั่วไปสำหรับการยืนยันตัวตน/สถานะ/ตัวเลือกโมเดลควรมองเห็นได้โดยไม่ต้อง โหลดรันไทม์ของ Plugin ใช้ providerAuthAliases ของ manifest เมื่อรหัสผู้ให้บริการหนึ่งควรใช้ตัวแปรสภาพแวดล้อม โปรไฟล์การยืนยันตัวตน การยืนยันตัวตนจากการกำหนดค่า และตัวเลือกการเริ่มต้นใช้งานด้วยคีย์ API ของรหัสผู้ให้บริการอื่นซ้ำ ใช้ providerAuthChoices ของ manifest เมื่อพื้นผิว CLI สำหรับการเริ่มต้นใช้งาน/การเลือกวิธียืนยันตัวตนควรทราบ รหัสตัวเลือก ป้ายกำกับกลุ่ม และการเชื่อมต่อการยืนยันตัวตนแบบแฟล็กเดียวอย่างง่ายของผู้ให้บริการ โดยไม่ต้อง โหลดรันไทม์ของผู้ให้บริการ เก็บ envVars ของรันไทม์ผู้ให้บริการ ไว้สำหรับคำแนะนำที่แสดงต่อผู้ปฏิบัติงาน เช่น ป้ายกำกับการเริ่มต้นใช้งาน หรือตัวแปรการตั้งค่า รหัสไคลเอนต์/ข้อมูลลับไคลเอนต์ OAuth

อธิบายการตั้งค่าช่องทางและการยืนยันตัวตนที่ขับเคลื่อนด้วยตัวแปรสภาพแวดล้อมผ่าน channelConfigs.<id>.schema และตัวอธิบายการตั้งค่าที่เป็นเจ้าของส่วนดังกล่าว

ลำดับและการใช้งานฮุก

สำหรับ Plugin โมเดล/ผู้ให้บริการ OpenClaw จะเรียกฮุกตามลำดับคร่าว ๆ ดังนี้ คอลัมน์ "ควรใช้เมื่อใด" เป็นคู่มือประกอบการตัดสินใจฉบับย่อ ฟิลด์ผู้ให้บริการที่มีไว้เพื่อความเข้ากันได้เท่านั้นและ OpenClaw ไม่ได้เรียกใช้อีกต่อไป เช่น ProviderPlugin.capabilities และ suppressBuiltInModel จะไม่แสดงไว้ ที่นี่โดยเจตนา

Hook หน้าที่ ควรใช้เมื่อ
catalog เผยแพร่การกำหนดค่าผู้ให้บริการไปยัง models.providers ระหว่างการสร้าง models.json ผู้ให้บริการเป็นเจ้าของแค็ตตาล็อกหรือค่าเริ่มต้นของ URL ฐาน
applyConfigDefaults ใช้ค่าเริ่มต้นของการกำหนดค่าส่วนกลางที่ผู้ให้บริการเป็นเจ้าของระหว่างการทำให้การกำหนดค่าเป็นรูปธรรม ค่าเริ่มต้นขึ้นอยู่กับโหมดการตรวจสอบสิทธิ์ สภาพแวดล้อม หรือความหมายของตระกูลโมเดลของผู้ให้บริการ
(การค้นหาโมเดลในตัว) OpenClaw ลองใช้เส้นทางรีจิสทรี/แค็ตตาล็อกตามปกติก่อน (ไม่ใช่ hook ของ plugin)
normalizeModelId ปรับนามแฝงรหัสโมเดลแบบเดิมหรือแบบพรีวิวให้เป็นมาตรฐานก่อนค้นหา ผู้ให้บริการเป็นเจ้าของการล้างนามแฝงก่อนการแก้ไขโมเดลเป็นรูปแบบมาตรฐาน
normalizeTransport ปรับ api / baseUrl ของตระกูลผู้ให้บริการให้เป็นมาตรฐานก่อนการประกอบโมเดลทั่วไป ผู้ให้บริการเป็นเจ้าของการล้างข้อมูลการขนส่งสำหรับรหัสผู้ให้บริการแบบกำหนดเองในตระกูลการขนส่งเดียวกัน
normalizeConfig ปรับ models.providers.<id> ให้เป็นมาตรฐานก่อนการแก้ไขรันไทม์/ผู้ให้บริการ ผู้ให้บริการต้องการการล้างการกำหนดค่าที่ควรอยู่กับ plugin โดยตัวช่วยตระกูล Google ที่รวมมาด้วยยังช่วยรองรับรายการกำหนดค่า Google ที่รองรับ
applyNativeStreamingUsageCompat ใช้การเขียนการกำหนดค่าใหม่เพื่อความเข้ากันได้กับการใช้งานสตรีมมิงแบบเนทีฟกับผู้ให้บริการในการกำหนดค่า ผู้ให้บริการต้องการแก้ไขข้อมูลเมตาการใช้งานสตรีมมิงแบบเนทีฟที่ขับเคลื่อนโดย endpoint
resolveConfigApiKey แก้ไขการตรวจสอบสิทธิ์ด้วยเครื่องหมายสภาพแวดล้อมสำหรับผู้ให้บริการในการกำหนดค่าก่อนโหลดการตรวจสอบสิทธิ์ของรันไทม์ ผู้ให้บริการเปิดเผย hook ของตนเองสำหรับแก้ไข API key จากเครื่องหมายสภาพแวดล้อม
resolveSyntheticAuth แสดงการตรวจสอบสิทธิ์แบบภายในเครื่อง/โฮสต์เองหรือที่อิงการกำหนดค่าโดยไม่จัดเก็บข้อความธรรมดา ผู้ให้บริการสามารถทำงานด้วยเครื่องหมายข้อมูลประจำตัวแบบสังเคราะห์/ภายในเครื่อง
resolveExternalAuthProfiles ซ้อนทับโปรไฟล์การตรวจสอบสิทธิ์ภายนอกที่ผู้ให้บริการเป็นเจ้าของ โดยค่าเริ่มต้น persistence คือ runtime-only สำหรับข้อมูลประจำตัวที่ CLI/แอปเป็นเจ้าของ ผู้ให้บริการนำข้อมูลประจำตัวการตรวจสอบสิทธิ์ภายนอกมาใช้ซ้ำโดยไม่จัดเก็บ refresh token ที่คัดลอกมา ให้ประกาศ contracts.externalAuthProviders ใน manifest
shouldDeferSyntheticProfileAuth ลดลำดับความสำคัญของ placeholder โปรไฟล์สังเคราะห์ที่จัดเก็บไว้ภายใต้การตรวจสอบสิทธิ์ที่อิงสภาพแวดล้อม/การกำหนดค่า ผู้ให้บริการจัดเก็บโปรไฟล์ placeholder สังเคราะห์ที่ไม่ควรมีลำดับความสำคัญเหนือกว่า
resolveDynamicModel การสำรองแบบซิงโครนัสสำหรับรหัสโมเดลที่ผู้ให้บริการเป็นเจ้าของซึ่งยังไม่มีในรีจิสทรีภายในเครื่อง ผู้ให้บริการยอมรับรหัสโมเดล upstream ใดก็ได้
prepareDynamicModel อุ่นเครื่องแบบอะซิงโครนัส จากนั้น resolveDynamicModel จะทำงานอีกครั้ง ผู้ให้บริการต้องการข้อมูลเมตาจากเครือข่ายก่อนแก้ไขรหัสที่ไม่รู้จัก
normalizeResolvedModel เขียนใหม่เป็นครั้งสุดท้ายก่อนที่ runner แบบฝังตัวจะใช้โมเดลที่แก้ไขแล้ว ผู้ให้บริการต้องการเขียนการขนส่งใหม่แต่ยังคงใช้การขนส่งหลัก
normalizeToolSchemas ปรับ schema ของเครื่องมือให้เป็นมาตรฐานก่อนที่ runner แบบฝังตัวจะเห็น ผู้ให้บริการต้องการล้าง schema ของตระกูลการขนส่ง
inspectToolSchemas แสดงการวินิจฉัย schema ที่ผู้ให้บริการเป็นเจ้าของหลังการปรับให้เป็นมาตรฐาน ผู้ให้บริการต้องการคำเตือนเกี่ยวกับ keyword โดยไม่ต้องสอนกฎเฉพาะผู้ให้บริการให้กับแกนหลัก
resolveReasoningOutputMode เลือกสัญญาเอาต์พุตการให้เหตุผลแบบเนทีฟหรือแบบมีแท็ก ผู้ให้บริการต้องการเอาต์พุตการให้เหตุผล/ขั้นสุดท้ายแบบมีแท็กแทนฟิลด์แบบเนทีฟ
prepareExtraParams ปรับพารามิเตอร์คำขอให้เป็นมาตรฐานก่อน wrapper ตัวเลือกสตรีมทั่วไป ผู้ให้บริการต้องการพารามิเตอร์คำขอเริ่มต้นหรือการล้างพารามิเตอร์รายผู้ให้บริการ
createStreamFn แทนที่เส้นทางสตรีมปกติทั้งหมดด้วยการขนส่งแบบกำหนดเอง ผู้ให้บริการต้องการ wire protocol แบบกำหนดเอง ไม่ใช่เพียง wrapper
wrapStreamFn wrapper สตรีมหลังจากใช้ wrapper ทั่วไปแล้ว ผู้ให้บริการต้องการ wrapper ความเข้ากันได้สำหรับส่วนหัว/เนื้อหาคำขอ/โมเดลโดยไม่ใช้การขนส่งแบบกำหนดเอง
resolveTransportTurnState แนบส่วนหัวหรือข้อมูลเมตาการขนส่งแบบเนทีฟต่อ turn ผู้ให้บริการต้องการให้การขนส่งทั่วไปส่งข้อมูลระบุ turn แบบเนทีฟของผู้ให้บริการ
resolveWebSocketSessionPolicy แนบส่วนหัว WebSocket แบบเนทีฟหรือนโยบายช่วงพักของเซสชัน ผู้ให้บริการต้องการให้การขนส่ง WS ทั่วไปปรับส่วนหัวเซสชันหรือนโยบายสำรอง
formatApiKey ตัวจัดรูปแบบโปรไฟล์การตรวจสอบสิทธิ์: โปรไฟล์ที่จัดเก็บไว้จะกลายเป็นสตริง apiKey ของรันไทม์ ผู้ให้บริการจัดเก็บข้อมูลเมตาการตรวจสอบสิทธิ์เพิ่มเติมและต้องการรูปแบบ token ของรันไทม์ที่กำหนดเอง
refreshOAuth แทนที่การรีเฟรช OAuth สำหรับ endpoint การรีเฟรชแบบกำหนดเองหรือนโยบายเมื่อรีเฟรชล้มเหลว ผู้ให้บริการไม่เข้ากับตัวรีเฟรช OpenClaw ที่ใช้ร่วมกัน
buildAuthDoctorHint คำแนะนำการซ่อมแซมที่เพิ่มต่อท้ายเมื่อการรีเฟรช OAuth ล้มเหลว ผู้ให้บริการต้องการคำแนะนำการซ่อมแซมการตรวจสอบสิทธิ์ที่ผู้ให้บริการเป็นเจ้าของหลังการรีเฟรชล้มเหลว
matchesContextOverflowError ตัวจับคู่การล้นของหน้าต่างบริบทที่ผู้ให้บริการเป็นเจ้าของ ผู้ให้บริการมีข้อผิดพลาดการล้นแบบดิบที่ heuristic ทั่วไปตรวจไม่พบ
classifyFailoverReason การจำแนกเหตุผลการ failover ที่ผู้ให้บริการเป็นเจ้าของ ผู้ให้บริการสามารถแมปข้อผิดพลาด API/การขนส่งแบบดิบเป็นการจำกัดอัตรา/โหลดเกิน/อื่นๆ
isCacheTtlEligible นโยบายแคชพรอมต์สำหรับผู้ให้บริการพร็อกซี/backhaul ผู้ให้บริการต้องการการควบคุม TTL ของแคชที่เฉพาะกับพร็อกซี
buildMissingAuthMessage ข้อความทดแทนสำหรับข้อความกู้คืนเมื่อขาดการตรวจสอบสิทธิ์แบบทั่วไป ผู้ให้บริการต้องการคำแนะนำการกู้คืนเมื่อขาดการตรวจสอบสิทธิ์ที่เฉพาะกับผู้ให้บริการ
augmentModelCatalog แถวแค็ตตาล็อกสังเคราะห์/ขั้นสุดท้ายที่เพิ่มหลังการค้นพบ (เลิกใช้แล้ว ดูด้านล่าง) ผู้ให้บริการต้องการแถวสังเคราะห์เพื่อความเข้ากันได้ในอนาคตใน models list และตัวเลือก
resolveThinkingProfile ชุดระดับ /think เฉพาะโมเดล ป้ายกำกับที่แสดง และค่าเริ่มต้น ผู้ให้บริการเปิดเผยลำดับขั้นการคิดแบบกำหนดเองหรือป้ายกำกับไบนารีสำหรับโมเดลที่เลือก
isBinaryThinking hook ความเข้ากันได้สำหรับเปิด/ปิดการให้เหตุผล ผู้ให้บริการเปิดเผยเฉพาะการเปิด/ปิดการคิดแบบไบนารี
supportsXHighThinking hook ความเข้ากันได้สำหรับการรองรับการให้เหตุผล xhigh ผู้ให้บริการต้องการ xhigh เฉพาะในโมเดลบางส่วน
resolveDefaultThinkingLevel hook ความเข้ากันได้สำหรับระดับ /think เริ่มต้น ผู้ให้บริการเป็นเจ้าของนโยบาย /think เริ่มต้นสำหรับตระกูลโมเดล
isModernModelRef ตัวจับคู่โมเดลสมัยใหม่สำหรับตัวกรองโปรไฟล์แบบสดและการเลือก smoke ผู้ให้บริการเป็นเจ้าของการจับคู่โมเดลที่ต้องการสำหรับแบบสด/smoke
prepareRuntimeAuth แลกเปลี่ยนข้อมูลประจำตัวที่กำหนดค่าไว้เป็น token/key ของรันไทม์จริงก่อนการอนุมาน ผู้ให้บริการต้องการการแลกเปลี่ยน token หรือข้อมูลประจำตัวคำขออายุสั้น
resolveUsageAuth แก้ไขข้อมูลประจำตัวการใช้งาน/การเรียกเก็บเงินสำหรับ /usage และพื้นผิวสถานะที่เกี่ยวข้อง ผู้ให้บริการต้องการการแยกวิเคราะห์ token การใช้งาน/โควตาแบบกำหนดเองหรือข้อมูลประจำตัวการใช้งานที่แตกต่าง
fetchUsageSnapshot ดึงและปรับสแนปช็อตการใช้งาน/โควตาเฉพาะผู้ให้บริการให้เป็นมาตรฐานหลังจากแก้ไขการตรวจสอบสิทธิ์แล้ว ผู้ให้บริการต้องการ endpoint การใช้งานหรือตัวแยกวิเคราะห์ payload ที่เฉพาะกับผู้ให้บริการ
createEmbeddingProvider สร้างอะแดปเตอร์ embedding ที่ผู้ให้บริการเป็นเจ้าของสำหรับหน่วยความจำ/การค้นหา ลักษณะการทำงานของ embedding สำหรับหน่วยความจำควรอยู่ใน Plugin ของผู้ให้บริการ
buildReplayPolicy ส่งคืนนโยบายการเล่นซ้ำที่ควบคุมการจัดการทรานสคริปต์สำหรับผู้ให้บริการ ผู้ให้บริการต้องใช้นโยบายทรานสคริปต์แบบกำหนดเอง (เช่น การลบบล็อกการคิด)
sanitizeReplayHistory เขียนประวัติการเล่นซ้ำใหม่หลังจากล้างทรานสคริปต์แบบทั่วไป ผู้ให้บริการต้องเขียนข้อมูลการเล่นซ้ำใหม่เฉพาะสำหรับผู้ให้บริการ นอกเหนือจากตัวช่วย Compaction ที่ใช้ร่วมกัน
validateReplayTurns ตรวจสอบความถูกต้องหรือปรับโครงสร้างเทิร์นการเล่นซ้ำขั้นสุดท้ายก่อนส่งไปยังรันเนอร์แบบฝัง การขนส่งของผู้ให้บริการต้องตรวจสอบความถูกต้องของเทิร์นอย่างเข้มงวดยิ่งขึ้นหลังจากการล้างข้อมูลแบบทั่วไป
onModelSelected เรียกใช้ผลข้างเคียงหลังการเลือกที่ผู้ให้บริการเป็นเจ้าของ ผู้ให้บริการต้องใช้เทเลเมทรีหรือสถานะที่ผู้ให้บริการเป็นเจ้าของเมื่อโมเดลเริ่มทำงาน

normalizeModelId, normalizeTransport และ normalizeConfig จะตรวจสอบ Plugin ของผู้ให้บริการที่ตรงกันก่อน จากนั้นจึงไล่ตรวจสอบ Plugin ของผู้ให้บริการรายอื่นที่รองรับ hook จนกว่าจะพบ Plugin ที่เปลี่ยนรหัสโมเดลหรือ transport/config จริง วิธีนี้ช่วยให้ shim ของผู้ให้บริการสำหรับ alias/compat ยังคงทำงานได้ โดยผู้เรียกไม่จำเป็นต้องทราบว่า Plugin ที่รวมมาด้วยตัวใดเป็นเจ้าของการเขียนค่าใหม่ หากไม่มี hook ของผู้ให้บริการใดเขียนรายการการกำหนดค่าตระกูล Google ที่รองรับใหม่ ตัวปรับการกำหนดค่า Google ที่รวมมาด้วยจะยังคงดำเนินการล้างข้อมูลเพื่อความเข้ากันได้นั้น

หากผู้ให้บริการต้องใช้โพรโทคอลรับส่งข้อมูลแบบกำหนดเองทั้งหมดหรือตัวดำเนินการคำขอแบบกำหนดเอง นั่นถือเป็นส่วนขยายคนละประเภท hook เหล่านี้มีไว้สำหรับพฤติกรรมของผู้ให้บริการที่ยังคงทำงานบนลูปการอนุมานตามปกติของ OpenClaw

resolveUsageAuth ใช้ตัดสินใจว่า OpenClaw ควรเรียก fetchUsageSnapshot หรือย้อนกลับไปใช้การแก้ไขข้อมูลประจำตัวแบบทั่วไปสำหรับส่วนแสดงการใช้งาน/สถานะ ให้คืนค่า { token, accountId?, subscriptionType?, rateLimitTier? } เมื่อผู้ให้บริการมีข้อมูลประจำตัวสำหรับการใช้งาน (ข้อมูลเมตาของแผนซึ่งเป็นตัวเลือกจะถูกส่งต่อไปยัง fetchUsageSnapshot) ให้คืนค่า { handled: true } เมื่อการยืนยันตัวตนสำหรับการใช้งานที่ผู้ให้บริการเป็นเจ้าของได้จัดการคำขอแล้วและต้องระงับการย้อนกลับไปใช้คีย์ API/OAuth แบบทั่วไป และให้คืนค่า null หรือ undefined เมื่อผู้ให้บริการไม่ได้จัดการการยืนยันตัวตนสำหรับการใช้งาน

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

ตัวอย่างผู้ให้บริการ

ts
api.registerProvider({  id: "example-proxy",  label: "พร็อกซีตัวอย่าง",  auth: [],  catalog: {    order: "simple",    run: async (ctx) => {      const apiKey = ctx.resolveProviderApiKey("example-proxy").apiKey;      if (!apiKey) {        return null;      }      return {        provider: {          baseUrl: "https://proxy.example.com/v1",          apiKey,          api: "openai-completions",          models: [{ id: "auto", name: "อัตโนมัติ" }],        },      };    },  },  resolveDynamicModel: (ctx) => ({    id: ctx.modelId,    name: ctx.modelId,    provider: "example-proxy",    api: "openai-completions",    baseUrl: "https://proxy.example.com/v1",    reasoning: false,    input: ["text"],    cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },    contextWindow: 128000,    maxTokens: 8192,  }),  prepareRuntimeAuth: async (ctx) => {    const exchanged = await exchangeToken(ctx.apiKey);    return {      apiKey: exchanged.token,      baseUrl: exchanged.baseUrl,      expiresAt: exchanged.expiresAt,    };  },  resolveUsageAuth: async (ctx) => {    const auth = await ctx.resolveOAuthToken();    return auth ? { token: auth.token } : null;  },  fetchUsageSnapshot: async (ctx) => {    return await fetchExampleProxyUsage(ctx.token, ctx.timeoutMs, ctx.fetchFn);  },});

ตัวอย่างที่มีมาให้ในตัว

Plugin ของผู้ให้บริการที่รวมมาด้วยจะผสาน hook ข้างต้นให้เหมาะกับแค็ตตาล็อก การยืนยันตัวตน การคิด การเล่นซ้ำ และความต้องการด้านการใช้งานของผู้จำหน่ายแต่ละราย ชุด hook ที่เป็นแหล่งอ้างอิงหลักอยู่กับแต่ละ Plugin ภายใต้ extensions/; หน้านี้แสดงรูปแบบต่าง ๆ แทนการทำสำเนารายการดังกล่าว

ผู้ให้บริการแค็ตตาล็อกแบบส่งผ่าน

OpenRouter, Kilocode, Z.AI และ xAI ลงทะเบียน catalog ร่วมกับ resolveDynamicModel / prepareDynamicModel เพื่อให้สามารถแสดงรหัสโมเดลจากต้นทางก่อนแค็ตตาล็อกแบบคงที่ของ OpenClaw

ผู้ให้บริการปลายทาง OAuth และข้อมูลการใช้งาน

GitHub Copilot, Gemini CLI, ChatGPT Codex, MiniMax, Xiaomi และ z.ai จับคู่ prepareRuntimeAuth หรือ formatApiKey กับ resolveUsageAuth + fetchUsageSnapshot เพื่อเป็นเจ้าของการแลกเปลี่ยนโทเค็นและการผสานรวม /usage

ตระกูลการเล่นซ้ำและการล้างทรานสคริปต์

ตระกูลที่มีชื่อร่วมกัน (google-gemini, passthrough-gemini, anthropic-by-model, hybrid-anthropic-openai) ช่วยให้ผู้ให้บริการเลือกใช้นโยบายทรานสคริปต์ผ่าน buildReplayPolicy แทนที่แต่ละ Plugin จะนำกระบวนการล้างข้อมูลไปใช้งานซ้ำเอง

ผู้ให้บริการเฉพาะแค็ตตาล็อก

byteplus, cloudflare-ai-gateway, huggingface, kimi-coding, nvidia, qianfan, synthetic, together, venice, vercel-ai-gateway และ volcengine ลงทะเบียนเพียง catalog และใช้ลูปการอนุมานร่วมกัน

ตัวช่วยสตรีมเฉพาะ Anthropic

ส่วนหัวเบตา, /fast / serviceTier และ context1m อยู่ภายในจุดเชื่อมต่อ api.ts / contract-api.ts แบบสาธารณะของ Plugin Anthropic (wrapAnthropicProviderStream, resolveAnthropicBetas, resolveAnthropicFastMode, resolveAnthropicServiceTier) แทนที่จะอยู่ใน SDK แบบทั่วไป

ตัวช่วยรันไทม์

Plugin สามารถเข้าถึงตัวช่วยหลักที่เลือกไว้ผ่าน api.runtime สำหรับ TTS:

ts
const clip = await api.runtime.tts.textToSpeech({  text: "สวัสดีจาก OpenClaw",  cfg: api.config,}); const result = await api.runtime.tts.textToSpeechTelephony({  text: "สวัสดีจาก OpenClaw",  cfg: api.config,}); const voices = await api.runtime.tts.listVoices({  provider: "elevenlabs",  cfg: api.config,});

หมายเหตุ:

  • textToSpeech คืนเพย์โหลดเอาต์พุต TTS หลักตามปกติสำหรับส่วนไฟล์/บันทึกเสียง
  • ใช้การกำหนดค่า messages.tts และการเลือกผู้ให้บริการจากส่วนหลัก
  • คืนบัฟเฟอร์เสียง PCM พร้อมอัตราการสุ่มตัวอย่าง Plugin ต้องปรับอัตราการสุ่มตัวอย่าง/เข้ารหัสให้เหมาะกับผู้ให้บริการ
  • listVoices เป็นตัวเลือกสำหรับผู้ให้บริการแต่ละราย ใช้สำหรับตัวเลือกเสียงหรือขั้นตอนการตั้งค่าที่ผู้จำหน่ายเป็นเจ้าของ
  • ส่วนหลักจะส่งกำหนดเวลาสิ้นสุดของคำขอที่แก้ไขแล้วไปยัง hook listVoices ของผู้ให้บริการ โดยการตั้งค่าระยะหมดเวลาเฉพาะผู้ให้บริการอาจเขียนทับค่านี้
  • รายการเสียงสามารถมีข้อมูลเมตาที่ละเอียดยิ่งขึ้น เช่น โลเคล เพศ และแท็กบุคลิกภาพ สำหรับตัวเลือกที่รับรู้ผู้ให้บริการ
  • ปัจจุบัน OpenAI และ ElevenLabs รองรับระบบโทรศัพท์ Microsoft ไม่รองรับ

Plugin ยังสามารถลงทะเบียนผู้ให้บริการเสียงพูดผ่าน api.registerSpeechProvider(...) ได้ด้วย

ts
api.registerSpeechProvider({  id: "acme-speech",  label: "เสียงพูด Acme",  isConfigured: ({ config }) => Boolean(config.messages?.tts),  synthesize: async (req) => {    return {      audioBuffer: Buffer.from([]),      outputFormat: "mp3",      fileExtension: ".mp3",      voiceCompatible: false,    };  },});

หมายเหตุ:

  • เก็บนโยบาย TTS การย้อนกลับ และการส่งคำตอบไว้ในส่วนหลัก
  • ใช้ผู้ให้บริการเสียงพูดสำหรับพฤติกรรมการสังเคราะห์ที่ผู้จำหน่ายเป็นเจ้าของ
  • อินพุต Microsoft แบบเดิม edge จะถูกปรับให้เป็นรหัสผู้ให้บริการ microsoft
  • รูปแบบความเป็นเจ้าของที่แนะนำจะยึดตามบริษัท โดย Plugin ของผู้จำหน่ายหนึ่งรายสามารถเป็นเจ้าของผู้ให้บริการข้อความ เสียงพูด รูปภาพ และสื่อในอนาคตได้ เมื่อ OpenClaw เพิ่มสัญญาความสามารถเหล่านั้น

สำหรับการทำความเข้าใจรูปภาพ/เสียง/วิดีโอ Plugin จะลงทะเบียนผู้ให้บริการทำความเข้าใจสื่อแบบมีชนิดหนึ่งราย แทนการใช้ชุดคีย์/ค่าแบบทั่วไป:

ts
api.registerMediaUnderstandingProvider({  id: "google",  capabilities: ["image", "audio", "video"],  describeImage: async (req) => ({ text: "..." }),  transcribeAudio: async (req) => ({ text: "..." }),  describeVideo: async (req) => ({ text: "..." }),});

หมายเหตุ:

  • เก็บการประสานงาน การย้อนกลับ การกำหนดค่า และการเชื่อมต่อช่องทางไว้ในส่วนหลัก
  • เก็บพฤติกรรมของผู้จำหน่ายไว้ใน Plugin ของผู้ให้บริการ
  • การขยายแบบเพิ่มส่วนควรคงการระบุชนิดไว้ ได้แก่ เมธอดตัวเลือกใหม่ ฟิลด์ผลลัพธ์ตัวเลือกใหม่ และความสามารถตัวเลือกใหม่
  • การสร้างวิดีโอใช้รูปแบบเดียวกันอยู่แล้ว:
    • ส่วนหลักเป็นเจ้าของสัญญาความสามารถและตัวช่วยรันไทม์
    • Plugin ของผู้จำหน่ายลงทะเบียน api.registerVideoGenerationProvider(...)
    • Plugin ฟีเจอร์/ช่องทางใช้ api.runtime.videoGeneration.*

สำหรับตัวช่วยรันไทม์เพื่อทำความเข้าใจสื่อ Plugin สามารถเรียกใช้:

ts
const image = await api.runtime.mediaUnderstanding.describeImageFile({  filePath: "/tmp/inbound-photo.jpg",  cfg: api.config,  agentDir: "/tmp/agent",}); const video = await api.runtime.mediaUnderstanding.describeVideoFile({  filePath: "/tmp/inbound-video.mp4",  cfg: api.config,}); const extraction = await api.runtime.mediaUnderstanding.extractStructuredWithModel({  provider: "codex",  model: "gpt-5.6-sol",  input: [    {      type: "image",      buffer: receiptImageBuffer,      fileName: "receipt.png",      mime: "image/png",    },    { type: "text", text: "ใช้ฟิลด์ที่พิมพ์ไว้เป็นแหล่งข้อมูลที่เชื่อถือได้" },  ],  instructions: "คืนเอนทิตีและแท็กที่ค้นหาได้",  schemaName: "example.evidence",  jsonSchema: {    type: "object",    properties: {      entities: { type: "array", items: { type: "string" } },      tags: { type: "array", items: { type: "string" } },    },  },  cfg: api.config,});

สำหรับการถอดเสียง Plugin สามารถใช้รันไทม์การทำความเข้าใจสื่อหรือ alias STT รุ่นเก่าก็ได้:

ts
const { text } = await api.runtime.mediaUnderstanding.transcribeAudioFile({  filePath: "/tmp/inbound-audio.ogg",  cfg: api.config,  // เป็นตัวเลือกเมื่อไม่สามารถอนุมาน MIME ได้อย่างน่าเชื่อถือ:  mime: "audio/ogg",});

หมายเหตุ:

  • api.runtime.mediaUnderstanding.* เป็นส่วนร่วมที่แนะนำสำหรับการทำความเข้าใจรูปภาพ/เสียง/วิดีโอ
  • extractStructuredWithModel(...) เป็นจุดเชื่อมต่อสำหรับ Plugin เพื่อการแยกข้อมูลแบบเน้นรูปภาพเป็นหลัก ภายในขอบเขตที่กำหนด และผู้ให้บริการเป็นเจ้าของ ต้องมีอินพุตรูปภาพอย่างน้อยหนึ่งรายการ ส่วนอินพุตข้อความเป็นบริบทเสริม Plugin ผลิตภัณฑ์เป็นเจ้าของเส้นทางและสคีมาของตน ขณะที่ OpenClaw เป็นเจ้าของขอบเขตผู้ให้บริการ/รันไทม์
  • ใช้การกำหนดค่าเสียงสำหรับการทำความเข้าใจสื่อจากส่วนหลัก (tools.media.audio) และลำดับการย้อนกลับของผู้ให้บริการ
  • คืนค่า { text: undefined } เมื่อไม่มีเอาต์พุตการถอดเสียงเกิดขึ้น (เช่น อินพุตถูกข้าม/ไม่รองรับ)

Plugin ยังสามารถเปิดการรันเอเจนต์ย่อยเบื้องหลังผ่าน api.runtime.subagent ได้ด้วย:

ts
const result = await api.runtime.subagent.run({  sessionKey: "agent:main:subagent:search-helper",  message: "ขยายคำค้นนี้เป็นการค้นหาติดตามผลที่เจาะจง",  toolsAlsoAllow: ["my_plugin_progress"],  provider: "openai",  model: "gpt-4.1-mini",  deliver: false,});

หมายเหตุ:

  • provider และ model เป็นค่าที่เขียนทับได้สำหรับแต่ละการรัน ไม่ใช่การเปลี่ยนแปลงเซสชันแบบถาวร
  • toolsAlsoAllow รับชื่อเครื่องมือที่ตรงทั้งหมดและมีเจ้าของเพียงรายเดียว ซึ่งลงทะเบียนโดย Plugin ที่เรียกใช้ ชื่อของส่วนหลักและชื่อที่กำกวมจะถูกปฏิเสธ ค่านี้เพิ่มจากโปรไฟล์ปกติ แต่รายการอนุญาตและรายการปฏิเสธของผู้ปฏิบัติงานยังคงมีอำนาจสูงสุด
  • OpenClaw จะยอมรับฟิลด์เขียนทับเหล่านั้นเฉพาะจากผู้เรียกที่เชื่อถือได้
  • สำหรับการรันย้อนกลับที่ Plugin เป็นเจ้าของ ผู้ปฏิบัติงานต้องเลือกใช้งานด้วย plugins.entries.<id>.subagent.allowModelOverride: true
  • ใช้ plugins.entries.<id>.subagent.allowedModels เพื่อจำกัด Plugin ที่เชื่อถือได้ให้ใช้เฉพาะเป้าหมาย provider/model แบบมาตรฐานที่ระบุ หรือใช้ "*" เพื่ออนุญาตเป้าหมายใด ๆ อย่างชัดเจน
  • การรันเอเจนต์ย่อยของ Plugin ที่ไม่น่าเชื่อถือยังคงทำงานได้ แต่คำขอเขียนทับจะถูกปฏิเสธแทนที่จะย้อนกลับโดยไม่แจ้ง
  • เซสชันเอเจนต์ย่อยที่ Plugin สร้างจะถูกติดแท็กด้วยรหัส Plugin ผู้สร้าง api.runtime.subagent.deleteSession(...) สำหรับการย้อนกลับสามารถลบได้เฉพาะเซสชันที่เป็นเจ้าของเหล่านั้น การลบเซสชันใด ๆ โดยพลการยังคงต้องใช้คำขอ Gateway ที่มีขอบเขตผู้ดูแลระบบ

สำหรับการค้นหาเว็บ Plugin สามารถใช้ตัวช่วยรันไทม์ร่วมกันแทนการเข้าถึงการเชื่อมต่อเครื่องมือเอเจนต์โดยตรง:

ts
const providers = api.runtime.webSearch.listProviders({  config: api.config,}); const result = await api.runtime.webSearch.search({  config: api.config,  args: {    query: "ตัวช่วยรันไทม์ Plugin ของ OpenClaw",    count: 5,  },});

Plugin ยังสามารถลงทะเบียนผู้ให้บริการค้นหาเว็บผ่าน api.registerWebSearchProvider(...) ได้ด้วย

หมายเหตุ:

  • เก็บการเลือกผู้ให้บริการ การแก้ไขข้อมูลประจำตัว และความหมายร่วมของคำขอไว้ในส่วนหลัก
  • ใช้ผู้ให้บริการค้นหาเว็บสำหรับ transport การค้นหาเฉพาะผู้จำหน่าย
  • api.runtime.webSearch.* เป็นส่วนร่วมที่แนะนำสำหรับ Plugin ฟีเจอร์/ช่องทางที่ต้องใช้พฤติกรรมการค้นหาโดยไม่ขึ้นกับ wrapper ของเครื่องมือเอเจนต์

api.runtime.imageGeneration

ts
const result = await api.runtime.imageGeneration.generate({  config: api.config,  args: { prompt: "มาสคอตกุ้งล็อบสเตอร์ที่เป็นมิตร", size: "1024x1024" },}); const providers = api.runtime.imageGeneration.listProviders({  config: api.config,});
  • generate(...): สร้างรูปภาพโดยใช้สายโซ่ผู้ให้บริการสร้างรูปภาพที่กำหนดค่าไว้
  • listProviders(...): แสดงรายการผู้ให้บริการสร้างรูปภาพที่พร้อมใช้งานและความสามารถของแต่ละราย

เส้นทาง HTTP ของ Gateway

Plugin สามารถเปิดเผยปลายทาง HTTP ด้วย api.registerHttpRoute(...)

ts
api.registerHttpRoute({  path: "/acme/webhook",  auth: "plugin",  match: "exact",  handler: async (_req, res) => {    res.statusCode = 200;    res.end("ok");    return true;  },});

ฟิลด์ของเส้นทาง:

  • path: พาธของเส้นทางภายใต้เซิร์ฟเวอร์ HTTP ของ Gateway
  • auth: จำเป็นต้องระบุ โดยเป็น "gateway" หรือ "plugin" ใช้ "gateway" เพื่อกำหนดให้ใช้การยืนยันตัวตนตามปกติของ Gateway หรือใช้ "plugin" สำหรับการยืนยันตัวตน/การตรวจสอบ Webhook ที่ Plugin จัดการ
  • match: ไม่บังคับ โดยเป็น "exact" (ค่าเริ่มต้น) หรือ "prefix"
  • handleUpgrade: ตัวจัดการเสริมสำหรับคำขออัปเกรด WebSocket บนเส้นทางเดียวกัน
  • replaceExisting: ไม่บังคับ อนุญาตให้ Plugin เดิมแทนที่การลงทะเบียนเส้นทางที่มีอยู่ของตนเอง
  • handler: คืนค่า true เมื่อเส้นทางจัดการคำขอแล้ว

หมายเหตุ:

  • api.registerHttpHandler(...) ถูกนำออกแล้วและจะทำให้เกิดข้อผิดพลาดในการโหลด Plugin ให้ใช้ api.registerHttpRoute(...) แทน
  • เส้นทางของ Plugin ต้องประกาศ auth อย่างชัดเจน
  • ข้อขัดแย้งของ path + match แบบตรงกันทุกประการจะถูกปฏิเสธ เว้นแต่มี replaceExisting: true และ Plugin หนึ่งไม่สามารถแทนที่เส้นทางของ Plugin อื่นได้
  • เส้นทางที่ทับซ้อนกันและมีระดับ auth ต่างกันจะถูกปฏิเสธ ให้ใช้สายโซ่การส่งต่อ exact/prefix เฉพาะภายในระดับการยืนยันตัวตนเดียวกัน
  • เส้นทาง auth: "plugin" ไม่ได้ รับขอบเขตรันไทม์ของผู้ดำเนินการโดยอัตโนมัติ เส้นทางเหล่านี้มีไว้สำหรับ Webhook/การตรวจสอบลายเซ็นที่ Plugin จัดการ ไม่ใช่การเรียกตัวช่วย Gateway ที่มีสิทธิ์ระดับสูง
  • เส้นทาง auth: "gateway" ทำงานภายในขอบเขตรันไทม์ของคำขอ Gateway พื้นผิวเริ่มต้น (gatewayRuntimeScopeSurface: "write-default") ถูกกำหนดให้มีข้อจำกัดอย่างรอบคอบโดยเจตนา:
    • การยืนยันตัวตนแบบ bearer ด้วยข้อมูลลับร่วม (gateway.auth.mode = "token" / "password") และวิธีการยืนยันตัวตนใด ๆ ที่ไม่ใช่พร็อกซีที่เชื่อถือได้ จะได้รับขอบเขต operator.write เพียงขอบเขตเดียว แม้ว่าผู้เรียกจะส่ง x-openclaw-scopes
    • ผู้เรียก trusted-proxy ที่ไม่มีส่วนหัว x-openclaw-scopes อย่างชัดเจนจะยังคงใช้พื้นผิวเดิมที่มีเฉพาะ operator.write
    • ผู้เรียก trusted-proxy ที่ส่ง x-openclaw-scopes จะได้รับขอบเขตที่ประกาศไว้แทน
    • เส้นทางสามารถเลือกใช้ gatewayRuntimeScopeSurface: "trusted-operator" เพื่อให้เคารพ x-openclaw-scopes เสมอสำหรับโหมดการยืนยันตัวตนที่มีข้อมูลประจำตัว (และใช้ชุดขอบเขตเริ่มต้นทั้งหมดของ CLI เมื่อไม่มีส่วนหัว)
  • แท็บ Control UI ภายนอกที่อยู่ในแซนด์บ็อกซ์และรองรับโดยเส้นทาง auth: "gateway" จะใช้สิทธิ์ผ่านคุกกี้ที่ลงนามและมีอายุสั้น ซึ่งสร้างโดยการบูตสแตรปที่ผ่านการยืนยันตัวตนเท่านั้น ส่วนแท็บที่ใช้การยืนยันตัวตนของ Plugin จะยังคงใช้พาธ iframe โดยตรง ก่อนเมานต์ องค์ประกอบแม่จะเรียกโพรบที่เส้นทางเป็นเจ้าของภายในแซนด์บ็อกซ์ทึบเดียวกัน และจะปฏิเสธแบบปิดเมื่อการตั้งค่าความเป็นส่วนตัวของเบราว์เซอร์บล็อกคุกกี้ สิทธิ์นี้ผูกกับ Plugin เจ้าของ รากของเส้นทางที่ตรงกัน และรุ่นการยืนยันตัวตนปัจจุบัน ชื่อคุกกี้ที่สุ่มเฉพาะกระบวนการช่วยป้องกันไม่ให้ Gateway บนโฮสต์เดียวกันที่เชื่อถือได้เขียนทับกัน แต่คุกกี้ไม่สามารถแยกพอร์ต TCP ได้ ดังนั้นชื่อโฮสต์ของ Gateway จึงเป็นขอบเขตหนึ่งของข้อมูลรับรอง: อย่าโฮสต์บริการที่ไม่ไว้วางใจซึ่งกันและกันบนชื่อโฮสต์นั้น รวมถึงบนพอร์ตอื่น การส่งต่อเส้นทางจะปฏิเสธการนำสิทธิ์ไปใช้ซ้ำกับเส้นทางซ้อนที่ Plugin อื่นเป็นเจ้าของ เนื่องจากองค์ประกอบลูกของแซนด์บ็อกซ์ถือเป็นข้ามไซต์สำหรับวัตถุประสงค์ของคุกกี้ สิทธิ์นี้จึงยอมรับเฉพาะ GET และ HEAD พร้อม operator.read ส่วนการเปลี่ยนแปลงข้อมูลและการอัปเกรด WebSocket ยังคงอยู่บนพื้นผิวที่ผ่านการยืนยันตัวตนของ Gateway อย่างชัดเจน คุกกี้นี้ไม่สามารถใช้ CHIPS ได้โดยเจตนา: เบราว์เซอร์ปัจจุบันรวมบิตบรรพบุรุษข้ามไซต์ไว้ในคีย์พาร์ทิชัน ทำให้เฟรมแซนด์บ็อกซ์ทึบแบบซ้อนสูญเสียสิทธิ์เข้าถึงแอสเซ็ตของเส้นทางเดียวกัน คุกกี้ต้องใช้บริบทที่ปลอดภัยและสิทธิ์จากเบราว์เซอร์สำหรับคุกกี้ข้ามไซต์ ดังนั้นแท็บภายนอกที่ใช้การยืนยันตัวตนของ Gateway จึงไม่พร้อมใช้งานบนต้นทาง LAN แบบ HTTP ธรรมดาหรือเมื่อบล็อกคุกกี้ของบุคคลที่สามทั้งหมด ให้ใช้ HTTPS/Tailscale Serve หรือลูปแบ็กที่เบราว์เซอร์เชื่อถือพร้อมนโยบายคุกกี้ที่เข้ากันได้
  • สิทธิ์นี้ป้องกันการเปิดเผย bearer token ของ Gateway และการนำเส้นทาง/ขอบเขตไปใช้ซ้ำโดยไม่ตั้งใจ แต่ไม่ได้สร้างขอบเขตความปลอดภัยระหว่าง Plugin แบบเนทีฟ โค้ด Plugin แบบเนทีฟและเนื้อหา UI ที่โค้ดนั้นให้บริการยังคงเป็นส่วนหนึ่งของขอบเขต Plugin ภายในกระบวนการเดียวกันที่เชื่อถือได้
  • กฎในทางปฏิบัติ: อย่าถือว่าเส้นทาง Plugin ที่ใช้การยืนยันตัวตนของ Gateway เป็นพื้นผิวผู้ดูแลระบบโดยปริยาย หากเส้นทางต้องการพฤติกรรมเฉพาะผู้ดูแลระบบ ให้เลือกใช้พื้นผิวขอบเขต trusted-operator กำหนดให้ใช้โหมดการยืนยันตัวตนที่มีข้อมูลประจำตัว และจัดทำเอกสารสัญญาส่วนหัว x-openclaw-scopes อย่างชัดเจน
  • หลังจากจับคู่เส้นทางและยืนยันตัวตนแล้ว ตัวจัดการทั่วไปจะอยู่ภายใต้การรับงานระดับรากของ Gateway โดย Gateway ที่เตรียมพร้อมหรือกำลังเริ่มใหม่จะคืนค่า 503 ก่อนเรียกตัวจัดการ ข้อยกเว้นแบบจำกัดคือเส้นทาง auth: "gateway" ที่ได้รับสิทธิ์จากแมนิเฟสต์และเลือกใช้พื้นผิวเฉพาะเส้นทาง trusted-operator ด้วย เส้นทางนี้ยังคงเข้าถึงได้เพื่อไม่ให้การส่งคำสั่งควบคุมการระงับติดค้าง ขณะที่เส้นทางพี่น้องทั่วไปจาก Plugin เดียวกันยังคงอยู่หลังขอบเขตการรับงาน การเป็นเจ้าของ handleUpgrade ของ WebSocket ใช้ขอบเขตการรับงานแบบอะตอมเดียวกัน เมื่อใดที่ตัวจัดการยอมรับซ็อกเก็ตแล้ว อายุการใช้งานของซ็อกเก็ตหลังจากนั้นจะอยู่ภายใต้การดูแลของ Plugin และขอบเขตนี้จะไม่ติดตามอีกต่อไป

พาธนำเข้าของ Plugin SDK

เมื่อสร้าง Plugin ใหม่ ให้ใช้พาธย่อย SDK แบบเจาะจงแทน barrel ราก openclaw/plugin-sdk แบบรวมศูนย์ พาธย่อยหลัก:

พาธย่อย วัตถุประสงค์
openclaw/plugin-sdk/plugin-entry องค์ประกอบพื้นฐานสำหรับลงทะเบียน Plugin
openclaw/plugin-sdk/channel-core ตัวช่วยสำหรับจุดเข้าใช้งาน/การสร้างช่องทาง
openclaw/plugin-sdk/core ตัวช่วยร่วมทั่วไปและสัญญาครอบคลุมทั้งหมด

Plugin ช่องทางเลือกใช้จากชุดจุดเชื่อมต่อแบบเจาะจง ได้แก่ channel-setup, setup-runtime, setup-tools, channel-pairing, channel-contract, channel-feedback, channel-inbound, channel-outbound, command-auth, secret-input, webhook-ingress, channel-targets และ channel-actions พฤติกรรมการอนุมัติควรรวมไว้ใน สัญญา approvalCapability เดียว แทนการผสมข้ามฟิลด์ Plugin ที่ไม่เกี่ยวข้อง ดู Plugin ช่องทาง

ตัวช่วยรันไทม์และการกำหนดค่าอยู่ภายใต้พาธย่อย *-runtime แบบเจาะจงที่สอดคล้องกัน (approval-runtime, agent-runtime, lazy-runtime, directory-runtime, text-runtime, runtime-store, system-event-runtime, heartbeat-runtime, channel-activity-runtime เป็นต้น) ควรใช้ config-contracts, plugin-config-runtime, runtime-config-snapshot และ config-mutation แทน barrel ความเข้ากันได้แบบกว้าง config-runtime

จุดเข้าใช้งานภายในรีโพ (ต่อรากแพ็กเกจ Plugin ที่รวมมาให้แต่ละรายการ):

  • index.js — จุดเข้าใช้งานของ Plugin ที่รวมมาให้
  • api.js — barrel ตัวช่วย/ชนิดข้อมูล
  • runtime-api.js — barrel สำหรับรันไทม์เท่านั้น
  • setup-entry.js — จุดเข้าใช้งาน Plugin สำหรับการตั้งค่า

Plugin ภายนอกควรนำเข้าเฉพาะพาธย่อย openclaw/plugin-sdk/* เท่านั้น ห้าม นำเข้า src/* ของแพ็กเกจ Plugin อื่นจากแกนหลักหรือจาก Plugin อื่น จุดเข้าใช้งานที่โหลดผ่าน facade จะเลือกใช้สแนปช็อตการกำหนดค่ารันไทม์ที่ใช้งานอยู่เมื่อมี จากนั้นจึงใช้ไฟล์การกำหนดค่าที่แก้ไขพาธแล้วบนดิสก์เป็นทางเลือกสำรอง

พาธย่อยเฉพาะความสามารถ เช่น image-generation, media-understanding และ speech มีอยู่เพราะ Plugin ที่รวมมาให้ใช้งานอยู่ในปัจจุบัน พาธเหล่านี้ไม่ได้ กลายเป็นสัญญาภายนอกที่ตรึงไว้ระยะยาวโดยอัตโนมัติ โปรดตรวจสอบหน้าอ้างอิง SDK ที่เกี่ยวข้องเมื่อต้องพึ่งพาพาธเหล่านี้

สคีมาของเครื่องมือข้อความ

Plugin ควรเป็นเจ้าของส่วนเพิ่มเติมของสคีมา describeMessageTool(...) เฉพาะช่องทาง สำหรับองค์ประกอบที่ไม่ใช่ข้อความ เช่น ปฏิกิริยา การอ่าน และโพล การนำเสนอการส่งร่วมควรใช้สัญญา MessagePresentation แบบทั่วไป แทนฟิลด์ปุ่ม คอมโพเนนต์ บล็อก หรือการ์ดแบบเนทีฟของผู้ให้บริการ ดูสัญญา กฎทางเลือกสำรอง การแมปผู้ให้บริการ และรายการตรวจสอบสำหรับผู้สร้าง Plugin ได้ที่ การนำเสนอข้อความ

Plugin ที่ส่งข้อความได้จะประกาศสิ่งที่สามารถเรนเดอร์ผ่านความสามารถของข้อความ:

  • presentation สำหรับบล็อกการนำเสนอเชิงความหมาย (text, context, divider, chart, table, buttons, select)
  • delivery-pin สำหรับคำขอส่งแบบปักหมุด

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

การแก้ไขเป้าหมายช่องทาง

Plugin ช่องทางควรเป็นเจ้าของความหมายของเป้าหมายเฉพาะช่องทาง ให้โฮสต์ ขาออกร่วมยังคงเป็นแบบทั่วไป และใช้พื้นผิวอะแดปเตอร์การส่งข้อความสำหรับกฎของผู้ให้บริการ:

  • messaging.inferTargetChatType({ to }) ตัดสินใจว่าควรถือว่าเป้าหมายที่ทำให้เป็นมาตรฐานแล้ว เป็น direct, group หรือ channel ก่อนค้นหาในไดเรกทอรี
  • messaging.targetResolver.looksLikeId(raw, normalized) แจ้งแกนหลักว่าอินพุต ควรข้ามไปแก้ไขแบบคล้ายรหัสโดยตรงแทนการค้นหาไดเรกทอรีหรือไม่
  • messaging.targetResolver.reservedLiterals แสดงรายการคำเดี่ยวที่เป็น การอ้างอิงช่องทาง/เซสชันสำหรับผู้ให้บริการรายนั้น การแก้ไขจะรักษารายการไดเรกทอรี ที่กำหนดค่าไว้ก่อนปฏิเสธลิเทอรัลที่สงวนไว้ จากนั้นจะปฏิเสธแบบปิดเมื่อค้นหา ในไดเรกทอรีไม่พบ
  • messaging.targetResolver.resolveTarget(...) เป็นทางเลือกสำรองของ Plugin เมื่อ แกนหลักต้องการการแก้ไขขั้นสุดท้ายที่ผู้ให้บริการเป็นเจ้าของหลังการทำให้เป็นมาตรฐานหรือหลัง ค้นหาในไดเรกทอรีไม่พบ
  • messaging.resolveOutboundSessionRoute(...) เป็นเจ้าของการสร้างเส้นทางเซสชัน เฉพาะผู้ให้บริการเมื่อแก้ไขเป้าหมายแล้ว

การแบ่งที่แนะนำ:

  • ใช้ inferTargetChatType สำหรับการตัดสินใจประเภทที่ควรเกิดขึ้นก่อน ค้นหาเพียร์/กลุ่ม
  • ใช้ looksLikeId สำหรับการตรวจสอบว่า "ให้ถือว่านี่เป็นรหัสเป้าหมายแบบชัดเจน/เนทีฟ"
  • ใช้ resolveTarget สำหรับทางเลือกสำรองในการทำให้เป็นมาตรฐานเฉพาะผู้ให้บริการ ไม่ใช่สำหรับ การค้นหาไดเรกทอรีแบบกว้าง
  • เก็บรหัสแบบเนทีฟของผู้ให้บริการ เช่น รหัสแชต รหัสเธรด JID แฮนเดิล และรหัสห้อง ไว้ภายในค่า target หรือพารามิเตอร์เฉพาะผู้ให้บริการ ไม่ใช่ในฟิลด์ SDK ทั่วไป

ไดเรกทอรีที่อิงการกำหนดค่า

Plugin ที่สร้างรายการไดเรกทอรีจากการกำหนดค่าควรเก็บตรรกะนั้นไว้ใน Plugin และใช้ตัวช่วยร่วมจาก openclaw/plugin-sdk/directory-runtime ซ้ำ

ใช้แนวทางนี้เมื่อช่องทางต้องการเพียร์/กลุ่มที่อิงการกำหนดค่า เช่น:

  • เพียร์ DM ที่ขับเคลื่อนด้วยรายการอนุญาต
  • แมปช่องทาง/กลุ่มที่กำหนดค่าไว้
  • ทางเลือกสำรองของไดเรกทอรีแบบคงที่ซึ่งจำกัดตามบัญชี

ตัวช่วยร่วมใน directory-runtime จัดการเฉพาะการดำเนินการทั่วไป:

  • การกรองคิวรี
  • การใช้ขีดจำกัด
  • ตัวช่วยขจัดรายการซ้ำ/ทำให้เป็นมาตรฐาน
  • การสร้าง ChannelDirectoryEntry[]

การตรวจสอบบัญชีและการทำให้รหัสเป็นมาตรฐานเฉพาะช่องทางควรอยู่ใน การนำ Plugin ไปใช้

แค็ตตาล็อกผู้ให้บริการ

Plugin ผู้ให้บริการสามารถกำหนดแค็ตตาล็อกโมเดลสำหรับการอนุมานด้วย registerProvider({ catalog: { run(...) { ... } } })

catalog.run(...) คืนค่าในรูปแบบเดียวกับที่ OpenClaw เขียนลงใน models.providers:

  • { provider } สำหรับรายการผู้ให้บริการหนึ่งรายการ
  • { providers } สำหรับรายการผู้ให้บริการหลายรายการ

ใช้ catalog เมื่อ Plugin เป็นเจ้าของรหัสโมเดลเฉพาะผู้ให้บริการ ค่าเริ่มต้นของ URL ฐาน หรือข้อมูลเมตาของโมเดลที่เข้าถึงได้เมื่อผ่านการตรวจสอบสิทธิ์แล้ว

catalog.order ควบคุมว่าจะผสานแค็ตตาล็อกของ Plugin เมื่อใดโดยเทียบกับผู้ให้บริการโดยนัย ที่มีมาให้ใน OpenClaw:

  • simple: ผู้ให้บริการที่ใช้คีย์ API ธรรมดาหรือขับเคลื่อนด้วยตัวแปรสภาพแวดล้อม
  • profile: ผู้ให้บริการที่ปรากฏเมื่อมีโปรไฟล์การตรวจสอบสิทธิ์
  • paired: ผู้ให้บริการที่สังเคราะห์รายการผู้ให้บริการที่เกี่ยวข้องกันหลายรายการ
  • late: รอบสุดท้าย หลังจากผู้ให้บริการโดยนัยอื่นๆ

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

Plugin ยังสามารถเผยแพร่แถวโมเดลแบบอ่านอย่างเดียวผ่าน api.registerModelCatalogProvider({ provider, kinds, staticCatalog, liveCatalog }) ได้ด้วย นี่คือแนวทางในอนาคตสำหรับพื้นผิวรายการ/วิธีใช้/ตัวเลือก และรองรับแถว text, voice, image_generation, video_generation และ music_generation Plugin ผู้ให้บริการยังคงเป็นเจ้าของการเรียกปลายทางจริง การแลกเปลี่ยนโทเค็น และ การแมปการตอบกลับจากผู้ขาย ส่วนแกนหลักเป็นเจ้าของรูปแบบแถวร่วม ป้ายกำกับแหล่งที่มา และ การจัดรูปแบบวิธีใช้เครื่องมือสื่อ การลงทะเบียนผู้ให้บริการสร้างสื่อจะสังเคราะห์ แถวแค็ตตาล็อกแบบคงที่จาก defaultModel, models และ capabilities โดยอัตโนมัติ

ความเข้ากันได้:

  • discovery ยังคงทำงานเป็นนามแฝงแบบเดิม แต่จะแสดงคำเตือนการเลิกใช้งาน
  • หากลงทะเบียนทั้ง catalog และ discovery OpenClaw จะใช้ catalog และแสดงคำเตือน
  • augmentModelCatalog เลิกใช้แล้ว ผู้ให้บริการที่รวมมาให้ควรเผยแพร่ แถวเสริมผ่าน registerModelCatalogProvider

การตรวจสอบช่องทางแบบอ่านอย่างเดียว

หาก Plugin ของคุณลงทะเบียนช่องทาง ควรใช้ plugin.config.inspectAccount(cfg, accountId) ควบคู่กับ resolveAccount(...)

เหตุผล:

  • resolveAccount(...) คือเส้นทางรันไทม์ โดยสามารถถือว่าข้อมูลประจำตัว ได้รับการจัดเตรียมไว้อย่างสมบูรณ์ และสามารถล้มเหลวทันทีเมื่อไม่มีข้อมูลลับที่จำเป็น
  • เส้นทางคำสั่งแบบอ่านอย่างเดียว เช่น openclaw status, openclaw status --all, openclaw channels status, openclaw channels resolve และโฟลว์การซ่อมแซม doctor/การกำหนดค่า ไม่ควรต้องจัดเตรียมข้อมูลประจำตัวของรันไทม์เพียงเพื่อ อธิบายการกำหนดค่า

ลักษณะการทำงานที่แนะนำสำหรับ inspectAccount(...):

  • ส่งคืนเฉพาะสถานะบัญชีเชิงอธิบาย
  • คง enabled และ configured ไว้
  • รวมฟิลด์แหล่งที่มา/สถานะของข้อมูลประจำตัวเมื่อเกี่ยวข้อง เช่น:
    • tokenSource, tokenStatus
    • botTokenSource, botTokenStatus
    • appTokenSource, appTokenStatus
    • signingSecretSource, signingSecretStatus
  • ไม่จำเป็นต้องส่งคืนค่าโทเค็นดิบเพียงเพื่อรายงานความพร้อมใช้งาน แบบอ่านอย่างเดียว การส่งคืน tokenStatus: "available" (และฟิลด์แหล่งที่มา ที่ตรงกัน) ก็เพียงพอสำหรับคำสั่งประเภทแสดงสถานะ
  • ใช้ configured_unavailable เมื่อกำหนดค่าข้อมูลประจำตัวผ่าน SecretRef แต่ ไม่พร้อมใช้งานในเส้นทางคำสั่งปัจจุบัน

วิธีนี้ช่วยให้คำสั่งแบบอ่านอย่างเดียวรายงานว่า "กำหนดค่าแล้วแต่ไม่พร้อมใช้งานในเส้นทาง คำสั่งนี้" แทนที่จะขัดข้องหรือรายงานผิดว่าบัญชียังไม่ได้กำหนดค่า

แพ็กเกจรวม

ไดเรกทอรี Plugin อาจมี package.json ที่มี openclaw.extensions:

json
{  "name": "my-pack",  "openclaw": {    "extensions": ["./src/safety.ts", "./src/tools.ts"],    "setupEntry": "./src/setup-entry.ts"  }}

แต่ละรายการจะกลายเป็น Plugin หากแพ็กรวมส่วนขยายหลายรายการ รหัส Plugin จะเป็น <manifestOrPackageName>/<fileBase> (รหัสในไฟล์กำกับจะมีลำดับความสำคัญเมื่อ มีอยู่ มิฉะนั้นจะใช้ชื่อ package.json ที่ไม่มีขอบเขต)

หาก Plugin ของคุณนำเข้าการขึ้นต่อกันของ npm ให้ติดตั้งรายการเหล่านั้นในไดเรกทอรีนั้นเพื่อให้ node_modules พร้อมใช้งาน (npm install / pnpm install)

ข้อจำกัดด้านความปลอดภัย: ทุกรายการ openclaw.extensions ต้องยังคงอยู่ภายในไดเรกทอรี Plugin หลังจากแก้ไข symlink แล้ว รายการที่ออกนอกไดเรกทอรีแพ็กเกจจะ ถูกปฏิเสธ

หมายเหตุด้านความปลอดภัย: openclaw plugins install ติดตั้งการขึ้นต่อกันของ Plugin ด้วย npm install --omit=dev --ignore-scripts ภายในโปรเจกต์ (ไม่มีสคริปต์วงจรชีวิต และไม่มีการขึ้นต่อกันสำหรับการพัฒนาในรันไทม์) โดยไม่สนใจการตั้งค่าการติดตั้ง npm ส่วนกลางที่สืบทอดมา ควรรักษาโครงสร้างการขึ้นต่อกันของ Plugin ให้เป็น "JS/TS ล้วน" และหลีกเลี่ยงแพ็กเกจที่ต้องใช้ บิลด์ postinstall

ไม่บังคับ: openclaw.setupEntry สามารถชี้ไปยังโมดูลขนาดเล็กที่ใช้สำหรับการตั้งค่าเท่านั้น เมื่อ OpenClaw ต้องการพื้นผิวการตั้งค่าสำหรับ Plugin ช่องทางที่ปิดใช้งาน หรือ เมื่อเปิดใช้ Plugin ช่องทางแล้วแต่ยังไม่ได้กำหนดค่า ระบบจะโหลด setupEntry แทนรายการหลักของ Plugin วิธีนี้ช่วยให้การเริ่มทำงานและการตั้งค่าเบาลง เมื่อรายการหลักของ Plugin เชื่อมต่อเครื่องมือ ฮุก หรือโค้ดอื่นๆ ที่ใช้เฉพาะในรันไทม์ด้วย

ไม่บังคับ: openclaw.startup.deferConfiguredChannelFullLoadUntilAfterListen สามารถกำหนดให้ Plugin ช่องทางใช้เส้นทาง setupEntry เดียวกันในช่วงเริ่มทำงาน ก่อนรับฟังของ Gateway ได้ แม้ว่าช่องทางจะได้รับการกำหนดค่าแล้วก็ตาม

ใช้ตัวเลือกนี้เฉพาะเมื่อ setupEntry ครอบคลุมพื้นผิวการเริ่มทำงานที่ต้องมี ก่อน Gateway เริ่มรับฟังอย่างสมบูรณ์ ในทางปฏิบัติ หมายความว่ารายการตั้งค่า ต้องลงทะเบียนความสามารถทั้งหมดที่ช่องทางเป็นเจ้าของและการเริ่มทำงานพึ่งพา เช่น:

  • การลงทะเบียนช่องทางเอง
  • เส้นทาง HTTP ใดๆ ที่ต้องพร้อมใช้งานก่อน Gateway เริ่มรับฟัง
  • เมธอด Gateway เครื่องมือ หรือบริการใดๆ ที่ต้องมีอยู่ในช่วงเวลาเดียวกันนั้น

หากรายการแบบเต็มของคุณยังคงเป็นเจ้าของความสามารถที่จำเป็นต่อการเริ่มทำงาน อย่าเปิดใช้ แฟล็กนี้ ให้ Plugin ใช้ลักษณะการทำงานเริ่มต้นต่อไป และปล่อยให้ OpenClaw โหลด รายการแบบเต็มระหว่างการเริ่มทำงาน

ช่องทางที่รวมมาให้ยังสามารถเผยแพร่ตัวช่วยพื้นผิวสัญญาที่ใช้เฉพาะการตั้งค่า ซึ่งแกนหลัก สามารถเรียกใช้ก่อนโหลดรันไทม์ช่องทางแบบเต็มได้ พื้นผิวส่งเสริมการตั้งค่า ปัจจุบันคือ:

  • singleAccountKeysToMove
  • namedAccountPromotionKeys
  • resolveSingleAccountPromotionTarget(...)

แกนหลักใช้พื้นผิวดังกล่าวเมื่อต้องส่งเสริมการกำหนดค่าช่องทางบัญชีเดียวแบบเดิม ให้เป็น channels.<id>.accounts.* โดยไม่โหลดรายการ Plugin แบบเต็ม Matrix เป็นตัวอย่างที่รวมมาให้ในปัจจุบัน โดยจะย้ายเฉพาะคีย์การตรวจสอบสิทธิ์/การเริ่มต้นระบบไปยัง บัญชีที่มีชื่อซึ่งส่งเสริมขึ้นเมื่อมีบัญชีที่มีชื่ออยู่แล้ว และสามารถคง คีย์บัญชีเริ่มต้นที่กำหนดค่าไว้แต่ไม่เป็นแบบมาตรฐาน แทนที่จะสร้าง accounts.default เสมอ

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

เมื่อพื้นผิวการเริ่มทำงานเหล่านั้นมีเมธอด RPC ของ Gateway ให้ใช้คำนำหน้า เฉพาะ Plugin เนมสเปซการดูแลระบบของแกนหลัก (config.*, exec.approvals.*, wizard.*, update.*) ยังคงสงวนไว้และจะได้รับการแก้ไข เป็น operator.admin เสมอ แม้ว่า Plugin จะร้องขอขอบเขตที่แคบกว่าก็ตาม

ตัวอย่าง:

json
{  "name": "@scope/my-channel",  "openclaw": {    "extensions": ["./index.ts"],    "setupEntry": "./setup-entry.ts",    "startup": {      "deferConfiguredChannelFullLoadUntilAfterListen": true    }  }}

ข้อมูลเมตาแค็ตตาล็อกช่องทาง

Plugin ช่องทางสามารถประกาศข้อมูลเมตาสำหรับการตั้งค่า/การค้นพบผ่าน openclaw.channel และ คำแนะนำการติดตั้งผ่าน openclaw.install วิธีนี้ทำให้ข้อมูลแค็ตตาล็อกของแกนหลักไม่มีข้อมูลเฉพาะช่องทาง

ตัวอย่าง:

json
{  "name": "@openclaw/nextcloud-talk",  "openclaw": {    "extensions": ["./index.ts"],    "channel": {      "id": "nextcloud-talk",      "label": "Nextcloud Talk",      "selectionLabel": "Nextcloud Talk (โฮสต์เอง)",      "docsPath": "/channels/nextcloud-talk",      "docsLabel": "nextcloud-talk",      "blurb": "แชตที่โฮสต์เองผ่านบอต Webhook ของ Nextcloud Talk",      "order": 65,      "aliases": ["nc-talk", "nc"]    },    "install": {      "npmSpec": "@openclaw/nextcloud-talk",      "localPath": "<bundled-plugin-local-path>",      "defaultChoice": "npm"    }  }}

ฟิลด์ openclaw.channel ที่มีประโยชน์นอกเหนือจากตัวอย่างขั้นต่ำ:

  • detailLabel: ป้ายกำกับรองสำหรับพื้นผิวแค็ตตาล็อก/สถานะที่มีรายละเอียดมากขึ้น
  • docsLabel: แทนที่ข้อความลิงก์สำหรับลิงก์เอกสาร
  • preferOver: รหัส Plugin/ช่องทางที่มีลำดับความสำคัญต่ำกว่าซึ่งรายการแค็ตตาล็อกนี้ควรมีลำดับเหนือกว่า
  • selectionDocsPrefix, selectionDocsOmitLabel, selectionExtras: ตัวควบคุมข้อความบนพื้นผิวการเลือก
  • markdownCapable: ระบุว่าช่องทางรองรับ Markdown เพื่อใช้ตัดสินใจจัดรูปแบบขาออก
  • exposure.configured: ซ่อนช่องทางจากพื้นผิวรายการช่องทางที่กำหนดค่าแล้วเมื่อตั้งค่าเป็น false
  • exposure.setup: ซ่อนช่องทางจากตัวเลือกการตั้งค่า/กำหนดค่าแบบโต้ตอบเมื่อตั้งค่าเป็น false
  • exposure.docs: ระบุช่องทางว่าเป็นช่องทางภายใน/ส่วนตัวสำหรับพื้นผิวการนำทางเอกสาร
  • quickstartAllowFrom: กำหนดให้ช่องทางใช้โฟลว์เริ่มต้นอย่างรวดเร็วมาตรฐาน allowFrom
  • forceAccountBinding: กำหนดให้ต้องผูกบัญชีอย่างชัดเจนแม้ว่าจะมีเพียงบัญชีเดียว
  • preferSessionLookupForAnnounceTarget: เลือกใช้การค้นหาเซสชันเป็นอันดับแรกเมื่อแก้ไขเป้าหมายการประกาศ

OpenClaw ยังสามารถผสาน แค็ตตาล็อกช่องทางภายนอก (เช่น ข้อมูลส่งออกจากรีจิสทรี MPM) ได้ วางไฟล์ JSON ไว้ที่ตำแหน่งใดตำแหน่งหนึ่งต่อไปนี้:

  • ~/.openclaw/mpm/plugins.json
  • ~/.openclaw/mpm/catalog.json
  • ~/.openclaw/plugins/catalog.json

หรือกำหนดให้ OPENCLAW_PLUGIN_CATALOG_PATHS (หรือ OPENCLAW_MPM_CATALOG_PATHS) ชี้ไปยัง ไฟล์ JSON อย่างน้อยหนึ่งไฟล์ (คั่นด้วยจุลภาค/อัฒภาค/PATH) แต่ละไฟล์ควร มี { "entries": [ { "name": "@scope/pkg", "openclaw": { "channel": {...}, "install": {...} } } ] } ตัวแยกวิเคราะห์ยังยอมรับ "packages" หรือ "plugins" เป็นนามแฝงแบบเดิมสำหรับคีย์ "entries"

รายการแค็ตตาล็อกช่องทางและรายการแค็ตตาล็อกการติดตั้งผู้ให้บริการที่สร้างขึ้นจะแสดง ข้อเท็จจริงแหล่งที่มาการติดตั้งที่ปรับเป็นมาตรฐานไว้ข้างบล็อก openclaw.install ดิบ ข้อเท็จจริงที่ปรับเป็นมาตรฐานจะระบุว่าข้อกำหนด npm เป็นเวอร์ชันแบบเจาะจงหรือตัวเลือก แบบลอยตัว มีข้อมูลเมตาความสมบูรณ์ที่คาดไว้หรือไม่ และมี เส้นทางแหล่งที่มาภายในเครื่องด้วยหรือไม่ เมื่อทราบอัตลักษณ์ของแค็ตตาล็อก/แพ็กเกจ ข้อเท็จจริงที่ปรับเป็นมาตรฐานจะแจ้งเตือนหากชื่อแพ็กเกจ npm ที่แยกวิเคราะห์ได้เบี่ยงเบนจากอัตลักษณ์นั้น นอกจากนี้ยังแจ้งเตือนเมื่อ defaultChoice ไม่ถูกต้องหรือชี้ไปยังแหล่งที่มา ที่ไม่พร้อมใช้งาน และเมื่อมีข้อมูลเมตาความสมบูรณ์ของ npm โดยไม่มีแหล่งที่มา npm ที่ถูกต้อง ผู้ใช้ข้อมูลควรถือว่า installSource เป็นฟิลด์เสริมแบบเพิ่มเข้าไป เพื่อให้ รายการที่สร้างด้วยตนเองและชิมแค็ตตาล็อกไม่จำเป็นต้องสังเคราะห์ฟิลด์นี้ วิธีนี้ช่วยให้การเริ่มต้นใช้งานและการวินิจฉัยอธิบายสถานะของระนาบแหล่งที่มาได้โดยไม่ต้อง นำเข้ารันไทม์ของ Plugin

รายการ npm ภายนอกอย่างเป็นทางการควรใช้ npmSpec แบบเจาะจงร่วมกับ expectedIntegrity เป็นหลัก ชื่อแพ็กเกจเปล่าและ dist-tag ยังคงทำงานได้เพื่อ ความเข้ากันได้ แต่จะแสดงคำเตือนของระนาบแหล่งที่มา เพื่อให้แค็ตตาล็อกสามารถมุ่งไปสู่ การติดตั้งแบบตรึงเวอร์ชันและตรวจสอบความสมบูรณ์โดยไม่ทำให้ Plugin ที่มีอยู่เสียหาย เมื่อการเริ่มต้นใช้งานติดตั้งจากเส้นทางแค็ตตาล็อกภายในเครื่อง ระบบจะบันทึกรายการดัชนี Plugin ที่มีการจัดการด้วย source: "path" และ sourcePath ที่สัมพันธ์กับเวิร์กสเปซเมื่อเป็นไปได้ เส้นทางโหลดเชิงปฏิบัติการแบบสัมบูรณ์จะยังคงอยู่ใน plugins.load.paths ส่วนระเบียนการติดตั้งจะหลีกเลี่ยงการทำซ้ำเส้นทางเวิร์กสเตชันภายในเครื่อง ลงในการกำหนดค่าระยะยาว วิธีนี้ช่วยให้การติดตั้งเพื่อการพัฒนาภายในเครื่องยังคงมองเห็นได้ในการ วินิจฉัยระนาบแหล่งที่มา โดยไม่เพิ่มพื้นผิวเปิดเผยเส้นทางระบบไฟล์ดิบอีกชุดหนึ่ง ตาราง SQLite installed_plugin_index ที่บันทึกถาวรเป็นแหล่งข้อมูลจริงของการติดตั้ง และสามารถรีเฟรชได้โดยไม่ต้องโหลดโมดูลรันไทม์ของ Plugin แมป installRecords ของตารางนี้จะคงทนแม้ว่าไฟล์กำกับ Plugin จะหายไปหรือ ไม่ถูกต้อง ส่วนเพย์โหลด plugins เป็นมุมมองไฟล์กำกับที่สร้างใหม่ได้

Plugin กลไกบริบท

Plugin กลไกบริบทเป็นเจ้าของการประสานบริบทเซสชันสำหรับการรับเข้า การประกอบ และ Compaction ลงทะเบียนจาก Plugin ของคุณด้วย api.registerContextEngine(id, factory) จากนั้นเลือกกลไกที่ใช้งานด้วย plugins.slots.contextEngine

ใช้วิธีนี้เมื่อ Plugin ของคุณต้องแทนที่หรือขยายไปป์ไลน์บริบทเริ่มต้น แทนที่จะเพียงเพิ่มการค้นหาหน่วยความจำหรือฮุก

ts
 export default function (api) {  api.registerContextEngine("lossless-claw", (ctx) => ({    info: { id: "lossless-claw", name: "Lossless Claw", ownsCompaction: true },    async ingest() {      return { ingested: true };    },    async assemble({ messages, sessionKey, availableTools, citationsMode }) {      return {        messages,        estimatedTokens: 0,        systemPromptAddition: buildMemorySystemPromptAddition({          availableTools: availableTools ?? new Set(),          citationsMode,          agentSessionKey: sessionKey,        }),      };    },    async compact() {      return { ok: true, compacted: false };    },  }));}

แฟกทอรี ctx เปิดเผยค่า config, agentDir และ workspaceDir ที่เป็นทางเลือกสำหรับการกำหนดค่าเริ่มต้นในขณะสร้าง

โฮสต์เตรียมพรอมต์หน่วยความจำแบบอะซิงโครนัสที่ลงทะเบียนไว้จนเสร็จสิ้นก่อนเรียก assemble() ของเอนจินที่ไม่ใช่แบบเดิม buildMemorySystemPromptAddition(...) ยังคง ทำงานแบบซิงโครนัสและอ่านสแนปช็อตการรันแบบเปลี่ยนแปลงไม่ได้ดังกล่าวขณะที่ assemble() ทำงานอยู่ ส่งต่อบริบทของเครื่องมือและการอ้างอิงที่ได้รับมาโดยไม่เปลี่ยนแปลง เพื่อไม่ให้สแนปช็อต ข้ามขอบเขตการรัน

assemble() อาจส่งคืน contextProjection เมื่อฮาร์เนสที่ทำงานอยู่มี เธรดแบ็กเอนด์แบบถาวร ให้ละเว้นค่านี้สำหรับการฉายภาพรายเทิร์นแบบเดิม ส่งคืน { mode: "thread_bootstrap", epoch } เมื่อควรแทรกบริบทที่ประกอบแล้ว ลงในเธรดแบ็กเอนด์หนึ่งครั้งและนำกลับมาใช้ซ้ำจนกว่ายุคจะเปลี่ยนแปลง ให้เปลี่ยน ยุคหลังจากบริบทเชิงความหมายของเอนจินเปลี่ยนแปลง เช่น หลังจากรอบ Compaction ที่เอนจินเป็นเจ้าของ โฮสต์อาจเก็บรักษาเมทาดาทาของการเรียกเครื่องมือ รูปแบบ อินพุต และผลลัพธ์เครื่องมือที่ปกปิดข้อมูลแล้วไว้ในการฉายภาพเริ่มต้นเธรด เพื่อให้เธรด แบ็กเอนด์ใหม่ยังคงความต่อเนื่องของเครื่องมือโดยไม่คัดลอกเพย์โหลดดิบ ที่มีข้อมูลลับ

หากเอนจินของคุณ ไม่ได้ เป็นเจ้าของอัลกอริทึม Compaction ให้คงการใช้งาน compact() ไว้และมอบหมายการทำงานอย่างชัดเจน:

ts
   buildMemorySystemPromptAddition,  delegateCompactionToRuntime,} from "openclaw/plugin-sdk/core"; export default function (api) {  api.registerContextEngine("my-memory-engine", (ctx) => ({    info: {      id: "my-memory-engine",      name: "My Memory Engine",      ownsCompaction: false,    },    async ingest() {      return { ingested: true };    },    async assemble({ messages, sessionKey, availableTools, citationsMode }) {      return {        messages,        estimatedTokens: 0,        systemPromptAddition: buildMemorySystemPromptAddition({          availableTools: availableTools ?? new Set(),          citationsMode,          agentSessionKey: sessionKey,        }),      };    },    async compact(params) {      return await delegateCompactionToRuntime(params);    },  }));}

การเพิ่มความสามารถใหม่

เมื่อ Plugin ต้องการพฤติกรรมที่ไม่เข้ากับ API ปัจจุบัน อย่าหลีกเลี่ยง ระบบ Plugin ด้วยการเข้าถึงภายในแบบส่วนตัว ให้เพิ่มความสามารถที่ขาดหายไป

ลำดับที่แนะนำ:

  1. กำหนดสัญญาของแกนหลัก ตัดสินใจว่าแกนหลักควรเป็นเจ้าของพฤติกรรมที่ใช้ร่วมกันใดบ้าง: นโยบาย การทำงานสำรอง การผสานการกำหนดค่า วงจรชีวิต ความหมายที่แสดงต่อช่องทาง และ รูปแบบตัวช่วยรันไทม์
  2. เพิ่มพื้นผิวการลงทะเบียน Plugin/รันไทม์ที่มีชนิดข้อมูล ขยาย OpenClawPluginApi และ/หรือ api.runtime ด้วยพื้นผิวความสามารถแบบมีชนิดข้อมูล ที่เล็กที่สุดและเป็นประโยชน์
  3. เชื่อมต่อแกนหลักกับผู้ใช้ช่องทาง/ฟีเจอร์ ช่องทางและ Plugin ฟีเจอร์ ควรใช้ความสามารถใหม่ผ่านแกนหลัก ไม่ใช่นำเข้าการใช้งานของผู้ให้บริการโดยตรง
  4. ลงทะเบียนการใช้งานของผู้ให้บริการ จากนั้น Plugin ของผู้ให้บริการจึงลงทะเบียน แบ็กเอนด์ของตนกับความสามารถนั้น
  5. เพิ่มการครอบคลุมสัญญา เพิ่มการทดสอบเพื่อให้รูปแบบความเป็นเจ้าของและการลงทะเบียน ยังคงระบุไว้อย่างชัดเจนเมื่อเวลาผ่านไป

นี่คือวิธีที่ OpenClaw รักษาแนวทางที่ชัดเจนโดยไม่ถูกฮาร์ดโค้ดให้ยึดติดกับมุมมองของ ผู้ให้บริการรายเดียว ดูรายการตรวจสอบไฟล์และตัวอย่างโดยละเอียดได้ที่ คู่มือความสามารถ

รายการตรวจสอบความสามารถ

เมื่อเพิ่มความสามารถใหม่ โดยทั่วไปการใช้งานควรแก้ไขพื้นผิวต่อไปนี้ ร่วมกัน:

  • ชนิดสัญญาของแกนหลักใน src/<capability>/types.ts
  • ตัวรัน/ตัวช่วยรันไทม์ของแกนหลักใน src/<capability>/runtime.ts
  • พื้นผิวการลงทะเบียน API ของ Plugin ใน src/plugins/types.ts
  • การเชื่อมต่อรีจิสทรี Plugin ใน src/plugins/registry.ts
  • การเปิดเผยรันไทม์ของ Plugin ใน src/plugins/runtime/* เมื่อ Plugin ฟีเจอร์/ช่องทาง จำเป็นต้องใช้งาน
  • ตัวช่วยบันทึก/ทดสอบใน src/test-utils/plugin-registration.ts
  • การยืนยันความเป็นเจ้าของ/สัญญาใน src/plugins/contracts/registry.ts
  • เอกสารสำหรับผู้ดำเนินการ/Plugin ใน docs/

หากพื้นผิวใดพื้นผิวหนึ่งขาดหายไป โดยทั่วไปเป็นสัญญาณว่าความสามารถนั้น ยังไม่ได้ผสานรวมอย่างสมบูรณ์

เทมเพลตความสามารถ

รูปแบบขั้นต่ำ:

ts
// core contractexport type VideoGenerationProviderPlugin = {  id: string;  label: string;  generateVideo: (req: VideoGenerationRequest) => Promise&lt;VideoGenerationResult&gt;;}; // plugin APIapi.registerVideoGenerationProvider({  id: "openai",  label: "OpenAI",  async generateVideo(req) {    return await generateOpenAiVideo(req);  },}); // shared runtime helper for feature/channel pluginsconst clip = await api.runtime.videoGeneration.generate({  prompt: "Show the robot walking through the lab.",  cfg,});

รูปแบบการทดสอบสัญญา (src/plugins/contracts/registry.ts เปิดเผยการค้นหา ความเป็นเจ้าของ เช่น providerContractPluginIds; การทดสอบยืนยันว่ารายการ contracts.videoGenerationProviders ของ Plugin ตรงกับสิ่งที่ Plugin ลงทะเบียนจริง):

ts
expect(pluginManifest.contracts?.videoGenerationProviders).toEqual(["openai"]);

ซึ่งทำให้กฎเรียบง่าย:

  • แกนหลักเป็นเจ้าของสัญญาความสามารถและการประสานงาน
  • Plugin ของผู้ให้บริการเป็นเจ้าของการใช้งานของผู้ให้บริการ
  • Plugin ฟีเจอร์/ช่องทางใช้ตัวช่วยรันไทม์
  • การทดสอบสัญญาทำให้ความเป็นเจ้าของยังคงชัดเจน

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

Was this useful?
On this page

On this page