import type { AgentMessage } from "@mariozechner/pi-agent-core"; import type { SourceReplyDeliveryMode } from "../auto-reply/get-reply-options.types.js"; 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 type { DiagnosticTraceContext } from "../infra/diagnostic-trace-context.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 { PluginJsonValue } from "./host-hook-json.js"; import type { PluginAgentTurnPrepareEvent, PluginAgentTurnPrepareResult, PluginHeartbeatPromptContributionEvent, PluginHeartbeatPromptContributionResult } from "./host-hook-turn-types.js"; export type { PluginHookBeforeAgentStartEvent, PluginHookBeforeAgentStartOverrideResult, PluginHookBeforeAgentStartResult, PluginHookBeforeModelResolveAttachment, 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 { PluginAgentTurnPrepareEvent, PluginAgentTurnPrepareResult, PluginHeartbeatPromptContributionEvent, PluginHeartbeatPromptContributionResult, } from "./host-hook-turn-types.js"; export type { PluginHookInboundClaimContext, PluginHookInboundClaimEvent, PluginHookMessageContext, PluginHookMessageReceivedEvent, PluginHookMessageSendingEvent, PluginHookMessageSendingResult, PluginHookMessageSentEvent, } from "./hook-message.types.js"; export type PluginHookName = "before_model_resolve" | "agent_turn_prepare" | "before_prompt_build" | "before_agent_start" | "before_agent_reply" | "model_call_started" | "model_call_ended" | "llm_input" | "llm_output" | "before_agent_finalize" | "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" | "heartbeat_prompt_contribution" | "cron_changed" | "before_dispatch" | "reply_dispatch" | "before_install"; export declare const PLUGIN_HOOK_NAMES: readonly ["before_model_resolve", "agent_turn_prepare", "before_prompt_build", "before_agent_start", "before_agent_reply", "model_call_started", "model_call_ended", "llm_input", "llm_output", "before_agent_finalize", "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", "heartbeat_prompt_contribution", "cron_changed", "before_dispatch", "reply_dispatch", "before_install"]; export declare const isPluginHookName: (hookName: unknown) => hookName is PluginHookName; export declare const PROMPT_INJECTION_HOOK_NAMES: readonly ["agent_turn_prepare", "before_prompt_build", "before_agent_start", "heartbeat_prompt_contribution"]; export type PromptInjectionHookName = (typeof PROMPT_INJECTION_HOOK_NAMES)[number]; export declare const isPromptInjectionHookName: (hookName: PluginHookName) => boolean; export declare const CONVERSATION_HOOK_NAMES: readonly ["llm_input", "llm_output", "before_agent_finalize", "agent_end"]; export type ConversationHookName = (typeof CONVERSATION_HOOK_NAMES)[number]; export declare const isConversationHookName: (hookName: PluginHookName) => boolean; export type PluginHookAgentContext = { runId?: string; jobId?: string; trace?: DiagnosticTraceContext; 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 PluginHookModelCallBaseEvent = { runId: string; callId: string; sessionKey?: string; sessionId?: string; provider: string; model: string; api?: string; transport?: string; }; export type PluginHookModelCallStartedEvent = PluginHookModelCallBaseEvent; export type PluginHookModelCallEndedEvent = PluginHookModelCallBaseEvent & { durationMs: number; outcome: "completed" | "error"; errorCategory?: string; failureKind?: "aborted" | "connection_closed" | "connection_reset" | "terminated" | "timeout"; requestPayloadBytes?: number; responseStreamBytes?: number; timeToFirstByteMs?: number; upstreamRequestIdHash?: string; }; export type PluginHookLlmOutputEvent = { runId: string; sessionId: string; provider: string; model: string; /** * Fully resolved provider/model ref used for the call. * * This intentionally keeps the provider prefix so operator tooling can * distinguish e.g. openai-codex/gpt-5.4 from codex/gpt-5.4 even when display * names collapse to just the model id. */ resolvedRef?: string; /** * Harness/backend responsible for the model loop. Kept separate from * `resolvedRef` so provider/model consumers keep a stable parse contract. */ harnessId?: string; assistantTexts: string[]; lastAssistant?: unknown; usage?: { input?: number; output?: number; cacheRead?: number; cacheWrite?: number; total?: number; }; }; export type PluginHookAgentEndEvent = { runId?: string; messages: unknown[]; success: boolean; error?: string; durationMs?: number; }; export type PluginHookBeforeAgentFinalizeEvent = { runId?: string; sessionId: string; sessionKey?: string; turnId?: string; provider?: string; model?: string; cwd?: string; transcriptPath?: string; stopHookActive: boolean; lastAssistantMessage?: string; messages?: unknown[]; }; export type PluginHookBeforeAgentFinalizeResult = { /** * continue: accept normal finalization. * revise: block finalization and ask the harness for another model pass. * finalize: force finalization even if another hook requested revision. */ action?: "continue" | "revise" | "finalize"; reason?: string; retry?: { instruction: string; idempotencyKey?: string; maxAttempts?: 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; reply?: ReplyPayload; }; 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; images?: Array<{ data: string; mimeType: string; }>; inboundAudio: boolean; sessionTtsAuto?: TtsAutoMode; ttsChannel?: string; suppressUserDelivery?: boolean; suppressReplyLifecycle?: boolean; sourceReplyDeliveryMode?: SourceReplyDeliveryMode; 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; recordProcessed: (outcome: "completed" | "skipped" | "error", opts?: { reason?: string; error?: string; }) => void; markIdle: (reason: string) => void; }; export type PluginHookReplyDispatchResult = { handled: boolean; queuedFinal: boolean; counts: Record; }; export type PluginHookToolContext = { agentId?: string; sessionKey?: string; sessionId?: string; runId?: string; trace?: DiagnosticTraceContext; toolName: string; toolCallId?: string; getSessionExtension?: (namespace: string) => T | undefined; }; export type PluginHookBeforeToolCallEvent = { toolName: string; params: Record; runId?: string; toolCallId?: string; }; export declare const PluginApprovalResolutions: { readonly ALLOW_ONCE: "allow-once"; readonly ALLOW_ALWAYS: "allow-always"; readonly DENY: "deny"; readonly TIMEOUT: "timeout"; readonly CANCELLED: "cancelled"; }; export type PluginApprovalResolution = (typeof PluginApprovalResolutions)[keyof typeof PluginApprovalResolutions]; export type PluginHookBeforeToolCallResult = { params?: Record; 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; }; }; export type PluginHookAfterToolCallEvent = { toolName: string; params: Record; 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; deliveryOrigin?: { channel?: string; accountId?: string; to?: string; threadId?: string | number; }; } | { 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; config?: OpenClawConfig; workspaceDir?: string; getCron?: () => PluginHookGatewayCronService | undefined; }; export type PluginHookGatewayStartEvent = { port: number; }; export type PluginHookGatewayStopEvent = { reason?: string; }; export type PluginHookGatewayCronRunStatus = "ok" | "error" | "skipped"; export type PluginHookGatewayCronDeliveryStatus = "not-requested" | "delivered" | "not-delivered" | "unknown"; export type PluginHookGatewayCronJobState = { nextRunAtMs?: number; runningAtMs?: number; lastRunAtMs?: number; lastRunStatus?: PluginHookGatewayCronRunStatus; lastError?: string; lastDurationMs?: number; }; export type PluginHookGatewayCronJob = { id: string; name?: string; description?: string; enabled?: boolean; schedule?: { kind: "cron"; expr?: string; tz?: string; staggerMs?: number; } | { kind: "at"; at?: string; } | { kind: "every"; everyMs?: number; anchorMs?: number; }; sessionTarget?: string; wakeMode?: string; payload?: { kind?: string; text?: string; }; state?: PluginHookGatewayCronJobState; createdAtMs?: number; updatedAtMs?: number; }; export type PluginHookCronChangedEvent = { action: "added" | "updated" | "removed" | "started" | "finished"; jobId: string; job?: PluginHookGatewayCronJob; runAtMs?: number; durationMs?: number; status?: PluginHookGatewayCronRunStatus; error?: string; summary?: string; delivered?: boolean; deliveryStatus?: PluginHookGatewayCronDeliveryStatus; deliveryError?: string; sessionId?: string; sessionKey?: string; runId?: string; nextRunAtMs?: number; model?: string; provider?: string; }; export type PluginHookGatewayCronCreateInput = { name: string; description: string; enabled: boolean; schedule: { kind: string; expr: string; tz?: string; }; sessionTarget: string; wakeMode: string; payload: { kind: string; text?: string; }; }; export type PluginHookGatewayCronUpdateInput = Partial; export type PluginHookGatewayCronRemoveResult = { removed?: boolean; }; export type PluginHookGatewayCronService = { list: (opts?: { includeDisabled?: boolean; }) => Promise; add: (input: PluginHookGatewayCronCreateInput) => Promise; update: (id: string, patch: PluginHookGatewayCronUpdateInput) => Promise; remove: (id: string) => Promise; }; export type PluginInstallTargetType = "skill" | "plugin"; export type PluginInstallRequestKind = "skill-install" | "plugin-dir" | "plugin-archive" | "plugin-file" | "plugin-npm" | "plugin-git"; 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 = { agent_turn_prepare: (event: PluginAgentTurnPrepareEvent, ctx: PluginHookAgentContext) => Promise | PluginAgentTurnPrepareResult | void; before_model_resolve: (event: PluginHookBeforeModelResolveEvent, ctx: PluginHookAgentContext) => Promise | PluginHookBeforeModelResolveResult | void; before_prompt_build: (event: PluginHookBeforePromptBuildEvent, ctx: PluginHookAgentContext) => Promise | PluginHookBeforePromptBuildResult | void; /** @deprecated Use before_model_resolve and before_prompt_build. */ before_agent_start: (event: PluginHookBeforeAgentStartEvent, ctx: PluginHookAgentContext) => Promise | PluginHookBeforeAgentStartResult | void; before_agent_reply: (event: PluginHookBeforeAgentReplyEvent, ctx: PluginHookAgentContext) => Promise | PluginHookBeforeAgentReplyResult | void; model_call_started: (event: PluginHookModelCallStartedEvent, ctx: PluginHookAgentContext) => Promise | void; model_call_ended: (event: PluginHookModelCallEndedEvent, ctx: PluginHookAgentContext) => Promise | void; llm_input: (event: PluginHookLlmInputEvent, ctx: PluginHookAgentContext) => Promise | void; llm_output: (event: PluginHookLlmOutputEvent, ctx: PluginHookAgentContext) => Promise | void; before_agent_finalize: (event: PluginHookBeforeAgentFinalizeEvent, ctx: PluginHookAgentContext) => Promise | PluginHookBeforeAgentFinalizeResult | void; agent_end: (event: PluginHookAgentEndEvent, ctx: PluginHookAgentContext) => Promise | void; before_compaction: (event: PluginHookBeforeCompactionEvent, ctx: PluginHookAgentContext) => Promise | void; after_compaction: (event: PluginHookAfterCompactionEvent, ctx: PluginHookAgentContext) => Promise | void; before_reset: (event: PluginHookBeforeResetEvent, ctx: PluginHookAgentContext) => Promise | void; inbound_claim: (event: PluginHookInboundClaimEvent, ctx: PluginHookInboundClaimContext) => Promise | PluginHookInboundClaimResult | void; before_dispatch: (event: PluginHookBeforeDispatchEvent, ctx: PluginHookBeforeDispatchContext) => Promise | PluginHookBeforeDispatchResult | void; reply_dispatch: (event: PluginHookReplyDispatchEvent, ctx: PluginHookReplyDispatchContext) => Promise | PluginHookReplyDispatchResult | void; message_received: (event: PluginHookMessageReceivedEvent, ctx: PluginHookMessageContext) => Promise | void; message_sending: (event: PluginHookMessageSendingEvent, ctx: PluginHookMessageContext) => Promise | PluginHookMessageSendingResult | void; message_sent: (event: PluginHookMessageSentEvent, ctx: PluginHookMessageContext) => Promise | void; before_tool_call: (event: PluginHookBeforeToolCallEvent, ctx: PluginHookToolContext) => Promise | PluginHookBeforeToolCallResult | void; after_tool_call: (event: PluginHookAfterToolCallEvent, ctx: PluginHookToolContext) => Promise | 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; session_end: (event: PluginHookSessionEndEvent, ctx: PluginHookSessionContext) => Promise | void; subagent_spawning: (event: PluginHookSubagentSpawningEvent, ctx: PluginHookSubagentContext) => Promise | PluginHookSubagentSpawningResult | void; subagent_delivery_target: (event: PluginHookSubagentDeliveryTargetEvent, ctx: PluginHookSubagentContext) => Promise | PluginHookSubagentDeliveryTargetResult | void; subagent_spawned: (event: PluginHookSubagentSpawnedEvent, ctx: PluginHookSubagentContext) => Promise | void; subagent_ended: (event: PluginHookSubagentEndedEvent, ctx: PluginHookSubagentContext) => Promise | void; gateway_start: (event: PluginHookGatewayStartEvent, ctx: PluginHookGatewayContext) => Promise | void; gateway_stop: (event: PluginHookGatewayStopEvent, ctx: PluginHookGatewayContext) => Promise | void; heartbeat_prompt_contribution: (event: PluginHeartbeatPromptContributionEvent, ctx: PluginHookAgentContext) => Promise | PluginHeartbeatPromptContributionResult | void; cron_changed: (event: PluginHookCronChangedEvent, ctx: PluginHookGatewayContext) => Promise | void; before_install: (event: PluginHookBeforeInstallEvent, ctx: PluginHookBeforeInstallContext) => Promise | PluginHookBeforeInstallResult | void; }; export type PluginHookRegistration = { pluginId: string; hookName: K; handler: PluginHookHandlerMap[K]; priority?: number; timeoutMs?: number; source: string; };