Building plugins
ฮุกของ Plugin
ฮุกของ Plugin เป็นจุดขยายในกระบวนการสำหรับ Plugin ของ OpenClaw ใช้ฮุกเหล่านี้ เมื่อ Plugin ต้องตรวจสอบหรือเปลี่ยนแปลงการรันของเอเจนต์ การเรียกเครื่องมือ โฟลว์ข้อความ วงจรชีวิตเซสชัน การกำหนดเส้นทางซับเอเจนต์ การติดตั้ง หรือการเริ่มต้น Gateway
ใช้ ฮุกภายใน แทนเมื่อคุณต้องการสคริปต์ HOOK.md
ขนาดเล็กที่ติดตั้งโดยผู้ปฏิบัติงานสำหรับเหตุการณ์คำสั่งและ Gateway เช่น
/new, /reset, /stop, agent:bootstrap หรือ gateway:startup
เริ่มต้นอย่างรวดเร็ว
ลงทะเบียนฮุก Plugin แบบมีชนิดด้วย api.on(...) จากเอนทรีของ Plugin:
export default definePluginEntry({ id: "tool-preflight", name: "Tool Preflight", register(api) { api.on( "before_tool_call", async (event) => { if (event.toolName !== "web_search") { return; } return { requireApproval: { title: "Run web search", description: `Allow search query: ${String(event.params.query ?? "")}`, severity: "info", timeoutMs: 60_000, timeoutBehavior: "deny", }, }; }, { priority: 50 }, ); },});ตัวจัดการฮุกทำงานตามลำดับจาก priority สูงไปต่ำ ฮุกที่มี priority เท่ากัน
จะคงลำดับการลงทะเบียนไว้
api.on(name, handler, opts?) รับค่า:
priority- ลำดับการทำงานของตัวจัดการ (ค่าสูงกว่าจะทำงานก่อน)timeoutMs- งบเวลาต่อฮุกแบบไม่บังคับ เมื่อตั้งค่า ตัวรันฮุกจะยกเลิก ตัวจัดการนั้นหลังงบเวลาหมดลงและทำงานตัวถัดไปต่อ แทนที่จะปล่อยให้การตั้งค่า หรือการเรียกคืนที่ช้ากิน timeout โมเดลที่ผู้เรียกกำหนดไว้ ละไว้เพื่อใช้ timeout การสังเกต/การตัดสินใจเริ่มต้นที่ตัวรันฮุกนำไปใช้แบบทั่วไป
ผู้ปฏิบัติงานยังตั้งงบเวลาฮุกได้โดยไม่ต้องแพตช์โค้ด Plugin:
{ "plugins": { "entries": { "my-plugin": { "hooks": { "timeoutMs": 30000, "timeouts": { "before_prompt_build": 90000, "agent_end": 60000 } } } } }}hooks.timeouts.<hookName> จะแทนที่ hooks.timeoutMs ซึ่งจะแทนที่ค่า
api.on(..., { timeoutMs }) ที่ Plugin เขียนไว้ ค่าที่กำหนดแต่ละค่าต้องเป็น
จำนวนเต็มบวกที่ไม่เกิน 600000 มิลลิวินาที ควรใช้การแทนที่รายฮุกสำหรับฮุกที่ทราบว่า
ช้า เพื่อไม่ให้ Plugin หนึ่งได้รับงบเวลาที่ยาวขึ้นทุกที่
แต่ละฮุกจะได้รับ event.context.pluginConfig ซึ่งเป็น config ที่ resolve แล้วสำหรับ
Plugin ที่ลงทะเบียนตัวจัดการนั้น ใช้ค่านี้สำหรับการตัดสินใจของฮุกที่ต้องใช้ตัวเลือก
Plugin ปัจจุบัน OpenClaw ฉีดค่านี้ให้ต่อหนึ่งตัวจัดการโดยไม่กลายพันธุ์อ็อบเจกต์
เหตุการณ์ร่วมที่ Plugin อื่นเห็น
แคตตาล็อกฮุก
ฮุกถูกจัดกลุ่มตามพื้นผิวที่ฮุกขยาย ชื่อที่เป็น ตัวหนา รับผลการตัดสินใจได้ (บล็อก ยกเลิก แทนที่ หรือขออนุมัติ); รายการอื่นทั้งหมดเป็นการสังเกตเท่านั้น
เทิร์นเอเจนต์
before_model_resolve- แทนที่ผู้ให้บริการหรือโมเดลก่อนโหลดข้อความเซสชันagent_turn_prepare- ใช้การฉีดเทิร์นของ Plugin ที่เข้าคิวไว้ และเพิ่มบริบทในเทิร์นเดียวกันก่อนฮุกพรอมป์before_prompt_build- เพิ่มบริบทแบบไดนามิกหรือข้อความ system prompt ก่อนการเรียกโมเดลbefore_agent_start- เฟสรวมเพื่อความเข้ากันได้เท่านั้น; ควรใช้สองฮุกด้านบนbefore_agent_run- ตรวจสอบพรอมป์สุดท้ายและข้อความเซสชันก่อนส่งไปยังโมเดล และเลือกบล็อกการรันได้before_agent_reply- ตัดเทิร์นโมเดลให้สั้นลงด้วยการตอบกลับสังเคราะห์หรือความเงียบbefore_agent_finalize- ตรวจสอบคำตอบสุดท้ายตามธรรมชาติและขอให้โมเดลทำงานอีกหนึ่งรอบagent_end- สังเกตข้อความสุดท้าย สถานะสำเร็จ และระยะเวลาการรันheartbeat_prompt_contribution- เพิ่มบริบทเฉพาะ Heartbeat สำหรับ Plugin มอนิเตอร์เบื้องหลังและวงจรชีวิต
การสังเกตบทสนทนา
model_call_started/model_call_ended- สังเกตเมทาดาทาการเรียกผู้ให้บริการ/โมเดลที่ผ่านการทำให้ปลอดภัยแล้ว เวลา ผลลัพธ์ และแฮช request id ที่มีขอบเขต โดยไม่มีเนื้อหาพรอมป์หรือคำตอบllm_input- สังเกตอินพุตของผู้ให้บริการ (system prompt, prompt, history)llm_output- สังเกตเอาต์พุตของผู้ให้บริการ การใช้งาน และcontextTokenBudgetที่ resolve แล้วเมื่อมี
เครื่องมือ
before_tool_call- เขียนพารามิเตอร์เครื่องมือใหม่ บล็อกการทำงาน หรือขออนุมัติafter_tool_call- สังเกตผลลัพธ์เครื่องมือ ข้อผิดพลาด และระยะเวลาresolve_exec_env- เพิ่มตัวแปรสภาพแวดล้อมที่ Plugin เป็นเจ้าของให้กับexectool_result_persist- เขียนข้อความ assistant ที่ผลิตจากผลลัพธ์เครื่องมือใหม่before_message_write- ตรวจสอบหรือบล็อกการเขียนข้อความที่กำลังดำเนินอยู่ (พบไม่บ่อย)
ข้อความและการส่งมอบ
inbound_claim- claim ข้อความขาเข้าก่อนการกำหนดเส้นทางเอเจนต์ (การตอบกลับสังเคราะห์)message_received— สังเกตเนื้อหาขาเข้า ผู้ส่ง เธรด และเมทาดาทาmessage_sending— เขียนเนื้อหาขาออกใหม่หรือยกเลิกการส่งมอบreply_payload_sending— กลายพันธุ์หรือยกเลิก payload การตอบกลับที่ normalize แล้วก่อนส่งมอบmessage_sent— สังเกตความสำเร็จหรือความล้มเหลวของการส่งมอบขาออกbefore_dispatch- ตรวจสอบหรือเขียน dispatch ขาออกใหม่ก่อนส่งต่อให้ช่องทางreply_dispatch- เข้าร่วมใน pipeline dispatch การตอบกลับสุดท้าย
เซสชันและ Compaction
session_start/session_end- ติดตามขอบเขตวงจรชีวิตเซสชันreasonของเหตุการณ์เป็นหนึ่งในnew,reset,idle,daily,compaction,deleted,shutdown,restartหรือunknownค่าshutdownและrestartจะทำงานจากตัวปิดท้ายการปิด Gateway เมื่อกระบวนการถูกหยุดหรือรีสตาร์ตขณะที่เซสชันยังทำงานอยู่ เพื่อให้ Plugin ปลายทาง (เช่น memory หรือ transcript store) ปิดท้ายแถว ghost ที่ไม่เช่นนั้นจะถูกทิ้งไว้ในสถานะเปิดข้ามการรีสตาร์ตได้ ตัวปิดท้ายมีขอบเขตเวลาเพื่อไม่ให้ Plugin ที่ช้าบล็อก SIGTERM/SIGINT ได้before_compaction/after_compaction- สังเกตหรือใส่หมายเหตุรอบ Compactionbefore_reset- สังเกตเหตุการณ์รีเซ็ตเซสชัน (/reset, การรีเซ็ตผ่านโปรแกรม)
ซับเอเจนต์
subagent_spawned/subagent_ended- สังเกตการเปิดตัวและการเสร็จสิ้นของซับเอเจนต์subagent_delivery_target- ฮุกความเข้ากันได้สำหรับการส่งมอบเมื่อเสร็จสิ้นเมื่อไม่มีการผูกเซสชันหลักที่ฉายเส้นทางได้subagent_spawning- ฮุกความเข้ากันได้ที่เลิกใช้แล้ว ตอนนี้ core เตรียมการผูกซับเอเจนต์thread: trueผ่านอะแดปเตอร์ session-binding ของช่องทางก่อนที่subagent_spawnedจะทำงานsubagent_spawnedรวมresolvedModelและresolvedProviderเมื่อ OpenClaw resolve โมเดลเนทีฟของเซสชันลูกก่อนเปิดตัวแล้วsubagent_endedพกtargetSessionKey(ตัวตน — ค่านี้ตรงกับsubagent_spawned.childSessionKey),targetKind("subagent"หรือ"acp"),reason,outcomeแบบไม่บังคับ ("ok","error","timeout","killed","reset"หรือ"deleted"),errorแบบไม่บังคับ,runId,endedAt,accountIdและsendFarewellโดย ไม่ รวมagentIdหรือchildSessionKey; ใช้targetSessionKeyเพื่อเชื่อมโยงกับเหตุการณ์subagent_spawnedที่สอดคล้องกัน
วงจรชีวิต
gateway_start/gateway_stop- เริ่มหรือหยุดบริการที่ Plugin เป็นเจ้าของพร้อมกับ Gatewaydeactivate- alias ความเข้ากันได้ที่เลิกใช้แล้วสำหรับgateway_stop; ใช้gateway_stopใน Plugin ใหม่cron_changed- สังเกตการเปลี่ยนแปลงวงจรชีวิต Cron ที่ Gateway เป็นเจ้าของ (เพิ่ม อัปเดต ลบ เริ่ม เสร็จสิ้น ตั้งเวลา)before_install- ตรวจสอบวัสดุติดตั้ง skill หรือ Plugin ที่ staging แล้วจาก runtime Plugin ที่โหลดไว้
ดีบักฮุก runtime
ใช้ before_model_resolve เมื่อ Plugin ต้องสลับผู้ให้บริการหรือโมเดล
สำหรับเทิร์นเอเจนต์ ฮุกนี้ทำงานก่อนการ resolve โมเดล; llm_output จะทำงานหลังจาก
ความพยายามใช้โมเดลผลิตเอาต์พุต assistant แล้วเท่านั้น
เพื่อพิสูจน์โมเดลเซสชันที่มีผลจริง ให้ตรวจสอบการลงทะเบียน runtime จากนั้นใช้
openclaw sessions หรือพื้นผิวเซสชัน/สถานะของ Gateway เมื่อดีบัก payload
ของผู้ให้บริการ ให้เริ่ม Gateway ด้วย --raw-stream และ
--raw-stream-path <path>; flags เหล่านั้นจะเขียนเหตุการณ์ raw model stream ไปยังไฟล์
jsonl
นโยบายการเรียกเครื่องมือ
before_tool_call ได้รับ:
event.toolNameevent.paramsevent.toolKindและevent.toolInputKindแบบไม่บังคับ ซึ่งเป็นตัวจำแนกที่ host มีอำนาจตัดสิน สำหรับเครื่องมือที่ตั้งใจใช้ชื่อร่วมกัน; ตัวอย่างเช่น การเรียกexecในโหมด code ภายนอก ใช้toolKind: "code_mode_exec"และรวมtoolInputKind: "javascript" | "typescript"เมื่อทราบภาษาอินพุตevent.derivedPathsแบบไม่บังคับ ซึ่งมีคำใบ้ path เป้าหมายที่ host อนุมานแบบดีที่สุดเท่าที่ทำได้ สำหรับ envelope เครื่องมือที่รู้จักดี เช่นapply_patch; เมื่อมี path เหล่านี้อาจไม่สมบูรณ์หรืออาจประมาณเกินสิ่งที่เครื่องมือจะแตะต้องจริง (ตัวอย่างเช่น เมื่ออินพุตผิดรูปหรือเป็นบางส่วน)event.runIdแบบไม่บังคับevent.toolCallIdแบบไม่บังคับ- ฟิลด์บริบท เช่น
ctx.agentId,ctx.sessionKey,ctx.sessionId,ctx.runId,ctx.jobId(ตั้งค่าในการรันที่ขับเคลื่อนด้วย Cron),ctx.toolKind,ctx.toolInputKindและctx.traceเพื่อการวินิจฉัย
สามารถคืนค่า:
type BeforeToolCallResult = { params?: Record<string, unknown>; block?: boolean; blockReason?: string; requireApproval?: { title: string; description: string; severity?: "info" | "warning" | "critical"; timeoutMs?: number; timeoutBehavior?: "allow" | "deny"; allowedDecisions?: Array<"allow-once" | "allow-always" | "deny">; pluginId?: string; onResolution?: ( decision: "allow-once" | "allow-always" | "deny" | "timeout" | "cancelled", ) => Promise<void> | void; };};พฤติกรรม guard ของฮุกสำหรับฮุกวงจรชีวิตแบบมีชนิด:
block: trueเป็นค่าสุดท้ายและข้ามตัวจัดการที่มี priority ต่ำกว่าblock: falseถือว่าไม่มีการตัดสินใจparamsเขียนพารามิเตอร์เครื่องมือใหม่สำหรับการทำงานrequireApprovalหยุดการรันเอเจนต์ชั่วคราวและถามผู้ใช้ผ่านการอนุมัติของ Plugin คำสั่ง/approveสามารถอนุมัติได้ทั้ง exec และการอนุมัติของ Plugin ใน relayPreToolUseเนทีฟของโหมด report ใน Codex app-server สิ่งนี้จะถูกเลื่อนไปยัง คำขออนุมัติ app-server ที่ตรงกัน; ดู runtime ฮาร์เนส Codexblock: trueที่มี priority ต่ำกว่ายังสามารถบล็อกได้หลังจากฮุกที่มี priority สูงกว่า ขออนุมัติแล้วonResolutionได้รับการตัดสินใจอนุมัติที่ resolve แล้ว -allow-once,allow-always,deny,timeoutหรือcancelled
ดู คำขอสิทธิ์ของ Plugin สำหรับ
การกำหนดเส้นทางการอนุมัติ พฤติกรรมการตัดสินใจ และเวลาที่ควรใช้ requireApproval แทน
เครื่องมือแบบไม่บังคับหรือการอนุมัติ exec
Plugin ที่ต้องการนโยบายระดับ host สามารถลงทะเบียนนโยบายเครื่องมือที่เชื่อถือได้ด้วย
api.registerTrustedToolPolicy(...) นโยบายเหล่านี้ทำงานก่อนฮุก
before_tool_call ปกติ และก่อนการตัดสินใจของฮุกตามปกติ นโยบายที่เชื่อถือได้แบบ bundled
ทำงานก่อน; นโยบายที่เชื่อถือได้ของ Plugin ที่ติดตั้งแล้วทำงานถัดไปตามลำดับการโหลด Plugin;
ฮุก before_tool_call ปกติทำงานหลังจากนั้น Plugin แบบ bundled คง path
trusted-policy ที่มีอยู่ไว้ Plugin ที่ติดตั้งแล้วต้องเปิดใช้อย่างชัดเจน
และประกาศ id นโยบายทุกตัวใน contracts.trustedToolPolicies; id ที่ไม่ได้ประกาศ
จะถูกปฏิเสธก่อนการลงทะเบียน id นโยบายถูกจำกัดขอบเขตตาม Plugin ที่ลงทะเบียน
ดังนั้น Plugin ต่างกันอาจใช้ id ภายในเดียวกันซ้ำได้ ใช้ชั้นนี้เฉพาะสำหรับ
ด่านที่ host เชื่อถือ เช่น นโยบาย workspace การบังคับใช้งบประมาณ หรือ
ความปลอดภัยของ workflow ที่สงวนไว้
ฮุกสภาพแวดล้อม Exec
resolve_exec_env ให้ Plugin เพิ่มตัวแปรสภาพแวดล้อมให้กับการเรียกเครื่องมือ
exec หลังจากสร้างสภาพแวดล้อม exec พื้นฐานแล้วและก่อนที่คำสั่งจะทำงาน โดยได้รับ:
event.sessionKeyevent.toolName, ปัจจุบันเป็น"exec"เสมอevent.host, หนึ่งใน"gateway","sandbox"หรือ"node"- ฟิลด์บริบท เช่น
ctx.agentId,ctx.sessionKey,ctx.messageProviderและctx.channelId
คืนค่า Record<string, string> เพื่อ merge เข้าในสภาพแวดล้อม exec ตัวจัดการ
ทำงานตามลำดับ priority และผลลัพธ์ฮุกที่มาทีหลังจะแทนที่ผลลัพธ์ฮุกที่มาก่อน
สำหรับ key เดียวกัน
เอาต์พุตของ hook จะถูกกรองผ่านนโยบายคีย์ของสภาพแวดล้อม exec ฝั่งโฮสต์ก่อน
ที่จะถูกผสาน คีย์ที่ไม่ถูกต้อง, PATH, และคีย์อันตรายที่เขียนทับโฮสต์ เช่น
LD_*, DYLD_*, NODE_OPTIONS, ตัวแปร proxy และตัวแปรเขียนทับ TLS
จะถูกทิ้ง env ของ plugin ที่ผ่านการกรองแล้วจะถูกรวมไว้ใน metadata การอนุมัติ/การตรวจสอบของ gateway
และส่งต่อไปยังคำขอ execution ของ node-host
การคงอยู่ของผลลัพธ์เครื่องมือ
ผลลัพธ์เครื่องมือสามารถมี details แบบมีโครงสร้างสำหรับการเรนเดอร์ UI, การวินิจฉัย,
การกำหนดเส้นทางสื่อ หรือ metadata ที่ plugin เป็นเจ้าของ ให้ถือว่า details เป็น metadata ของ runtime
ไม่ใช่เนื้อหา prompt:
- OpenClaw จะตัด
toolResult.detailsออกก่อน provider replay และอินพุต compaction เพื่อไม่ให้ metadata กลายเป็น context ของโมเดล - รายการ session ที่คงอยู่จะเก็บเฉพาะ
detailsที่มีขอบเขตจำกัด details ที่ใหญ่เกินไปจะ ถูกแทนที่ด้วยสรุปแบบกระชับและpersistedDetailsTruncated: true tool_result_persistและbefore_message_writeทำงานก่อนเพดานการคงอยู่ขั้นสุดท้าย Hooks จึงควรรักษาdetailsที่ส่งคืนให้มีขนาดเล็ก และหลีกเลี่ยงการวางข้อความที่เกี่ยวข้องกับ prompt ไว้เฉพาะในdetails; ให้วางเอาต์พุตเครื่องมือที่โมเดลมองเห็นได้ไว้ในcontent
Hooks สำหรับ prompt และโมเดล
ใช้ hooks เฉพาะ phase สำหรับ plugins ใหม่:
before_model_resolve: รับเฉพาะ prompt ปัจจุบันและ metadata ของไฟล์แนบ ส่งคืนproviderOverrideหรือmodelOverrideagent_turn_prepare: รับ prompt ปัจจุบัน, ข้อความ session ที่เตรียมไว้, และ queued injections แบบ exactly-once ใดๆ ที่ถูก drain สำหรับ session นี้ ส่งคืนprependContextหรือappendContextbefore_prompt_build: รับ prompt ปัจจุบันและข้อความ session ส่งคืนprependContext,appendContext,systemPrompt,prependSystemContextหรือappendSystemContextheartbeat_prompt_contribution: ทำงานเฉพาะสำหรับรอบ heartbeat และส่งคืนprependContextหรือappendContextมีไว้สำหรับตัวเฝ้าดูเบื้องหลัง ที่ต้องสรุปสถานะปัจจุบันโดยไม่เปลี่ยนรอบที่ผู้ใช้เริ่มต้น
before_agent_start ยังคงอยู่เพื่อความเข้ากันได้ ควรใช้ hooks แบบชัดเจนด้านบน
เพื่อให้ plugin ของคุณไม่ขึ้นกับ phase รวมแบบ legacy
before_agent_run ทำงานหลังจากสร้าง prompt และก่อนอินพุตโมเดลใดๆ
รวมถึงการโหลดรูปภาพเฉพาะ prompt และการสังเกต llm_input โดยรับอินพุตผู้ใช้ปัจจุบันเป็น prompt,
พร้อมประวัติ session ที่โหลดแล้วใน messages
และ system prompt ที่ใช้งานอยู่ ส่งคืน { outcome: "block", reason, message? }
เพื่อหยุด run ก่อนที่โมเดลจะอ่าน prompt ได้ reason เป็นข้อมูลภายใน;
message เป็นข้อความแทนที่ที่แสดงต่อผู้ใช้ outcome ที่รองรับมีเพียง
pass และ block; รูปแบบ decision ที่ไม่รองรับจะ fail closed
เมื่อ run ถูกบล็อก OpenClaw จะเก็บเฉพาะข้อความแทนที่ใน
message.content พร้อม metadata การบล็อกที่ไม่ละเอียดอ่อน เช่น id ของ plugin ที่บล็อก
และ timestamp ข้อความต้นฉบับของผู้ใช้จะไม่ถูกเก็บไว้ใน transcript หรือ context ในอนาคต
เหตุผลการบล็อกภายในถือเป็นข้อมูลละเอียดอ่อนและถูกแยกออกจาก
transcript, history, broadcast, log และ payload การวินิจฉัย Observability
ควรใช้ฟิลด์ที่ผ่านการทำให้ปลอดภัยแล้ว เช่น blocker id, outcome, timestamp หรือ category ที่ปลอดภัย
before_agent_start และ agent_end จะมี event.runId เมื่อ OpenClaw สามารถ
ระบุ run ที่ใช้งานอยู่ได้ ค่าเดียวกันนี้ยังอยู่ใน ctx.runId ด้วย
run ที่ขับเคลื่อนด้วย Cron จะเปิดเผย ctx.jobId ด้วย (id ของงาน cron ต้นทาง) เพื่อให้
plugin hooks สามารถกำหนดขอบเขต metrics, side effects หรือ state ไปยังงานที่ตั้งเวลาไว้เฉพาะงานหนึ่งได้
สำหรับ run ที่เริ่มจาก channel, ctx.channel และ ctx.messageProvider จะระบุ
พื้นผิวของ provider เช่น discord หรือ telegram ส่วน ctx.channelId คือ
ตัวระบุเป้าหมายการสนทนาเมื่อ OpenClaw สามารถอนุมานได้จากคีย์ session
หรือ metadata การส่งมอบ
เมื่อมีตัวตนของผู้ส่ง context ของ agent hook จะรวมสิ่งต่อไปนี้ด้วย:
ctx.senderId— ID ผู้ส่งที่อยู่ในขอบเขต channel (เช่น Feishuopen_id, Discord user ID) เติมค่าเมื่อ run มีต้นทางจากข้อความผู้ใช้ที่มี metadata ผู้ส่งที่ทราบctx.chatId— ตัวระบุการสนทนาตาม transport ดั้งเดิม (เช่น Feishuchat_id, Telegramchat_id) เติมค่าเมื่อ channel ต้นทาง ให้ ID การสนทนาดั้งเดิมctx.channelContext.sender.id— ID ผู้ส่งเดียวกับctx.senderIdภายใต้ ออบเจกต์ที่ channel เป็นเจ้าของ ซึ่ง plugins สามารถขยายด้วยฟิลด์เฉพาะ channel ได้ctx.channelContext.chat.id— ID การสนทนาเดียวกับctx.chatIdภายใต้ ออบเจกต์ที่ channel เป็นเจ้าของ ซึ่ง plugins สามารถขยายด้วยฟิลด์เฉพาะ channel ได้
Core กำหนดเฉพาะฟิลด์ id ที่ซ้อนอยู่เท่านั้น Channel plugins ที่ส่ง
metadata ผู้ส่งหรือแชทที่สมบูรณ์ขึ้นผ่าน inbound helper สามารถเพิ่ม
PluginHookChannelSenderContext หรือ PluginHookChannelChatContext จาก
openclaw/plugin-sdk/channel-inbound ได้:
declare module "openclaw/plugin-sdk/channel-inbound" { interface PluginHookChannelSenderContext { unionId?: string; userId?: string; }}Channel plugins ส่งฟิลด์เหล่านั้นผ่าน inbound SDK helper:
buildChannelInboundEventContext({ // ... channelContext: { sender: { id: senderOpenId, unionId, userId }, chat: { id: chatId }, },});ฟิลด์เหล่านี้เป็นตัวเลือกและจะไม่มีอยู่สำหรับ run ที่มีต้นทางจากระบบ (heartbeat, cron, exec-event)
ctx.senderExternalId ยังคงอยู่ในฐานะฟิลด์ความเข้ากันได้กับซอร์สที่เลิกแนะนำแล้วสำหรับ
plugins รุ่นเก่า Core จะไม่เติมค่านี้; ตัวตนผู้ส่งเฉพาะ channel แบบใหม่
ควรอยู่ภายใต้ ctx.channelContext.sender ผ่าน module augmentation
agent_end เป็น observation hook เส้นทาง Gateway และ harness แบบคงอยู่จะเรียกแบบ
fire-and-forget หลังจบรอบ ขณะที่เส้นทาง CLI แบบ one-shot อายุสั้นจะรอ
promise ของ hook ก่อน cleanup process เพื่อให้ plugins ที่เชื่อถือได้สามารถ flush
observability ของ terminal หรือ capture state ได้ hook runner ใช้ timeout 30 วินาที เพื่อให้
plugin หรือ endpoint ฝังตัวที่ค้างไม่สามารถปล่อยให้ promise ของ hook pending
ตลอดไป เมื่อ timeout จะมีการบันทึก log และ OpenClaw จะทำงานต่อ; จะไม่ยกเลิก
งานเครือข่ายที่ plugin เป็นเจ้าของ เว้นแต่ plugin จะใช้ abort signal ของตัวเองด้วย
ใช้ model_call_started และ model_call_ended สำหรับ telemetry ของการเรียก provider
ที่ไม่ควรได้รับ raw prompts, history, responses, headers, request
bodies หรือ provider request IDs hooks เหล่านี้มี metadata ที่เสถียร เช่น
runId, callId, provider, model, api/transport ที่เป็นตัวเลือก,
durationMs/outcome ขั้นสุดท้าย และ upstreamRequestIdHash เมื่อ OpenClaw สามารถอนุมาน
hash ของ provider request-id ที่มีขอบเขตจำกัดได้ เมื่อ runtime resolve metadata ของ context-window แล้ว
event และ context ของ hook จะมี contextTokenBudget ด้วย ซึ่งเป็น
token budget ที่มีผลหลังจากเพดานของ model/config/agent รวมถึง
contextWindowSource และ contextWindowReferenceTokens เมื่อมีการใช้เพดานที่ต่ำกว่า
before_agent_finalize ทำงานเฉพาะเมื่อ harness กำลังจะยอมรับคำตอบ assistant ขั้นสุดท้ายตามธรรมชาติ
ไม่ใช่เส้นทางการยกเลิก /stop และจะไม่
ทำงานเมื่อผู้ใช้ abort รอบ ส่งคืน { action: "revise", reason } เพื่อขอให้
harness เรียกโมเดลอีกหนึ่งรอบก่อน finalization, { action: "finalize", reason? } เพื่อบังคับ finalization หรือไม่ส่งผลลัพธ์เพื่อดำเนินการต่อ
hooks Stop ดั้งเดิมของ Codex จะถูก relay เข้าสู่ hook นี้ในฐานะ decision ของ OpenClaw
before_agent_finalize
เมื่อส่งคืน action: "revise" plugins สามารถรวม metadata retry เพื่อทำให้
การเรียกโมเดลเพิ่มเติมมีขอบเขตจำกัดและ replay-safe:
type BeforeAgentFinalizeRetry = { instruction: string; idempotencyKey?: string; maxAttempts?: number;};instruction จะถูก append เข้ากับเหตุผล revision ที่ส่งไปยัง harness
idempotencyKey ช่วยให้โฮสต์นับ retries สำหรับคำขอ plugin เดียวกันข้าม
finalize decisions ที่เทียบเท่ากันได้ และ maxAttempts จำกัดจำนวนรอบเพิ่มเติม
ที่โฮสต์จะอนุญาตก่อนดำเนินการต่อด้วยคำตอบขั้นสุดท้ายตามธรรมชาติ
Plugins ที่ไม่ใช่ bundled และต้องการ raw conversation hooks (before_model_resolve,
before_agent_reply, llm_input, llm_output, before_agent_finalize,
agent_end หรือ before_agent_run) ต้องตั้งค่า:
{ "plugins": { "entries": { "my-plugin": { "hooks": { "allowConversationAccess": true } } } }}hooks ที่แก้ไข prompt และ durable next-turn injections สามารถปิดใช้งานเป็นราย plugin
ด้วย plugins.entries.<id>.hooks.allowPromptInjection=false
Session extensions และ next-turn injections
Workflow plugins สามารถคง state ของ session ขนาดเล็กที่เข้ากันได้กับ JSON ด้วย
api.registerSessionExtension(...) และอัปเดตผ่านเมธอด Gateway
sessions.pluginPatch แถว session จะ project state ของ extension ที่ลงทะเบียนไว้
ผ่าน pluginExtensions ทำให้ Control UI และ clients อื่นๆ เรนเดอร์
สถานะที่ plugin เป็นเจ้าของได้โดยไม่ต้องรู้ internals ของ plugin
ใช้ api.enqueueNextTurnInjection(...) เมื่อ plugin ต้องการ context แบบ durable เพื่อ
ส่งถึงรอบโมเดลถัดไปเพียงครั้งเดียว OpenClaw จะ drain queued injections ก่อน
prompt hooks, ทิ้ง injections ที่หมดอายุ และ deduplicate ตาม idempotencyKey
ต่อ plugin นี่คือ seam ที่เหมาะสำหรับ approval resumes, policy summaries,
background monitor deltas และ command continuations ที่ควรให้โมเดลเห็น
ในรอบถัดไปแต่ไม่ควรกลายเป็นข้อความ system prompt ถาวร
semantics ของ cleanup เป็นส่วนหนึ่งของ contract callbacks สำหรับ session extension cleanup และ
runtime lifecycle cleanup จะได้รับ reset, delete, disable หรือ
restart โฮสต์จะลบ state ของ session extension แบบคงอยู่ที่ plugin เป็นเจ้าของ
และ pending next-turn injections สำหรับ reset/delete/disable; restart จะเก็บ
state ของ session แบบ durable ไว้ ขณะที่ cleanup callbacks ให้ plugins ปล่อย scheduler
jobs, run context และทรัพยากร out-of-band อื่นๆ สำหรับ runtime generation เดิม
Message hooks
ใช้ message hooks สำหรับการกำหนดเส้นทางและนโยบายการส่งมอบระดับ channel:
message_received: สังเกต content ขาเข้า, sender,threadId,messageId,senderId, ความสัมพันธ์ run/session ที่เป็นตัวเลือก และ metadatamessage_sending: เขียนcontentใหม่ หรือส่งคืน{ cancel: true }reply_payload_sending: เขียนออบเจกต์ReplyPayloadที่ normalize แล้วใหม่ (รวมถึงpresentation,delivery, media refs และ text) หรือส่งคืน{ cancel: true }message_sent: สังเกตความสำเร็จหรือความล้มเหลวขั้นสุดท้าย
สำหรับการตอบกลับ TTS แบบเสียงเท่านั้น content อาจมี transcript คำพูดที่ซ่อนอยู่
แม้ว่า payload ของ channel จะไม่มีข้อความ/คำบรรยายที่มองเห็นได้ การเขียน
content นั้นใหม่จะอัปเดตเฉพาะ transcript ที่ hook มองเห็นได้; จะไม่ถูกเรนเดอร์เป็น
คำบรรยายสื่อ
events reply_payload_sending อาจมี usageState ซึ่งเป็น snapshot แบบ live best-effort
ต่อรอบของ model/usage/context การส่งมอบแบบ durable, replay ที่กู้คืนมา และ
การตอบกลับที่ไม่มีความสัมพันธ์ run ที่แน่นอนจะละเว้นค่านี้
context ของ message hook เปิดเผยฟิลด์ความสัมพันธ์ที่เสถียรเมื่อมี:
ctx.sessionKey, ctx.runId, ctx.messageId, ctx.senderId, ctx.trace,
ctx.traceId, ctx.spanId, ctx.parentSpanId และ ctx.callDepth context ขาเข้า
และ before_dispatch ยังเปิดเผย metadata การตอบกลับเมื่อ channel มี
ข้อมูลข้อความที่อ้างอิงซึ่งผ่านการกรองการมองเห็นแล้ว: replyToId, replyToIdFull,
replyToBody, replyToSender และ replyToIsQuote ควรใช้ฟิลด์ชั้นหนึ่งเหล่านี้
ก่อนอ่าน metadata แบบ legacy
ควรใช้ฟิลด์ threadId และ replyToId ที่มี type ก่อนใช้ metadata เฉพาะ channel
กฎการตัดสินใจ:
message_sendingที่มีcancel: trueเป็นสถานะสิ้นสุดmessage_sendingที่มีcancel: falseจะถือว่าไม่มีการตัดสินใจcontentที่เขียนใหม่จะดำเนินต่อไปยังฮุกที่มีลำดับความสำคัญต่ำกว่า เว้นแต่ฮุกถัดไป จะยกเลิกการส่งreply_payload_sendingทำงานหลังการทำให้ payload เป็นมาตรฐานและก่อนการส่งผ่านช่องทาง รวมถึงการตอบกลับที่ถูกส่งกลับไปยังช่องทางต้นทาง ตัวจัดการ ทำงานตามลำดับ และตัวจัดการแต่ละตัวจะเห็น payload ล่าสุดที่สร้างโดย ตัวจัดการที่มีลำดับความสำคัญสูงกว่า- payload ของ
reply_payload_sendingไม่เปิดเผยเครื่องหมายความน่าเชื่อถือของรันไทม์ เช่นtrustedLocalMedia; Plugin สามารถแก้ไขรูปแบบ payload ได้ แต่ไม่สามารถให้ความน่าเชื่อถือแก่สื่อภายในเครื่องได้ message_sendingสามารถคืนค่าcancelReasonและmetadataที่มีขอบเขตพร้อมกับ การยกเลิก API วงจรชีวิตข้อความใหม่เปิดเผยสิ่งนี้เป็นผลลัพธ์การส่งที่ถูกระงับ พร้อมเหตุผลcancelled_by_message_sending_hook; การส่งโดยตรงแบบเดิม ยังคงคืนค่าอาร์เรย์ผลลัพธ์ว่างเพื่อความเข้ากันได้message_sentใช้สำหรับสังเกตการณ์เท่านั้น ความล้มเหลวของตัวจัดการจะถูกบันทึก และไม่ เปลี่ยนผลลัพธ์การส่ง
ฮุกการติดตั้ง
ใช้ security.installPolicy สำหรับการตัดสินใจอนุญาต/บล็อกที่ผู้ปฏิบัติงานเป็นเจ้าของ
นโยบายนั้นทำงานจากการกำหนดค่า OpenClaw ครอบคลุมเส้นทางการติดตั้งและอัปเดตของ CLI และจะปิดกั้นโดยค่าเริ่มต้นเมื่อเปิดใช้แต่ไม่พร้อมใช้งาน
before_install เป็นฮุกวงจรชีวิตของรันไทม์ Plugin โดยทำงานหลัง
security.installPolicy เฉพาะในกระบวนการ OpenClaw ที่โหลดฮุกของ Plugin
แล้ว เช่น โฟลว์การติดตั้งที่รองรับโดย Gateway ซึ่งมีประโยชน์สำหรับ
การสังเกตการณ์ คำเตือน และการตรวจสอบความเข้ากันได้ที่ Plugin เป็นเจ้าของ แต่ไม่ใช่
ขอบเขตความปลอดภัยหลักระดับองค์กรหรือโฮสต์สำหรับการติดตั้ง ฟิลด์ builtinScan
ยังคงอยู่ใน payload ของเหตุการณ์เพื่อความเข้ากันได้ แต่ OpenClaw จะไม่
เรียกใช้การบล็อกโค้ดอันตรายแบบติดตั้งในตัวระหว่างการติดตั้งอีกต่อไป ดังนั้นจึงเป็นผลลัพธ์ ok
ว่าง ส่งคืนข้อค้นพบเพิ่มเติมหรือ { block: true, blockReason } เพื่อหยุด
การติดตั้งในกระบวนการนั้น
block: true เป็นสถานะสิ้นสุด block: false จะถือว่าไม่มีการตัดสินใจ
ความล้มเหลวของตัวจัดการจะบล็อกการติดตั้งแบบปิดกั้นโดยค่าเริ่มต้น
วงจรชีวิต Gateway
ใช้ gateway_start สำหรับบริการ Plugin ที่ต้องใช้สถานะที่ Gateway เป็นเจ้าของ
บริบทเปิดเผย ctx.config, ctx.workspaceDir และ ctx.getCron?.() สำหรับ
การตรวจสอบและอัปเดต cron ใช้ gateway_stop เพื่อล้างทรัพยากร
ที่ทำงานระยะยาว
อย่าพึ่งพาฮุกภายใน gateway:startup สำหรับบริการรันไทม์
ที่ Plugin เป็นเจ้าของ
cron_changed จะทำงานสำหรับเหตุการณ์วงจรชีวิต cron ที่ gateway เป็นเจ้าของ พร้อม
payload เหตุการณ์แบบมีชนิดซึ่งครอบคลุมเหตุผล added, updated, removed, started, finished,
และ scheduled เหตุการณ์มีสแนปชอต PluginHookGatewayCronJob
(รวมถึง state.nextRunAtMs, state.lastRunStatus และ
state.lastError เมื่อมี) พร้อม PluginHookGatewayCronDeliveryStatus
เป็น not-requested | delivered | not-delivered | unknown เหตุการณ์ที่ถูกลบ
ยังคงมีสแนปชอตงานที่ถูกลบ เพื่อให้ตัวจัดตารางเวลาภายนอกสามารถ
ปรับสถานะให้ตรงกันได้ ใช้ ctx.getCron?.() และ ctx.config จากบริบท
รันไทม์เมื่อซิงค์ตัวจัดตารางเวลาปลุกภายนอก และให้ OpenClaw เป็น
แหล่งข้อมูลจริงสำหรับการตรวจสอบกำหนดเวลาและการดำเนินการ
การเลิกใช้งานที่กำลังจะมาถึง
มีพื้นผิวบางส่วนที่อยู่ใกล้กับฮุกซึ่งเลิกใช้แล้วแต่ยังรองรับอยู่ ให้ย้าย ก่อนรุ่นหลักถัดไป:
- ซองช่องทางข้อความธรรมดา ในตัวจัดการ
inbound_claimและmessage_receivedอ่านBodyForAgentและบล็อกบริบทผู้ใช้แบบมีโครงสร้าง แทนการแยกวิเคราะห์ข้อความซองแบบแบน ดู ซองช่องทางข้อความธรรมดา → BodyForAgent before_agent_startยังคงอยู่เพื่อความเข้ากันได้ Plugin ใหม่ควรใช้before_model_resolveและbefore_prompt_buildแทนเฟสที่รวมกันsubagent_spawningยังคงอยู่เพื่อความเข้ากันได้กับ Plugin รุ่นเก่า แต่ Plugin ใหม่ไม่ควรคืนค่าการกำหนดเส้นทางเธรดจากฮุกนี้ Core เตรียม การผูก subagent แบบthread: trueผ่านอะแดปเตอร์การผูกเซสชันช่องทาง ก่อนที่subagent_spawnedจะทำงานdeactivateยังคงเป็นนามแฝงความเข้ากันได้สำหรับการล้างข้อมูลที่เลิกใช้แล้วจนถึง หลัง 2026-08-16 Plugin ใหม่ควรใช้gateway_stoponResolutionในbefore_tool_callตอนนี้ใช้ยูเนียนแบบมีชนิดPluginApprovalResolution(allow-once/allow-always/deny/timeout/cancelled) แทนstringแบบอิสระ
สำหรับรายการทั้งหมด ได้แก่ การลงทะเบียนความสามารถหน่วยความจำ โปรไฟล์การคิดของผู้ให้บริการ
ผู้ให้บริการยืนยันตัวตนภายนอก ชนิดการค้นหาผู้ให้บริการ ตัวเข้าถึงรันไทม์งาน
และการเปลี่ยนชื่อ command-auth → command-status โปรดดู
การย้าย Plugin SDK → การเลิกใช้งานที่ยังมีผล
ที่เกี่ยวข้อง
- การย้าย Plugin SDK - การเลิกใช้งานที่ยังมีผลและไทม์ไลน์การลบ
- การสร้าง Plugin
- ภาพรวม Plugin SDK
- จุดเริ่มต้น Plugin
- ฮุกภายใน
- รายละเอียดภายในสถาปัตยกรรม Plugin