UNPKG

@gguf/claw

Version:

Multi-channel AI gateway with extensible messaging integrations

471 lines (470 loc) 17.8 kB
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; };