pub struct Extensions {Show 16 fields
pub request: Option<Arc<RequestExtension>>,
pub agent: Option<Arc<AgentExtension>>,
pub http: Option<Arc<HttpExtension>>,
pub security: Option<Arc<SecurityExtension>>,
pub delegation: Option<Arc<DelegationExtension>>,
pub raw_credentials: Option<Arc<RawCredentialsExtension>>,
pub mcp: Option<Arc<MCPExtension>>,
pub completion: Option<Arc<CompletionExtension>>,
pub provenance: Option<Arc<ProvenanceExtension>>,
pub llm: Option<Arc<LLMExtension>>,
pub framework: Option<Arc<FrameworkExtension>>,
pub meta: Option<Arc<MetaExtension>>,
pub custom: Option<Arc<HashMap<String, Value>>>,
pub http_write_token: Option<WriteToken>,
pub labels_write_token: Option<WriteToken>,
pub delegation_write_token: Option<WriteToken>,
}Expand description
Typed container for all message extensions.
All slots are Arc<T> — fully immutable, zero-copy shareable.
Cloning is all refcount bumps. filter_extensions() creates a
filtered view by setting unwanted slots to None (still all Arc,
no deep copies). Plugins receive &Extensions (zero cost).
To modify, plugins call cow_copy() which returns an
OwnedExtensions with mutable/monotonic/guarded slots cloned
out of Arc and write tokens propagated.
Mirrors Python’s cpex.framework.extensions.Extensions.
Fields§
§request: Option<Arc<RequestExtension>>Execution environment and request tracing (immutable).
agent: Option<Arc<AgentExtension>>Agent execution context — session, conversation, lineage (immutable).
http: Option<Arc<HttpExtension>>HTTP headers (frozen as Arc — unfrozen in OwnedExtensions).
security: Option<Arc<SecurityExtension>>Security — labels, classification, subject (frozen as Arc).
delegation: Option<Arc<DelegationExtension>>Delegation chain (frozen as Arc).
raw_credentials: Option<Arc<RawCredentialsExtension>>Raw credential material — Layer 3 of the credential storage
model (see RawCredentialsExtension docs). Capability-gated;
filter_extensions strips this slot for plugins without
read_inbound_credentials / read_delegated_tokens. Token
fields inside this extension are #[serde(skip)], so any
serialization (logs, audit dumps, hot-reload snapshots) drops
secret material even when the slot itself survives. The
out-of-process consequence — remote / WASM plugins can’t see
raw tokens at all — is intentional and documented on
RawCredentialsExtension.
mcp: Option<Arc<MCPExtension>>MCP entity metadata (immutable).
completion: Option<Arc<CompletionExtension>>LLM completion information (immutable).
provenance: Option<Arc<ProvenanceExtension>>Origin and message threading (immutable).
llm: Option<Arc<LLMExtension>>Model identity and capabilities (immutable).
framework: Option<Arc<FrameworkExtension>>Agentic framework context (immutable).
meta: Option<Arc<MetaExtension>>Host-provided operational metadata (immutable).
custom: Option<Arc<HashMap<String, Value>>>Custom extensions (frozen as Arc — unfrozen in OwnedExtensions).
http_write_token: Option<WriteToken>Write tokens — set by the executor per plugin, NOT serialized.
Used by cow_copy() to propagate write access to OwnedExtensions.
labels_write_token: Option<WriteToken>§delegation_write_token: Option<WriteToken>Implementations§
Source§impl Extensions
impl Extensions
Sourcepub fn cow_copy(&self) -> OwnedExtensions
pub fn cow_copy(&self) -> OwnedExtensions
Create a copy-on-write owned copy for modification.
Immutable slots share the same Arc (refcount bump, ~1ns).
Mutable/monotonic/guarded slots are cloned out of Arc into
owned values — the plugin can modify them directly.
Write tokens are propagated from the original.
§Usage
fn handle(&self, payload: &P, ext: &Extensions, ctx: &mut PluginContext) -> PluginResult<P> {
let mut owned = ext.cow_copy();
owned.security.as_mut().unwrap().add_label("CHECKED");
if let Some(ref token) = owned.http_write_token {
owned.http.as_mut().unwrap().write(token).set_header("X-Foo", "bar");
}
PluginResult::modify_extensions(owned)
}Examples found in repository?
139 async fn handle(
140 &self,
141 _payload: &MessagePayload,
142 extensions: &Extensions,
143 _ctx: &mut PluginContext,
144 ) -> PluginResult<MessagePayload> {
145 // Can see HTTP (has read_headers)
146 if let Some(ref http) = extensions.http {
147 println!(
148 " [header-injector] HTTP headers visible: {:?}",
149 http.request_headers
150 );
151 }
152
153 // Can NOT see security subject (no read_subject)
154 if let Some(ref security) = extensions.security {
155 if security.subject.is_some() {
156 println!(" [header-injector] WARNING: Subject visible (unexpected!)");
157 } else {
158 println!(" [header-injector] Security subject: not visible (no read_subject)");
159 }
160 }
161
162 // COW copy to modify — tokens propagate from the executor
163 let mut modified = extensions.cow_copy();
164
165 // Add a label via MonotonicSet (has append_labels)
166 if modified.labels_write_token.is_some() {
167 modified.security.as_mut().unwrap().add_label("PROCESSED");
168 println!(" [header-injector] Added label 'PROCESSED'");
169 }
170
171 // Inject a header via Guarded (has write_headers)
172 if let Some(ref token) = modified.http_write_token {
173 modified
174 .http
175 .as_mut()
176 .unwrap()
177 .write(token)
178 .set_header("X-Processed-By", "header-injector");
179 println!(" [header-injector] Injected header 'X-Processed-By'");
180 }
181
182 PluginResult::modify_extensions(modified)
183 }Sourcepub fn validate_immutable(&self, modified: &OwnedExtensions) -> bool
pub fn validate_immutable(&self, modified: &OwnedExtensions) -> bool
Validate that immutable slots were not tampered with.
A slot that is None in modified (because capability filtering
hid it from the plugin) is always valid — the plugin never saw
it. Only flag as tampering when both are Some with different
Arc pointers, or when the original is None but modified is
Some (the plugin fabricated a slot it shouldn’t have).
Sourcepub fn merge_owned(&mut self, owned: OwnedExtensions)
pub fn merge_owned(&mut self, owned: OwnedExtensions)
Merge an OwnedExtensions back into this Extensions.