Developer guide / Providers

Providers

A provider is a swappable backend behind a contract interface — files, secrets, memory, search, fetch, compaction. Replace one outright, or add an option the operator can select at runtime.

Two ways to register

Last-wins override: AddProvider

Replace a contract entirely. The last registration wins — ideal when your plugin is the implementation.

csharp
// in Configure
ctx.Services.AddProvider<IWebFetchProvider, HeadlessBrowserFetch>();
// or register a pre-built instance
ctx.Services.AddProvider<IWebFetchProvider>(new HeadlessBrowserFetch(opts));

Operator-selectable: AddSelectableProvider

Add an implementation to a group without displacing the others. Every implementation stays registered; the contract resolves through a transparent proxy to whichever the operator selected (the last registered is the default). Switching takes effect live — consumers keep injecting the contract.

csharp
// in Configure — signature:
// AddSelectableProvider<TContract, TImpl>(group, groupTitle, id, displayName, description = "")
ctx.Services.AddSelectableProvider<IWebSearchProvider, MySearchProvider>(
    "websearch",         // group key (config.providerSelections[group])
    "Web search",        // console group title
    "mysearch",          // stable id stored in config
    "My Search",         // display name in the picker
    "Custom search via My API (requires an API key).");  // one-line description
The description is shown beneath the option in Settings → Providers, so operators understand what each backend does. Always provide one.

Provider contracts

The built-in groups a plugin can implement or override (all in AgentParley.Abstractions):

ContractGroupBacks
IFilesProviderfilesReading/writing/editing files (local FS, SFTP)
ISecretsProvidersecretsSecret storage (encrypted vault by default)
IMemoryStorememory-storeVector-indexed memory storage
IMemoryRetrievalmemory-retrievalRecall strategy (top-K, rerank…)
IWikiProviderwikiShared agent wiki (git-backed markdown)
ISkillSearchskill-searchSkill search ranking (BM25 default)
IWebSearchProviderwebsearchWeb search (DuckDuckGo default; Brave/Tavily plugins)
IWebFetchProviderwebfetchFetch a URL → readable text
ICompactionProvidercompactionSummarize history on compaction

Declaring settings & secrets

A provider declares the operator-set config it needs as ConfigFields. The console renders a form; a Secret field is written straight to the vault. At runtime, read values back through the injected ISettings — never hardcode a secret name.

MySearchProvider.cscsharp
using AgentParley.Abstractions.Config;
using AgentParley.Abstractions.Providers;

public sealed class MySearchProvider(ISettings settings) : IWebSearchProvider
{
    public string Id => "mysearch";
    public string DisplayName => "My Search";
    public bool RequiresKey => true;

    // rendered by the console under Settings → Web search
    public IReadOnlyList<ConfigField> ConfigFields =>
    [
        new("apiKey", "API key", "Get one at my.search.com",
            ConfigFieldType.Secret, Required: true),
    ];

    public async Task<IReadOnlyList<WebSearchResult>> Search(string query, int count, CancellationToken ct = default)
    {
        // trusted path: scope/key are operator config, never agent input
        var apiKey = await settings.ResolveSecret($"websearch.{Id}", "apiKey", ct);
        if (string.IsNullOrEmpty(apiKey))
            throw new InvalidOperationException("mysearch API key not set — add it under Settings → Web search");

        // ... call the API, map results, return them
        return [];
    }
}
Error contract. Configuration problems should throw InvalidOperationExceptionwith a message that tells the operator how to fix it; transport/backend failures (including rate limits) should throw HttpRequestException. Never return an empty list for a failure — the model must be able to tell "no results" from "broken".