Table of Contents

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.

Plugin details with package metadata and available actions.

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 entryType in the declared assembly, confirms that it implements IAgentPlugin, creates it with a public parameterless constructor, and calls Configure.
  • A missing type, incompatible type, constructor failure, or exception from Configure records 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.json is at the package root.
  • The declared assembly is a file name, not a path.
  • The declared assembly is selected as a net10.0 runtime 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.json and sits below the configured plugin root.
  • Assembly was not found: make assembly relative 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 IAgentPlugin entry class.
  • Plugin failed to load: inspect the diagnostic for an assembly-load, constructor, or Configure exception.
  • NuGet package is unsupported: verify OscovaPlugin, the root manifest, and a declared entry assembly selected as a net10.0 runtime asset.
  • Dependency restore fails: publish every ordinary package dependency and supported-RID runtime asset to NuGet.org. Do not depend on another Oscova plugin.