Skip to main content

ExtensionHandler

Trait ExtensionHandler 

Source
pub trait ExtensionHandler: Send + Sync {
Show 17 methods // Required methods fn id(&self) -> &str; fn handle<'life0, 'life1, 'async_trait>( &'life0 self, event: &'life1 HookEvent, ) -> Pin<Box<dyn Future<Output = HookResult> + Send + 'async_trait>> where 'life0: 'async_trait, 'life1: 'async_trait, Self: 'async_trait; fn shutdown<'life0, 'async_trait>( &'life0 self, ) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>> where 'life0: 'async_trait, Self: 'async_trait; // Provided methods fn lifecycle_snapshot(&self) -> Option<ExtensionLifecycle> { ... } fn handle_no_spawn<'life0, 'life1, 'async_trait>( &'life0 self, event: &'life1 HookEvent, ) -> Pin<Box<dyn Future<Output = HookResult> + Send + 'async_trait>> where 'life0: 'async_trait, 'life1: 'async_trait, Self: 'async_trait { ... } fn call_tool_for<'life0, 'life1, 'life2, 'life3, 'async_trait>( &'life0 self, name: &'life1 str, input: Value, session_id: Option<&'life2 str>, cwd: Option<&'life3 Path>, ) -> Pin<Box<dyn Future<Output = Result<Value, String>> + Send + 'async_trait>> where 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait, Self: 'async_trait { ... } fn call_tool_as<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>( &'life0 self, name: &'life1 str, input: Value, session_id: Option<&'life2 str>, cwd: Option<&'life3 Path>, caller: Option<&'life4 Author>, ) -> Pin<Box<dyn Future<Output = Result<Value, String>> + Send + 'async_trait>> where 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait, 'life4: 'async_trait, Self: 'async_trait { ... } fn provider_complete<'life0, 'async_trait>( &'life0 self, _params: ProviderCompleteParams, ) -> Pin<Box<dyn Future<Output = Result<ProviderCompleteResult, String>> + Send + 'async_trait>> where 'life0: 'async_trait, Self: 'async_trait { ... } fn provider_stream<'life0, 'async_trait>( &'life0 self, _params: ProviderCompleteParams, _sink: Sender<ProviderStreamEvent>, ) -> Pin<Box<dyn Future<Output = Result<ProviderCompleteResult, String>> + Send + 'async_trait>> where 'life0: 'async_trait, Self: 'async_trait { ... } fn invoke_command<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, _command: &'life1 str, _args: Vec<String>, _request_id: &'life2 str, _sink: InvokeEventSink, ) -> Pin<Box<dyn Future<Output = Result<Value, String>> + Send + 'async_trait>> where 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, Self: 'async_trait { ... } fn get_info<'life0, 'async_trait>( &'life0 self, ) -> Pin<Box<dyn Future<Output = Result<PluginInfo, String>> + Send + 'async_trait>> where 'life0: 'async_trait, Self: 'async_trait { ... } fn settings_editor_open<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, _category: &'life1 str, _field: &'life2 str, ) -> Pin<Box<dyn Future<Output = Result<Value, String>> + Send + 'async_trait>> where 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, Self: 'async_trait { ... } fn settings_editor_key<'life0, 'life1, 'life2, 'life3, 'async_trait>( &'life0 self, _category: &'life1 str, _field: &'life2 str, _key: &'life3 str, ) -> Pin<Box<dyn Future<Output = Result<Value, String>> + Send + 'async_trait>> where 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait, Self: 'async_trait { ... } fn settings_editor_commit<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, _category: &'life1 str, _field: &'life2 str, _value: Value, ) -> Pin<Box<dyn Future<Output = Result<Value, String>> + Send + 'async_trait>> where 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, Self: 'async_trait { ... } fn subscribe_notifications<'life0, 'async_trait>( &'life0 self, ) -> Pin<Box<dyn Future<Output = (usize, Receiver<NotificationFrame>)> + Send + 'async_trait>> where 'life0: 'async_trait, Self: 'async_trait { ... } fn health<'life0, 'async_trait>( &'life0 self, ) -> Pin<Box<dyn Future<Output = ExtensionHealth> + Send + 'async_trait>> where 'life0: 'async_trait, Self: 'async_trait { ... } fn restart_count<'life0, 'async_trait>( &'life0 self, ) -> Pin<Box<dyn Future<Output = usize> + Send + 'async_trait>> where 'life0: 'async_trait, Self: 'async_trait { ... }
}
Expand description

Trait for extension runtimes that can handle hook events.

Required Methods§

Source

fn id(&self) -> &str

Unique identifier for this extension.

Source

fn handle<'life0, 'life1, 'async_trait>( &'life0 self, event: &'life1 HookEvent, ) -> Pin<Box<dyn Future<Output = HookResult> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, Self: 'async_trait,

Handle a hook event. Returns the handler’s decision.

Source

fn shutdown<'life0, 'async_trait>( &'life0 self, ) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>
where 'life0: 'async_trait, Self: 'async_trait,

Gracefully shut down the extension.

Provided Methods§

Source

fn lifecycle_snapshot(&self) -> Option<ExtensionLifecycle>

Observe the actual serving runtime without waiting for locks or RPC I/O.

None means unsupported or temporarily unavailable, never “healthy”. Implementations must invalidate the generation on runtime/transport loss and before replacement, even if the handler itself remains the same Arc. Consumers retaining authority must fail closed on None, non-live health, or a changed generation, including while a previously valid reply waits to execute. Recheck at dispatch, reply acceptance, and each frontend tick; this snapshot cannot eliminate exit races after the final observation.

Source

fn handle_no_spawn<'life0, 'life1, 'async_trait>( &'life0 self, event: &'life1 HookEvent, ) -> Pin<Box<dyn Future<Output = HookResult> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, Self: 'async_trait,

Self::handle for a caller that must never start or restart the extension: a subagent worker’s hook view (HookBus::worker_transforms). A worker runs on its own throwaway current-thread tokio runtime, so a child process or reader task spawned from it would die with the worker and leave the shared extension broken for the foreground and every other worker. An extension that is not running (crashed, stopped by a reload, deferred and not started yet) answers Continue; the foreground’s next call starts it as before. Default: Self::handle (nothing to start).

Source

fn call_tool_for<'life0, 'life1, 'life2, 'life3, 'async_trait>( &'life0 self, name: &'life1 str, input: Value, session_id: Option<&'life2 str>, cwd: Option<&'life3 Path>, ) -> Pin<Box<dyn Future<Output = Result<Value, String>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait, Self: 'async_trait,

Call an extension-provided tool on behalf of a conversation. The only way to call a tool: every caller says who the call is for.

session_id is the owning conversation id (None = worker/subagent or no conversation); cwd is the folder the caller runs in (None = the process folder, which under the daemon is the daemon’s, so a call made for a chat passes the chat’s). Default: this runtime serves no tools.

Source

fn call_tool_as<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>( &'life0 self, name: &'life1 str, input: Value, session_id: Option<&'life2 str>, cwd: Option<&'life3 Path>, caller: Option<&'life4 Author>, ) -> Pin<Box<dyn Future<Output = Result<Value, String>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait, 'life4: 'async_trait, Self: 'async_trait,

Self::call_tool_for, naming the run that calls: caller is the author of the calling run’s memory binding (None = the call’s context has no binding), sent as tool.call params.caller so an extension can tell a chat from each of its workers. What a registered extension tool calls. Default: Self::call_tool_for, for a handler that has no wire to put it on.

Source

fn provider_complete<'life0, 'async_trait>( &'life0 self, _params: ProviderCompleteParams, ) -> Pin<Box<dyn Future<Output = Result<ProviderCompleteResult, String>> + Send + 'async_trait>>
where 'life0: 'async_trait, Self: 'async_trait,

Complete a chat request through an extension-provided model provider.

Source

fn provider_stream<'life0, 'async_trait>( &'life0 self, _params: ProviderCompleteParams, _sink: Sender<ProviderStreamEvent>, ) -> Pin<Box<dyn Future<Output = Result<ProviderCompleteResult, String>> + Send + 'async_trait>>
where 'life0: 'async_trait, Self: 'async_trait,

Stream a chat request through an extension-provided model provider.

The handler must forward provider.stream.event notifications to sink in order. The returned ProviderCompleteResult is the final aggregated response (so callers that don’t need streaming can use it as the final state). Implementations that don’t support streaming should return Err("provider.stream is not supported by this extension").

Source

fn invoke_command<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, _command: &'life1 str, _args: Vec<String>, _request_id: &'life2 str, _sink: InvokeEventSink, ) -> Pin<Box<dyn Future<Output = Result<Value, String>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, Self: 'async_trait,

Invoke a plugin-registered interactive slash command. The handler must forward command.output notifications matching request_id and any task.* notifications to sink. Returns the final response value.

CP-11 fix-3: the sink is BOUNDED and metered (crate::extensions::invoke_output::InvokeEventSink). It must be paired with an EAGERLY CONCURRENT consumer — normally the collector half of crate::extensions::invoke_output::invoke_event_channel, joined with this call by ExtensionManager::invoke_command_collected — which enforces invocation-local byte/event budgets at production time. A hostile command.output flood is therefore paced by awaited sends and bounded retention instead of parking aggregate bytes in an unbounded post-hoc queue (the pre-fix behavior). Handlers must treat a closed sink like the old closed-unbounded-sink case: stop forwarding, still complete the call.

Source

fn get_info<'life0, 'async_trait>( &'life0 self, ) -> Pin<Box<dyn Future<Output = Result<PluginInfo, String>> + Send + 'async_trait>>
where 'life0: 'async_trait, Self: 'async_trait,

Fetch optional plugin capability/build/model information.

Source

fn settings_editor_open<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, _category: &'life1 str, _field: &'life2 str, ) -> Pin<Box<dyn Future<Output = Result<Value, String>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, Self: 'async_trait,

Open a plugin-owned custom settings editor and return its initial render payload.

Source

fn settings_editor_key<'life0, 'life1, 'life2, 'life3, 'async_trait>( &'life0 self, _category: &'life1 str, _field: &'life2 str, _key: &'life3 str, ) -> Pin<Box<dyn Future<Output = Result<Value, String>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait, Self: 'async_trait,

Forward a keypress to the active plugin-owned custom settings editor.

Source

fn settings_editor_commit<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, _category: &'life1 str, _field: &'life2 str, _value: Value, ) -> Pin<Box<dyn Future<Output = Result<Value, String>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, Self: 'async_trait,

Ask the plugin to commit a custom editor value selected by the UI.

Source

fn subscribe_notifications<'life0, 'async_trait>( &'life0 self, ) -> Pin<Box<dyn Future<Output = (usize, Receiver<NotificationFrame>)> + Send + 'async_trait>>
where 'life0: 'async_trait, Self: 'async_trait,

Subscribe to raw JSON-RPC notifications from this extension. Returns a monotonic subscription id (for unsubscribing) and a receiver that yields every notification frame. The default implementation returns a channel whose sender is immediately dropped, so the receiver yields None right away — effectively a no-op for handler impls that don’t support notifications.

Source

fn health<'life0, 'async_trait>( &'life0 self, ) -> Pin<Box<dyn Future<Output = ExtensionHealth> + Send + 'async_trait>>
where 'life0: 'async_trait, Self: 'async_trait,

Current health state of this handler.

Source

fn restart_count<'life0, 'async_trait>( &'life0 self, ) -> Pin<Box<dyn Future<Output = usize> + Send + 'async_trait>>
where 'life0: 'async_trait, Self: 'async_trait,

Number of transport restarts observed by this handler.

Dyn Compatibility§

This trait is dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§