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.
// 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.
// 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 descriptiondescription 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):
| Contract | Group | Backs |
|---|---|---|
IFilesProvider | files | Reading/writing/editing files (local FS, SFTP) |
ISecretsProvider | secrets | Secret storage (encrypted vault by default) |
IMemoryStore | memory-store | Vector-indexed memory storage |
IMemoryRetrieval | memory-retrieval | Recall strategy (top-K, rerank…) |
IWikiProvider | wiki | Shared agent wiki (git-backed markdown) |
ISkillSearch | skill-search | Skill search ranking (BM25 default) |
IWebSearchProvider | websearch | Web search (DuckDuckGo default; Brave/Tavily plugins) |
IWebFetchProvider | webfetch | Fetch a URL → readable text |
ICompactionProvider | compaction | Summarize 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.
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 [];
}
}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".