<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>CPEX — ContextForge Plugin Extensibility Framework on CPEX Documentation</title><link>https://contextforge-org.github.io/contextforge-plugins-framework/</link><description>Recent content in CPEX — ContextForge Plugin Extensibility Framework on CPEX Documentation</description><generator>Hugo</generator><language>en-us</language><atom:link href="https://contextforge-org.github.io/contextforge-plugins-framework/index.xml" rel="self" type="application/rss+xml"/><item><title>Overview</title><link>https://contextforge-org.github.io/contextforge-plugins-framework/docs/overview/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://contextforge-org.github.io/contextforge-plugins-framework/docs/overview/</guid><description>&lt;h1 id="overview"&gt;Overview&lt;a class="anchor" href="#overview"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;h2 id="why-cpex"&gt;Why CPEX?&lt;a class="anchor" href="#why-cpex"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;AI systems interact with tools, APIs, data sources, and other agents. Adding guardrails, observability, or policy checks typically means embedding that logic directly into application code — leading to duplication, tight coupling, and drift.&lt;/p&gt;
&lt;p&gt;CPEX introduces &lt;strong&gt;standardized interception hooks&lt;/strong&gt; between your application and its operations. Plugins attach to these hooks and run automatically, keeping enforcement logic separate from business logic.&lt;/p&gt;
&lt;h2 id="how-it-works"&gt;How It Works&lt;a class="anchor" href="#how-it-works"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Your application defines &lt;strong&gt;hooks&lt;/strong&gt; — named interception points before and after critical operations. Plugins register against these hooks and execute automatically when triggered. The plugin manager handles registration, ordering, execution, timeouts, and error isolation.&lt;/p&gt;</description></item><item><title>Quick Start</title><link>https://contextforge-org.github.io/contextforge-plugins-framework/docs/quickstart/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://contextforge-org.github.io/contextforge-plugins-framework/docs/quickstart/</guid><description>&lt;h1 id="your-first-plugin-in-5-minutes"&gt;Your First Plugin in 5 Minutes&lt;a class="anchor" href="#your-first-plugin-in-5-minutes"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;This guide walks you through installing CPEX, writing a plugin, configuring it, and running it.&lt;/p&gt;
&lt;h2 id="install"&gt;Install&lt;a class="anchor" href="#install"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pip install cpex&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="what-are-plugins"&gt;What Are Plugins?&lt;a class="anchor" href="#what-are-plugins"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Plugins let you intercept and modify execution at well-defined points — without changing the targeted application code.&lt;/p&gt;
&lt;p&gt;You define &lt;strong&gt;hooks&lt;/strong&gt; in your application where you want extensibility. Plugins attach to those hooks and run automatically whenever they fire.&lt;/p&gt;
&lt;h2 id="1-write-a-plugin"&gt;1. Write a Plugin&lt;a class="anchor" href="#1-write-a-plugin"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A plugin is a class that subclasses &lt;code&gt;Plugin&lt;/code&gt; and implements one or more hook handlers. Here you will create a plugin that blocks specific tools by name.&lt;/p&gt;</description></item><item><title>Hooks</title><link>https://contextforge-org.github.io/contextforge-plugins-framework/docs/hooks/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://contextforge-org.github.io/contextforge-plugins-framework/docs/hooks/</guid><description>&lt;h1 id="hooks"&gt;Hooks&lt;a class="anchor" href="#hooks"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;A &lt;strong&gt;hook&lt;/strong&gt; is a named interception point in your application. You define hooks where you want plugins to run, then call them at those points. The plugin manager dispatches the payload to every registered plugin and returns the combined result.&lt;/p&gt;
&lt;h2 id="anatomy-of-a-hook-handler"&gt;Anatomy of a Hook Handler&lt;a class="anchor" href="#anatomy-of-a-hook-handler"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Every hook handler is an &lt;code&gt;async&lt;/code&gt; method on a &lt;code&gt;Plugin&lt;/code&gt; subclass. It receives a typed &lt;strong&gt;payload&lt;/strong&gt; and a &lt;strong&gt;context&lt;/strong&gt;, and returns a &lt;strong&gt;result&lt;/strong&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; cpex.framework &lt;span style="color:#f92672"&gt;import&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Plugin,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; PluginContext,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; PluginResult,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ToolPreInvokePayload,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ToolPreInvokeResult,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;MyPlugin&lt;/span&gt;(Plugin):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;async&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;tool_pre_invoke&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self, payload: ToolPreInvokePayload, context: PluginContext
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; ToolPreInvokeResult:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; ToolPreInvokeResult(continue_processing&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The framework validates the signature at registration time. It checks that the method:&lt;/p&gt;</description></item><item><title>Hook Types Reference</title><link>https://contextforge-org.github.io/contextforge-plugins-framework/docs/hook-types/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://contextforge-org.github.io/contextforge-plugins-framework/docs/hook-types/</guid><description>&lt;h1 id="hook-types-reference"&gt;Hook Types Reference&lt;a class="anchor" href="#hook-types-reference"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;CPEX ships with built-in hooks for common AI and application operations. Each hook defines a typed payload (the data your plugin receives) and a result type (what you return). You can also &lt;a href="https://contextforge-org.github.io/contextforge-plugins-framework/docs/hooks/#custom-hooks"&gt;register custom hooks&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="tool-hooks"&gt;Tool Hooks&lt;a class="anchor" href="#tool-hooks"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Intercept tool/function calls before execution and inspect results after.&lt;/p&gt;
&lt;h3 id="tool_pre_invoke"&gt;&lt;code&gt;tool_pre_invoke&lt;/code&gt;&lt;a class="anchor" href="#tool_pre_invoke"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Fires &lt;strong&gt;before&lt;/strong&gt; a tool is executed.&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Field&lt;/th&gt;
 &lt;th&gt;Type&lt;/th&gt;
 &lt;th&gt;Description&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;name&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;str&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Tool name&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;args&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;dict[str, Any]&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Tool arguments&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Payload:&lt;/strong&gt; &lt;code&gt;ToolPreInvokePayload&lt;/code&gt; | &lt;strong&gt;Result:&lt;/strong&gt; &lt;code&gt;ToolPreInvokeResult&lt;/code&gt;&lt;/p&gt;</description></item><item><title>Common Message Format</title><link>https://contextforge-org.github.io/contextforge-plugins-framework/docs/cmf/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://contextforge-org.github.io/contextforge-plugins-framework/docs/cmf/</guid><description>&lt;h1 id="common-message-format-cmf"&gt;Common Message Format (CMF)&lt;a class="anchor" href="#common-message-format-cmf"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;The Common Message Format is a canonical message representation for interactions between users, agents, tools, and language models. It lets you write a single plugin that evaluates content at &lt;em&gt;every&lt;/em&gt; interception point — tool calls, LLM input/output, resource access — using one unified interface.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="why-cmf"&gt;Why CMF?&lt;a class="anchor" href="#why-cmf"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Without CMF, you write separate handlers for each hook type:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;async&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;tool_pre_invoke&lt;/span&gt;(self, payload: ToolPreInvokePayload, context):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# check tool arguments for prohibited content&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;async&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;agent_pre_invoke&lt;/span&gt;(self, payload: AgentPreInvokePayload, context):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# check agent messages for prohibited content — same logic, different payload&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;...&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With CMF, you write the logic once and register for multiple hook points:&lt;/p&gt;</description></item><item><title>Execution Modes</title><link>https://contextforge-org.github.io/contextforge-plugins-framework/docs/execution-modes/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://contextforge-org.github.io/contextforge-plugins-framework/docs/execution-modes/</guid><description>&lt;h1 id="execution-modes"&gt;Execution Modes&lt;a class="anchor" href="#execution-modes"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Every plugin has an execution &lt;strong&gt;mode&lt;/strong&gt; that controls whether it can block the pipeline, modify payloads, and how it runs relative to other plugins. Modes are set in the plugin&amp;rsquo;s &lt;a href="https://contextforge-org.github.io/contextforge-plugins-framework/docs/configuration/"&gt;YAML configuration&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="phase-order"&gt;Phase Order&lt;a class="anchor" href="#phase-order"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;At each hook invocation, plugins are grouped by mode and dispatched in strict phase order:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;sequential → transform → audit → concurrent → fire_and_forget&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Within each serial phase, plugins execute in &lt;strong&gt;priority order&lt;/strong&gt; — lower numbers run first (e.g., priority &lt;code&gt;10&lt;/code&gt; runs before &lt;code&gt;20&lt;/code&gt;). The default priority is &lt;code&gt;100&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>Configuration</title><link>https://contextforge-org.github.io/contextforge-plugins-framework/docs/configuration/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://contextforge-org.github.io/contextforge-plugins-framework/docs/configuration/</guid><description>&lt;h1 id="configuration-reference"&gt;Configuration Reference&lt;a class="anchor" href="#configuration-reference"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Plugins are configured in a YAML file. You pass the file path when creating the &lt;code&gt;PluginManager&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;manager &lt;span style="color:#f92672"&gt;=&lt;/span&gt; PluginManager(&lt;span style="color:#e6db74"&gt;&amp;#34;plugins/config.yaml&amp;#34;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Or set it via environment variable:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;export PLUGINS_CONFIG_FILE&lt;span style="color:#f92672"&gt;=&lt;/span&gt;plugins/config.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;export PLUGINS_ENABLED&lt;span style="color:#f92672"&gt;=&lt;/span&gt;true&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="yaml-structure"&gt;YAML Structure&lt;a class="anchor" href="#yaml-structure"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;plugin_dirs&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;./plugins&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;plugins&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;content_filter&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;my_app.plugins.ContentFilterPlugin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;version&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;1.0.0&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;description&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Blocks prohibited content in tool arguments&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;author&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;platform-team&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;hooks&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;tool_pre_invoke&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;tags&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;security&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;content&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;mode&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;sequential&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;on_error&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;fail&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;priority&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;10&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;conditions&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;server_ids&lt;/span&gt;: [&lt;span style="color:#ae81ff"&gt;prod-gateway]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;tenant_ids&lt;/span&gt;: [&lt;span style="color:#ae81ff"&gt;tenant-a, tenant-b]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;config&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;blocked_patterns&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#e6db74"&gt;&amp;#34;DROP TABLE&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#e6db74"&gt;&amp;#34;rm -rf&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="plugin-fields"&gt;Plugin Fields&lt;a class="anchor" href="#plugin-fields"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Field&lt;/th&gt;
 &lt;th&gt;Type&lt;/th&gt;
 &lt;th&gt;Default&lt;/th&gt;
 &lt;th&gt;Description&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;name&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;str&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;em&gt;required&lt;/em&gt;&lt;/td&gt;
 &lt;td&gt;Unique plugin identifier&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;kind&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;str&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;em&gt;required&lt;/em&gt;&lt;/td&gt;
 &lt;td&gt;Fully qualified class path (e.g., &lt;code&gt;my_app.plugins.MyPlugin&lt;/code&gt;), &lt;code&gt;&amp;quot;external&amp;quot;&lt;/code&gt; for remote plugins, or &lt;code&gt;&amp;quot;isolated_venv&amp;quot;&lt;/code&gt; for venv-isolated plugins&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;version&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;str&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;—&lt;/td&gt;
 &lt;td&gt;Semantic version&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;description&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;str&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;—&lt;/td&gt;
 &lt;td&gt;Human-readable description&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;author&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;str&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;—&lt;/td&gt;
 &lt;td&gt;Plugin author&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;hooks&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;list[str]&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;[]&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Hook types this plugin handles&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;tags&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;list[str]&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;[]&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Searchable tags&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;mode&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;str&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;sequential&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Execution mode — see &lt;a href="https://contextforge-org.github.io/contextforge-plugins-framework/docs/execution-modes/"&gt;Execution Modes&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;on_error&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;str&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;fail&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Error behavior: &lt;code&gt;fail&lt;/code&gt;, &lt;code&gt;ignore&lt;/code&gt;, &lt;code&gt;disable&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;priority&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;int&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;100&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Execution order within mode (lower = higher priority)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;conditions&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;list&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;[]&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;When the plugin should execute&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;capabilities&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;list[str]&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;[]&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Declared capabilities for &lt;a href="https://contextforge-org.github.io/contextforge-plugins-framework/docs/extensions/"&gt;extension access&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;config&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;dict&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;—&lt;/td&gt;
 &lt;td&gt;Plugin-specific settings passed to the constructor&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;max_content_size&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;int&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;10000000&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Maximum payload size in bytes&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;mcp&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;object&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;—&lt;/td&gt;
 &lt;td&gt;MCP client config (for &lt;a href="https://contextforge-org.github.io/contextforge-plugins-framework/docs/external-plugins/"&gt;external plugins&lt;/a&gt;)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;grpc&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;object&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;—&lt;/td&gt;
 &lt;td&gt;gRPC client config (for external plugins)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;unix_socket&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;object&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;—&lt;/td&gt;
 &lt;td&gt;Unix socket client config (for external plugins)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="plugin-directories"&gt;Plugin Directories&lt;a class="anchor" href="#plugin-directories"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;plugin_dirs&lt;/code&gt; lists directories that CPEX adds to the Python path for plugin discovery. Use this when your plugin classes live outside the main application package:&lt;/p&gt;</description></item><item><title>Extensions &amp; Capabilities</title><link>https://contextforge-org.github.io/contextforge-plugins-framework/docs/extensions/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://contextforge-org.github.io/contextforge-plugins-framework/docs/extensions/</guid><description>&lt;h1 id="extensions--capabilities"&gt;Extensions &amp;amp; Capabilities&lt;a class="anchor" href="#extensions--capabilities"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Extensions carry typed contextual metadata — identity, security labels, HTTP headers, delegation chains — through the plugin pipeline. The capability system controls which plugins can see and modify which extension slots.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="the-extensions-container"&gt;The Extensions Container&lt;a class="anchor" href="#the-extensions-container"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Extensions&lt;/code&gt; is a frozen Pydantic model that attaches to payloads flowing through the pipeline. Each field is an optional typed slot:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; cpex.framework.extensions.extensions &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Extensions
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; cpex.framework.extensions.request &lt;span style="color:#f92672"&gt;import&lt;/span&gt; RequestExtension
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; cpex.framework.extensions.security &lt;span style="color:#f92672"&gt;import&lt;/span&gt; SecurityExtension
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ext &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Extensions(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; request&lt;span style="color:#f92672"&gt;=&lt;/span&gt;RequestExtension(environment&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;production&amp;#34;&lt;/span&gt;, request_id&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;req-001&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; security&lt;span style="color:#f92672"&gt;=&lt;/span&gt;SecurityExtension(labels&lt;span style="color:#f92672"&gt;=&lt;/span&gt;frozenset({&lt;span style="color:#e6db74"&gt;&amp;#34;pii&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;confidential&amp;#34;&lt;/span&gt;})),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ext&lt;span style="color:#f92672"&gt;.&lt;/span&gt;request&lt;span style="color:#f92672"&gt;.&lt;/span&gt;environment &lt;span style="color:#75715e"&gt;# &amp;#34;production&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ext&lt;span style="color:#f92672"&gt;.&lt;/span&gt;security&lt;span style="color:#f92672"&gt;.&lt;/span&gt;labels &lt;span style="color:#75715e"&gt;# frozenset({&amp;#34;pii&amp;#34;, &amp;#34;confidential&amp;#34;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ext&lt;span style="color:#f92672"&gt;.&lt;/span&gt;http &lt;span style="color:#75715e"&gt;# None — not populated&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Extensions are frozen. To modify, use &lt;code&gt;model_copy(update={...})&lt;/code&gt;:&lt;/p&gt;</description></item><item><title>External Plugins</title><link>https://contextforge-org.github.io/contextforge-plugins-framework/docs/external-plugins/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://contextforge-org.github.io/contextforge-plugins-framework/docs/external-plugins/</guid><description>&lt;h1 id="external-plugins"&gt;External Plugins&lt;a class="anchor" href="#external-plugins"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;External plugins run as standalone services — separate processes, containers, or remote hosts — connected to CPEX over MCP (Streamable HTTP), gRPC, or Unix domain sockets. You write the same &lt;code&gt;Plugin&lt;/code&gt; subclass, but it runs in its own server process.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="why-external-plugins"&gt;Why External Plugins?&lt;a class="anchor" href="#why-external-plugins"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Isolation&lt;/strong&gt; — plugin failures don&amp;rsquo;t crash the host application&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Independent scaling&lt;/strong&gt; — scale plugin servers separately from your gateway&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Language independence&lt;/strong&gt; — implement the protocol in any language (the wire format is JSON/protobuf)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Security boundaries&lt;/strong&gt; — run untrusted plugins in sandboxed environments&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="building-an-external-plugin-server"&gt;Building an External Plugin Server&lt;a class="anchor" href="#building-an-external-plugin-server"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Create your plugin class exactly as you would for a native plugin, then wrap it in &lt;code&gt;ExternalPluginServer&lt;/code&gt;:&lt;/p&gt;</description></item><item><title>Isolated Plugins</title><link>https://contextforge-org.github.io/contextforge-plugins-framework/docs/isolated-plugins/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://contextforge-org.github.io/contextforge-plugins-framework/docs/isolated-plugins/</guid><description>&lt;h1 id="isolated-plugins-venv"&gt;Isolated Plugins (venv)&lt;a class="anchor" href="#isolated-plugins-venv"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Isolated plugins run in a separate Python virtual environment, preventing their dependencies from interfering with the host application. Each plugin gets its own venv with its own &lt;code&gt;requirements.txt&lt;/code&gt;, while still communicating with the plugin manager through the standard hook interface.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="when-to-use"&gt;When to Use&lt;a class="anchor" href="#when-to-use"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Conflicting dependencies&lt;/strong&gt; — a plugin needs a different version of a library than your application&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Untrusted code&lt;/strong&gt; — run third-party plugins in a sandboxed environment&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dependency hygiene&lt;/strong&gt; — keep the host environment clean from plugin-specific packages&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="configuration"&gt;Configuration&lt;a class="anchor" href="#configuration"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Set &lt;code&gt;kind&lt;/code&gt; to &lt;code&gt;&amp;quot;isolated_venv&amp;quot;&lt;/code&gt; and provide the plugin details in the &lt;code&gt;config&lt;/code&gt; section:&lt;/p&gt;</description></item><item><title>Patterns &amp; Best Practices</title><link>https://contextforge-org.github.io/contextforge-plugins-framework/docs/patterns/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://contextforge-org.github.io/contextforge-plugins-framework/docs/patterns/</guid><description>&lt;h1 id="patterns--best-practices"&gt;Patterns &amp;amp; Best Practices&lt;a class="anchor" href="#patterns--best-practices"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Curated patterns for building production plugin pipelines.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="layered-security-pipeline"&gt;Layered Security Pipeline&lt;a class="anchor" href="#layered-security-pipeline"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Compose modes and priorities to build defense-in-depth. Each layer has a specific responsibility:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;plugins&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Layer 1: hard enforcement — blocks requests that violate policy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;token_budget&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;security.TokenBudgetPlugin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;mode&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;sequential&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;priority&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;10&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;hooks&lt;/span&gt;: [&lt;span style="color:#ae81ff"&gt;tool_pre_invoke]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Layer 2: content policy — blocks prohibited content&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;content_policy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;security.ContentPolicyPlugin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;mode&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;sequential&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;priority&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;20&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;hooks&lt;/span&gt;: [&lt;span style="color:#ae81ff"&gt;tool_pre_invoke, agent_pre_invoke]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Layer 3: transformation — redacts PII without blocking&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;pii_redactor&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;privacy.PIIRedactionPlugin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;mode&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;transform&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;priority&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;30&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;hooks&lt;/span&gt;: [&lt;span style="color:#ae81ff"&gt;tool_pre_invoke, tool_post_invoke]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Layer 4: background logging — never blocks or slows&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;audit_logger&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;observability.AuditLogPlugin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;mode&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;fire_and_forget&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;priority&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;100&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;hooks&lt;/span&gt;: [&lt;span style="color:#ae81ff"&gt;tool_pre_invoke, tool_post_invoke, prompt_pre_fetch]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Execution order: &lt;code&gt;token_budget&lt;/code&gt; (sequential) → &lt;code&gt;content_policy&lt;/code&gt; (sequential) → &lt;code&gt;pii_redactor&lt;/code&gt; (transform) → &lt;code&gt;audit_logger&lt;/code&gt; (fire_and_forget). Each layer can only do what its mode permits.&lt;/p&gt;</description></item><item><title>Testing Plugins</title><link>https://contextforge-org.github.io/contextforge-plugins-framework/docs/testing/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://contextforge-org.github.io/contextforge-plugins-framework/docs/testing/</guid><description>&lt;h1 id="testing-plugins"&gt;Testing Plugins&lt;a class="anchor" href="#testing-plugins"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Plugins are plain async classes — you can test them directly without the full framework. For integration testing, use &lt;code&gt;PluginManager&lt;/code&gt; with a test configuration.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="unit-testing"&gt;Unit Testing&lt;a class="anchor" href="#unit-testing"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Call hook methods directly with constructed payloads and contexts. No framework overhead needed.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pytest
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; cpex.framework &lt;span style="color:#f92672"&gt;import&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; GlobalContext,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; PluginConfig,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; PluginContext,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ToolPreInvokePayload,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@pytest.mark.asyncio&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;async&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;test_tool_blocker_blocks_dangerous_tool&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; config &lt;span style="color:#f92672"&gt;=&lt;/span&gt; PluginConfig(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; name&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;test_blocker&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; kind&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;plugins.tool_blocker.ToolBlockerPlugin&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; version&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;1.0.0&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; hooks&lt;span style="color:#f92672"&gt;=&lt;/span&gt;[&lt;span style="color:#e6db74"&gt;&amp;#34;tool_pre_invoke&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; config&lt;span style="color:#f92672"&gt;=&lt;/span&gt;{&lt;span style="color:#e6db74"&gt;&amp;#34;blocked_tools&amp;#34;&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;dangerous_tool&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;admin_delete&amp;#34;&lt;/span&gt;]},
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Import your plugin class&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;from&lt;/span&gt; plugins.tool_blocker &lt;span style="color:#f92672"&gt;import&lt;/span&gt; ToolBlockerPlugin
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; plugin &lt;span style="color:#f92672"&gt;=&lt;/span&gt; ToolBlockerPlugin(config)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; payload &lt;span style="color:#f92672"&gt;=&lt;/span&gt; ToolPreInvokePayload(name&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;dangerous_tool&amp;#34;&lt;/span&gt;, args&lt;span style="color:#f92672"&gt;=&lt;/span&gt;{&lt;span style="color:#e6db74"&gt;&amp;#34;target&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;prod&amp;#34;&lt;/span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; context &lt;span style="color:#f92672"&gt;=&lt;/span&gt; PluginContext(global_context&lt;span style="color:#f92672"&gt;=&lt;/span&gt;GlobalContext(request_id&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;test-001&amp;#34;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; result &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; plugin&lt;span style="color:#f92672"&gt;.&lt;/span&gt;tool_pre_invoke(payload, context)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; result&lt;span style="color:#f92672"&gt;.&lt;/span&gt;continue_processing &lt;span style="color:#f92672"&gt;is&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; result&lt;span style="color:#f92672"&gt;.&lt;/span&gt;violation &lt;span style="color:#f92672"&gt;is&lt;/span&gt; &lt;span style="color:#f92672"&gt;not&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; result&lt;span style="color:#f92672"&gt;.&lt;/span&gt;violation&lt;span style="color:#f92672"&gt;.&lt;/span&gt;code &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;TOOL_BLOCKED&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="testing-allowed-requests"&gt;Testing Allowed Requests&lt;a class="anchor" href="#testing-allowed-requests"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@pytest.mark.asyncio&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;async&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;test_tool_blocker_allows_safe_tool&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; config &lt;span style="color:#f92672"&gt;=&lt;/span&gt; PluginConfig(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; name&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;test_blocker&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; kind&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;plugins.tool_blocker.ToolBlockerPlugin&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; version&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;1.0.0&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; hooks&lt;span style="color:#f92672"&gt;=&lt;/span&gt;[&lt;span style="color:#e6db74"&gt;&amp;#34;tool_pre_invoke&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; config&lt;span style="color:#f92672"&gt;=&lt;/span&gt;{&lt;span style="color:#e6db74"&gt;&amp;#34;blocked_tools&amp;#34;&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;dangerous_tool&amp;#34;&lt;/span&gt;]},
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;from&lt;/span&gt; plugins.tool_blocker &lt;span style="color:#f92672"&gt;import&lt;/span&gt; ToolBlockerPlugin
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; plugin &lt;span style="color:#f92672"&gt;=&lt;/span&gt; ToolBlockerPlugin(config)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; payload &lt;span style="color:#f92672"&gt;=&lt;/span&gt; ToolPreInvokePayload(name&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;web_search&amp;#34;&lt;/span&gt;, args&lt;span style="color:#f92672"&gt;=&lt;/span&gt;{&lt;span style="color:#e6db74"&gt;&amp;#34;query&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;CPEX docs&amp;#34;&lt;/span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; context &lt;span style="color:#f92672"&gt;=&lt;/span&gt; PluginContext(global_context&lt;span style="color:#f92672"&gt;=&lt;/span&gt;GlobalContext(request_id&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;test-002&amp;#34;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; result &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; plugin&lt;span style="color:#f92672"&gt;.&lt;/span&gt;tool_pre_invoke(payload, context)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; result&lt;span style="color:#f92672"&gt;.&lt;/span&gt;continue_processing &lt;span style="color:#f92672"&gt;is&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; result&lt;span style="color:#f92672"&gt;.&lt;/span&gt;violation &lt;span style="color:#f92672"&gt;is&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="testing-payload-modification"&gt;Testing Payload Modification&lt;a class="anchor" href="#testing-payload-modification"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@pytest.mark.asyncio&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;async&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;test_pii_redaction_removes_emails&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; config &lt;span style="color:#f92672"&gt;=&lt;/span&gt; PluginConfig(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; name&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;test_redactor&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; kind&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;plugins.pii.PIIRedactionPlugin&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; version&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;1.0.0&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; hooks&lt;span style="color:#f92672"&gt;=&lt;/span&gt;[&lt;span style="color:#e6db74"&gt;&amp;#34;tool_pre_invoke&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;from&lt;/span&gt; plugins.pii &lt;span style="color:#f92672"&gt;import&lt;/span&gt; PIIRedactionPlugin
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; plugin &lt;span style="color:#f92672"&gt;=&lt;/span&gt; PIIRedactionPlugin(config)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; payload &lt;span style="color:#f92672"&gt;=&lt;/span&gt; ToolPreInvokePayload(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; name&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;send_email&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; args&lt;span style="color:#f92672"&gt;=&lt;/span&gt;{&lt;span style="color:#e6db74"&gt;&amp;#34;body&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Contact alice@example.com for details&amp;#34;&lt;/span&gt;},
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; context &lt;span style="color:#f92672"&gt;=&lt;/span&gt; PluginContext(global_context&lt;span style="color:#f92672"&gt;=&lt;/span&gt;GlobalContext(request_id&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;test-003&amp;#34;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; result &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; plugin&lt;span style="color:#f92672"&gt;.&lt;/span&gt;redact_pii(payload, context)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; result&lt;span style="color:#f92672"&gt;.&lt;/span&gt;continue_processing &lt;span style="color:#f92672"&gt;is&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; result&lt;span style="color:#f92672"&gt;.&lt;/span&gt;modified_payload &lt;span style="color:#f92672"&gt;is&lt;/span&gt; &lt;span style="color:#f92672"&gt;not&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;alice@example.com&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;not&lt;/span&gt; &lt;span style="color:#f92672"&gt;in&lt;/span&gt; result&lt;span style="color:#f92672"&gt;.&lt;/span&gt;modified_payload&lt;span style="color:#f92672"&gt;.&lt;/span&gt;args[&lt;span style="color:#e6db74"&gt;&amp;#34;body&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;assert&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;[REDACTED]&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;in&lt;/span&gt; result&lt;span style="color:#f92672"&gt;.&lt;/span&gt;modified_payload&lt;span style="color:#f92672"&gt;.&lt;/span&gt;args[&lt;span style="color:#e6db74"&gt;&amp;#34;body&amp;#34;&lt;/span&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="integration-testing"&gt;Integration Testing&lt;a class="anchor" href="#integration-testing"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Use &lt;code&gt;PluginManager&lt;/code&gt; with a test configuration to verify the full pipeline — mode ordering, priority, chaining, and condition matching.&lt;/p&gt;</description></item><item><title>CLI Tools</title><link>https://contextforge-org.github.io/contextforge-plugins-framework/docs/cli/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://contextforge-org.github.io/contextforge-plugins-framework/docs/cli/</guid><description>&lt;h1 id="cli-tools"&gt;CLI Tools&lt;a class="anchor" href="#cli-tools"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;CPEX includes the &lt;code&gt;cpex&lt;/code&gt; command-line tool for scaffolding new plugin projects from templates.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="installation"&gt;Installation&lt;a class="anchor" href="#installation"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The CLI is included when you install CPEX:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pip install cpex&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;cpex&lt;/code&gt; command becomes available in your environment. To use the &lt;code&gt;bootstrap&lt;/code&gt; subcommand, install the CLI extras:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pip install &lt;span style="color:#e6db74"&gt;&amp;#34;cpex[cli]&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="cpex-bootstrap"&gt;&lt;code&gt;cpex bootstrap&lt;/code&gt;&lt;a class="anchor" href="#cpex-bootstrap"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Scaffolds a new plugin project from a cookiecutter template.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cpex bootstrap --destination ./my_plugin --template_type native&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="options"&gt;Options&lt;a class="anchor" href="#options"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Flag&lt;/th&gt;
 &lt;th&gt;Short&lt;/th&gt;
 &lt;th&gt;Default&lt;/th&gt;
 &lt;th&gt;Description&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;--destination&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;-d&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;.&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Output directory for the new plugin project&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;--template_type&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;-t&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;native&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Template type: &lt;code&gt;native&lt;/code&gt;, &lt;code&gt;external&lt;/code&gt;, or &lt;code&gt;isolated&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;--template_url&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;-u&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;—&lt;/td&gt;
 &lt;td&gt;Custom template URL (overrides built-in templates)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;--vcs_ref&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;-r&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;main&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Git branch/tag/commit for remote templates&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;--no_input&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;—&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Use defaults without prompting&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;--dry_run&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;—&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Preview what would be created without writing files&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="template-types"&gt;Template Types&lt;a class="anchor" href="#template-types"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;native&lt;/code&gt;&lt;/strong&gt; — an in-process plugin with hook stubs for tools and prompts:&lt;/p&gt;</description></item><item><title>API Reference</title><link>https://contextforge-org.github.io/contextforge-plugins-framework/docs/api-reference/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://contextforge-org.github.io/contextforge-plugins-framework/docs/api-reference/</guid><description>&lt;h1 id="api-reference"&gt;API Reference&lt;a class="anchor" href="#api-reference"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;All public symbols are importable from &lt;code&gt;cpex.framework&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; cpex.framework &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Plugin, hook, PluginManager, PluginConfig, &lt;span style="color:#f92672"&gt;...&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="core"&gt;Core&lt;a class="anchor" href="#core"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Symbol&lt;/th&gt;
 &lt;th&gt;Description&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;Plugin&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Base class for all plugins&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;hook&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Decorator to bind a method to one or more hook types&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;PluginManager&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Orchestrates plugin lifecycle and hook execution&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;get_plugin_manager&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Get or initialize the singleton plugin manager&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="configuration"&gt;Configuration&lt;a class="anchor" href="#configuration"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Symbol&lt;/th&gt;
 &lt;th&gt;Description&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;PluginConfig&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Plugin configuration model (frozen)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;PluginMode&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Execution mode enum: &lt;code&gt;SEQUENTIAL&lt;/code&gt;, &lt;code&gt;TRANSFORM&lt;/code&gt;, &lt;code&gt;AUDIT&lt;/code&gt;, &lt;code&gt;CONCURRENT&lt;/code&gt;, &lt;code&gt;FIRE_AND_FORGET&lt;/code&gt;, &lt;code&gt;DISABLED&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;OnError&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Error handling enum: &lt;code&gt;FAIL&lt;/code&gt;, &lt;code&gt;IGNORE&lt;/code&gt;, &lt;code&gt;DISABLE&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;PluginCondition&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Conditions for when a plugin should execute&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;ConfigLoader&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Loads plugin configuration from YAML files&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;PluginLoader&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Discovers and instantiates plugin classes&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="context"&gt;Context&lt;a class="anchor" href="#context"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Symbol&lt;/th&gt;
 &lt;th&gt;Description&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;GlobalContext&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Shared context across all plugins (request ID, user, tenant, server, state, metadata)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;PluginContext&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Per-plugin context that persists across hooks within a request lifecycle&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;PluginContextTable&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Type alias: &lt;code&gt;dict[str, PluginContext]&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="results--errors"&gt;Results &amp;amp; Errors&lt;a class="anchor" href="#results--errors"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Symbol&lt;/th&gt;
 &lt;th&gt;Description&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;PluginPayload&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Base class for all hook payloads (frozen)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;PluginResult&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Generic result from plugin hook processing&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;PluginViolation&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Policy violation with reason, description, code, and details&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;PluginError&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Exception for errors internal to a plugin&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;PluginViolationError&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Exception wrapping a &lt;code&gt;PluginViolation&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;PluginErrorModel&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Pydantic model for plugin error details&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="tool-hooks"&gt;Tool Hooks&lt;a class="anchor" href="#tool-hooks"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Symbol&lt;/th&gt;
 &lt;th&gt;Description&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;ToolHookType&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Enum: &lt;code&gt;TOOL_PRE_INVOKE&lt;/code&gt;, &lt;code&gt;TOOL_POST_INVOKE&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;ToolPreInvokePayload&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Payload for &lt;code&gt;tool_pre_invoke&lt;/code&gt; (name, args)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;ToolPreInvokeResult&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Result type for &lt;code&gt;tool_pre_invoke&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;ToolPostInvokePayload&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Payload for &lt;code&gt;tool_post_invoke&lt;/code&gt; (name, result)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;ToolPostInvokeResult&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Result type for &lt;code&gt;tool_post_invoke&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="prompt-hooks"&gt;Prompt Hooks&lt;a class="anchor" href="#prompt-hooks"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Symbol&lt;/th&gt;
 &lt;th&gt;Description&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;PromptHookType&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Enum: &lt;code&gt;PROMPT_PRE_FETCH&lt;/code&gt;, &lt;code&gt;PROMPT_POST_FETCH&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;PromptPrehookPayload&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Payload for &lt;code&gt;prompt_pre_fetch&lt;/code&gt; (prompt_id, args)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;PromptPrehookResult&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Result type for &lt;code&gt;prompt_pre_fetch&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;PromptPosthookPayload&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Payload for &lt;code&gt;prompt_post_fetch&lt;/code&gt; (prompt_id, result)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;PromptPosthookResult&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Result type for &lt;code&gt;prompt_post_fetch&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="resource-hooks"&gt;Resource Hooks&lt;a class="anchor" href="#resource-hooks"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Symbol&lt;/th&gt;
 &lt;th&gt;Description&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;ResourceHookType&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Enum: &lt;code&gt;RESOURCE_PRE_FETCH&lt;/code&gt;, &lt;code&gt;RESOURCE_POST_FETCH&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;ResourcePreFetchPayload&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Payload for &lt;code&gt;resource_pre_fetch&lt;/code&gt; (uri, metadata)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;ResourcePreFetchResult&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Result type for &lt;code&gt;resource_pre_fetch&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;ResourcePostFetchPayload&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Payload for &lt;code&gt;resource_post_fetch&lt;/code&gt; (uri, content)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;ResourcePostFetchResult&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Result type for &lt;code&gt;resource_post_fetch&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="agent-hooks"&gt;Agent Hooks&lt;a class="anchor" href="#agent-hooks"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Symbol&lt;/th&gt;
 &lt;th&gt;Description&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;AgentHookType&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Enum: &lt;code&gt;AGENT_PRE_INVOKE&lt;/code&gt;, &lt;code&gt;AGENT_POST_INVOKE&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;AgentPreInvokePayload&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Payload for &lt;code&gt;agent_pre_invoke&lt;/code&gt; (agent_id, messages, tools, model, system_prompt, parameters)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;AgentPreInvokeResult&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Result type for &lt;code&gt;agent_pre_invoke&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;AgentPostInvokePayload&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Payload for &lt;code&gt;agent_post_invoke&lt;/code&gt; (agent_id, messages, tool_calls)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;AgentPostInvokeResult&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Result type for &lt;code&gt;agent_post_invoke&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="http-hooks"&gt;HTTP Hooks&lt;a class="anchor" href="#http-hooks"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Symbol&lt;/th&gt;
 &lt;th&gt;Description&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;HttpHookType&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Enum: &lt;code&gt;HTTP_PRE_REQUEST&lt;/code&gt;, &lt;code&gt;HTTP_POST_REQUEST&lt;/code&gt;, &lt;code&gt;HTTP_AUTH_RESOLVE_USER&lt;/code&gt;, &lt;code&gt;HTTP_AUTH_CHECK_PERMISSION&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;HttpPreRequestPayload&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Payload for &lt;code&gt;http_pre_request&lt;/code&gt; (path, method, client_host, client_port, headers)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;HttpPreRequestResult&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Result type for &lt;code&gt;http_pre_request&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;HttpPostRequestPayload&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Payload for &lt;code&gt;http_post_request&lt;/code&gt; (extends pre-request with response_headers, status_code)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;HttpPostRequestResult&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Result type for &lt;code&gt;http_post_request&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;HttpAuthResolveUserPayload&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Payload for &lt;code&gt;http_auth_resolve_user&lt;/code&gt; (credentials, headers)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;HttpAuthResolveUserResult&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Result type for &lt;code&gt;http_auth_resolve_user&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;HttpAuthCheckPermissionPayload&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Payload for &lt;code&gt;http_auth_check_permission&lt;/code&gt; (user_email, permission, resource_type, &amp;hellip;)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;HttpAuthCheckPermissionResult&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Result type for &lt;code&gt;http_auth_check_permission&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;HttpAuthCheckPermissionResultPayload&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Result payload with granted/reason fields&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;HttpHeaderPayload&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;HTTP headers dict wrapper&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="hook-registry"&gt;Hook Registry&lt;a class="anchor" href="#hook-registry"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Symbol&lt;/th&gt;
 &lt;th&gt;Description&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;HookRegistry&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Singleton registry mapping hook types to payload/result classes&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;get_hook_registry&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Get the global &lt;code&gt;HookRegistry&lt;/code&gt; instance&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;HookPayloadPolicy&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Defines which payload fields plugins may modify&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="external-plugins"&gt;External Plugins&lt;a class="anchor" href="#external-plugins"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Symbol&lt;/th&gt;
 &lt;th&gt;Description&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;ExternalPluginServer&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;MCP-compatible server for hosting external plugins&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;MCPClientConfig&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Client-side MCP transport configuration&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;MCPServerConfig&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Server-side MCP configuration&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;TransportType&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Enum: &lt;code&gt;SSE&lt;/code&gt;, &lt;code&gt;HTTP&lt;/code&gt;, &lt;code&gt;STDIO&lt;/code&gt;, &lt;code&gt;STREAMABLEHTTP&lt;/code&gt;, &lt;code&gt;GRPC&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="observability"&gt;Observability&lt;a class="anchor" href="#observability"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Symbol&lt;/th&gt;
 &lt;th&gt;Description&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;ObservabilityProvider&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Protocol for injecting tracing into the plugin pipeline&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="utilities"&gt;Utilities&lt;a class="anchor" href="#utilities"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Symbol&lt;/th&gt;
 &lt;th&gt;Description&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;get_attr&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Safe nested attribute access utility&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;</description></item></channel></rss>