Plugin Manifest
Last updated:
Every plugin installation needs an oscova-plugin.json file. Oscova reads this manifest to identify the plugin, find its assembly, and locate the IAgentPlugin entry type.
Important
Passing manifest validation does not make a plugin trustworthy. Plugins run as trusted code inside the Oscova process, without a plugin sandbox.

Create the manifest
Place the manifest in the plugin directory. The loader searches the configured plugin root recursively for the exact file name oscova-plugin.json.
{
"id": "example.hello",
"name": "Example Hello Tools",
"version": "1.0.0",
"assembly": "Example.Hello.dll",
"entryType": "Example.Hello.MyAgentPlugin"
}
| Field | Required | Contract |
|---|---|---|
id |
Yes | Stable plugin identity. Duplicate IDs are compared case-insensitively. |
name |
Yes | Nonblank display name. |
version |
Yes | Nonblank plugin-owned version string. |
assembly |
Yes | Relative path from the manifest directory to the plugin assembly. |
entryType |
Yes | Fully qualified type name of the class that implements IAgentPlugin. |
Do not add leading or trailing whitespace to id, assembly, or entryType. The assembly path must be relative, remain inside the manifest directory, and resolve to an existing file. A manually copied plugin may place the assembly in a subdirectory as long as those rules hold.
Understand inspection and loading
AgentPluginLoader.InspectDirectory(pluginRoot) reads manifests and validates their required values and referenced assembly paths without loading plugin code. It returns an AgentPluginInspection for each discovered manifest, including an AgentPluginLoadDiagnostic when inspection fails.
Loading is a separate step:
LoadDirectory(pluginRoot)attempts to load every valid discovered plugin.LoadDirectory(pluginRoot, enabledPluginIds)attempts to load only valid plugins selected by the supplied set.- The loader finds
entryTypein the declared assembly, confirms that it implementsIAgentPlugin, creates it with a public parameterless constructor, and callsConfigure. - A missing type, incompatible type, constructor failure, or exception from
Configurerecords an error diagnostic and skips that plugin.
Discovery order is the case-insensitive manifest-path order. For duplicate IDs, the first valid manifest remains eligible to load and each later duplicate is skipped. A failure in one plugin does not stop other valid plugins.
An empty or missing plugin root returns an empty result. A root that cannot be searched returns a directory diagnostic instead of throwing from InspectDirectory.
Package for online installation
The online installer accepts the latest stable NuGet package version when all of these structural rules pass:
- The NuGet package type is
OscovaPlugin. - The package is no larger than 25 MB.
oscova-plugin.jsonis at the package root.- The declared assembly is a file name, not a path.
- The declared assembly is selected as a
net10.0runtime asset. - Ordinary managed, native, and resource dependencies are available for the current supported RID.
- The manifest ID is a folder-safe identifier and does not conflict with an installed plugin.
A minimal project file follows the same shape as the shipped plugins:
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<EnableDynamicLoading>true</EnableDynamicLoading>
<PackageType>OscovaPlugin</PackageType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
<None Include="oscova-plugin.json"
Pack="true"
PackagePath="\" />
</ItemGroup>
The installer restores the NuGet dependency graph for net10.0 and the current supported RID, then installs the manifest, entry assembly, managed assemblies, native libraries, resource assemblies, and dependency files under content/** selected by NuGet. Dependency content remains scoped under that dependency's installed package directory. Root-package content, contentFiles/**, analyzers, build assets, tools, and scripts are ignored, and package build targets are never executed. Dependencies on another OscovaPlugin package are not supported. Manual folder installation is less restrictive about layout, but the assembly and its dependencies must still be loadable by the running .NET 10 host.
Place root-package native assets for win-x64, linux-x64, osx-x64, and osx-arm64 under conventional runtimes/{rid}/native/... paths. The installer retains the selected RID's relative tree, so deliberate variants can use the same native filename in different subdirectories such as runtimes/{rid}/native/{variant}/{native-file}. Native assets from ordinary dependency packages remain package-scoped. The package-size and structural limits above still apply.
Know the trust boundary
Manifest and package checks verify structure, paths, required fields, and load compatibility. They do not verify:
- a code signature.
- the publisher's identity or reputation.
- the safety or intent of the assembly.
Administrators should review the package source, publisher, project, and license before installation, then explicitly enable the plugin for the next host restart.
Troubleshooting
- Plugin is not discovered: confirm the file is named
oscova-plugin.jsonand sits below the configured plugin root. - Assembly was not found: make
assemblyrelative to the manifest and copy the DLL to that resolved path. - Entry type was not found: use the type's namespace-qualified name and confirm it is in the declared assembly.
- Public parameterless constructor error: add a public constructor with no parameters to the
IAgentPluginentry class. - Plugin failed to load: inspect the diagnostic for an assembly-load, constructor, or
Configureexception. - NuGet package is unsupported: verify
OscovaPlugin, the root manifest, and a declared entry assembly selected as anet10.0runtime asset. - Dependency restore fails: publish every ordinary package dependency and supported-RID runtime asset to NuGet.org. Do not depend on another Oscova plugin.