1use std::collections::{BTreeMap, BTreeSet};
2use std::sync::Arc;
3
4use super::*;
5
6#[derive(Clone)]
7pub(crate) struct RegisteredHook<T> {
8 pub(crate) plugin_id: String,
9 pub(crate) hook: T,
10}
11
12#[derive(Clone)]
13pub(crate) struct RegisteredExclusiveHook<T> {
14 pub(crate) plugin_id: String,
15 pub(crate) hook: T,
16}
17
18pub(crate) fn current_registration_owner(registering_plugin_id: &Option<String>) -> String {
19 registering_plugin_id
20 .clone()
21 .unwrap_or_else(|| "__unknown__".to_string())
22}
23
24fn push_registered_hook<T>(
25 hooks: &mut Vec<RegisteredHook<T>>,
26 registering_plugin_id: &Option<String>,
27 hook: T,
28) {
29 hooks.push(RegisteredHook {
30 plugin_id: current_registration_owner(registering_plugin_id),
31 hook,
32 });
33}
34
35fn push_prioritized_registered_hook<T>(
36 hooks: &mut Vec<(i32, RegisteredHook<T>)>,
37 registering_plugin_id: &Option<String>,
38 priority: i32,
39 hook: T,
40) {
41 hooks.push((
42 priority,
43 RegisteredHook {
44 plugin_id: current_registration_owner(registering_plugin_id),
45 hook,
46 },
47 ));
48}
49
50fn exclusive_hook_owner(
51 existing_owner: Option<&str>,
52 registering_plugin_id: &Option<String>,
53 hook_kind: &str,
54 hook_name: &str,
55) -> Result<String, PluginError> {
56 let plugin_id = registering_plugin_id
57 .clone()
58 .ok_or_else(|| PluginError::Registration("missing registering plugin id".to_string()))?;
59 if let Some(existing) = existing_owner {
60 return Err(PluginError::Registration(format!(
61 "duplicate {hook_kind} for `{hook_name}`: `{plugin_id}` conflicts with `{existing}`"
62 )));
63 }
64 Ok(plugin_id)
65}
66
67fn register_singleton_hook<H>(
68 slot: &mut Option<RegisteredExclusiveHook<H>>,
69 registering_plugin_id: &Option<String>,
70 hook_kind: &str,
71 hook_name: &str,
72 hook: H,
73) -> Result<(), PluginError> {
74 let plugin_id = exclusive_hook_owner(
75 slot.as_ref()
76 .map(|registered| registered.plugin_id.as_str()),
77 registering_plugin_id,
78 hook_kind,
79 hook_name,
80 )?;
81 *slot = Some(RegisteredExclusiveHook { plugin_id, hook });
82 Ok(())
83}
84
85#[derive(Clone, Default)]
86pub(crate) struct PluginContributions {
87 pub(crate) tool_providers: Vec<Arc<dyn ToolProvider>>,
88 pub(crate) triggers: Vec<crate::TriggerEvent>,
89 pub(crate) prompt_contributors: Vec<RegisteredHook<PromptContributor>>,
90 pub(crate) tool_catalog_contributors: Vec<RegisteredHook<ToolCatalogContributor>>,
91 pub(crate) before_turn_hooks: Vec<RegisteredHook<BeforeTurnHook>>,
92 pub(crate) before_tool_call_hooks: Vec<RegisteredHook<BeforeToolCallHook>>,
93 pub(crate) after_tool_call_hooks: Vec<RegisteredHook<AfterToolCallHook>>,
94 pub(crate) after_turn_hooks: Vec<RegisteredHook<AfterTurnHook>>,
95 pub(crate) checkpoint_hooks: Vec<RegisteredHook<CheckpointHook>>,
96 pub(crate) assistant_stream_hooks: Vec<RegisteredHook<AssistantStreamHook>>,
97 pub(crate) assistant_response_hooks: Vec<RegisteredHook<AssistantResponseHook>>,
98 pub(crate) tool_result_projector: Option<RegisteredExclusiveHook<ToolResultProjector>>,
99 pub(crate) runtime_event_hooks: Vec<RegisteredHook<PluginLifecycleEventHook>>,
100 pub(crate) session_config_mutators: Vec<SessionConfigMutator>,
101 pub(crate) plugin_queries: BTreeMap<String, RegisteredPluginQuery>,
102 pub(crate) plugin_commands: BTreeMap<String, RegisteredPluginCommand>,
103 pub(crate) plugin_tasks: BTreeMap<String, RegisteredPluginTask>,
104 pub(crate) turn_context_transforms: Vec<(i32, RegisteredHook<Arc<dyn TurnContextTransform>>)>,
105 pub(crate) context_compactors: Vec<(i32, RegisteredHook<Arc<dyn ContextCompactor>>)>,
106 pub(crate) protocol_session: Option<RegisteredExclusiveHook<Arc<dyn ProtocolSessionPlugin>>>,
107 pub(crate) protocol_driver: Option<RegisteredExclusiveHook<Arc<dyn ProtocolDriverPlugin>>>,
108 pub(crate) code_executor: Option<RegisteredExclusiveHook<Arc<dyn CodeExecutorPlugin>>>,
109 pub(crate) assistant_prose_projector:
110 Option<RegisteredExclusiveHook<Arc<dyn AssistantProseProjectorPlugin>>>,
111}
112
113pub struct PluginRegistrar {
114 pub(crate) tool_names: BTreeSet<String>,
115 pub(crate) contributions: PluginContributions,
116 pub(crate) registering_plugin_id: Option<String>,
117}
118
119pub struct ToolRegistrations<'a> {
120 reg: &'a mut PluginRegistrar,
121}
122
123impl ToolRegistrations<'_> {
124 pub fn provider(self, provider: Arc<dyn ToolProvider>) -> Result<(), PluginError> {
125 self.reg.add_tool_provider(provider)
126 }
127}
128
129pub struct TriggerEventRegistrations<'a> {
130 reg: &'a mut PluginRegistrar,
131}
132
133impl TriggerEventRegistrations<'_> {
134 pub fn declare(self, event: crate::TriggerEvent) -> Result<(), PluginError> {
135 self.reg.add_trigger(event)
136 }
137}
138
139pub struct PromptRegistrations<'a> {
140 reg: &'a mut PluginRegistrar,
141}
142
143impl PromptRegistrations<'_> {
144 pub fn contribute(self, contributor: PromptContributor) {
145 self.reg.add_prompt_contributor(contributor);
146 }
147}
148
149pub struct ToolCatalogRegistrations<'a> {
150 reg: &'a mut PluginRegistrar,
151}
152
153impl ToolCatalogRegistrations<'_> {
154 pub fn contribute(self, contributor: ToolCatalogContributor) {
155 self.reg.add_tool_catalog_contributor(contributor);
156 }
157}
158
159pub struct TurnRegistrations<'a> {
160 reg: &'a mut PluginRegistrar,
161}
162
163impl TurnRegistrations<'_> {
164 pub fn before(self, hook: BeforeTurnHook) {
165 self.reg.add_before_turn_hook(hook);
166 }
167
168 pub fn after(self, hook: AfterTurnHook) {
169 self.reg.add_after_turn_hook(hook);
170 }
171
172 pub fn checkpoint(self, hook: CheckpointHook) {
173 self.reg.add_checkpoint_hook(hook);
174 }
175}
176
177pub struct ToolCallRegistrations<'a> {
178 reg: &'a mut PluginRegistrar,
179}
180
181impl ToolCallRegistrations<'_> {
182 pub fn before(self, hook: BeforeToolCallHook) {
183 self.reg.add_before_tool_call_hook(hook);
184 }
185
186 pub fn after(self, hook: AfterToolCallHook) {
187 self.reg.add_after_tool_call_hook(hook);
188 }
189}
190
191pub struct OutputRegistrations<'a> {
192 reg: &'a mut PluginRegistrar,
193}
194
195impl OutputRegistrations<'_> {
196 pub fn stream(self, hook: AssistantStreamHook) {
197 self.reg.add_assistant_stream_hook(hook);
198 }
199
200 pub fn response(self, hook: AssistantResponseHook) {
201 self.reg.add_assistant_response_hook(hook);
202 }
203
204 pub fn assistant_prose_projector(
205 self,
206 provider: Arc<dyn AssistantProseProjectorPlugin>,
207 ) -> Result<(), PluginError> {
208 self.reg.add_assistant_prose_projector(provider)
209 }
210}
211
212pub struct ToolResultRegistrations<'a> {
213 reg: &'a mut PluginRegistrar,
214}
215
216impl ToolResultRegistrations<'_> {
217 pub fn projector(self, hook: ToolResultProjector) -> Result<(), PluginError> {
218 self.reg.add_tool_result_projector(hook)
219 }
220}
221
222pub struct SessionRegistrations<'a> {
223 reg: &'a mut PluginRegistrar,
224}
225
226impl SessionRegistrations<'_> {
227 pub fn on_event(self, hook: PluginLifecycleEventHook) {
228 push_registered_hook(
229 &mut self.reg.contributions.runtime_event_hooks,
230 &self.reg.registering_plugin_id,
231 hook,
232 );
233 }
234
235 pub fn config_mutator(self, hook: SessionConfigMutator) {
236 self.reg.contributions.session_config_mutators.push(hook);
237 }
238}
239
240pub struct PluginOperationRegistrations<'a> {
241 reg: &'a mut PluginRegistrar,
242}
243
244impl PluginOperationRegistrations<'_> {
245 pub(crate) fn query(
246 self,
247 def: PluginOperationDef,
248 handler: PluginQueryHandler,
249 ) -> Result<(), PluginError> {
250 self.reg.add_plugin_query(def, handler)
251 }
252
253 pub(crate) fn command(
254 self,
255 def: PluginOperationDef,
256 handler: PluginCommandHandler,
257 ) -> Result<(), PluginError> {
258 self.reg.add_plugin_command(def, handler)
259 }
260
261 pub(crate) fn task(
262 self,
263 def: PluginOperationDef,
264 handler: PluginTaskHandler,
265 ) -> Result<(), PluginError> {
266 self.reg.add_plugin_task(def, handler)
267 }
268
269 pub fn typed_query<Op, F, Fut>(self, handler: F) -> Result<(), PluginError>
270 where
271 Op: PluginQuery,
272 F: Fn(PluginQueryContext, Op::Args) -> Fut + Send + Sync + 'static,
273 Fut: Future<Output = Result<Op::Output, PluginOperationFailure>> + Send + 'static,
274 {
275 self.query(
276 plugin_operation_def::<Op>(PluginOperationKind::Query),
277 Arc::new(move |ctx, args| {
278 let parsed = serde_json::from_value::<Op::Args>(args);
279 match parsed {
280 Ok(args) => {
281 let fut = handler(ctx, args);
282 Box::pin(async move {
283 let output = fut.await?;
284 serde_json::to_value(output).map_err(|err| {
285 PluginOperationFailure::new(format!(
286 "failed to serialize {} output: {err}",
287 Op::NAME
288 ))
289 })
290 }) as PluginQueryInvokeFuture
291 }
292 Err(err) => Box::pin(async move {
293 Err(PluginOperationFailure::new(format!(
294 "invalid {} args: {err}",
295 Op::NAME
296 )))
297 }) as PluginQueryInvokeFuture,
298 }
299 }),
300 )
301 }
302
303 pub fn typed_command<Op, F, Fut>(self, handler: F) -> Result<(), PluginError>
304 where
305 Op: PluginCommand,
306 F: Fn(PluginCommandContext, Op::Args) -> Fut + Send + Sync + 'static,
307 Fut: Future<Output = Result<PluginCommandOutcome<Op::Output>, PluginOperationFailure>>
308 + Send
309 + 'static,
310 {
311 self.command(
312 plugin_operation_def::<Op>(PluginOperationKind::Command),
313 Arc::new(move |ctx, args| {
314 let parsed = serde_json::from_value::<Op::Args>(args);
315 match parsed {
316 Ok(args) => {
317 let fut = handler(ctx, args);
318 Box::pin(async move {
319 let outcome = fut.await?;
320 let output = serde_json::to_value(outcome.output).map_err(|err| {
321 PluginOperationFailure::new(format!(
322 "failed to serialize {} output: {err}",
323 Op::NAME
324 ))
325 })?;
326 Ok(ErasedPluginCommandOutcome {
327 output,
328 events: outcome.events,
329 directives: outcome.directives,
330 })
331 }) as PluginCommandInvokeFuture
332 }
333 Err(err) => Box::pin(async move {
334 Err(PluginOperationFailure::new(format!(
335 "invalid {} args: {err}",
336 Op::NAME
337 )))
338 }) as PluginCommandInvokeFuture,
339 }
340 }),
341 )
342 }
343
344 pub fn typed_command_value<Op, F, Fut>(self, handler: F) -> Result<(), PluginError>
345 where
346 Op: PluginCommand,
347 F: Fn(PluginCommandContext, Op::Args) -> Fut + Send + Sync + 'static,
348 Fut: Future<Output = Result<Op::Output, PluginOperationFailure>> + Send + 'static,
349 {
350 self.typed_command::<Op, _, _>(move |ctx, args| {
351 let fut = handler(ctx, args);
352 async move { fut.await.map(PluginCommandOutcome::new) }
353 })
354 }
355
356 pub fn typed_task<Op, F, Fut>(self, handler: F) -> Result<(), PluginError>
357 where
358 Op: PluginTask,
359 F: Fn(PluginTaskContext, Op::Args) -> Fut + Send + Sync + 'static,
360 Fut: Future<Output = Result<PluginTaskOutcome<Op::Output>, PluginOperationFailure>>
361 + Send
362 + 'static,
363 {
364 self.task(
365 plugin_operation_def::<Op>(PluginOperationKind::Task),
366 Arc::new(move |ctx, args| {
367 let parsed = serde_json::from_value::<Op::Args>(args);
368 match parsed {
369 Ok(args) => {
370 let fut = handler(ctx, args);
371 Box::pin(async move {
372 let outcome = fut.await?;
373 let output = serde_json::to_value(outcome.output).map_err(|err| {
374 PluginOperationFailure::new(format!(
375 "failed to serialize {} output: {err}",
376 Op::NAME
377 ))
378 })?;
379 Ok(ErasedPluginTaskOutcome {
380 output,
381 events: outcome.events,
382 directives: outcome.directives,
383 })
384 }) as PluginTaskInvokeFuture
385 }
386 Err(err) => Box::pin(async move {
387 Err(PluginOperationFailure::new(format!(
388 "invalid {} args: {err}",
389 Op::NAME
390 )))
391 }) as PluginTaskInvokeFuture,
392 }
393 }),
394 )
395 }
396
397 pub fn typed_task_value<Op, F, Fut>(self, handler: F) -> Result<(), PluginError>
398 where
399 Op: PluginTask,
400 F: Fn(PluginTaskContext, Op::Args) -> Fut + Send + Sync + 'static,
401 Fut: Future<Output = Result<Op::Output, PluginOperationFailure>> + Send + 'static,
402 {
403 self.typed_task::<Op, _, _>(move |ctx, args| {
404 let fut = handler(ctx, args);
405 async move { fut.await.map(PluginTaskOutcome::new) }
406 })
407 }
408}
409
410pub struct ContextRegistrations<'a> {
411 reg: &'a mut PluginRegistrar,
412}
413
414impl ContextRegistrations<'_> {
415 pub fn prepare_turn(self, priority: i32, transform: Arc<dyn TurnContextTransform>) {
417 push_prioritized_registered_hook(
418 &mut self.reg.contributions.turn_context_transforms,
419 &self.reg.registering_plugin_id,
420 priority,
421 transform,
422 );
423 }
424
425 pub fn compact(self, priority: i32, compactor: Arc<dyn ContextCompactor>) {
427 push_prioritized_registered_hook(
428 &mut self.reg.contributions.context_compactors,
429 &self.reg.registering_plugin_id,
430 priority,
431 compactor,
432 );
433 }
434}
435
436pub struct ProtocolRegistrations<'a> {
437 reg: &'a mut PluginRegistrar,
438}
439
440impl ProtocolRegistrations<'_> {
441 pub fn session(self, provider: Arc<dyn ProtocolSessionPlugin>) -> Result<(), PluginError> {
442 self.reg.add_protocol_session(provider)
443 }
444
445 pub fn protocol_driver(
449 self,
450 provider: Arc<dyn ProtocolDriverPlugin>,
451 ) -> Result<(), PluginError> {
452 self.reg.add_protocol_driver(provider)
453 }
454}
455
456pub struct ExecutionRegistrations<'a> {
457 reg: &'a mut PluginRegistrar,
458}
459
460impl ExecutionRegistrations<'_> {
461 pub fn code_executor(self, provider: Arc<dyn CodeExecutorPlugin>) -> Result<(), PluginError> {
462 self.reg.add_code_executor(provider)
463 }
464}
465
466impl PluginRegistrar {
467 pub(crate) fn new() -> Self {
468 Self {
469 tool_names: BTreeSet::new(),
470 contributions: PluginContributions::default(),
471 registering_plugin_id: None,
472 }
473 }
474
475 pub fn tools(&mut self) -> ToolRegistrations<'_> {
476 ToolRegistrations { reg: self }
477 }
478
479 pub fn triggers(&mut self) -> TriggerEventRegistrations<'_> {
480 TriggerEventRegistrations { reg: self }
481 }
482
483 pub fn prompt(&mut self) -> PromptRegistrations<'_> {
484 PromptRegistrations { reg: self }
485 }
486
487 pub fn tool_catalog(&mut self) -> ToolCatalogRegistrations<'_> {
488 ToolCatalogRegistrations { reg: self }
489 }
490
491 pub fn turn(&mut self) -> TurnRegistrations<'_> {
492 TurnRegistrations { reg: self }
493 }
494
495 pub fn tool_calls(&mut self) -> ToolCallRegistrations<'_> {
496 ToolCallRegistrations { reg: self }
497 }
498
499 pub fn output(&mut self) -> OutputRegistrations<'_> {
500 OutputRegistrations { reg: self }
501 }
502
503 pub fn tool_results(&mut self) -> ToolResultRegistrations<'_> {
504 ToolResultRegistrations { reg: self }
505 }
506
507 pub fn session(&mut self) -> SessionRegistrations<'_> {
508 SessionRegistrations { reg: self }
509 }
510
511 pub fn operations(&mut self) -> PluginOperationRegistrations<'_> {
512 PluginOperationRegistrations { reg: self }
513 }
514
515 pub fn context(&mut self) -> ContextRegistrations<'_> {
516 ContextRegistrations { reg: self }
517 }
518
519 pub fn protocol(&mut self) -> ProtocolRegistrations<'_> {
520 ProtocolRegistrations { reg: self }
521 }
522
523 pub fn execution(&mut self) -> ExecutionRegistrations<'_> {
524 ExecutionRegistrations { reg: self }
525 }
526
527 fn add_tool_provider(&mut self, provider: Arc<dyn ToolProvider>) -> Result<(), PluginError> {
528 for manifest in provider.tool_manifests() {
529 if !self.tool_names.insert(manifest.name.clone()) {
530 return Err(PluginError::Registration(format!(
531 "duplicate plugin tool name `{}`",
532 manifest.name
533 )));
534 }
535 }
536 self.contributions.tool_providers.push(provider);
537 Ok(())
538 }
539
540 fn add_trigger(&mut self, event: crate::TriggerEvent) -> Result<(), PluginError> {
541 if self
542 .contributions
543 .triggers
544 .iter()
545 .any(|existing| existing.key() == event.key())
546 {
547 return Err(PluginError::Registration(format!(
548 "duplicate trigger occurrence `{}.{}.{}`",
549 event.resource_type, event.alias, event.event
550 )));
551 }
552 self.contributions.triggers.push(event);
553 Ok(())
554 }
555
556 fn add_prompt_contributor(&mut self, contributor: PromptContributor) {
557 push_registered_hook(
558 &mut self.contributions.prompt_contributors,
559 &self.registering_plugin_id,
560 contributor,
561 );
562 }
563
564 fn add_tool_catalog_contributor(&mut self, contributor: ToolCatalogContributor) {
565 push_registered_hook(
566 &mut self.contributions.tool_catalog_contributors,
567 &self.registering_plugin_id,
568 contributor,
569 );
570 }
571
572 fn add_before_turn_hook(&mut self, hook: BeforeTurnHook) {
573 push_registered_hook(
574 &mut self.contributions.before_turn_hooks,
575 &self.registering_plugin_id,
576 hook,
577 );
578 }
579
580 fn add_before_tool_call_hook(&mut self, hook: BeforeToolCallHook) {
581 push_registered_hook(
582 &mut self.contributions.before_tool_call_hooks,
583 &self.registering_plugin_id,
584 hook,
585 );
586 }
587
588 fn add_after_tool_call_hook(&mut self, hook: AfterToolCallHook) {
589 push_registered_hook(
590 &mut self.contributions.after_tool_call_hooks,
591 &self.registering_plugin_id,
592 hook,
593 );
594 }
595
596 fn add_after_turn_hook(&mut self, hook: AfterTurnHook) {
597 push_registered_hook(
598 &mut self.contributions.after_turn_hooks,
599 &self.registering_plugin_id,
600 hook,
601 );
602 }
603
604 fn add_checkpoint_hook(&mut self, hook: CheckpointHook) {
605 push_registered_hook(
606 &mut self.contributions.checkpoint_hooks,
607 &self.registering_plugin_id,
608 hook,
609 );
610 }
611
612 fn add_assistant_stream_hook(&mut self, hook: AssistantStreamHook) {
613 push_registered_hook(
614 &mut self.contributions.assistant_stream_hooks,
615 &self.registering_plugin_id,
616 hook,
617 );
618 }
619
620 fn add_assistant_response_hook(&mut self, hook: AssistantResponseHook) {
621 push_registered_hook(
622 &mut self.contributions.assistant_response_hooks,
623 &self.registering_plugin_id,
624 hook,
625 );
626 }
627
628 fn add_assistant_prose_projector(
629 &mut self,
630 provider: Arc<dyn AssistantProseProjectorPlugin>,
631 ) -> Result<(), PluginError> {
632 register_singleton_hook(
633 &mut self.contributions.assistant_prose_projector,
634 &self.registering_plugin_id,
635 "assistant prose projector",
636 "assistant_prose_projector",
637 provider,
638 )
639 }
640
641 fn add_tool_result_projector(&mut self, hook: ToolResultProjector) -> Result<(), PluginError> {
642 register_singleton_hook(
643 &mut self.contributions.tool_result_projector,
644 &self.registering_plugin_id,
645 "tool result projector",
646 "model_observation",
647 hook,
648 )
649 }
650
651 fn operation_owner(&self) -> Result<String, PluginError> {
652 self.registering_plugin_id
653 .clone()
654 .ok_or_else(|| PluginError::Registration("missing registering plugin id".to_string()))
655 }
656
657 fn ensure_unique_operation_name(&self, name: &str) -> Result<(), PluginError> {
658 if self.contributions.plugin_queries.contains_key(name)
659 || self.contributions.plugin_commands.contains_key(name)
660 || self.contributions.plugin_tasks.contains_key(name)
661 {
662 return Err(PluginError::Registration(format!(
663 "duplicate plugin operation name `{name}`"
664 )));
665 }
666 Ok(())
667 }
668
669 fn add_plugin_query(
670 &mut self,
671 def: PluginOperationDef,
672 handler: PluginQueryHandler,
673 ) -> Result<(), PluginError> {
674 self.ensure_unique_operation_name(&def.name)?;
675 let plugin_id = self.operation_owner()?;
676 self.contributions.plugin_queries.insert(
677 def.name.clone(),
678 RegisteredPluginQuery {
679 plugin_id,
680 def,
681 handler,
682 },
683 );
684 Ok(())
685 }
686
687 fn add_plugin_command(
688 &mut self,
689 def: PluginOperationDef,
690 handler: PluginCommandHandler,
691 ) -> Result<(), PluginError> {
692 self.ensure_unique_operation_name(&def.name)?;
693 let plugin_id = self.operation_owner()?;
694 self.contributions.plugin_commands.insert(
695 def.name.clone(),
696 RegisteredPluginCommand {
697 plugin_id,
698 def,
699 handler,
700 },
701 );
702 Ok(())
703 }
704
705 fn add_plugin_task(
706 &mut self,
707 def: PluginOperationDef,
708 handler: PluginTaskHandler,
709 ) -> Result<(), PluginError> {
710 self.ensure_unique_operation_name(&def.name)?;
711 let plugin_id = self.operation_owner()?;
712 self.contributions.plugin_tasks.insert(
713 def.name.clone(),
714 RegisteredPluginTask {
715 plugin_id,
716 def,
717 handler,
718 },
719 );
720 Ok(())
721 }
722
723 fn add_protocol_session(
724 &mut self,
725 provider: Arc<dyn ProtocolSessionPlugin>,
726 ) -> Result<(), PluginError> {
727 register_singleton_hook(
728 &mut self.contributions.protocol_session,
729 &self.registering_plugin_id,
730 "protocol session capability",
731 "protocol_session",
732 provider,
733 )
734 }
735
736 fn add_code_executor(
737 &mut self,
738 provider: Arc<dyn CodeExecutorPlugin>,
739 ) -> Result<(), PluginError> {
740 register_singleton_hook(
741 &mut self.contributions.code_executor,
742 &self.registering_plugin_id,
743 "code executor capability",
744 "code_executor",
745 provider,
746 )
747 }
748
749 fn add_protocol_driver(
750 &mut self,
751 provider: Arc<dyn ProtocolDriverPlugin>,
752 ) -> Result<(), PluginError> {
753 register_singleton_hook(
754 &mut self.contributions.protocol_driver,
755 &self.registering_plugin_id,
756 "protocol driver capability",
757 "protocol_driver",
758 provider,
759 )
760 }
761}