# Hook Registration ## Available Hooks | Hook | Purpose | Dedup Method | |------|---------|-------------| | `before_model_resolve` | Override model/provider before LLM call | WeakSet on event | | `before_prompt_build` | Inject into system prompt | WeakSet on event | | `before_agent_start` | Legacy prompt injection | WeakSet on event | | `agent_end` | Post-conversation actions | Set on runId | | `message_received` | React to inbound messages | — | | `llm_input` / `llm_output` | Observe LLM traffic | — | | `before_tool_call` / `after_tool_call` | Observe tool execution | — | | `gateway_start` / `gateway_stop` | Gateway lifecycle | globalThis flag | ## Dedup Patterns ### WeakSet (for event-object hooks) ```typescript const _G = globalThis as Record; const DEDUP_KEY = "_myPluginHookDedup"; if (!(_G[DEDUP_KEY] instanceof WeakSet)) _G[DEDUP_KEY] = new WeakSet(); const dedup = _G[DEDUP_KEY] as WeakSet; api.on("before_model_resolve", async (event, ctx) => { if (dedup.has(event as object)) return; dedup.add(event as object); // ... handler logic }); ``` ### Set with runId (for agent_end) ```typescript const DEDUP_KEY = "_myPluginAgentEndDedup"; if (!(_G[DEDUP_KEY] instanceof Set)) _G[DEDUP_KEY] = new Set(); const dedup = _G[DEDUP_KEY] as Set; api.on("agent_end", async (event, ctx) => { const runId = (event as any).runId as string; if (runId) { if (dedup.has(runId)) return; dedup.add(runId); if (dedup.size > 500) dedup.delete(dedup.values().next().value!); } // ... handler logic }); ``` ## Prompt Injection Hooks `before_prompt_build` and `before_agent_start` can return prompt mutation fields: | Field | Caching | Use for | |-------|---------|---------| | `prependSystemContext` | Cached | Static role/identity prompts | | `appendSystemContext` | Cached | Static supplementary guidance | | `prependContext` | Not cached | Per-turn dynamic context | | `systemPrompt` | — | Full system prompt replacement (rarely used) | Requires `plugins.entries..hooks.allowPromptInjection: true` in openclaw.json.