How to Integrate Custom Modules into OpenWork: A Complete Plugin Development Guide
OpenWork's plugin architecture lets you extend the platform by creating self-contained packages that declare capabilities, registering them with the Den server via the REST API, and invoking them via the skill runtime without modifying core code.
The different-ai/openwork repository implements a flexible plugin system that enables developers to integrate custom modules seamlessly. Whether you need to add data connectors, AI skills, or UI widgets, understanding how to integrate custom modules into OpenWork allows you to extend functionality while keeping the core platform untouched.
Understanding OpenWork's Plugin Architecture
OpenWork treats custom modules as plugins—self-contained packages that declare one or more capabilities (skills). The integration flow follows three distinct phases: creating the plugin package with its manifest and handlers, registering the metadata with the Den server, and consuming the capability through the runtime's execute_capability endpoint.
Step 1: Create the Plugin Package
A valid plugin requires both a skill manifest and a capability handler. The manifest describes inputs, outputs, and metadata, while the handler contains the execution logic.
Define the Skill Manifest
Create a skill.yml file that declares the capability interface. The manifest must specify the capability ID, input schema, and output schema according to the plugin schema defined in packages/types/src/dynamic-artifacts.ts.
# skill.yml
name: My Plugin – Hello World
description: |
Demonstrates how to expose a custom capability.
capability: my-plugin:hello-world
inputs:
- name: name
type: string
description: Name of the person to greet
outputs:
- type: string
description: Greeting message
Implement the Capability Handler
Write the execution logic in TypeScript using the @openwork/skill-runtime SDK. The handler runs inside the OpenWork agent sandbox and processes input parameters defined in the manifest.
// my-plugin/src/index.ts
import { registerCapability } from '@openwork/skill-runtime';
registerCapability({
id: 'my-plugin:hello-world',
description: 'Returns a friendly greeting.',
handler: async (input: { name: string }) => {
return `👋 Hello, ${input.name}! – from MyPlugin`;
},
});
Add Optional UI Components
If your plugin provides user interface elements, export a React component and reference it in the skill manifest under uiComponent. OpenWork's UI loader dynamically imports the component when the skill is invoked.
// my-plugin/src/ui/GreetingWidget.tsx
export const GreetingWidget = ({ name }: { name: string }) => (
<div className="p-2 bg-gray-100 rounded">{`👋 Hello, ${name}!`}</div>
);
Update skill.yml to include the component path:
uiComponent: "./ui/GreetingWidget"
Step 2: Register the Plugin with the Den
The Den server stores plugin metadata and makes capabilities available to all OpenWork clients. Registration occurs via the Den API endpoint POST /v1/plugins, which accepts a payload matching the schema in packages/types/src/dynamic-artifacts.ts containing fields such as pluginId, artifactId, and artifactVersionId.
Use the OpenWork CLI to register your plugin:
openwork den plugins create \
--name "My Plugin" \
--manifest ./skill.yml \
--artifact ./my-plugin.tar.gz
Internally, the server persists this metadata and prepares the plugin for runtime loading.
Step 3: Consume the Plugin in Agents
Once registered, OpenWork automatically loads the plugin at startup through the runtime-opencode-config-store.ts module, which merges built-in and custom plugins into the runtime's capability registry. Agents invoke the new capability using the standardized format skill:<pluginId>:<capability>.
import { executeCapability } from '@openwork/agent';
const result = await executeCapability(
'skill:my-plugin:hello-world',
{ name: 'Alice' }
);
console.log(result); // → "👋 Hello, Alice! – from MyPlugin"
The apps/server/src/skills.ts file handles the underlying validation and routes the request to the execute_capability endpoint, ensuring the handler receives the correct input parameters.
Debugging Plugin Integration
When troubleshooting registration or execution failures, consult the diagnostic events emitted from packages/types/src/agent-context-diagnostics.ts. This module tracks plugin loading states, validation errors, and runtime exceptions, providing visibility into the integration pipeline without requiring core code modifications.
Key Source Files and Implementation Details
Understanding the following files helps when debugging complex integrations:
packages/types/src/dynamic-artifacts.ts: Defines the JSON schema for plugin artifacts includingpluginIdand version identifiers.apps/server/src/runtime-opencode-config-store.ts: Merges custom plugins into the runtime configuration at startup.apps/server/src/skills.ts: Validates skill manifests and exposes capabilities through the Den API.packages/types/src/agent-context-diagnostics.ts: Emits diagnostic events for plugin lifecycle monitoring.
Summary
- Create a self-contained plugin package with a
skill.ymlmanifest and TypeScript handler using@openwork/skill-runtime. - Register the plugin via the Den API (
POST /v1/plugins) or CLI, supplying metadata that matches the schema indynamic-artifacts.ts. - Invoke capabilities using the
skill:<pluginId>:<capability>namespace throughexecuteCapability. - Debug integration issues using diagnostic events from
agent-context-diagnostics.ts. - Extend functionality with optional React UI components declared in the skill manifest.
Frequently Asked Questions
What file format should I use for the skill manifest?
OpenWork supports YAML (skill.yml) or Markdown (skill.md) formats for the skill manifest. The file must declare the capability ID, input parameters, output types, and optional UI component paths so the server in apps/server/src/skills.ts can validate and register the capability correctly.
How does OpenWork validate custom plugins before loading them?
The Den server validates plugin metadata against the schema defined in packages/types/src/dynamic-artifacts.ts, checking required fields like pluginId and artifactId. The runtime-opencode-config-store.ts module then merges valid plugins into the runtime configuration, while skills.ts verifies the skill manifest structure before exposing the capability via the execute_capability endpoint.
Can I integrate plugins written in languages other than TypeScript?
While the examples use TypeScript, OpenWork's plugin architecture supports any language that can compile to JavaScript or run in the agent sandbox. The critical requirement is that the final artifact must export capabilities using the registerCapability interface or equivalent bindings, and the manifest must conform to the YAML schema expected by the Den server.
Where can I find diagnostic logs if my plugin fails to load?
Diagnostic events for plugin registration, loading, and execution are emitted from packages/types/src/agent-context-diagnostics.ts. These events provide detailed error messages about validation failures, missing artifacts, or runtime exceptions, allowing you to trace failures through the Den server logs or agent context outputs without modifying core OpenWork code.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →