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