mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-11 17:21:13 +00:00
fix(plugins): split hook contract types
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import type { HookEntry } from "../hooks/types.js";
|
||||
import type { PluginHookRegistration as TypedPluginHookRegistration } from "./types.js";
|
||||
import type { PluginHookRegistration as TypedPluginHookRegistration } from "./hook-types.js";
|
||||
|
||||
export type PluginLegacyHookRegistration = {
|
||||
pluginId: string;
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
import { createSubsystemLogger } from "../logging/subsystem.js";
|
||||
import { resolveGlobalSingleton } from "../shared/global-singleton.js";
|
||||
import type { GlobalHookRunnerRegistry } from "./hook-registry.types.js";
|
||||
import type { PluginHookGatewayContext, PluginHookGatewayStopEvent } from "./hook-types.js";
|
||||
import { createHookRunner, type HookRunner } from "./hooks.js";
|
||||
import type { PluginHookGatewayContext, PluginHookGatewayStopEvent } from "./types.js";
|
||||
|
||||
type HookRunnerGlobalState = {
|
||||
hookRunner: HookRunner | null;
|
||||
|
||||
693
src/plugins/hook-types.ts
Normal file
693
src/plugins/hook-types.ts
Normal file
@@ -0,0 +1,693 @@
|
||||
import type { AgentMessage } from "@mariozechner/pi-agent-core";
|
||||
import type {
|
||||
ReplyDispatchKind,
|
||||
ReplyDispatcher,
|
||||
} from "../auto-reply/reply/reply-dispatcher.types.js";
|
||||
import type { FinalizedMsgContext } from "../auto-reply/templating.js";
|
||||
import type { ReplyPayload } from "../auto-reply/types.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;
|
||||
};
|
||||
@@ -65,7 +65,7 @@ import type {
|
||||
PluginHookBeforeInstallContext,
|
||||
PluginHookBeforeInstallEvent,
|
||||
PluginHookBeforeInstallResult,
|
||||
} from "./types.js";
|
||||
} from "./hook-types.js";
|
||||
|
||||
// Re-export types for consumers
|
||||
export type {
|
||||
|
||||
@@ -17,11 +17,6 @@ import type { ModelProviderRequestTransportOverrides } from "../agents/provider-
|
||||
import type { ProviderSystemPromptContribution } from "../agents/system-prompt-contribution.js";
|
||||
import type { PromptMode } from "../agents/system-prompt.types.js";
|
||||
import type { AnyAgentTool } from "../agents/tools/common.js";
|
||||
import type {
|
||||
ReplyDispatchKind,
|
||||
ReplyDispatcher,
|
||||
} from "../auto-reply/reply/reply-dispatcher.types.js";
|
||||
import type { FinalizedMsgContext } from "../auto-reply/templating.js";
|
||||
import type { ThinkLevel } from "../auto-reply/thinking.shared.js";
|
||||
import type { ReplyPayload } from "../auto-reply/types.js";
|
||||
import type { ChannelPlugin } from "../channels/plugins/types.plugin.js";
|
||||
@@ -29,7 +24,6 @@ import type { ChannelId } from "../channels/plugins/types.public.js";
|
||||
import type { ModelProviderConfig } from "../config/types.js";
|
||||
import type { ModelCompatConfig } from "../config/types.models.js";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import type { TtsAutoMode } from "../config/types.tts.js";
|
||||
import type { OperatorScope } from "../gateway/operator-scopes.js";
|
||||
import type { GatewayRequestHandler } from "../gateway/server-methods/types.js";
|
||||
import type { InternalHookHandler } from "../hooks/internal-hook-types.js";
|
||||
@@ -86,27 +80,7 @@ import type {
|
||||
PluginConversationBindingResolvedEvent,
|
||||
PluginConversationBindingResolutionDecision,
|
||||
} from "./conversation-binding.types.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";
|
||||
import type { PluginHookHandlerMap, PluginHookName } from "./hook-types.js";
|
||||
import type {
|
||||
PluginBundleFormat,
|
||||
PluginConfigUiHint,
|
||||
@@ -154,19 +128,6 @@ export type {
|
||||
} from "./tool-types.js";
|
||||
export type { AnyAgentTool } from "../agents/tools/common.js";
|
||||
export type { AgentHarness } from "../agents/harness/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 {
|
||||
PluginConversationBinding,
|
||||
PluginConversationBindingRequestParams,
|
||||
@@ -180,6 +141,7 @@ export type {
|
||||
PluginTextReplacement,
|
||||
PluginTextTransforms,
|
||||
} from "./cli-backend.types.js";
|
||||
export * from "./hook-types.js";
|
||||
|
||||
export type ProviderAuthOptionBag = {
|
||||
token?: string;
|
||||
@@ -197,15 +159,6 @@ export type PluginLogger = {
|
||||
};
|
||||
|
||||
export type { PluginKind } from "./plugin-kind.types.js";
|
||||
export type {
|
||||
PluginHookInboundClaimContext,
|
||||
PluginHookInboundClaimEvent,
|
||||
PluginHookMessageContext,
|
||||
PluginHookMessageReceivedEvent,
|
||||
PluginHookMessageSendingEvent,
|
||||
PluginHookMessageSendingResult,
|
||||
PluginHookMessageSentEvent,
|
||||
} from "./hook-message.types.js";
|
||||
export type {
|
||||
ProviderExternalAuthProfile,
|
||||
ProviderExternalOAuthProfile,
|
||||
@@ -2096,739 +2049,5 @@ export type OpenClawPluginApi = {
|
||||
) => void;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// Plugin Hooks
|
||||
// ============================================================================
|
||||
|
||||
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);
|
||||
|
||||
// Agent context shared across agent hooks
|
||||
export type PluginHookAgentContext = {
|
||||
/** Unique identifier for this agent run. */
|
||||
runId?: string;
|
||||
agentId?: string;
|
||||
sessionKey?: string;
|
||||
sessionId?: string;
|
||||
workspaceDir?: string;
|
||||
/** Resolved model provider for this run (for example "openai"). */
|
||||
modelProviderId?: string;
|
||||
/** Resolved model id for this run (for example "gpt-5.4"). */
|
||||
modelId?: string;
|
||||
messageProvider?: string;
|
||||
/** What initiated this agent run: "user", "heartbeat", "cron", or "memory". */
|
||||
trigger?: string;
|
||||
/** Channel identifier (e.g. "telegram", "discord", "whatsapp"). */
|
||||
channelId?: string;
|
||||
};
|
||||
|
||||
// before_agent_reply hook
|
||||
export type PluginHookBeforeAgentReplyEvent = {
|
||||
/** The final user message text heading to the LLM (after commands/directives). */
|
||||
cleanedBody: string;
|
||||
};
|
||||
|
||||
export type PluginHookBeforeAgentReplyResult = {
|
||||
/** Whether the plugin is claiming this message (short-circuits the LLM agent). */
|
||||
handled: boolean;
|
||||
/** Synthetic reply that short-circuits the LLM agent. */
|
||||
reply?: ReplyPayload;
|
||||
/** Reason for interception (for logging/debugging). */
|
||||
reason?: string;
|
||||
};
|
||||
|
||||
// llm_input hook
|
||||
export type PluginHookLlmInputEvent = {
|
||||
runId: string;
|
||||
sessionId: string;
|
||||
provider: string;
|
||||
model: string;
|
||||
systemPrompt?: string;
|
||||
prompt: string;
|
||||
historyMessages: unknown[];
|
||||
imagesCount: number;
|
||||
};
|
||||
|
||||
// llm_output hook
|
||||
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;
|
||||
};
|
||||
};
|
||||
|
||||
// agent_end hook
|
||||
export type PluginHookAgentEndEvent = {
|
||||
messages: unknown[];
|
||||
success: boolean;
|
||||
error?: string;
|
||||
durationMs?: number;
|
||||
};
|
||||
|
||||
// Compaction hooks
|
||||
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;
|
||||
};
|
||||
|
||||
// before_reset hook - fired when /new or /reset clears a session
|
||||
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 PluginHookInboundClaimResult = {
|
||||
handled: boolean;
|
||||
};
|
||||
|
||||
// before_dispatch hook
|
||||
export type PluginHookBeforeDispatchEvent = {
|
||||
/** Message text content. */
|
||||
content: string;
|
||||
/** Body text prepared for agent (after command parsing). */
|
||||
body?: string;
|
||||
/** Channel identifier (e.g. "telegram", "discord"). */
|
||||
channel?: string;
|
||||
/** Session key for this message. */
|
||||
sessionKey?: string;
|
||||
/** Sender identifier. */
|
||||
senderId?: string;
|
||||
/** Whether this is a group message. */
|
||||
isGroup?: boolean;
|
||||
/** Message timestamp. */
|
||||
timestamp?: number;
|
||||
};
|
||||
|
||||
export type PluginHookBeforeDispatchContext = {
|
||||
channelId?: string;
|
||||
accountId?: string;
|
||||
conversationId?: string;
|
||||
sessionKey?: string;
|
||||
senderId?: string;
|
||||
};
|
||||
|
||||
export type PluginHookBeforeDispatchResult = {
|
||||
/** Whether the plugin handled this message (skips default dispatch). */
|
||||
handled: boolean;
|
||||
/** Plugin-defined reply text (used when handled=true). */
|
||||
text?: string;
|
||||
};
|
||||
|
||||
// reply_dispatch hook
|
||||
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>;
|
||||
};
|
||||
|
||||
// Tool context
|
||||
export type PluginHookToolContext = {
|
||||
agentId?: string;
|
||||
sessionKey?: string;
|
||||
/** Ephemeral session UUID - regenerated on /new and /reset. */
|
||||
sessionId?: string;
|
||||
/** Stable run identifier for this agent invocation. */
|
||||
runId?: string;
|
||||
toolName: string;
|
||||
/** Provider-specific tool call ID when available. */
|
||||
toolCallId?: string;
|
||||
};
|
||||
|
||||
// before_tool_call hook
|
||||
export type PluginHookBeforeToolCallEvent = {
|
||||
toolName: string;
|
||||
params: Record<string, unknown>;
|
||||
/** Stable run identifier for this agent invocation. */
|
||||
runId?: string;
|
||||
/** Provider-specific tool call ID when available. */
|
||||
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";
|
||||
/** Set automatically by the hook runner - plugins should not set this. */
|
||||
pluginId?: string;
|
||||
/**
|
||||
* Best-effort callback invoked with the final outcome after approval resolves, times out, or is cancelled.
|
||||
* OpenClaw does not await this callback before allowing or denying the tool call.
|
||||
*/
|
||||
onResolution?: (decision: PluginApprovalResolution) => Promise<void> | void;
|
||||
};
|
||||
};
|
||||
|
||||
// after_tool_call hook
|
||||
export type PluginHookAfterToolCallEvent = {
|
||||
toolName: string;
|
||||
params: Record<string, unknown>;
|
||||
/** Stable run identifier for this agent invocation. */
|
||||
runId?: string;
|
||||
/** Provider-specific tool call ID when available. */
|
||||
toolCallId?: string;
|
||||
result?: unknown;
|
||||
error?: string;
|
||||
durationMs?: number;
|
||||
};
|
||||
|
||||
// tool_result_persist hook
|
||||
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;
|
||||
};
|
||||
|
||||
// before_message_write hook
|
||||
export type PluginHookBeforeMessageWriteEvent = {
|
||||
message: AgentMessage;
|
||||
sessionKey?: string;
|
||||
agentId?: string;
|
||||
};
|
||||
|
||||
export type PluginHookBeforeMessageWriteResult = {
|
||||
block?: boolean; // If true, message is NOT written to JSONL
|
||||
message?: AgentMessage; // Optional: modified message to write instead
|
||||
};
|
||||
|
||||
// Session context
|
||||
export type PluginHookSessionContext = {
|
||||
agentId?: string;
|
||||
sessionId: string;
|
||||
sessionKey?: string;
|
||||
};
|
||||
|
||||
// session_start hook
|
||||
export type PluginHookSessionStartEvent = {
|
||||
sessionId: string;
|
||||
sessionKey?: string;
|
||||
resumedFrom?: string;
|
||||
};
|
||||
|
||||
// session_end hook
|
||||
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;
|
||||
};
|
||||
|
||||
// Subagent context
|
||||
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;
|
||||
};
|
||||
|
||||
// subagent_spawning hook
|
||||
export type PluginHookSubagentSpawningEvent = PluginHookSubagentSpawnBase;
|
||||
|
||||
export type PluginHookSubagentSpawningResult =
|
||||
| {
|
||||
status: "ok";
|
||||
threadBindingReady?: boolean;
|
||||
}
|
||||
| {
|
||||
status: "error";
|
||||
error: string;
|
||||
};
|
||||
|
||||
// subagent_delivery_target hook
|
||||
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;
|
||||
};
|
||||
};
|
||||
|
||||
// subagent_spawned hook
|
||||
export type PluginHookSubagentSpawnedEvent = PluginHookSubagentSpawnBase & {
|
||||
runId: string;
|
||||
};
|
||||
|
||||
// subagent_ended hook
|
||||
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;
|
||||
};
|
||||
|
||||
// Gateway context
|
||||
export type PluginHookGatewayContext = {
|
||||
port?: number;
|
||||
};
|
||||
|
||||
// gateway_start hook
|
||||
export type PluginHookGatewayStartEvent = {
|
||||
port: number;
|
||||
};
|
||||
|
||||
// gateway_stop hook
|
||||
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 = {
|
||||
/** Original install entrypoint/provenance. */
|
||||
kind: PluginInstallRequestKind;
|
||||
/** Install mode requested by the caller. */
|
||||
mode: "install" | "update";
|
||||
/** Raw user-facing specifier or path when available. */
|
||||
requestedSpecifier?: string;
|
||||
};
|
||||
|
||||
export type PluginHookBeforeInstallBuiltinScan = {
|
||||
/** Whether the built-in scan completed successfully. */
|
||||
status: "ok" | "error";
|
||||
/** Number of files the built-in scanner actually inspected. */
|
||||
scannedFiles: number;
|
||||
critical: number;
|
||||
warn: number;
|
||||
info: number;
|
||||
findings: PluginInstallFinding[];
|
||||
/** Scanner failure reason when status=`error`. */
|
||||
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 = {
|
||||
/** Canonical plugin id OpenClaw will install under. */
|
||||
pluginId: string;
|
||||
/** Normalized installable content shape after source resolution. */
|
||||
contentType: "bundle" | "package" | "file";
|
||||
packageName?: string;
|
||||
manifestId?: string;
|
||||
version?: string;
|
||||
extensions?: string[];
|
||||
};
|
||||
|
||||
// before_install hook
|
||||
export type PluginHookBeforeInstallContext = {
|
||||
/** Category of install target being checked. */
|
||||
targetType: PluginInstallTargetType;
|
||||
/** Original install entrypoint/provenance. */
|
||||
requestKind: PluginInstallRequestKind;
|
||||
/** Normalized origin of the install target (e.g. "openclaw-bundled", "plugin-package"). */
|
||||
origin?: string;
|
||||
};
|
||||
|
||||
export type PluginHookBeforeInstallEvent = {
|
||||
/** Category of install target being checked. */
|
||||
targetType: PluginInstallTargetType;
|
||||
/** Human-readable skill or plugin name. */
|
||||
targetName: string;
|
||||
/** Absolute path to the install target content being scanned. */
|
||||
sourcePath: string;
|
||||
/** Whether the install target content is a file or directory. */
|
||||
sourcePathKind: PluginInstallSourcePathKind;
|
||||
/** Normalized origin of the install target (e.g. "openclaw-bundled", "plugin-package"). */
|
||||
origin?: string;
|
||||
/** Install request provenance and caller mode. */
|
||||
request: PluginHookBeforeInstallRequest;
|
||||
/** Structured result of the built-in scanner. */
|
||||
builtinScan: PluginHookBeforeInstallBuiltinScan;
|
||||
/** Present when targetType=`skill`. */
|
||||
skill?: PluginHookBeforeInstallSkill;
|
||||
/** Present when targetType=`plugin`. */
|
||||
plugin?: PluginHookBeforeInstallPlugin;
|
||||
};
|
||||
|
||||
export type PluginHookBeforeInstallResult = {
|
||||
/** Additional findings to merge with built-in scanner results. */
|
||||
findings?: PluginInstallFinding[];
|
||||
/** If true, block the installation entirely. */
|
||||
block?: boolean;
|
||||
/** Human-readable reason for blocking. */
|
||||
blockReason?: string;
|
||||
};
|
||||
|
||||
// Hook handler types mapped by hook name
|
||||
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;
|
||||
};
|
||||
// Plugin hook contracts now live in hook-types.ts so hook runners can import a
|
||||
// leaf contract surface instead of pulling the full plugin runtime barrel.
|
||||
|
||||
Reference in New Issue
Block a user