Skip to content

Plugin System

scarecr0w12 edited this page Jun 25, 2026 · 7 revisions

Plugin System

CortexPrism supports plugins that extend its capabilities with new tools, UI panels, CLI commands, LLM providers, and middleware.

Plugin manager

Plugin Types

Kind Description Best For
ESM JavaScript/TypeScript modules loaded via import() Most plugins — tools, UI, providers, middleware
MCP Model Context Protocol servers (JSON-RPC) Wrapping existing MCP servers as tools
WASM WebAssembly modules (any language → WASM) Performance-critical or multi-language plugins

Quick Start

# Install from marketplace
cortex marketplace install plugins/slack-bot

# Install from URL
cortex plugins install https://example.com/my-plugin/manifest.json

# Install from local manifest
cortex plugins install ./my-plugin/manifest.json

# List installed plugins
cortex plugins list

# Manage plugins
cortex plugins enable my-plugin
cortex plugins disable my-plugin
cortex plugins update my-plugin
cortex plugins update --all
cortex plugins verify my-plugin
cortex plugins permissions my-plugin
cortex plugins remove my-plugin

Plugin Lifecycle

DISCOVERED → INSTALLED → LOADING → ACTIVE
                                      ↓
                                  UNLOADING → REMOVED

Lifecycle hooks:

  • onInstall — plugin first installed
  • onLoad — module imported into memory
  • onActivate — tools registered, providers bound
  • onDeactivate — plugin begins disabling
  • onUnload — module unloaded from memory
  • onUninstall — database row deleted, files cleaned

Manifest Structure

Every plugin requires a manifest.json:

{
  "name": "my-plugin",
  "version": "1.0.0",
  "description": "An example plugin",
  "kind": "esm",
  "entryPoint": "./mod.ts",
  "runtime": "deno",
  "capabilities": ["tools", "network:fetch"],
  "tools": [
    {
      "name": "get_weather",
      "description": "Get current weather for a city",
      "params": [
        { "name": "city", "type": "string", "description": "City name", "required": true }
      ]
    }
  ]
}

Extension Points

Capability Description
tools Register tools in the global agent tool registry
cli:commands Add cortex <name> subcommands
ui:panel Add a Web UI panel/tab
ui:widget Add a dashboard widget
config:schema Extend config schema with new settings
config:provider Register LLM provider factories
memory:store Custom memory backend
memory:embedder Custom embedding provider
events:listener Subscribe to the event bus
middleware:pre Pre-execution middleware
middleware:post Post-execution middleware

Trust Levels

Level Sandbox How assigned
untrusted Deno Worker sandbox (minimal perms) No verification, or blocked/suspicious supply-chain result
signed Deno Worker sandbox Supply-chain ran but no hash match (unverified)
trusted In-process (full declared perms) Supply-chain verification passes

PluginContext API

Every lifecycle hook and tool execute receives a PluginContext:

interface PluginContext {
  pluginId: string;           // Plugin name
  pluginDir: string;          // ~/.cortex/data/plugins/<name>/
  state: PluginStateStore;    // Persistent SQLite-backed key-value store
  config: PluginConfigStore;  // Typed access to ~/.cortex/config.json plugins.<name>
  logger: PluginLogger;       // Scoped logger — prefixes [plugin:<name>]
  host: HostApi;              // Register/unregister tools at runtime
}

interface PluginStateStore {
  get(key: string): Promise<string | null>;
  set(key: string, value: string): Promise<void>;
  delete(key: string): Promise<void>;
  list(): Promise<Record<string, string>>;
}

interface PluginConfigStore {
  get<T = unknown>(key: string): Promise<T | null>;
  set<T = unknown>(key: string, value: T): Promise<void>;
  getAll(): Promise<Record<string, unknown>>;
}

interface PluginLogger {
  info(msg: string): void;
  warn(msg: string): void;
  error(msg: string): void;
  debug(msg: string): void;
}

interface HostApi {
  registerTool(tool: Tool): void;
  unregisterTool(name: string): void;
}

Pipeline Hooks

Plugins can intercept the agent's 12-stage execution pipeline by exporting middlewarePre and/or middlewarePost from their module and declaring middleware:pre / middleware:post in capabilities.

pre-assess → post-assess → pre-reason → post-reason
→ pre-tool (middlewarePre) → post-tool (middlewarePost)
→ pre-llm → post-llm → pre-reflect → post-reflect
→ pre-output → post-output

Hooks receive a PipelineContext and return a HookResult:

interface HookResult {
  abort?: { reason: string; message: string }; // aborts the current turn
  modifyInput?: string;        // replaces user input
  modifyLLMResponse?: string;  // replaces raw LLM response
  modifyOutput?: string;       // replaces final output
  injectMessages?: Message[];  // appends to conversation history
  sideEffects?: SideEffect[];  // log | metric | store | notify
}
  • Sync hooks time out after 5 s
  • Async hooks time out after 15 s
  • Plugin middleware hooks run at priority: 50

See Developing Plugins — Pipeline Hooks for full examples.

Event Subscriptions

Declare events:listener capability and list event types in the manifest events field. Subscribe via globalEventBus.on(type, handler) in onLoad.

Event Payload
session:start { sessionId: string }
session:end { sessionId: string }
tool:pre-execute { toolName: string, args: Record<string, unknown> }
tool:post-execute { toolName: string, result: unknown }
llm:pre-call { provider: string, model: string }
llm:post-call { provider: string, model: string, tokensIn: number, tokensOut: number }
agent:turn-start { sessionId: string, turnId: string }
agent:turn-end { sessionId: string, turnId: string, response: string }
config:change { key: string, value: unknown }
daemon:status { daemon: string, status: 'up' | 'down' }

UI Slots

Plugin panels register into named UI slots:

UISlot Description
sidebar Left sidebar item
panel Full-page panel/tab
modal Modal overlay
timeline-item Timeline feed entry
widget Dashboard widget

Panel iframes receive the window.Cortex JavaScript API injected by the host, providing authenticated fetch, config get/set, event subscription, and host notifications.

window.Cortex API (panel iframes)

window.Cortex.fetch(path, init?)        // Authenticated fetch (relative to /api/plugins/<name>/ or absolute)
window.Cortex.getConfig(key)            // Promise<unknown>
window.Cortex.setConfig(key, value)     // Promise<void>
window.Cortex.onEvent(event, handler)   // Subscribe to host events
window.Cortex.emit(event, data)         // Emit event to host
window.Cortex.notify(msg, type)         // type: 'info' | 'warn' | 'error'

Plugin Commands

Panels can issue structured commands to the host via postMessage:

Command type Effect
navigate Navigate host UI to a route (to: string)
open-modal Open host modal (title: string, content: string)
notification Show notification (text: string, level: string)
config-get Read a config key
config-set Write a config key
query Execute a query

Sandbox Mechanics

untrusted and signed plugins run in a Deno Worker sandbox. The sandbox uses JSON-RPC over postMessage:

  1. Host spawns new Worker(entryPoint, { type: 'module', deno: { permissions: ... } })
  2. Worker must post { type: 'ready' } within 30 seconds
  3. Host sends RPC: { id, method: 'getTools', params: {} } → worker returns tool definitions
  4. On tool call: { id, method: 'executeTool', params: { toolName, args } } → worker returns ToolCallResult

Capability → Deno Worker permission mapping:

Capability Deno permission
fs:read, fs:list read: true
fs:write, fs:edit, fs:delete write: true
shell:run run: true
network:fetch, net:outbound, net:inbound net: true

WASM Plugins (v0.52.0+)

WASM plugins received major upgrades in v0.52.0:

  • ABI versioningplugin_get_abi_version() / host_get_abi_version() for forward/backward compatibility
  • Linear memory allocator — bump allocator with host_alloc/host_free host functions, bounds-checked with auto-grow
  • Synchronous HTTPhost_http_request blocks via dedicated Worker + SharedArrayBuffer + Atomics.wait (30s timeout)
  • Permission enforcementhost_http_request gates on network:fetch or net:outbound capability
  • Execution timeouts — 120-second maximum per tool execution
  • Tool parameter schemasparams[] with name, type, description, and required fields
  • PluginContext integration — WASM plugins access logging, state persistence (SQLite-backed), and configuration
  • Supply-chain binary scanningscanWasmBinary() detects suspicious imports, excessive memory, and version mismatches
  • C SDK — C-compatible header (wasm-plugin.h) and TypeScript client library (client.ts)

Permission Overrides

Admins can grant or deny capabilities per-plugin beyond manifest declarations. Overrides are stored in plugins.db and merged at load time:

effective = declared − denied + granted

REST: POST /api/plugins/:name/permissions with { permission_path, action: 'grant'|'deny', value }

Plugin Modules (src/plugins/)

File Purpose
types.ts Core plugin types, capabilities, lifecycle interfaces
manager.ts Plugin lifecycle manager (enable/disable/reload)
registry.ts Plugin registry and database CRUD
loader.ts ESM / MCP / WASM module loader
install.ts Installation from marketplace/URL/local manifest
update.ts Update checking, version enrichment from GitHub
permissions.ts Permission resolution and Deno Worker perm derivation
integrity.ts SHA-256 hash verification
supply-chain.ts Full supply-chain: hash, signature, reputation, malware scan
sandbox.ts Deno Worker sandbox with JSON-RPC bridge
events.ts EventBus class + globalEventBus singleton
deps.ts Semver dependency resolution, topological sort, circular detection
context.ts createPluginContext() — builds the PluginContext for each plugin
namespace.ts @author/name scoping, tool name resolution, alias management
ui-slots.ts UI slot registration, window.Cortex API generation
wasm-runtime.ts WebAssembly host functions, ABI versioning, linear allocator, sync HTTP via Worker, state persistence, permission enforcement, diagnostics
wasm-worker-http.ts Dedicated Worker thread for synchronous WASM HTTP via SharedArrayBuffer + Atomics.wait
sdk/wasm-plugin.h C-compatible header declaring all host functions, memory layout, required exports
sdk/client.ts TypeScript helper library — defineTool(), definePlugin(), generateCapabilitiesJson(), generateWasmPluginModule()
extensions/cli.ts Cliffy command builder for plugin CLI subcommands
extensions/provider.ts LLM provider factory registry
extensions/config.ts Config schema extension
extensions/ui.ts CortexUiApi, panel JS/HTML generation
dependency-guardian.ts Continuous CVE monitoring, remediation suggestions

Install Pipeline

verifyPluginIntegrity() runs before every plugin install:

  • SHA-256 hash check against known-good hashes per package@version
  • Blocked hash list — known-malicious hashes are rejected outright
  • Digital signature verification — optional GPG signature validation
  • Author reputation scoring — 0–100 score; configurable blocked/allowed author lists
  • Malware pattern scanning — 6 default patterns: eval, child_process, rm -rf /, curl|sh, wget|sh

blockSuspicious mode rejects suspicious plugins. requireKnownHash defaults to false for community marketplace compatibility.

Plugin Dependency Resolution

src/plugins/deps.ts provides:

  • Semver constraint satisfaction (^, ~, >=, * operators)
  • Topological sort for install ordering
  • Circular dependency detection
  • Transitive dependency traversal

installPlugin() validates dependencies before install and returns missing dependency and warning lists.

Supply Chain Verification API

POST /api/plugins/:name/verification — re-runs supply-chain check, persists the report GET /api/plugins/:name/verification — retrieves stored report with color-coded trust badges

See Also

Clone this wiki locally