Files
openclaw/src/plugins/hook-types.ts
2026-04-11 22:24:55 +01:00

694 lines
18 KiB
TypeScript

import type { AgentMessage } from "@mariozechner/pi-agent-core";
import type { ReplyPayload } from "../auto-reply/reply-payload.js";
import type {
ReplyDispatchKind,
ReplyDispatcher,
} from "../auto-reply/reply/reply-dispatcher.types.js";
import type { FinalizedMsgContext } from "../auto-reply/templating.js";
import type { OpenClawConfig } from "../config/types.openclaw.js";
import type { TtsAutoMode } from "../config/types.tts.js";
import {
PLUGIN_PROMPT_MUTATION_RESULT_FIELDS,
stripPromptMutationFieldsFromLegacyHookResult,
} from "./hook-before-agent-start.types.js";
import type {
PluginHookBeforeAgentStartEvent,
PluginHookBeforeAgentStartResult,
PluginHookBeforeModelResolveEvent,
PluginHookBeforeModelResolveResult,
PluginHookBeforePromptBuildEvent,
PluginHookBeforePromptBuildResult,
} from "./hook-before-agent-start.types.js";
import type {
PluginHookInboundClaimContext,
PluginHookInboundClaimEvent,
PluginHookMessageContext,
PluginHookMessageReceivedEvent,
PluginHookMessageSendingEvent,
PluginHookMessageSendingResult,
PluginHookMessageSentEvent,
} from "./hook-message.types.js";
export type {
PluginHookBeforeAgentStartEvent,
PluginHookBeforeAgentStartOverrideResult,
PluginHookBeforeAgentStartResult,
PluginHookBeforeModelResolveEvent,
PluginHookBeforeModelResolveResult,
PluginHookBeforePromptBuildEvent,
PluginHookBeforePromptBuildResult,
} from "./hook-before-agent-start.types.js";
export {
PLUGIN_PROMPT_MUTATION_RESULT_FIELDS,
stripPromptMutationFieldsFromLegacyHookResult,
} from "./hook-before-agent-start.types.js";
export type {
PluginHookInboundClaimContext,
PluginHookInboundClaimEvent,
PluginHookMessageContext,
PluginHookMessageReceivedEvent,
PluginHookMessageSendingEvent,
PluginHookMessageSendingResult,
PluginHookMessageSentEvent,
} from "./hook-message.types.js";
export type PluginHookName =
| "before_model_resolve"
| "before_prompt_build"
| "before_agent_start"
| "before_agent_reply"
| "llm_input"
| "llm_output"
| "agent_end"
| "before_compaction"
| "after_compaction"
| "before_reset"
| "inbound_claim"
| "message_received"
| "message_sending"
| "message_sent"
| "before_tool_call"
| "after_tool_call"
| "tool_result_persist"
| "before_message_write"
| "session_start"
| "session_end"
| "subagent_spawning"
| "subagent_delivery_target"
| "subagent_spawned"
| "subagent_ended"
| "gateway_start"
| "gateway_stop"
| "before_dispatch"
| "reply_dispatch"
| "before_install";
export const PLUGIN_HOOK_NAMES = [
"before_model_resolve",
"before_prompt_build",
"before_agent_start",
"before_agent_reply",
"llm_input",
"llm_output",
"agent_end",
"before_compaction",
"after_compaction",
"before_reset",
"inbound_claim",
"message_received",
"message_sending",
"message_sent",
"before_tool_call",
"after_tool_call",
"tool_result_persist",
"before_message_write",
"session_start",
"session_end",
"subagent_spawning",
"subagent_delivery_target",
"subagent_spawned",
"subagent_ended",
"gateway_start",
"gateway_stop",
"before_dispatch",
"reply_dispatch",
"before_install",
] as const satisfies readonly PluginHookName[];
type MissingPluginHookNames = Exclude<PluginHookName, (typeof PLUGIN_HOOK_NAMES)[number]>;
type AssertAllPluginHookNamesListed = MissingPluginHookNames extends never ? true : never;
const assertAllPluginHookNamesListed: AssertAllPluginHookNamesListed = true;
void assertAllPluginHookNamesListed;
const pluginHookNameSet = new Set<PluginHookName>(PLUGIN_HOOK_NAMES);
export const isPluginHookName = (hookName: unknown): hookName is PluginHookName =>
typeof hookName === "string" && pluginHookNameSet.has(hookName as PluginHookName);
export const PROMPT_INJECTION_HOOK_NAMES = [
"before_prompt_build",
"before_agent_start",
] as const satisfies readonly PluginHookName[];
export type PromptInjectionHookName = (typeof PROMPT_INJECTION_HOOK_NAMES)[number];
const promptInjectionHookNameSet = new Set<PluginHookName>(PROMPT_INJECTION_HOOK_NAMES);
export const isPromptInjectionHookName = (hookName: PluginHookName): boolean =>
promptInjectionHookNameSet.has(hookName);
export type PluginHookAgentContext = {
runId?: string;
agentId?: string;
sessionKey?: string;
sessionId?: string;
workspaceDir?: string;
modelProviderId?: string;
modelId?: string;
messageProvider?: string;
trigger?: string;
channelId?: string;
};
export type PluginHookBeforeAgentReplyEvent = {
cleanedBody: string;
};
export type PluginHookBeforeAgentReplyResult = {
handled: boolean;
reply?: ReplyPayload;
reason?: string;
};
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 = {
messageCount: number;
compactingCount?: number;
tokenCount?: number;
messages?: unknown[];
sessionFile?: string;
};
export type PluginHookBeforeResetEvent = {
sessionFile?: string;
messages?: unknown[];
reason?: string;
};
export type PluginHookAfterCompactionEvent = {
messageCount: number;
tokenCount?: number;
compactedCount: number;
sessionFile?: string;
};
export type PluginHookInboundClaimResult = {
handled: boolean;
};
export type PluginHookBeforeDispatchEvent = {
content: string;
body?: string;
channel?: string;
sessionKey?: string;
senderId?: string;
isGroup?: boolean;
timestamp?: number;
};
export type PluginHookBeforeDispatchContext = {
channelId?: string;
accountId?: string;
conversationId?: string;
sessionKey?: string;
senderId?: string;
};
export type PluginHookBeforeDispatchResult = {
handled: boolean;
text?: string;
};
export type PluginHookReplyDispatchEvent = {
ctx: FinalizedMsgContext;
runId?: string;
sessionKey?: string;
inboundAudio: boolean;
sessionTtsAuto?: TtsAutoMode;
ttsChannel?: string;
suppressUserDelivery?: boolean;
shouldRouteToOriginating: boolean;
originatingChannel?: string;
originatingTo?: string;
shouldSendToolSummaries: boolean;
sendPolicy: "allow" | "deny";
isTailDispatch?: boolean;
};
export type PluginHookReplyDispatchContext = {
cfg: OpenClawConfig;
dispatcher: ReplyDispatcher;
abortSignal?: AbortSignal;
onReplyStart?: () => Promise<void> | void;
recordProcessed: (
outcome: "completed" | "skipped" | "error",
opts?: {
reason?: string;
error?: string;
},
) => void;
markIdle: (reason: string) => void;
};
export type PluginHookReplyDispatchResult = {
handled: boolean;
queuedFinal: boolean;
counts: Record<ReplyDispatchKind, number>;
};
export type PluginHookToolContext = {
agentId?: string;
sessionKey?: string;
sessionId?: string;
runId?: string;
toolName: string;
toolCallId?: string;
};
export type PluginHookBeforeToolCallEvent = {
toolName: string;
params: Record<string, unknown>;
runId?: string;
toolCallId?: string;
};
export const PluginApprovalResolutions = {
ALLOW_ONCE: "allow-once",
ALLOW_ALWAYS: "allow-always",
DENY: "deny",
TIMEOUT: "timeout",
CANCELLED: "cancelled",
} as const;
export type PluginApprovalResolution =
(typeof PluginApprovalResolutions)[keyof typeof PluginApprovalResolutions];
export type PluginHookBeforeToolCallResult = {
params?: Record<string, unknown>;
block?: boolean;
blockReason?: string;
requireApproval?: {
title: string;
description: string;
severity?: "info" | "warning" | "critical";
timeoutMs?: number;
timeoutBehavior?: "allow" | "deny";
pluginId?: string;
onResolution?: (decision: PluginApprovalResolution) => Promise<void> | void;
};
};
export type PluginHookAfterToolCallEvent = {
toolName: string;
params: Record<string, unknown>;
runId?: string;
toolCallId?: string;
result?: unknown;
error?: string;
durationMs?: number;
};
export type PluginHookToolResultPersistContext = {
agentId?: string;
sessionKey?: string;
toolName?: string;
toolCallId?: string;
};
export type PluginHookToolResultPersistEvent = {
toolName?: string;
toolCallId?: string;
message: AgentMessage;
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;
sessionKey?: string;
};
export type PluginHookSessionStartEvent = {
sessionId: string;
sessionKey?: string;
resumedFrom?: string;
};
export type PluginHookSessionEndReason =
| "new"
| "reset"
| "idle"
| "daily"
| "compaction"
| "deleted"
| "unknown";
export type PluginHookSessionEndEvent = {
sessionId: string;
sessionKey?: string;
messageCount: number;
durationMs?: number;
reason?: PluginHookSessionEndReason;
sessionFile?: string;
transcriptArchived?: boolean;
nextSessionId?: string;
nextSessionKey?: string;
};
export type PluginHookSubagentContext = {
runId?: string;
childSessionKey?: string;
requesterSessionKey?: string;
};
export type PluginHookSubagentTargetKind = "subagent" | "acp";
type PluginHookSubagentSpawnBase = {
childSessionKey: string;
agentId: string;
label?: string;
mode: "run" | "session";
requester?: {
channel?: string;
accountId?: string;
to?: string;
threadId?: string | number;
};
threadRequested: boolean;
};
export type PluginHookSubagentSpawningEvent = PluginHookSubagentSpawnBase;
export type PluginHookSubagentSpawningResult =
| {
status: "ok";
threadBindingReady?: boolean;
}
| {
status: "error";
error: string;
};
export type PluginHookSubagentDeliveryTargetEvent = {
childSessionKey: string;
requesterSessionKey: string;
requesterOrigin?: {
channel?: string;
accountId?: string;
to?: string;
threadId?: string | number;
};
childRunId?: string;
spawnMode?: "run" | "session";
expectsCompletionMessage: boolean;
};
export type PluginHookSubagentDeliveryTargetResult = {
origin?: {
channel?: string;
accountId?: string;
to?: string;
threadId?: string | number;
};
};
export type PluginHookSubagentSpawnedEvent = PluginHookSubagentSpawnBase & {
runId: string;
};
export type PluginHookSubagentEndedEvent = {
targetSessionKey: string;
targetKind: PluginHookSubagentTargetKind;
reason: string;
sendFarewell?: boolean;
accountId?: string;
runId?: string;
endedAt?: number;
outcome?: "ok" | "error" | "timeout" | "killed" | "reset" | "deleted";
error?: string;
};
export type PluginHookGatewayContext = {
port?: number;
};
export type PluginHookGatewayStartEvent = {
port: number;
};
export type PluginHookGatewayStopEvent = {
reason?: string;
};
export type PluginInstallTargetType = "skill" | "plugin";
export type PluginInstallRequestKind =
| "skill-install"
| "plugin-dir"
| "plugin-archive"
| "plugin-file"
| "plugin-npm";
export type PluginInstallSourcePathKind = "file" | "directory";
export type PluginInstallFinding = {
ruleId: string;
severity: "info" | "warn" | "critical";
file: string;
line: number;
message: string;
};
export type PluginHookBeforeInstallRequest = {
kind: PluginInstallRequestKind;
mode: "install" | "update";
requestedSpecifier?: string;
};
export type PluginHookBeforeInstallBuiltinScan = {
status: "ok" | "error";
scannedFiles: number;
critical: number;
warn: number;
info: number;
findings: PluginInstallFinding[];
error?: string;
};
export type PluginHookBeforeInstallSkillInstallSpec = {
id?: string;
kind: "brew" | "node" | "go" | "uv" | "download";
label?: string;
bins?: string[];
os?: string[];
formula?: string;
package?: string;
module?: string;
url?: string;
archive?: string;
extract?: boolean;
stripComponents?: number;
targetDir?: string;
};
export type PluginHookBeforeInstallSkill = {
installId: string;
installSpec?: PluginHookBeforeInstallSkillInstallSpec;
};
export type PluginHookBeforeInstallPlugin = {
pluginId: string;
contentType: "bundle" | "package" | "file";
packageName?: string;
manifestId?: string;
version?: string;
extensions?: string[];
};
export type PluginHookBeforeInstallContext = {
targetType: PluginInstallTargetType;
requestKind: PluginInstallRequestKind;
origin?: string;
};
export type PluginHookBeforeInstallEvent = {
targetType: PluginInstallTargetType;
targetName: string;
sourcePath: string;
sourcePathKind: PluginInstallSourcePathKind;
origin?: string;
request: PluginHookBeforeInstallRequest;
builtinScan: PluginHookBeforeInstallBuiltinScan;
skill?: PluginHookBeforeInstallSkill;
plugin?: PluginHookBeforeInstallPlugin;
};
export type PluginHookBeforeInstallResult = {
findings?: PluginInstallFinding[];
block?: boolean;
blockReason?: 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;
before_agent_reply: (
event: PluginHookBeforeAgentReplyEvent,
ctx: PluginHookAgentContext,
) => Promise<PluginHookBeforeAgentReplyResult | void> | PluginHookBeforeAgentReplyResult | 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;
inbound_claim: (
event: PluginHookInboundClaimEvent,
ctx: PluginHookInboundClaimContext,
) => Promise<PluginHookInboundClaimResult | void> | PluginHookInboundClaimResult | void;
before_dispatch: (
event: PluginHookBeforeDispatchEvent,
ctx: PluginHookBeforeDispatchContext,
) => Promise<PluginHookBeforeDispatchResult | void> | PluginHookBeforeDispatchResult | void;
reply_dispatch: (
event: PluginHookReplyDispatchEvent,
ctx: PluginHookReplyDispatchContext,
) => Promise<PluginHookReplyDispatchResult | void> | PluginHookReplyDispatchResult | 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;
subagent_spawning: (
event: PluginHookSubagentSpawningEvent,
ctx: PluginHookSubagentContext,
) => Promise<PluginHookSubagentSpawningResult | void> | PluginHookSubagentSpawningResult | void;
subagent_delivery_target: (
event: PluginHookSubagentDeliveryTargetEvent,
ctx: PluginHookSubagentContext,
) =>
| Promise<PluginHookSubagentDeliveryTargetResult | void>
| PluginHookSubagentDeliveryTargetResult
| void;
subagent_spawned: (
event: PluginHookSubagentSpawnedEvent,
ctx: PluginHookSubagentContext,
) => Promise<void> | void;
subagent_ended: (
event: PluginHookSubagentEndedEvent,
ctx: PluginHookSubagentContext,
) => Promise<void> | void;
gateway_start: (
event: PluginHookGatewayStartEvent,
ctx: PluginHookGatewayContext,
) => Promise<void> | void;
gateway_stop: (
event: PluginHookGatewayStopEvent,
ctx: PluginHookGatewayContext,
) => Promise<void> | void;
before_install: (
event: PluginHookBeforeInstallEvent,
ctx: PluginHookBeforeInstallContext,
) => Promise<PluginHookBeforeInstallResult | void> | PluginHookBeforeInstallResult | void;
};
export type PluginHookRegistration<K extends PluginHookName = PluginHookName> = {
pluginId: string;
hookName: K;
handler: PluginHookHandlerMap[K];
priority?: number;
source: string;
};