feat: add plugin architecture

This commit is contained in:
Peter Steinberger
2026-01-11 12:11:12 +00:00
parent f2b8f7bd5b
commit cf0c72a557
37 changed files with 2408 additions and 8 deletions

View File

@@ -47,6 +47,7 @@ const GROUP_LABELS: Record<string, string> = {
imessage: "iMessage",
whatsapp: "WhatsApp",
skills: "Skills",
plugins: "Plugins",
discovery: "Discovery",
presence: "Presence",
voicewake: "Voice Wake",
@@ -75,6 +76,7 @@ const GROUP_ORDER: Record<string, number> = {
imessage: 180,
whatsapp: 190,
skills: 200,
plugins: 205,
discovery: 210,
presence: 220,
voicewake: 230,
@@ -153,6 +155,13 @@ const FIELD_LABELS: Record<string, string> = {
"slack.appToken": "Slack App Token",
"signal.account": "Signal Account",
"imessage.cliPath": "iMessage CLI Path",
"plugins.enabled": "Enable Plugins",
"plugins.allow": "Plugin Allowlist",
"plugins.deny": "Plugin Denylist",
"plugins.load.paths": "Plugin Load Paths",
"plugins.entries": "Plugin Entries",
"plugins.entries.*.enabled": "Plugin Enabled",
"plugins.entries.*.config": "Plugin Config",
};
const FIELD_HELP: Record<string, string> = {
@@ -187,6 +196,17 @@ const FIELD_HELP: Record<string, string> = {
"Failure window (hours) for backoff counters (default: 24).",
"agents.defaults.models":
"Configured model catalog (keys are full provider/model IDs).",
"plugins.enabled": "Enable plugin/extension loading (default: true).",
"plugins.allow":
"Optional allowlist of plugin ids; when set, only listed plugins load.",
"plugins.deny": "Optional denylist of plugin ids; deny wins over allowlist.",
"plugins.load.paths": "Additional plugin files or directories to load.",
"plugins.entries":
"Per-plugin settings keyed by plugin id (enable/disable + config payloads).",
"plugins.entries.*.enabled":
"Overrides plugin enable/disable for this entry (restart required).",
"plugins.entries.*.config":
"Plugin-defined config payload (schema is provided by the plugin).",
"agents.defaults.model.primary": "Primary model (provider/model).",
"agents.defaults.model.fallbacks":
"Ordered fallback models (provider/model). Used when the primary model fails.",

View File

@@ -1276,6 +1276,27 @@ export type SkillsConfig = {
entries?: Record<string, SkillConfig>;
};
export type PluginEntryConfig = {
enabled?: boolean;
config?: Record<string, unknown>;
};
export type PluginsLoadConfig = {
/** Additional plugin/extension paths to load. */
paths?: string[];
};
export type PluginsConfig = {
/** Enable or disable plugin loading. */
enabled?: boolean;
/** Optional plugin allowlist (plugin ids). */
allow?: string[];
/** Optional plugin denylist (plugin ids). */
deny?: string[];
load?: PluginsLoadConfig;
entries?: Record<string, PluginEntryConfig>;
};
export type ModelApi =
| "openai-completions"
| "openai-responses"
@@ -1580,6 +1601,7 @@ export type ClawdbotConfig = {
seamColor?: string;
};
skills?: SkillsConfig;
plugins?: PluginsConfig;
models?: ModelsConfig;
agents?: AgentsConfig;
tools?: ToolsConfig;

View File

@@ -1608,6 +1608,29 @@ export const ClawdbotSchema = z
.optional(),
})
.optional(),
plugins: z
.object({
enabled: z.boolean().optional(),
allow: z.array(z.string()).optional(),
deny: z.array(z.string()).optional(),
load: z
.object({
paths: z.array(z.string()).optional(),
})
.optional(),
entries: z
.record(
z.string(),
z
.object({
enabled: z.boolean().optional(),
config: z.record(z.string(), z.unknown()).optional(),
})
.passthrough(),
)
.optional(),
})
.optional(),
})
.superRefine((cfg, ctx) => {
const agents = cfg.agents?.list ?? [];