Table of Contents

Plugin Builder

Last updated:

Use AgentPluginBuilder inside IAgentPlugin.Configure to declare the toolset and provider factories an enabled plugin contributes to Oscova.

Note

The builder records registrations. It does not create toolsets, start providers, resolve credentials, or call remote services while Configure runs.

Model provider editor with the available provider types open.

Supported registrations

AgentPluginBuilder exposes four registration methods:

Method Type requirement What the builder stores
AddToolset<TToolset>(Func<AgentPluginContext, TToolset>) TToolset is a class implementing ICrewToolset The toolset type and factory
AddModelProvider<TProvider>(Func<AgentPluginContext, TProvider>) TProvider is a class implementing IModelProvider The provider type and factory
AddSpeechRecognitionProvider<TProvider>(Func<AgentPluginContext, TProvider>) TProvider is a class implementing ISpeechRecognitionProvider The provider type and factory
AddSpeechSynthesisProvider<TProvider>(Func<AgentPluginContext, TProvider>) TProvider is a class implementing ISpeechSynthesisProvider The provider type and factory

Each method returns the same builder, so registrations can be chained:

using System.ComponentModel;
using Oscova.Agents;
using Oscova.Agents.Plugins;

public sealed class ExampleAgentPlugin : IAgentPlugin
{
    public void Configure(AgentPluginBuilder builder)
    {
        builder.AddToolset(_ => new ExampleToolset());
    }
}

public sealed class ExampleToolset : ICrewToolset
{
    [DisplayName("example_echo")]
    [Description("Returns the supplied text unchanged.")]
    public static string Echo(string text)
    {
        return text;
    }
}

Chain AddModelProvider, AddSpeechRecognitionProvider, or AddSpeechSynthesisProvider after AddToolset when the plugin implements those provider interfaces. These are the complete public builder registrations. There is no supported builder registration for middleware, background services, routes, settings pages, or arbitrary host services.

Toolset lifecycle and naming

AddToolset requires a non-null factory, and that factory must return a non-null toolset. Oscova calls the factory once while assembling each user scope and passes an AgentPluginContext for that user. A toolset can serve multiple runs in that user scope, so keep run-specific state inside each method call.

Return a new toolset when it depends on credentials, HTTP clients, logging, or the user's time zone. Do not cache the context, resolver, or toolset in static state. Treat clients returned by CreateHttpClient as host-managed and do not dispose them from the toolset.

Plugin tool methods use the same reflection contract as built-in toolsets:

  • Public methods declared directly on the toolset type become tools.
  • [DisplayName] supplies the base tool name. Without it, the method name is used.
  • [Description], [ToolPolicy], and [ToolNetworkScope] provide optional metadata.
  • Default approval is ToolApprovalMode.Default.
  • Default network scope is NetworkScope.None.

The qualified wire name begins with the plugin manifest ID, followed by the toolset owner and tool name. The owner is the class name with a trailing Toolset removed. Every segment is normalized. For example, plugin acme.search, class ExampleToolset, and display name search_records produce acme_search__example__search_records.

Provider lifecycle

The three provider methods register factories. Oscova invokes them during startup and shares the resulting providers across users. All provider factories from one plugin receive the same AgentPluginContext. Factories from different plugins receive different contexts. Use CreateHttpClient and LoggerFactory for supported host capabilities. The provider context does not expose user credential values.

Keep contributed providers stateless and thread-safe. Runtime calls receive provider configuration, the selected plaintext API key, and cancellation through the corresponding public provider interface.

A model provider can implement IModelDownloadProvider when its SDK owns model acquisition. Oscova then offers the provider's catalog models for download in the provider editor and calls DownloadModelAsync with progress and cancellation. Return true from IModelProvider.DefaultIsLocalEndpoint only when the provider is local by design. A saved endpoint-scope choice still takes precedence.

A successfully activated provider can implement IDisposable when it owns synchronous resources. Oscova disposes those providers during host shutdown. Plugins still cannot register background services or arbitrary shutdown callbacks.

Provider Type values are stable product identifiers. Use a unique value for each model, recognition, or synthesis provider. Built-in speech providers take precedence over plugin providers with the same Type. Among speech plugins, the first provider registered for a Type is retained and each later collision produces an operator warning naming the dropped implementation.

Speech routing

SpeechRecognitionRouter and SpeechSynthesisRouter resolve the exact registered provider whose Type matches the supplied SpeechProviderConfig. A host selects the configuration and supplies the resolved API key, offline requirement, download URL, and session input needed by each operation. The routers reject non-local endpoints when offline operation is required, before calling the provider.

SpeechProviderKeys is a non-exhaustive set of well-known identifiers, not an allowlist. Choose a stable, unique type for your plugin provider and register it through the builder. It routes without adding a framework constant.

Ordering and duplicates

A new builder starts with four empty collections. It appends registrations in call order and exposes them through read-only lists:

  • Toolsets
  • ModelProviders
  • SpeechRecognitionProviders
  • SpeechSynthesisProviders

The builder does not deduplicate registrations. Register each contribution once. Repeated toolsets can produce duplicate qualified tool names, which Oscova rejects instead of replacing the earlier tool. Model-provider duplicate behavior remains host-owned. For speech providers, Oscova retains built-ins before plugins and retains the first plugin contribution when plugins collide, logging each dropped implementation.

Failure behavior

  • A null contribution factory throws ArgumentNullException during Configure.
  • A factory that returns null cannot produce a valid toolset and fails when Oscova publishes the tools.
  • If Configure throws, the plugin is skipped and a load diagnostic is recorded.
  • If a toolset factory throws for one user scope, that contribution is skipped for that user and the failure is logged.
  • If a provider factory throws or returns null, that provider is skipped and the failure is logged.
  • Thrown factory and provider-construction failures are isolated, so later contributions are still attempted.
  • Invalid reflected metadata or duplicate qualified tool names are publication errors, not user-setup failures.

Keep expected setup failures, such as a missing credential or unavailable remote service, inside the tool or provider operation. Return actionable guidance for user-correctable failures and honor the supplied cancellation token.

The builder has no disposal or background-service registration. Do not start background work or create resources that need a plugin-specific shutdown callback.