Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
d87c11f
feat(plugins): enhance plugin system with transforms, updates, icons …
JamesDAdams Sep 24, 2026
2328328
refactor(rtk): extract RTK to plugin, add rtk gain dashboard, and imp…
JamesDAdams Sep 24, 2026
fd5541c
feat(plugins): add list settings fields, native install formats with …
JamesDAdams Sep 26, 2026
de519f7
feat(plugins): plugin menu slot, openSettings activation, and header …
JamesDAdams Sep 26, 2026
b931596
chore(plugins): list RTK and Community Hub in the curated registry
JamesDAdams Sep 26, 2026
3276f9d
Merge remote-tracking branch 'co-l/develop' into feat/update-plugins-…
JamesDAdams Sep 26, 2026
b9fe01b
feat(plugins): add generic plugin danger levels and display customiza…
JamesDAdams Sep 26, 2026
4a009ea
test(plugins): update whitelist plugin test name and path
JamesDAdams Sep 26, 2026
353eab6
feat(plugins): add openfox-whitelist to official plugin registry
JamesDAdams Sep 26, 2026
e803bc4
feat(plugins,providers): enhance plugin system and provider adapter m…
JamesDAdams Sep 27, 2026
9b5e8bb
test(plugins): remove untracked whitelist plugin test file
JamesDAdams Sep 27, 2026
e171ba2
test: fix git environment leakage in executor test and adapt windows …
JamesDAdams Sep 27, 2026
409a1c5
test: ensure getDescendants returns alive processes to prevent flaky …
JamesDAdams Sep 27, 2026
2c0db57
feat(ui): add toggle to show projects above sessions on home page
JamesDAdams Sep 27, 2026
0b4d435
feat(plugins): add declarative toggle component to DeclarativeRenderer
JamesDAdams Sep 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 16 additions & 14 deletions docs/PLUGIN-ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ host pushes the aggregated registry state into the server internals:
| `setPluginTools(...)` | Plugin tools become callable agent tools (Settings → Tools) |
| `setPluginCommands(...)` | Slash commands join the command registry with `pluginId` provenance |
| `setPluginModelMetadataProviders(...)` | Metadata providers merge into `/api/providers` responses |
| `setPluginMessageTransforms(...)` | Message transforms are wired into the pre-LLM agent loop pipeline |
| `refreshSkillSources()` | Calls each source's `load()`, skills join discovery with `source: 'plugin'` |

UI contributions, badges, panels, RPC methods, hooks, transitions and assets
Expand Down Expand Up @@ -331,20 +332,21 @@ unchanged.

## 7. Extension points summary

| Capability | Registry method(s) | Surfaces in |
| --------------- | -------------------------------------------------------- | ---------------------------------------- |
| `providers` | `registerAuth`, `registerTransport`, `registerPreset` | Provider setup wizard, agent loop |
| `models` | `registerModelMetadataProvider` | Model picker, `/api/providers` |
| `tools` | `registerTool` | Agent tool list, Settings → Tools |
| `commands` | `registerCommand` | Slash commands, Commands settings |
| `skills` | `registerSkillSource` | Skill discovery (`source: 'plugin'`) |
| `settings` | `registerSettings` | Plugins tab auto-form |
| `ui` | `registerUiAction`, `registerUiBadge`, `registerUiPanel` | Header, session, message, composer slots |
| `hooks` | `registerHook` | Background (observational) |
| `workflows` | `registerTransitionHandler` | Workflow `when: custom` transitions |
| `rpc` | `registerRpc` | `POST /api/plugins/<id>/rpc/<method>` |
| `assets` | `registerAsset` | `/api/plugins/<id>/assets/<path>` |
| `notifications` | `context.notify` | Toasts, bell, notification center |
| Capability | Registry method(s) | Surfaces in |
| --------------- | -------------------------------------------------------- | -------------------------------------------- |
| `providers` | `registerAuth`, `registerTransport`, `registerPreset` | Provider setup wizard, agent loop |
| `models` | `registerModelMetadataProvider` | Model picker, `/api/providers` |
| `tools` | `registerTool` | Agent tool list, Settings → Tools |
| `commands` | `registerCommand` | Slash commands, Commands settings |
| `skills` | `registerSkillSource` | Skill discovery (`source: 'plugin'`) |
| `settings` | `registerSettings` | Plugins tab auto-form |
| `ui` | `registerUiAction`, `registerUiBadge`, `registerUiPanel` | Header, session, message, composer slots |
| `hooks` | `registerHook` | Background (observational) |
| `workflows` | `registerTransitionHandler` | Workflow `when: custom` transitions |
| `rpc` | `registerRpc` | `POST /api/plugins/<id>/rpc/<method>` |
| `assets` | `registerAsset` | `/api/plugins/<id>/assets/<path>` |
| `transforms` | `registerMessageTransform` | Pre-LLM stream message pipeline (agent loop) |
| `notifications` | `context.notify` | Toasts, bell, notification center |

## 8. Where to look next

Expand Down
266 changes: 239 additions & 27 deletions docs/PLUGINS.md

Large diffs are not rendered by default.

96 changes: 90 additions & 6 deletions plugins-registry.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,120 @@
"name": "openfox-chatgpt",
"displayName": "ChatGPT",
"description": "Authenticate with a ChatGPT Plus or Pro account via browser-based OAuth.",
"githubUrl": "https://github.com/arthurlacoste/openfox-chatgpt"
"githubUrl": "https://github.com/arthurlacoste/openfox-chatgpt",
"author": "arthurlacoste",
"icon": "https://chatgpt.com/favicon.ico"
},
{
"name": "openfox-github-copilot",
"displayName": "Github Copilot",
"description": "Authenticate with a Github copilot account via browser-based OAuth.",
"githubUrl": "https://github.com/JamesDAdams/openfox-github-copilot"
"githubUrl": "https://github.com/JamesDAdams/openfox-github-copilot",
"author": "James Adams",
"icon": "https://github.githubassets.com/favicons/favicon.png"
},
{
"name": "openfox-google-antigravity",
"displayName": "Google Antigravity",
"description": "Authenticate with a Google Antigravity account via browser-based OAuth | ⚠️ Using this plugin violates Google's Terms of Service.",
"githubUrl": "https://github.com/JamesDAdams/openfox-google-antigravity"
"githubUrl": "https://github.com/JamesDAdams/openfox-google-antigravity",
"author": "James Adams",
"icon": "https://brandlogos.net/wp-content/uploads/2025/12/google_antigravity-logo_brandlogos.net_qu4jc.png"
},
{
"name": "openfox-xai-supergrok",
"displayName": "xAI Grok (SuperGrok)",
"description": "Authenticate with an X (xAI) SuperGrok subscription via OAuth. No API key required. | ⚠️ Using this plugin may violate xAI's Terms of Service.",
"githubUrl": "https://github.com/Olgean-Group/openfox-xai-supergrok"
"githubUrl": "https://github.com/Olgean-Group/openfox-xai-supergrok",
"author": "Olgean-Group",
"icon": "https://x.ai/favicon.ico"
},
{
"name": "openfox-openrouter-free",
"displayName": "OpenRouter (Free Models)",
"description": "OpenRouter provider filtered to include only free plans, with automatic hourly updates (once per hour) and 1-Click OAuth / API key authentication.",
"githubUrl": "https://github.com/JamesDAdams/openfox-openrouter-free"
"githubUrl": "https://github.com/JamesDAdams/openfox-openrouter-free",
"author": "James Adams",
"icon": "https://openrouter.ai/favicon.ico"
},
{
"name": "openfox-opencode-free",
"displayName": "OpenCode (Free Models)",
"description": "OpenCode provider filtered to free models only (-free suffix), with automatic hourly model updates (1x/hour) and API key auth.",
"githubUrl": "https://github.com/JamesDAdams/openfox-opencode-free"
"githubUrl": "https://github.com/JamesDAdams/openfox-opencode-free",
"author": "James Adams",
"icon": "https://opencode.ai/_build/assets/preview-opencode-logo-dark-ZBwNGoYp.png"
},
{
"name": "openfox-opencode-go",
"displayName": "OpenCode Go",
"description": "Access curated open coding models with generous limits via your OpenCode Go subscription ($10/month).",
"githubUrl": "https://github.com/JamesDAdams/openfox-opencode-go",
"author": "James Adams",
"icon": "https://opencode.ai/_build/assets/preview-opencode-logo-dark-ZBwNGoYp.png"
},
{
"name": "openfox-cheaperinference",
"displayName": "Cheaper Inference",
"description": "Access leading discounted AI models from multiple providers through one API with live marketplace rates.",
"githubUrl": "https://github.com/JamesDAdams/openfox-cheaperinference",
"author": "James Adams",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcT61SdDKpiR436_O4PWXqxT2q9bx4G3E_GoijE7Hxskhw&s"
},
{
"name": "openfox-headroom",
"displayName": "Headroom Compression",
"description": "Native context compression for OpenFox using Headroom proxy to optimize tokens and reduce costs without breaking prefix caching.",
"githubUrl": "https://github.com/JamesDAdams/openfox-headroom",
"author": "James Adams",
"icon": "https://avatars.githubusercontent.com/u/294291659?s=60&v=4"
},
{
"name": "openfox-omniroute-quota",
"displayName": "OmniRoute Quota",
"description": "Reports OmniRoute usage and quotas to OpenFox via the quota plugin.",
"githubUrl": "https://github.com/JamesDAdams/openfox-omniroute-quota",
"author": "James Adams",
"icon": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#8B5CF6\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><polygon points=\"16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76\"/></svg>"
},
{
"name": "openfox-quota",
"displayName": "Usage & Quotas",
"description": "Usage and quota tracking plugin for OpenFox with generic providers and custom component overrides.",
"githubUrl": "https://github.com/JamesDAdams/openfox-quota",
"author": "James Adams",
"icon": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#3B82F6\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M12 2v4m0 12v4M4.93 4.93l2.83 2.83m8.48 8.48 2.83 2.83M2 12h4m12 0h4M4.93 19.07l2.83-2.83m8.48-8.48 2.83-2.83\"/><circle cx=\"12\" cy=\"12\" r=\"4\"/></svg>"
},
{
"name": "openfox-model-pricing",
"displayName": "Model Pricing",
"description": "Comprehensive model pricing, discount badges, dynamic rates, and real-time session cost tracking plugin for OpenFox.",
"githubUrl": "https://github.com/JamesDAdams/openfox-model-pricing",
"author": "James Adams",
"icon": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#10B981\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"12\" y1=\"1\" x2=\"12\" y2=\"23\"></line><path d=\"M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6\"></path></svg>"
},
{
"name": "openfox-rtk",
"displayName": "RTK Token Optimizer",
"description": "Reduce token consumption by filtering and rewriting shell command output through RTK (Rust Token Killer).",
"githubUrl": "https://github.com/JamesDAdams/openfox-rtk",
"author": "James Adams",
"icon": "https://avatars.githubusercontent.com/u/258253854?s=60&v=4"
},
{
"name": "openfox-community-hub",
"displayName": "Community Hub",
"description": "Share, export, import, and install OpenFox workflows, agents, sub-agents, MCP servers, skills, and commands via static Git/CDN registries.",
"githubUrl": "https://github.com/JamesDAdams/openfox-community-hub",
"author": "James Adams",
"icon": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.7\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"9\"></circle><path d=\"M3.3 9h17.4\"></path><path d=\"M3.3 15h17.4\"></path><path d=\"M12 3c2.4 2.5 3.7 5.6 3.7 9S14.4 18.5 12 21c-2.4-2.5-3.7-5.6-3.7-9S9.6 5.5 12 3z\"></path></svg>"
},
{
"name": "openfox-whitelist",
"displayName": "Whitelist File Access",
"description": "Adds Whitelist and Whitelist Only danger levels to allow configured external paths without confirmation, and optionally auto-reject unauthorized files.",
"githubUrl": "https://github.com/JamesDAdams/openfox-whitelist",
"author": "James Adams",
"icon": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#10B981\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z\"/><polyline points=\"9 12 11 14 15 10\"/></svg>"
}
]
4 changes: 2 additions & 2 deletions src/cli/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ const backendSchema = z.enum([
const modelConfigSchema = z
.object({
id: z.string(),
contextWindow: z.number(),
source: z.enum(['backend', 'user', 'default']),
contextWindow: z.number().default(128000),
source: z.enum(['backend', 'user', 'default']).default('backend'),
temperature: z.number().optional(),
topP: z.number().optional(),
topK: z.number().optional(),
Expand Down
54 changes: 54 additions & 0 deletions src/plugin/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { z } from 'zod'
import type { ModelConfig, Provider } from '../shared/types.js'
import type { LLMMessage } from '../server/llm/types.js'
import type {
LocalizedString,
PluginBadgeTone,
Expand Down Expand Up @@ -28,12 +29,15 @@ export type {
DeclarativeNode,
LocalizedString,
PluginActivation,
PluginBadgeTone,
PluginCapability,
PluginContributionSummary,
PluginDangerLevelView,
PluginInfo,
PluginNotification,
PluginNotificationAction,
PluginSettingsField,
PluginSettingsLinkButton,
PluginSettingsSchema,
PluginSettingsTab,
PluginSettingScope,
Expand Down Expand Up @@ -70,12 +74,14 @@ export const PLUGIN_API_VERSION = 2
export const pluginManifestSchema = z.object({
name: z.string().min(1),
version: z.string().min(1),
author: z.union([z.string(), z.object({ name: z.string().optional() })]).optional(),
openfox: z.object({
apiVersion: z.number().int(),
entry: z.string().min(1).optional(),
plugin: z.string().min(1).optional(),
displayName: z.string().min(1).optional(),
description: z.string().optional(),
author: z.string().optional(),
icon: z.string().optional(),
logo: z.string().optional(),
capabilities: z.array(z.string()).optional(),
Expand All @@ -86,12 +92,14 @@ export const pluginManifestSchema = z.object({
export interface PluginManifest {
name: string
version: string
author?: string
openfox: {
apiVersion: number
entry?: string
plugin?: string
displayName?: string
description?: string
author?: string
icon?: string
logo?: string
capabilities?: PluginCapability[]
Expand Down Expand Up @@ -275,6 +283,52 @@ export interface PluginRegistry {
): void
registerRpc(method: string, handler: PluginRpcHandler): void
registerAsset(relativePath: string): void
registerMessageTransform(transform: PluginMessageTransform): void
registerDangerLevel(dangerLevel: PluginDangerLevel): void
}

export interface PluginPathAccessContext {
paths: string[]
workdir: string
sessionId: string
projectId?: string | undefined
tool: string
command?: string | undefined
}

export type PluginPathAccessDecision = { action: 'allow' } | { action: 'deny'; message?: string } | { action: 'ask' }

export interface PluginDangerLevel {
id: string
label: LocalizedString
description?: LocalizedString
badgeTone?: PluginBadgeTone
evaluatePathAccess?(context: PluginPathAccessContext): Promise<PluginPathAccessDecision> | PluginPathAccessDecision
}

export interface PluginMessageTransformContext {
sessionId: string
projectId?: string
workdir: string
model: string
systemPrompt: string
mode?: string
signal?: AbortSignal
}

export interface PluginMessageTransformResult {
messages: LLMMessage[]
systemPrompt?: string
metadata?: Record<string, unknown>
}

export interface PluginMessageTransform {
id: string
priority?: number
transform(
messages: LLMMessage[],
context: PluginMessageTransformContext,
): Promise<PluginMessageTransformResult | LLMMessage[]> | PluginMessageTransformResult | LLMMessage[]
}

export interface PluginTransitionContext {
Expand Down
Loading
Loading