@gguf/claw
Version:
Multi-channel AI gateway with extensible messaging integrations
471 lines (470 loc) • 17.8 kB
TypeScript
import type { IncomingMessage, ServerResponse } from "node:http";
import type { AgentMessage } from "@mariozechner/pi-agent-core";
import type { Command } from "commander";
import type { AuthProfileCredential, OAuthCredential } from "../agents/auth-profiles/types.js";
import type { AnyAgentTool } from "../agents/tools/common.js";
import type { ReplyPayload } from "../auto-reply/types.js";
import type { ChannelDock } from "../channels/dock.js";
import type { ChannelId, ChannelPlugin } from "../channels/plugins/types.js";
import type { createVpsAwareOAuthHandlers } from "../commands/oauth-flow.js";
import type { OpenClawConfig } from "../config/config.js";
import type { ModelProviderConfig } from "../config/types.js";
import type { GatewayRequestHandler } from "../gateway/server-methods/types.js";
import type { InternalHookHandler } from "../hooks/internal-hooks.js";
import type { HookEntry } from "../hooks/types.js";
import type { RuntimeEnv } from "../runtime.js";
import type { WizardPrompter } from "../wizard/prompts.js";
import type { PluginRuntime } from "./runtime/types.js";
export type { PluginRuntime } from "./runtime/types.js";
export type { AnyAgentTool } from "../agents/tools/common.js";
export type PluginLogger = {
debug?: (message: string) => void;
info: (message: string) => void;
warn: (message: string) => void;
error: (message: string) => void;
};
export type PluginConfigUiHint = {
label?: string;
help?: string;
advanced?: boolean;
sensitive?: boolean;
placeholder?: string;
};
export type PluginKind = "memory";
export type PluginConfigValidation = {
ok: true;
value?: unknown;
} | {
ok: false;
errors: string[];
};
export type OpenClawPluginConfigSchema = {
safeParse?: (value: unknown) => {
success: boolean;
data?: unknown;
error?: {
issues?: Array<{
path: Array<string | number>;
message: string;
}>;
};
};
parse?: (value: unknown) => unknown;
validate?: (value: unknown) => PluginConfigValidation;
uiHints?: Record<string, PluginConfigUiHint>;
jsonSchema?: Record<string, unknown>;
};
export type OpenClawPluginToolContext = {
config?: OpenClawConfig;
workspaceDir?: string;
agentDir?: string;
agentId?: string;
sessionKey?: string;
messageChannel?: string;
agentAccountId?: string;
sandboxed?: boolean;
};
export type OpenClawPluginToolFactory = (ctx: OpenClawPluginToolContext) => AnyAgentTool | AnyAgentTool[] | null | undefined;
export type OpenClawPluginToolOptions = {
name?: string;
names?: string[];
optional?: boolean;
};
export type OpenClawPluginHookOptions = {
entry?: HookEntry;
name?: string;
description?: string;
register?: boolean;
};
export type ProviderAuthKind = "oauth" | "api_key" | "token" | "device_code" | "custom";
export type ProviderAuthResult = {
profiles: Array<{
profileId: string;
credential: AuthProfileCredential;
}>;
configPatch?: Partial<OpenClawConfig>;
defaultModel?: string;
notes?: string[];
};
export type ProviderAuthContext = {
config: OpenClawConfig;
agentDir?: string;
workspaceDir?: string;
prompter: WizardPrompter;
runtime: RuntimeEnv;
isRemote: boolean;
openUrl: (url: string) => Promise<void>;
oauth: {
createVpsAwareHandlers: typeof createVpsAwareOAuthHandlers;
};
};
export type ProviderAuthMethod = {
id: string;
label: string;
hint?: string;
kind: ProviderAuthKind;
run: (ctx: ProviderAuthContext) => Promise<ProviderAuthResult>;
};
export type ProviderPlugin = {
id: string;
label: string;
docsPath?: string;
aliases?: string[];
envVars?: string[];
models?: ModelProviderConfig;
auth: ProviderAuthMethod[];
formatApiKey?: (cred: AuthProfileCredential) => string;
refreshOAuth?: (cred: OAuthCredential) => Promise<OAuthCredential>;
};
export type OpenClawPluginGatewayMethod = {
method: string;
handler: GatewayRequestHandler;
};
/**
* Context passed to plugin command handlers.
*/
export type PluginCommandContext = {
/** The sender's identifier (e.g., Telegram user ID) */
senderId?: string;
/** The channel/surface (e.g., "telegram", "discord") */
channel: string;
/** Provider channel id (e.g., "telegram") */
channelId?: ChannelId;
/** Whether the sender is on the allowlist */
isAuthorizedSender: boolean;
/** Raw command arguments after the command name */
args?: string;
/** The full normalized command body */
commandBody: string;
/** Current OpenClaw configuration */
config: OpenClawConfig;
/** Raw "From" value (channel-scoped id) */
from?: string;
/** Raw "To" value (channel-scoped id) */
to?: string;
/** Account id for multi-account channels */
accountId?: string;
/** Thread/topic id if available */
messageThreadId?: number;
};
/**
* Result returned by a plugin command handler.
*/
export type PluginCommandResult = ReplyPayload;
/**
* Handler function for plugin commands.
*/
export type PluginCommandHandler = (ctx: PluginCommandContext) => PluginCommandResult | Promise<PluginCommandResult>;
/**
* Definition for a plugin-registered command.
*/
export type OpenClawPluginCommandDefinition = {
/** Command name without leading slash (e.g., "tts") */
name: string;
/** Description shown in /help and command menus */
description: string;
/** Whether this command accepts arguments */
acceptsArgs?: boolean;
/** Whether only authorized senders can use this command (default: true) */
requireAuth?: boolean;
/** The handler function */
handler: PluginCommandHandler;
};
export type OpenClawPluginHttpHandler = (req: IncomingMessage, res: ServerResponse) => Promise<boolean> | boolean;
export type OpenClawPluginHttpRouteHandler = (req: IncomingMessage, res: ServerResponse) => Promise<void> | void;
export type OpenClawPluginCliContext = {
program: Command;
config: OpenClawConfig;
workspaceDir?: string;
logger: PluginLogger;
};
export type OpenClawPluginCliRegistrar = (ctx: OpenClawPluginCliContext) => void | Promise<void>;
export type OpenClawPluginServiceContext = {
config: OpenClawConfig;
workspaceDir?: string;
stateDir: string;
logger: PluginLogger;
};
export type OpenClawPluginService = {
id: string;
start: (ctx: OpenClawPluginServiceContext) => void | Promise<void>;
stop?: (ctx: OpenClawPluginServiceContext) => void | Promise<void>;
};
export type OpenClawPluginChannelRegistration = {
plugin: ChannelPlugin;
dock?: ChannelDock;
};
export type OpenClawPluginDefinition = {
id?: string;
name?: string;
description?: string;
version?: string;
kind?: PluginKind;
configSchema?: OpenClawPluginConfigSchema;
register?: (api: OpenClawPluginApi) => void | Promise<void>;
activate?: (api: OpenClawPluginApi) => void | Promise<void>;
};
export type OpenClawPluginModule = OpenClawPluginDefinition | ((api: OpenClawPluginApi) => void | Promise<void>);
export type OpenClawPluginApi = {
id: string;
name: string;
version?: string;
description?: string;
source: string;
config: OpenClawConfig;
pluginConfig?: Record<string, unknown>;
runtime: PluginRuntime;
logger: PluginLogger;
registerTool: (tool: AnyAgentTool | OpenClawPluginToolFactory, opts?: OpenClawPluginToolOptions) => void;
registerHook: (events: string | string[], handler: InternalHookHandler, opts?: OpenClawPluginHookOptions) => void;
registerHttpHandler: (handler: OpenClawPluginHttpHandler) => void;
registerHttpRoute: (params: {
path: string;
handler: OpenClawPluginHttpRouteHandler;
}) => void;
registerChannel: (registration: OpenClawPluginChannelRegistration | ChannelPlugin) => void;
registerGatewayMethod: (method: string, handler: GatewayRequestHandler) => void;
registerCli: (registrar: OpenClawPluginCliRegistrar, opts?: {
commands?: string[];
}) => void;
registerService: (service: OpenClawPluginService) => void;
registerProvider: (provider: ProviderPlugin) => void;
/**
* Register a custom command that bypasses the LLM agent.
* Plugin commands are processed before built-in commands and before agent invocation.
* Use this for simple state-toggling or status commands that don't need AI reasoning.
*/
registerCommand: (command: OpenClawPluginCommandDefinition) => void;
resolvePath: (input: string) => string;
/** Register a lifecycle hook handler */
on: <K extends PluginHookName>(hookName: K, handler: PluginHookHandlerMap[K], opts?: {
priority?: number;
}) => void;
};
export type PluginOrigin = "bundled" | "global" | "workspace" | "config";
export type PluginDiagnostic = {
level: "warn" | "error";
message: string;
pluginId?: string;
source?: string;
};
export type PluginHookName = "before_model_resolve" | "before_prompt_build" | "before_agent_start" | "llm_input" | "llm_output" | "agent_end" | "before_compaction" | "after_compaction" | "before_reset" | "message_received" | "message_sending" | "message_sent" | "before_tool_call" | "after_tool_call" | "tool_result_persist" | "before_message_write" | "session_start" | "session_end" | "gateway_start" | "gateway_stop";
export type PluginHookAgentContext = {
agentId?: string;
sessionKey?: string;
sessionId?: string;
workspaceDir?: string;
messageProvider?: string;
};
export type PluginHookBeforeModelResolveEvent = {
/** User prompt for this run. No session messages are available yet in this phase. */
prompt: string;
};
export type PluginHookBeforeModelResolveResult = {
/** Override the model for this agent run. E.g. "llama3.3:8b" */
modelOverride?: string;
/** Override the provider for this agent run. E.g. "ollama" */
providerOverride?: string;
};
export type PluginHookBeforePromptBuildEvent = {
prompt: string;
/** Session messages prepared for this run. */
messages: unknown[];
};
export type PluginHookBeforePromptBuildResult = {
systemPrompt?: string;
prependContext?: string;
};
export type PluginHookBeforeAgentStartEvent = {
prompt: string;
/** Optional because legacy hook can run in pre-session phase. */
messages?: unknown[];
};
export type PluginHookBeforeAgentStartResult = PluginHookBeforePromptBuildResult & PluginHookBeforeModelResolveResult;
export type PluginHookLlmInputEvent = {
runId: string;
sessionId: string;
provider: string;
model: string;
systemPrompt?: string;
prompt: string;
historyMessages: unknown[];
imagesCount: number;
};
export type PluginHookLlmOutputEvent = {
runId: string;
sessionId: string;
provider: string;
model: string;
assistantTexts: string[];
lastAssistant?: unknown;
usage?: {
input?: number;
output?: number;
cacheRead?: number;
cacheWrite?: number;
total?: number;
};
};
export type PluginHookAgentEndEvent = {
messages: unknown[];
success: boolean;
error?: string;
durationMs?: number;
};
export type PluginHookBeforeCompactionEvent = {
/** Total messages in the session before any truncation or compaction */
messageCount: number;
/** Messages being fed to the compaction LLM (after history-limit truncation) */
compactingCount?: number;
tokenCount?: number;
messages?: unknown[];
/** Path to the session JSONL transcript. All messages are already on disk
* before compaction starts, so plugins can read this file asynchronously
* and process in parallel with the compaction LLM call. */
sessionFile?: string;
};
export type PluginHookBeforeResetEvent = {
sessionFile?: string;
messages?: unknown[];
reason?: string;
};
export type PluginHookAfterCompactionEvent = {
messageCount: number;
tokenCount?: number;
compactedCount: number;
/** Path to the session JSONL transcript. All pre-compaction messages are
* preserved on disk, so plugins can read and process them asynchronously
* without blocking the compaction pipeline. */
sessionFile?: string;
};
export type PluginHookMessageContext = {
channelId: string;
accountId?: string;
conversationId?: string;
};
export type PluginHookMessageReceivedEvent = {
from: string;
content: string;
timestamp?: number;
metadata?: Record<string, unknown>;
};
export type PluginHookMessageSendingEvent = {
to: string;
content: string;
metadata?: Record<string, unknown>;
};
export type PluginHookMessageSendingResult = {
content?: string;
cancel?: boolean;
};
export type PluginHookMessageSentEvent = {
to: string;
content: string;
success: boolean;
error?: string;
};
export type PluginHookToolContext = {
agentId?: string;
sessionKey?: string;
toolName: string;
};
export type PluginHookBeforeToolCallEvent = {
toolName: string;
params: Record<string, unknown>;
};
export type PluginHookBeforeToolCallResult = {
params?: Record<string, unknown>;
block?: boolean;
blockReason?: string;
};
export type PluginHookAfterToolCallEvent = {
toolName: string;
params: Record<string, unknown>;
result?: unknown;
error?: string;
durationMs?: number;
};
export type PluginHookToolResultPersistContext = {
agentId?: string;
sessionKey?: string;
toolName?: string;
toolCallId?: string;
};
export type PluginHookToolResultPersistEvent = {
toolName?: string;
toolCallId?: string;
/**
* The toolResult message about to be written to the session transcript.
* Handlers may return a modified message (e.g. drop non-essential fields).
*/
message: AgentMessage;
/** True when the tool result was synthesized by a guard/repair step. */
isSynthetic?: boolean;
};
export type PluginHookToolResultPersistResult = {
message?: AgentMessage;
};
export type PluginHookBeforeMessageWriteEvent = {
message: AgentMessage;
sessionKey?: string;
agentId?: string;
};
export type PluginHookBeforeMessageWriteResult = {
block?: boolean;
message?: AgentMessage;
};
export type PluginHookSessionContext = {
agentId?: string;
sessionId: string;
};
export type PluginHookSessionStartEvent = {
sessionId: string;
resumedFrom?: string;
};
export type PluginHookSessionEndEvent = {
sessionId: string;
messageCount: number;
durationMs?: number;
};
export type PluginHookGatewayContext = {
port?: number;
};
export type PluginHookGatewayStartEvent = {
port: number;
};
export type PluginHookGatewayStopEvent = {
reason?: string;
};
export type PluginHookHandlerMap = {
before_model_resolve: (event: PluginHookBeforeModelResolveEvent, ctx: PluginHookAgentContext) => Promise<PluginHookBeforeModelResolveResult | void> | PluginHookBeforeModelResolveResult | void;
before_prompt_build: (event: PluginHookBeforePromptBuildEvent, ctx: PluginHookAgentContext) => Promise<PluginHookBeforePromptBuildResult | void> | PluginHookBeforePromptBuildResult | void;
before_agent_start: (event: PluginHookBeforeAgentStartEvent, ctx: PluginHookAgentContext) => Promise<PluginHookBeforeAgentStartResult | void> | PluginHookBeforeAgentStartResult | void;
llm_input: (event: PluginHookLlmInputEvent, ctx: PluginHookAgentContext) => Promise<void> | void;
llm_output: (event: PluginHookLlmOutputEvent, ctx: PluginHookAgentContext) => Promise<void> | void;
agent_end: (event: PluginHookAgentEndEvent, ctx: PluginHookAgentContext) => Promise<void> | void;
before_compaction: (event: PluginHookBeforeCompactionEvent, ctx: PluginHookAgentContext) => Promise<void> | void;
after_compaction: (event: PluginHookAfterCompactionEvent, ctx: PluginHookAgentContext) => Promise<void> | void;
before_reset: (event: PluginHookBeforeResetEvent, ctx: PluginHookAgentContext) => Promise<void> | void;
message_received: (event: PluginHookMessageReceivedEvent, ctx: PluginHookMessageContext) => Promise<void> | void;
message_sending: (event: PluginHookMessageSendingEvent, ctx: PluginHookMessageContext) => Promise<PluginHookMessageSendingResult | void> | PluginHookMessageSendingResult | void;
message_sent: (event: PluginHookMessageSentEvent, ctx: PluginHookMessageContext) => Promise<void> | void;
before_tool_call: (event: PluginHookBeforeToolCallEvent, ctx: PluginHookToolContext) => Promise<PluginHookBeforeToolCallResult | void> | PluginHookBeforeToolCallResult | void;
after_tool_call: (event: PluginHookAfterToolCallEvent, ctx: PluginHookToolContext) => Promise<void> | void;
tool_result_persist: (event: PluginHookToolResultPersistEvent, ctx: PluginHookToolResultPersistContext) => PluginHookToolResultPersistResult | void;
before_message_write: (event: PluginHookBeforeMessageWriteEvent, ctx: {
agentId?: string;
sessionKey?: string;
}) => PluginHookBeforeMessageWriteResult | void;
session_start: (event: PluginHookSessionStartEvent, ctx: PluginHookSessionContext) => Promise<void> | void;
session_end: (event: PluginHookSessionEndEvent, ctx: PluginHookSessionContext) => Promise<void> | void;
gateway_start: (event: PluginHookGatewayStartEvent, ctx: PluginHookGatewayContext) => Promise<void> | void;
gateway_stop: (event: PluginHookGatewayStopEvent, ctx: PluginHookGatewayContext) => Promise<void> | void;
};
export type PluginHookRegistration<K extends PluginHookName = PluginHookName> = {
pluginId: string;
hookName: K;
handler: PluginHookHandlerMap[K];
priority?: number;
source: string;
};