Skip to content
OmniRoute source

OmniRoute Plugin SDK

import { definePlugin } from "omniroute/plugins/sdk";
export default definePlugin({
name: "my-plugin",
priority: 50,
onRequest: async (ctx) => {
console.log(`Request ${ctx.requestId} for ${ctx.model}`);
},
onResponse: async (ctx, response) => {
console.log(`Response for ${ctx.requestId}`);
return response;
},
onError: async (ctx, error) => {
console.error(`Error: ${error.message}`);
},
});

definePlugin(def: PluginDefinition): Plugin

Section titled “definePlugin(def: PluginDefinition): Plugin”

Factory function that creates a Plugin object with defaults.

Parameters:

  • name (string, required) — Plugin name in kebab-case
  • priority (number, optional, default: 100) — Lower runs first
  • enabled (boolean, optional, default: true) — Start enabled?
  • onRequest (function, optional) — Runs before chat handler
  • onResponse (function, optional) — Runs after chat handler
  • onError (function, optional) — Runs on handler error

blockRequest(response?): BlockingHookResult

Section titled “blockRequest(response?): BlockingHookResult”

Block the request and optionally return a custom response.

onRequest: (ctx) => {
if (!ctx.headers["authorization"]) {
return blockRequest({ error: "Unauthorized", status: 401 });
}
};

Modify the request body before it reaches the provider.

onRequest: (ctx) => {
return modifyBody({ ...ctx.body, temperature: 0.7 });
};

Attach metadata to the request context.

onRequest: (ctx) => {
return addMetadata({ source: "my-plugin", version: "1.0.0" });
};
Field Type Description
requestId string Unique request identifier
model string Requested model name
provider string Target provider ID
body Record<string, unknown> Request body
headers Record<string, string> Request headers
metadata Record<string, unknown> Mutable metadata
timestamp number Request timestamp
{
"name": "my-plugin",
"version": "1.0.0",
"description": "A sample plugin",
"author": "your-name",
"main": "index.js",
"hooks": {
"onRequest": { "enabled": true, "priority": 50 },
"onResponse": true,
"onError": false
},
"requires": {
"permissions": ["network", "file-read"]
},
"enabledByDefault": false,
"configSchema": {
"apiKey": { "type": "string", "description": "API key for external service" },
"maxRetries": { "type": "number", "min": 1, "max": 10, "default": 3 },
"debug": { "type": "boolean", "default": false },
"mode": { "type": "string", "enum": ["fast", "slow"], "default": "fast" }
}
}

Hooks can be configured with priority (lower = runs first):

{
"hooks": {
"onRequest": { "enabled": true, "priority": 10 },
"onResponse": { "enabled": true, "priority": 100 }
}
}

Or as simple booleans (default priority 100):

{
"hooks": {
"onRequest": true,
"onResponse": true
}
}

Plugins run in a sandboxed VM context. Access to external resources requires explicit permissions:

Permission Grants
network fetch, AbortController, Headers, Request, Response
file-read fs.readFile, fs.readdir, fs.stat
file-write fs.writeFile, fs.mkdir, fs.rm
env Read-only process.env proxy
exec child_process.exec, child_process.execSync

Without a permission, the corresponding globals are simply not available in the sandbox.

Define configurable settings in configSchema:

{
"configSchema": {
"apiKey": { "type": "string", "description": "External API key" },
"maxRetries": { "type": "number", "min": 1, "max": 10, "default": 3 },
"debug": { "type": "boolean", "default": false },
"mode": { "type": "string", "enum": ["fast", "slow"], "default": "fast" }
}
}

Field types: string, number, boolean, select

Field options: default, min, max, enum, description

Config values are persisted in the database and accessible via the dashboard config page.

Event When Payload
onRequest Before chat handler Request context
onResponse After chat handler Response data
onError On handler error Error object
onModelSelect Model selected for routing Model info
onComboResolve Combo routing resolved Combo targets
onRateLimit Rate limit hit Limit info
onQuotaExhaust Quota exhausted Quota info
onProviderError Provider returned error Error details
onStreamStart SSE stream started Stream info
onStreamEnd SSE stream ended Stream stats
onInstall Plugin installed { name, version, manifest }
onActivate Plugin activated { name, version, manifest }
onDeactivate Plugin deactivated { name, version, manifest }
onUninstall Plugin uninstalled (before files deleted) { name, version, manifest }
import { definePlugin } from "omniroute/plugins/sdk";
export default definePlugin({
name: "request-logger",
onRequest: async (ctx) => {
console.log(`[${new Date().toISOString()}] ${ctx.method} ${ctx.model} -> ${ctx.provider}`);
},
});
import { definePlugin, blockRequest } from "omniroute/plugins/sdk";
const requests = new Map<string, number[]>();
export default definePlugin({
name: "rate-limiter",
priority: 10,
onRequest: async (ctx) => {
const key = ctx.headers["x-api-key"] || "anonymous";
const now = Date.now();
const window = 60000; // 1 minute
const maxRequests = 100;
const timestamps = (requests.get(key) || []).filter((t) => t > now - window);
timestamps.push(now);
requests.set(key, timestamps);
if (timestamps.length > maxRequests) {
return blockRequest({ error: "Rate limit exceeded", status: 429 });
}
},
});
import { definePlugin } from "omniroute/plugins/sdk";
export default definePlugin({
name: "response-transformer",
onResponse: async (ctx, response) => {
if (response.choices) {
response.choices = response.choices.map((c: any) => ({
...c,
message: { ...c.message, content: c.message.content.trim() },
}));
}
return response;
},
});

OmniRoute source repository (a58000c7685f)

HagiCode

HagiCode is an agentic coding workspace: structured workflows, multi-agent execution, and Hero Dungeon views turn ideas into shipped software.

Turn ideas into polished, usable software with a smarter, faster, and more enjoyable agentic coding workflow.

HagiCode light theme main interface screenshot
  • SmartStructured workflows turn intent into an executable path from idea to shipped change.
  • EfficientMulti-agent workflows keep research, implementation, and review moving in parallel.
  • FunHero Dungeon interfaces make long coding sessions visual, collaborative, and rewarding.
Visit HagiCode