import type { IncomingMessage, ServerResponse } from "node:http";
import type { AgentMessage } from "@mariozechner/pi-agent-core";
import type { StreamFn } from "@mariozechner/pi-agent-core";
import type { Api, Model } from "@mariozechner/pi-ai";
import type { ModelRegistry } from "@mariozechner/pi-coding-agent";
import type { Command } from "commander";
import type { ApiKeyCredential, AuthProfileCredential, OAuthCredential, AuthProfileStore } from "../agents/auth-profiles/types.js";
import type { ModelCatalogEntry } from "../agents/model-catalog.js";
import type { ProviderCapabilities } from "../agents/provider-capabilities.js";
import type { AnyAgentTool } from "../agents/tools/common.js";
import type { ThinkLevel } from "../auto-reply/thinking.js";
import type { ReplyPayload } from "../auto-reply/types.js";
import type { ChannelId, ChannelPlugin, ChannelStructuredComponents } from "../channels/plugins/types.js";
import type { OpenClawConfig } from "../config/config.js";
import type { ModelProviderConfig } from "../config/types.js";
import type { GatewayRequestHandler } from "../gateway/server-methods/types.js";
import type { InternalHookHandler } from "../hooks/internal-hooks.js";
import type { HookEntry } from "../hooks/types.js";
import type { ImageGenerationProvider } from "../image-generation/types.js";
import type { ProviderUsageSnapshot } from "../infra/provider-usage.types.js";
import type { MediaUnderstandingProvider } from "../media-understanding/types.js";
import type { RuntimeEnv } from "../runtime.js";
import type { RuntimeWebSearchMetadata } from "../secrets/runtime-web-tools.types.js";
import type { SpeechProviderConfiguredContext, SpeechListVoicesRequest, SpeechProviderId, SpeechSynthesisRequest, SpeechSynthesisResult, SpeechTelephonySynthesisRequest, SpeechTelephonySynthesisResult, SpeechVoiceOption } from "../tts/provider-types.js";
import type { WizardPrompter } from "../wizard/prompts.js";
import type { SecretInputMode } from "./provider-auth-types.js";
import type { createVpsAwareOAuthHandlers } from "./provider-oauth-flow.js";
import type { PluginRuntime } from "./runtime/types.js";
export type { PluginRuntime } from "./runtime/types.js";
export type { AnyAgentTool } from "../agents/tools/common.js";
export type ProviderAuthOptionBag = {
    token?: string;
    tokenProvider?: string;
    secretInputMode?: SecretInputMode;
    [key: string]: unknown;
};
/** Logger passed into plugin registration, services, and CLI surfaces. */
export type PluginLogger = {
    debug?: (message: string) => void;
    info: (message: string) => void;
    warn: (message: string) => void;
    error: (message: string) => void;
};
export type PluginConfigUiHint = {
    label?: string;
    help?: string;
    tags?: string[];
    advanced?: boolean;
    sensitive?: boolean;
    placeholder?: string;
};
export type PluginKind = "memory" | "context-engine";
export type PluginConfigValidation = {
    ok: true;
    value?: unknown;
} | {
    ok: false;
    errors: string[];
};
/**
 * Config schema contract accepted by plugin manifests and runtime registration.
 *
 * Plugins can provide a Zod-like parser, a lightweight `validate(...)`
 * function, or both. `uiHints` and `jsonSchema` are optional extras for docs,
 * forms, and config UIs.
 */
export type OpenClawPluginConfigSchema = {
    safeParse?: (value: unknown) => {
        success: boolean;
        data?: unknown;
        error?: {
            issues?: Array<{
                path: Array<string | number>;
                message: string;
            }>;
        };
    };
    parse?: (value: unknown) => unknown;
    validate?: (value: unknown) => PluginConfigValidation;
    uiHints?: Record<string, PluginConfigUiHint>;
    jsonSchema?: Record<string, unknown>;
};
/** Trusted execution context passed to plugin-owned agent tool factories. */
export type OpenClawPluginToolContext = {
    config?: OpenClawConfig;
    workspaceDir?: string;
    agentDir?: string;
    agentId?: string;
    sessionKey?: string;
    /** Ephemeral session UUID — regenerated on /new and /reset. Use for per-conversation isolation. */
    sessionId?: string;
    messageChannel?: string;
    agentAccountId?: string;
    /** Trusted sender id from inbound context (runtime-provided, not tool args). */
    requesterSenderId?: string;
    /** Whether the trusted sender is an owner. */
    senderIsOwner?: boolean;
    sandboxed?: boolean;
};
export type OpenClawPluginToolFactory = (ctx: OpenClawPluginToolContext) => AnyAgentTool | AnyAgentTool[] | null | undefined;
export type OpenClawPluginToolOptions = {
    name?: string;
    names?: string[];
    optional?: boolean;
};
export type OpenClawPluginHookOptions = {
    entry?: HookEntry;
    name?: string;
    description?: string;
    register?: boolean;
};
export type ProviderAuthKind = "oauth" | "api_key" | "token" | "device_code" | "custom";
/** Standard result payload returned by provider auth methods. */
export type ProviderAuthResult = {
    profiles: Array<{
        profileId: string;
        credential: AuthProfileCredential;
    }>;
    /**
     * Optional config patch to merge after credentials are written.
     *
     * Use this for provider-owned onboarding defaults such as
     * `models.providers.<id>` entries, default aliases, or agent model helpers.
     * The caller still persists auth-profile bindings separately.
     */
    configPatch?: Partial<OpenClawConfig>;
    defaultModel?: string;
    notes?: string[];
};
/** Interactive auth context passed to provider login/setup methods. */
export type ProviderAuthContext = {
    config: OpenClawConfig;
    agentDir?: string;
    workspaceDir?: string;
    prompter: WizardPrompter;
    runtime: RuntimeEnv;
    /**
     * Optional onboarding CLI options that triggered this auth flow.
     *
     * Present for setup/configure/auth-choice flows so provider methods can
     * honor preseeded flags like `--openai-api-key` or generic
     * `--token/--token-provider` pairs. Direct `models auth login` usually
     * leaves this undefined.
     */
    opts?: ProviderAuthOptionBag;
    /**
     * Onboarding secret persistence preference.
     *
     * Interactive wizard flows set this when the caller explicitly requested
     * plaintext or env/file/exec ref storage. Ad-hoc `models auth login` flows
     * usually leave it undefined.
     */
    secretInputMode?: SecretInputMode;
    /**
     * Whether the provider auth flow should offer the onboarding secret-storage
     * mode picker when `secretInputMode` is unset.
     *
     * This is true for onboarding/configure flows and false for direct
     * `models auth` commands, which should keep a tighter, provider-owned prompt
     * surface.
     */
    allowSecretRefPrompt?: boolean;
    isRemote: boolean;
    openUrl: (url: string) => Promise<void>;
    oauth: {
        createVpsAwareHandlers: typeof createVpsAwareOAuthHandlers;
    };
};
export type ProviderNonInteractiveApiKeyResult = {
    key: string;
    source: "profile" | "env" | "flag";
    envVarName?: string;
};
export type ProviderResolveNonInteractiveApiKeyParams = {
    provider: string;
    flagValue?: string;
    flagName: `--${string}`;
    envVar: string;
    envVarName?: string;
    allowProfile?: boolean;
    required?: boolean;
};
export type ProviderNonInteractiveApiKeyCredentialParams = {
    provider: string;
    resolved: ProviderNonInteractiveApiKeyResult;
    email?: string;
    metadata?: Record<string, string>;
};
export type ProviderAuthMethodNonInteractiveContext = {
    authChoice: string;
    config: OpenClawConfig;
    baseConfig: OpenClawConfig;
    opts: ProviderAuthOptionBag;
    runtime: RuntimeEnv;
    agentDir?: string;
    workspaceDir?: string;
    resolveApiKey: (params: ProviderResolveNonInteractiveApiKeyParams) => Promise<ProviderNonInteractiveApiKeyResult | null>;
    toApiKeyCredential: (params: ProviderNonInteractiveApiKeyCredentialParams) => ApiKeyCredential | null;
};
export type ProviderAuthMethod = {
    id: string;
    label: string;
    hint?: string;
    kind: ProviderAuthKind;
    /**
     * Optional wizard/onboarding metadata for this specific auth method.
     *
     * Use this when one provider exposes multiple setup entries (for example API
     * key + OAuth, or region-specific login flows). OpenClaw uses this to expose
     * method-specific auth choices while keeping the provider id stable.
     */
    wizard?: ProviderPluginWizardSetup;
    run: (ctx: ProviderAuthContext) => Promise<ProviderAuthResult>;
    runNonInteractive?: (ctx: ProviderAuthMethodNonInteractiveContext) => Promise<OpenClawConfig | null>;
};
export type ProviderCatalogOrder = "simple" | "profile" | "paired" | "late";
export type ProviderCatalogContext = {
    config: OpenClawConfig;
    agentDir?: string;
    workspaceDir?: string;
    env: NodeJS.ProcessEnv;
    resolveProviderApiKey: (providerId?: string) => {
        apiKey: string | undefined;
        discoveryApiKey?: string;
    };
    resolveProviderAuth: (providerId?: string, options?: {
        oauthMarker?: string;
    }) => {
        apiKey: string | undefined;
        discoveryApiKey?: string;
        mode: "api_key" | "oauth" | "token" | "none";
        source: "env" | "profile" | "none";
        profileId?: string;
    };
};
export type ProviderCatalogResult = {
    provider: ModelProviderConfig;
} | {
    providers: Record<string, ModelProviderConfig>;
} | null | undefined;
export type ProviderPluginCatalog = {
    order?: ProviderCatalogOrder;
    run: (ctx: ProviderCatalogContext) => Promise<ProviderCatalogResult>;
};
/**
 * Fully-resolved runtime model shape used by the embedded runner.
 *
 * Catalog hooks publish config-time `models.providers` entries.
 * Runtime hooks below operate on the final `pi-ai` model object after
 * discovery/override merging, just before inference runs.
 */
export type ProviderRuntimeModel = Model<Api>;
export type ProviderRuntimeProviderConfig = {
    baseUrl?: string;
    api?: ModelProviderConfig["api"];
    models?: ModelProviderConfig["models"];
    headers?: unknown;
};
/**
 * Sync hook for provider-owned model ids that are not present in the local
 * registry/catalog yet.
 *
 * Use this for pass-through providers or provider-specific forward-compat
 * behavior. The hook should be cheap and side-effect free; async refreshes
 * belong in `prepareDynamicModel`.
 */
export type ProviderResolveDynamicModelContext = {
    config?: OpenClawConfig;
    agentDir?: string;
    workspaceDir?: string;
    provider: string;
    modelId: string;
    modelRegistry: ModelRegistry;
    providerConfig?: ProviderRuntimeProviderConfig;
};
/**
 * Optional async warm-up for dynamic model resolution.
 *
 * Called only from async model resolution paths, before retrying
 * `resolveDynamicModel`. This is the place to refresh caches or fetch provider
 * metadata over the network.
 */
export type ProviderPrepareDynamicModelContext = ProviderResolveDynamicModelContext;
/**
 * Last-chance rewrite hook for provider-owned transport normalization.
 *
 * Runs after OpenClaw resolves an explicit/discovered/dynamic model and before
 * the embedded runner uses it. Typical uses: swap API ids, fix base URLs, or
 * patch provider-specific compat bits.
 */
export type ProviderNormalizeResolvedModelContext = {
    config?: OpenClawConfig;
    agentDir?: string;
    workspaceDir?: string;
    provider: string;
    modelId: string;
    model: ProviderRuntimeModel;
};
/**
 * Runtime auth input for providers that need an extra exchange step before
 * inference. The incoming `apiKey` is the raw credential resolved from auth
 * profiles/env/config. The returned value should be the actual token/key to use
 * for the request.
 */
export type ProviderPrepareRuntimeAuthContext = {
    config?: OpenClawConfig;
    agentDir?: string;
    workspaceDir?: string;
    env: NodeJS.ProcessEnv;
    provider: string;
    modelId: string;
    model: ProviderRuntimeModel;
    apiKey: string;
    authMode: string;
    profileId?: string;
};
/**
 * Result of `prepareRuntimeAuth`.
 *
 * `apiKey` is required and becomes the runtime credential stored in auth
 * storage. `baseUrl` is optional and lets providers like GitHub Copilot swap to
 * an entitlement-specific endpoint at request time. `expiresAt` enables generic
 * background refresh in long-running turns.
 */
export type ProviderPreparedRuntimeAuth = {
    apiKey: string;
    baseUrl?: string;
    expiresAt?: number;
};
/**
 * Usage/billing auth input for providers that expose quota/usage endpoints.
 *
 * This hook is intentionally separate from `prepareRuntimeAuth`: usage
 * snapshots often need a different credential source than live inference
 * requests, and they run outside the embedded runner.
 *
 * The helper methods cover the common OpenClaw auth resolution paths:
 *
 * - `resolveApiKeyFromConfigAndStore`: env/config/plain token/api_key profiles
 * - `resolveOAuthToken`: oauth/token profiles resolved through the auth store
 *
 * Plugins can still do extra provider-specific work on top (for example parse a
 * token blob, read a legacy credential file, or pick between aliases).
 */
export type ProviderResolveUsageAuthContext = {
    config: OpenClawConfig;
    agentDir?: string;
    workspaceDir?: string;
    env: NodeJS.ProcessEnv;
    provider: string;
    resolveApiKeyFromConfigAndStore: (params?: {
        providerIds?: string[];
        envDirect?: Array<string | undefined>;
    }) => string | undefined;
    resolveOAuthToken: () => Promise<ProviderResolvedUsageAuth | null>;
};
/**
 * Result of `resolveUsageAuth`.
 *
 * `token` is the credential used for provider usage/billing endpoints.
 * `accountId` is optional provider-specific metadata used by some usage APIs.
 */
export type ProviderResolvedUsageAuth = {
    token: string;
    accountId?: string;
};
/**
 * Usage/quota snapshot input for providers that own their usage endpoint
 * fetch/parsing behavior.
 *
 * This hook runs after `resolveUsageAuth` succeeds. Core still owns summary
 * fan-out, timeout wrapping, filtering, and formatting; the provider plugin
 * owns the provider-specific HTTP request + response normalization.
 */
export type ProviderFetchUsageSnapshotContext = {
    config: OpenClawConfig;
    agentDir?: string;
    workspaceDir?: string;
    env: NodeJS.ProcessEnv;
    provider: string;
    token: string;
    accountId?: string;
    timeoutMs: number;
    fetchFn: typeof fetch;
};
/**
 * Provider-owned auth-doctor hint input.
 *
 * Called when OAuth refresh fails and OpenClaw wants a provider-specific repair
 * hint to append to the generic re-auth message. Use this for legacy profile-id
 * migrations or other provider-owned auth-store cleanup guidance.
 */
export type ProviderAuthDoctorHintContext = {
    config?: OpenClawConfig;
    store: AuthProfileStore;
    provider: string;
    profileId?: string;
};
/**
 * Provider-owned extra-param normalization before OpenClaw builds its generic
 * stream option wrapper.
 *
 * Use this to set provider defaults or rewrite provider-specific config keys
 * into the merged `extraParams` object. Return the full next extraParams object.
 */
export type ProviderPrepareExtraParamsContext = {
    config?: OpenClawConfig;
    agentDir?: string;
    workspaceDir?: string;
    provider: string;
    modelId: string;
    extraParams?: Record<string, unknown>;
    thinkingLevel?: ThinkLevel;
};
/**
 * Provider-owned stream wrapper hook after OpenClaw applies its generic
 * transport-independent wrappers.
 *
 * Use this for provider-specific payload/header/model mutations that still run
 * through the normal `pi-ai` stream path.
 */
export type ProviderWrapStreamFnContext = ProviderPrepareExtraParamsContext & {
    streamFn?: StreamFn;
};
/**
 * Provider-owned prompt-cache eligibility.
 *
 * Return `true` or `false` to override OpenClaw's built-in provider cache TTL
 * detection for this provider. Return `undefined` to fall back to core rules.
 */
export type ProviderCacheTtlEligibilityContext = {
    provider: string;
    modelId: string;
};
/**
 * Provider-owned missing-auth message override.
 *
 * Runs only after OpenClaw exhausts normal env/profile/config auth resolution
 * for the requested provider. Return a custom message to replace the generic
 * "No API key found" error.
 */
export type ProviderBuildMissingAuthMessageContext = {
    config?: OpenClawConfig;
    agentDir?: string;
    workspaceDir?: string;
    env: NodeJS.ProcessEnv;
    provider: string;
    listProfileIds: (providerId: string) => string[];
};
/**
 * Built-in model suppression hook.
 *
 * Use this when a provider/plugin needs to hide stale upstream catalog rows or
 * replace them with a vendor-specific hint. This hook is consulted by model
 * resolution, model listing, and catalog loading.
 */
export type ProviderBuiltInModelSuppressionContext = {
    config?: OpenClawConfig;
    agentDir?: string;
    workspaceDir?: string;
    env: NodeJS.ProcessEnv;
    provider: string;
    modelId: string;
};
export type ProviderBuiltInModelSuppressionResult = {
    suppress: boolean;
    errorMessage?: string;
};
/**
 * Provider-owned thinking policy input.
 *
 * Used by shared `/think`, ACP controls, and directive parsing to ask a
 * provider whether a model supports special reasoning UX such as xhigh or a
 * binary on/off toggle.
 */
export type ProviderThinkingPolicyContext = {
    provider: string;
    modelId: string;
};
/**
 * Provider-owned default thinking policy input.
 *
 * `reasoning` is the merged catalog hint for the selected model when one is
 * available. Providers can use it to keep "reasoning model => low" behavior
 * without re-reading the catalog themselves.
 */
export type ProviderDefaultThinkingPolicyContext = ProviderThinkingPolicyContext & {
    reasoning?: boolean;
};
/**
 * Provider-owned "modern model" policy input.
 *
 * Live smoke/model-profile selection uses this to keep provider-specific
 * inclusion/exclusion rules out of core.
 */
export type ProviderModernModelPolicyContext = {
    provider: string;
    modelId: string;
};
/**
 * Final catalog augmentation hook.
 *
 * Runs after OpenClaw loads the discovered model catalog and merges configured
 * opt-in providers. Use this for forward-compat rows or vendor-owned synthetic
 * entries that should appear in `models list` and model pickers even when the
 * upstream registry has not caught up yet.
 */
export type ProviderAugmentModelCatalogContext = {
    config?: OpenClawConfig;
    agentDir?: string;
    workspaceDir?: string;
    env: NodeJS.ProcessEnv;
    entries: ModelCatalogEntry[];
};
/**
 * @deprecated Use ProviderCatalogOrder.
 */
export type ProviderDiscoveryOrder = ProviderCatalogOrder;
/**
 * @deprecated Use ProviderCatalogContext.
 */
export type ProviderDiscoveryContext = ProviderCatalogContext;
/**
 * @deprecated Use ProviderCatalogResult.
 */
export type ProviderDiscoveryResult = ProviderCatalogResult;
/**
 * @deprecated Use ProviderPluginCatalog.
 */
export type ProviderPluginDiscovery = ProviderPluginCatalog;
export type ProviderPluginWizardSetup = {
    choiceId?: string;
    choiceLabel?: string;
    choiceHint?: string;
    groupId?: string;
    groupLabel?: string;
    groupHint?: string;
    methodId?: string;
    /**
     * Interactive onboarding surfaces where this auth choice should appear.
     * Defaults to `["text-inference"]` when omitted.
     */
    onboardingScopes?: Array<"text-inference" | "image-generation">;
    /**
     * Optional model-allowlist prompt policy applied after this auth choice is
     * selected in configure/onboarding flows.
     *
     * Keep this UI-facing and static. Provider logic that needs runtime state
     * should stay in `run`/`runNonInteractive`.
     */
    modelAllowlist?: {
        allowedKeys?: string[];
        initialSelections?: string[];
        message?: string;
    };
};
/** Optional model-picker metadata shown in interactive provider selection flows. */
export type ProviderPluginWizardModelPicker = {
    label?: string;
    hint?: string;
    methodId?: string;
};
/** UI metadata that lets provider plugins appear in onboarding and configure flows. */
export type ProviderPluginWizard = {
    setup?: ProviderPluginWizardSetup;
    modelPicker?: ProviderPluginWizardModelPicker;
};
export type ProviderModelSelectedContext = {
    config: OpenClawConfig;
    model: string;
    prompter: WizardPrompter;
    agentDir?: string;
    workspaceDir?: string;
};
/** Text-inference provider capability registered by a plugin. */
export type ProviderPlugin = {
    id: string;
    pluginId?: string;
    label: string;
    docsPath?: string;
    aliases?: string[];
    /**
     * Provider-related env vars shown in setup/search/help surfaces.
     *
     * Keep entries in preferred display order. This can include direct auth env
     * vars or setup inputs such as OAuth client id/secret vars.
     */
    envVars?: string[];
    auth: ProviderAuthMethod[];
    /**
     * Preferred hook for plugin-defined provider catalogs.
     * Returns provider config/model definitions that merge into models.providers.
     */
    catalog?: ProviderPluginCatalog;
    /**
     * Legacy alias for catalog.
     * Kept for compatibility with existing provider plugins.
     */
    discovery?: ProviderPluginDiscovery;
    /**
     * Sync runtime fallback for model ids not present in the local catalog.
     *
     * Hook order:
     * 1. discovered/static model lookup
     * 2. plugin `resolveDynamicModel`
     * 3. core fallback heuristics
     * 4. generic provider-config fallback
     *
     * Keep this hook cheap and deterministic. If you need network I/O first, use
     * `prepareDynamicModel` to prime state for the async retry path.
     */
    resolveDynamicModel?: (ctx: ProviderResolveDynamicModelContext) => ProviderRuntimeModel | null | undefined;
    /**
     * Optional async prefetch for dynamic model resolution.
     *
     * OpenClaw calls this only from async model resolution paths. After it
     * completes, `resolveDynamicModel` is called again.
     */
    prepareDynamicModel?: (ctx: ProviderPrepareDynamicModelContext) => Promise<void>;
    /**
     * Provider-owned transport normalization.
     *
     * Use this to rewrite a resolved model without forking the generic runner:
     * swap API ids, update base URLs, or adjust compat flags for a provider's
     * transport quirks.
     */
    normalizeResolvedModel?: (ctx: ProviderNormalizeResolvedModelContext) => ProviderRuntimeModel | null | undefined;
    /**
     * Static provider capability overrides consumed by shared transcript/tooling
     * logic.
     *
     * Use this when the provider behaves like OpenAI/Anthropic, needs transcript
     * sanitization quirks, or requires provider-family hints.
     */
    capabilities?: Partial<ProviderCapabilities>;
    /**
     * Provider-owned extra-param normalization before generic stream option
     * wrapping.
     *
     * Typical uses: set provider-default `transport`, map provider-specific
     * config aliases, or inject extra request metadata sourced from
     * `agents.defaults.models.<provider>/<model>.params`.
     */
    prepareExtraParams?: (ctx: ProviderPrepareExtraParamsContext) => Record<string, unknown> | null | undefined;
    /**
     * Provider-owned stream wrapper applied after generic OpenClaw wrappers.
     *
     * Typical uses: provider attribution headers, request-body rewrites, or
     * provider-specific compat payload patches that do not justify a separate
     * transport implementation.
     */
    wrapStreamFn?: (ctx: ProviderWrapStreamFnContext) => StreamFn | null | undefined;
    /**
     * Runtime auth exchange hook.
     *
     * Called after OpenClaw resolves the raw configured credential but before the
     * runner stores it in runtime auth storage. This lets plugins exchange a
     * source credential (for example a GitHub token) into a short-lived runtime
     * token plus optional base URL override.
     */
    prepareRuntimeAuth?: (ctx: ProviderPrepareRuntimeAuthContext) => Promise<ProviderPreparedRuntimeAuth | null | undefined>;
    /**
     * Usage/billing auth resolution hook.
     *
     * Called by provider-usage surfaces (`/usage`, status snapshots, reporting).
     * Use this when a provider's usage endpoint needs provider-owned token
     * extraction, blob parsing, or alias handling.
     */
    resolveUsageAuth?: (ctx: ProviderResolveUsageAuthContext) => Promise<ProviderResolvedUsageAuth | null | undefined> | ProviderResolvedUsageAuth | null | undefined;
    /**
     * Usage/quota snapshot fetch hook.
     *
     * Called after `resolveUsageAuth` by `/usage` and related reporting surfaces.
     * Use this when the provider's usage endpoint or payload shape is
     * provider-specific and you want that logic to live with the provider plugin
     * instead of the core switchboard.
     */
    fetchUsageSnapshot?: (ctx: ProviderFetchUsageSnapshotContext) => Promise<ProviderUsageSnapshot | null | undefined> | ProviderUsageSnapshot | null | undefined;
    /**
     * Provider-owned cache TTL eligibility.
     *
     * Use this when a proxy provider supports Anthropic-style prompt caching for
     * only a subset of upstream models.
     */
    isCacheTtlEligible?: (ctx: ProviderCacheTtlEligibilityContext) => boolean | undefined;
    /**
     * Provider-owned missing-auth message override.
     *
     * Return a custom message when the provider wants a more specific recovery
     * hint than OpenClaw's generic auth-store guidance.
     */
    buildMissingAuthMessage?: (ctx: ProviderBuildMissingAuthMessageContext) => string | null | undefined;
    /**
     * Provider-owned built-in model suppression.
     *
     * Return `{ suppress: true }` to hide a stale upstream row. Include
     * `errorMessage` when OpenClaw should surface a provider-specific hint for
     * direct model resolution failures.
     */
    suppressBuiltInModel?: (ctx: ProviderBuiltInModelSuppressionContext) => ProviderBuiltInModelSuppressionResult | null | undefined;
    /**
     * Provider-owned final catalog augmentation.
     *
     * Return extra rows to append to the final catalog after discovery/config
     * merging. OpenClaw deduplicates by `provider/id`, so plugins only need to
     * describe the desired supplemental rows.
     */
    augmentModelCatalog?: (ctx: ProviderAugmentModelCatalogContext) => Array<ModelCatalogEntry> | ReadonlyArray<ModelCatalogEntry> | Promise<Array<ModelCatalogEntry> | ReadonlyArray<ModelCatalogEntry> | null | undefined> | null | undefined;
    /**
     * Provider-owned binary thinking toggle.
     *
     * Return true when the provider exposes a coarse on/off reasoning control
     * instead of the normal multi-level ladder shown by `/think`.
     */
    isBinaryThinking?: (ctx: ProviderThinkingPolicyContext) => boolean | undefined;
    /**
     * Provider-owned xhigh reasoning support.
     *
     * Return true only for models that should expose the `xhigh` thinking level.
     */
    supportsXHighThinking?: (ctx: ProviderThinkingPolicyContext) => boolean | undefined;
    /**
     * Provider-owned default thinking level.
     *
     * Use this to keep model-family defaults (for example Claude 4.6 =>
     * adaptive) out of core command logic.
     */
    resolveDefaultThinkingLevel?: (ctx: ProviderDefaultThinkingPolicyContext) => "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "adaptive" | null | undefined;
    /**
     * Provider-owned "modern model" matcher used by live profile/smoke filters.
     *
     * Return true when the given provider/model ref should be treated as a
     * preferred modern model candidate.
     */
    isModernModelRef?: (ctx: ProviderModernModelPolicyContext) => boolean | undefined;
    wizard?: ProviderPluginWizard;
    /**
     * Provider-owned auth-profile API-key formatter.
     *
     * OpenClaw uses this when a stored auth profile is already valid and needs to
     * be converted into the runtime `apiKey` string expected by the provider. Use
     * this for providers whose auth profile stores extra metadata alongside the
     * bearer token (for example Gemini CLI's `{ token, projectId }` payload).
     */
    formatApiKey?: (cred: AuthProfileCredential) => string;
    /**
     * Legacy auth-profile ids that should be retired by `openclaw doctor`.
     *
     * Use this when a provider plugin replaces an older core-managed profile id
     * and wants cleanup/migration messaging to live with the provider instead of
     * in hardcoded doctor tables.
     */
    deprecatedProfileIds?: string[];
    /**
     * Provider-owned OAuth refresh.
     *
     * OpenClaw calls this before falling back to the shared `pi-ai` OAuth
     * refreshers. Use it when the provider has a custom refresh endpoint, or when
     * the provider needs custom refresh-failure behavior that should stay out of
     * core auth-profile code.
     */
    refreshOAuth?: (cred: OAuthCredential) => Promise<OAuthCredential>;
    /**
     * Provider-owned auth-doctor hint.
     *
     * Return a multiline repair hint when OAuth refresh fails and the provider
     * wants to steer users toward a specific auth-profile migration or recovery
     * path. Return nothing to keep OpenClaw's generic error text.
     */
    buildAuthDoctorHint?: (ctx: ProviderAuthDoctorHintContext) => string | Promise<string | null | undefined> | null | undefined;
    onModelSelected?: (ctx: ProviderModelSelectedContext) => Promise<void>;
};
export type WebSearchProviderId = string;
export type WebSearchProviderToolDefinition = {
    description: string;
    parameters: Record<string, unknown>;
    execute: (args: Record<string, unknown>) => Promise<Record<string, unknown>>;
};
export type WebSearchProviderContext = {
    config?: OpenClawConfig;
    searchConfig?: Record<string, unknown>;
    runtimeMetadata?: RuntimeWebSearchMetadata;
};
export type WebSearchCredentialResolutionSource = "config" | "secretRef" | "env" | "missing";
export type WebSearchRuntimeMetadataContext = {
    config?: OpenClawConfig;
    searchConfig?: Record<string, unknown>;
    runtimeMetadata?: RuntimeWebSearchMetadata;
    resolvedCredential?: {
        value?: string;
        source: WebSearchCredentialResolutionSource;
        fallbackEnvVar?: string;
    };
};
export type WebSearchProviderPlugin = {
    id: WebSearchProviderId;
    label: string;
    hint: string;
    requiresCredential?: boolean;
    credentialLabel?: string;
    envVars: string[];
    placeholder: string;
    signupUrl: string;
    docsUrl?: string;
    autoDetectOrder?: number;
    credentialPath: string;
    inactiveSecretPaths?: string[];
    getCredentialValue: (searchConfig?: Record<string, unknown>) => unknown;
    setCredentialValue: (searchConfigTarget: Record<string, unknown>, value: unknown) => void;
    getConfiguredCredentialValue?: (config?: OpenClawConfig) => unknown;
    setConfiguredCredentialValue?: (configTarget: OpenClawConfig, value: unknown) => void;
    applySelectionConfig?: (config: OpenClawConfig) => OpenClawConfig;
    resolveRuntimeMetadata?: (ctx: WebSearchRuntimeMetadataContext) => Partial<RuntimeWebSearchMetadata> | Promise<Partial<RuntimeWebSearchMetadata>>;
    createTool: (ctx: WebSearchProviderContext) => WebSearchProviderToolDefinition | null;
};
export type PluginWebSearchProviderEntry = WebSearchProviderPlugin & {
    pluginId: string;
};
/** Speech capability registered by a plugin. */
export type SpeechProviderPlugin = {
    id: SpeechProviderId;
    label: string;
    aliases?: string[];
    models?: readonly string[];
    voices?: readonly string[];
    isConfigured: (ctx: SpeechProviderConfiguredContext) => boolean;
    synthesize: (req: SpeechSynthesisRequest) => Promise<SpeechSynthesisResult>;
    synthesizeTelephony?: (req: SpeechTelephonySynthesisRequest) => Promise<SpeechTelephonySynthesisResult>;
    listVoices?: (req: SpeechListVoicesRequest) => Promise<SpeechVoiceOption[]>;
};
export type PluginSpeechProviderEntry = SpeechProviderPlugin & {
    pluginId: string;
};
export type MediaUnderstandingProviderPlugin = MediaUnderstandingProvider;
export type ImageGenerationProviderPlugin = ImageGenerationProvider;
export type OpenClawPluginGatewayMethod = {
    method: string;
    handler: GatewayRequestHandler;
};
/**
 * Context passed to plugin command handlers.
 */
export type PluginCommandContext = {
    /** The sender's identifier (e.g., Telegram user ID) */
    senderId?: string;
    /** The channel/surface (e.g., "telegram", "discord") */
    channel: string;
    /** Provider channel id (e.g., "telegram") */
    channelId?: ChannelId;
    /** Whether the sender is on the allowlist */
    isAuthorizedSender: boolean;
    /** Gateway client scopes for internal control-plane callers */
    gatewayClientScopes?: string[];
    /** Raw command arguments after the command name */
    args?: string;
    /** The full normalized command body */
    commandBody: string;
    /** Current OpenClaw configuration */
    config: OpenClawConfig;
    /** Raw "From" value (channel-scoped id) */
    from?: string;
    /** Raw "To" value (channel-scoped id) */
    to?: string;
    /** Account id for multi-account channels */
    accountId?: string;
    /** Thread/topic id if available */
    messageThreadId?: string | number;
    requestConversationBinding: (params?: PluginConversationBindingRequestParams) => Promise<PluginConversationBindingRequestResult>;
    detachConversationBinding: () => Promise<{
        removed: boolean;
    }>;
    getCurrentConversationBinding: () => Promise<PluginConversationBinding | null>;
};
export type PluginConversationBindingRequestParams = {
    summary?: string;
    detachHint?: string;
};
export type PluginConversationBindingResolutionDecision = "allow-once" | "allow-always" | "deny";
export type PluginConversationBinding = {
    bindingId: string;
    pluginId: string;
    pluginName?: string;
    pluginRoot: string;
    channel: string;
    accountId: string;
    conversationId: string;
    parentConversationId?: string;
    threadId?: string | number;
    boundAt: number;
    summary?: string;
    detachHint?: string;
};
export type PluginConversationBindingRequestResult = {
    status: "bound";
    binding: PluginConversationBinding;
} | {
    status: "pending";
    approvalId: string;
    reply: ReplyPayload;
} | {
    status: "error";
    message: string;
};
export type PluginConversationBindingResolvedEvent = {
    status: "approved" | "denied";
    binding?: PluginConversationBinding;
    decision: PluginConversationBindingResolutionDecision;
    request: {
        summary?: string;
        detachHint?: string;
        requestedBySenderId?: string;
        conversation: {
            channel: string;
            accountId: string;
            conversationId: string;
            parentConversationId?: string;
            threadId?: string | number;
        };
    };
};
/**
 * Result returned by a plugin command handler.
 */
export type PluginCommandResult = ReplyPayload;
/**
 * Handler function for plugin commands.
 */
export type PluginCommandHandler = (ctx: PluginCommandContext) => PluginCommandResult | Promise<PluginCommandResult>;
/**
 * Definition for a plugin-registered command.
 */
export type OpenClawPluginCommandDefinition = {
    /** Command name without leading slash (e.g., "tts") */
    name: string;
    /**
     * Optional native-command aliases for slash/menu surfaces.
     * `default` applies to all native providers unless a provider-specific
     * override exists (for example `{ default: "talkvoice", discord: "voice2" }`).
     */
    nativeNames?: Partial<Record<string, string>> & {
        default?: string;
    };
    /** Description shown in /help and command menus */
    description: string;
    /** Whether this command accepts arguments */
    acceptsArgs?: boolean;
    /** Whether only authorized senders can use this command (default: true) */
    requireAuth?: boolean;
    /** The handler function */
    handler: PluginCommandHandler;
};
export type PluginInteractiveChannel = "telegram" | "discord" | "slack";
export type PluginInteractiveButtons = Array<Array<{
    text: string;
    callback_data: string;
    style?: "danger" | "success" | "primary";
}>>;
export type PluginInteractiveTelegramHandlerResult = {
    handled?: boolean;
} | void;
export type PluginInteractiveTelegramHandlerContext = {
    channel: "telegram";
    accountId: string;
    callbackId: string;
    conversationId: string;
    parentConversationId?: string;
    senderId?: string;
    senderUsername?: string;
    threadId?: number;
    isGroup: boolean;
    isForum: boolean;
    auth: {
        isAuthorizedSender: boolean;
    };
    callback: {
        data: string;
        namespace: string;
        payload: string;
        messageId: number;
        chatId: string;
        messageText?: string;
    };
    respond: {
        reply: (params: {
            text: string;
            buttons?: PluginInteractiveButtons;
        }) => Promise<void>;
        editMessage: (params: {
            text: string;
            buttons?: PluginInteractiveButtons;
        }) => Promise<void>;
        editButtons: (params: {
            buttons: PluginInteractiveButtons;
        }) => Promise<void>;
        clearButtons: () => Promise<void>;
        deleteMessage: () => Promise<void>;
    };
    requestConversationBinding: (params?: PluginConversationBindingRequestParams) => Promise<PluginConversationBindingRequestResult>;
    detachConversationBinding: () => Promise<{
        removed: boolean;
    }>;
    getCurrentConversationBinding: () => Promise<PluginConversationBinding | null>;
};
export type PluginInteractiveDiscordHandlerResult = {
    handled?: boolean;
} | void;
export type PluginInteractiveDiscordHandlerContext = {
    channel: "discord";
    accountId: string;
    interactionId: string;
    conversationId: string;
    parentConversationId?: string;
    guildId?: string;
    senderId?: string;
    senderUsername?: string;
    auth: {
        isAuthorizedSender: boolean;
    };
    interaction: {
        kind: "button" | "select" | "modal";
        data: string;
        namespace: string;
        payload: string;
        messageId?: string;
        values?: string[];
        fields?: Array<{
            id: string;
            name: string;
            values: string[];
        }>;
    };
    respond: {
        acknowledge: () => Promise<void>;
        reply: (params: {
            text: string;
            ephemeral?: boolean;
        }) => Promise<void>;
        followUp: (params: {
            text: string;
            ephemeral?: boolean;
        }) => Promise<void>;
        editMessage: (params: {
            text?: string;
            components?: ChannelStructuredComponents;
        }) => Promise<void>;
        clearComponents: (params?: {
            text?: string;
        }) => Promise<void>;
    };
    requestConversationBinding: (params?: PluginConversationBindingRequestParams) => Promise<PluginConversationBindingRequestResult>;
    detachConversationBinding: () => Promise<{
        removed: boolean;
    }>;
    getCurrentConversationBinding: () => Promise<PluginConversationBinding | null>;
};
export type PluginInteractiveSlackHandlerResult = {
    handled?: boolean;
} | void;
export type PluginInteractiveSlackHandlerContext = {
    channel: "slack";
    accountId: string;
    interactionId: string;
    conversationId: string;
    parentConversationId?: string;
    senderId?: string;
    senderUsername?: string;
    threadId?: string;
    auth: {
        isAuthorizedSender: boolean;
    };
    interaction: {
        kind: "button" | "select";
        data: string;
        namespace: string;
        payload: string;
        actionId: string;
        blockId?: string;
        messageTs?: string;
        threadTs?: string;
        value?: string;
        selectedValues?: string[];
        selectedLabels?: string[];
        triggerId?: string;
        responseUrl?: string;
    };
    respond: {
        acknowledge: () => Promise<void>;
        reply: (params: {
            text: string;
            responseType?: "ephemeral" | "in_channel";
        }) => Promise<void>;
        followUp: (params: {
            text: string;
            responseType?: "ephemeral" | "in_channel";
        }) => Promise<void>;
        editMessage: (params: {
            text?: string;
            blocks?: unknown[];
        }) => Promise<void>;
    };
    requestConversationBinding: (params?: PluginConversationBindingRequestParams) => Promise<PluginConversationBindingRequestResult>;
    detachConversationBinding: () => Promise<{
        removed: boolean;
    }>;
    getCurrentConversationBinding: () => Promise<PluginConversationBinding | null>;
};
export type PluginInteractiveTelegramHandlerRegistration = {
    channel: "telegram";
    namespace: string;
    handler: (ctx: PluginInteractiveTelegramHandlerContext) => Promise<PluginInteractiveTelegramHandlerResult> | PluginInteractiveTelegramHandlerResult;
};
export type PluginInteractiveDiscordHandlerRegistration = {
    channel: "discord";
    namespace: string;
    handler: (ctx: PluginInteractiveDiscordHandlerContext) => Promise<PluginInteractiveDiscordHandlerResult> | PluginInteractiveDiscordHandlerResult;
};
export type PluginInteractiveSlackHandlerRegistration = {
    channel: "slack";
    namespace: string;
    handler: (ctx: PluginInteractiveSlackHandlerContext) => Promise<PluginInteractiveSlackHandlerResult> | PluginInteractiveSlackHandlerResult;
};
export type PluginInteractiveHandlerRegistration = PluginInteractiveTelegramHandlerRegistration | PluginInteractiveDiscordHandlerRegistration | PluginInteractiveSlackHandlerRegistration;
export type OpenClawPluginHttpRouteAuth = "gateway" | "plugin";
export type OpenClawPluginHttpRouteMatch = "exact" | "prefix";
export type OpenClawPluginHttpRouteHandler = (req: IncomingMessage, res: ServerResponse) => Promise<boolean | void> | boolean | void;
export type OpenClawPluginHttpRouteParams = {
    path: string;
    handler: OpenClawPluginHttpRouteHandler;
    auth: OpenClawPluginHttpRouteAuth;
    match?: OpenClawPluginHttpRouteMatch;
    replaceExisting?: boolean;
};
export type OpenClawPluginCliContext = {
    program: Command;
    config: OpenClawConfig;
    workspaceDir?: string;
    logger: PluginLogger;
};
export type OpenClawPluginCliRegistrar = (ctx: OpenClawPluginCliContext) => void | Promise<void>;
/** Context passed to long-lived plugin services. */
export type OpenClawPluginServiceContext = {
    config: OpenClawConfig;
    workspaceDir?: string;
    stateDir: string;
    logger: PluginLogger;
};
/** Background service registered by a plugin during `register(api)`. */
export type OpenClawPluginService = {
    id: string;
    start: (ctx: OpenClawPluginServiceContext) => void | Promise<void>;
    stop?: (ctx: OpenClawPluginServiceContext) => void | Promise<void>;
};
export type OpenClawPluginChannelRegistration = {
    plugin: ChannelPlugin;
};
/** Module-level plugin definition loaded from a native plugin entry file. */
export type OpenClawPluginDefinition = {
    id?: string;
    name?: string;
    description?: string;
    version?: string;
    kind?: PluginKind;
    configSchema?: OpenClawPluginConfigSchema;
    register?: (api: OpenClawPluginApi) => void | Promise<void>;
    activate?: (api: OpenClawPluginApi) => void | Promise<void>;
};
export type OpenClawPluginModule = OpenClawPluginDefinition | ((api: OpenClawPluginApi) => void | Promise<void>);
export type PluginRegistrationMode = "full" | "setup-only" | "setup-runtime";
/** Main registration API injected into native plugin entry files. */
export type OpenClawPluginApi = {
    id: string;
    name: string;
    version?: string;
    description?: string;
    source: string;
    rootDir?: string;
    registrationMode: PluginRegistrationMode;
    config: OpenClawConfig;
    pluginConfig?: Record<string, unknown>;
    /**
     * In-process runtime helpers for trusted native plugins.
     *
     * This surface is broader than hooks. Prefer hooks for third-party
     * automation/integration unless you need native registry integration.
     */
    runtime: PluginRuntime;
    logger: PluginLogger;
    registerTool: (tool: AnyAgentTool | OpenClawPluginToolFactory, opts?: OpenClawPluginToolOptions) => void;
    registerHook: (events: string | string[], handler: InternalHookHandler, opts?: OpenClawPluginHookOptions) => void;
    registerHttpRoute: (params: OpenClawPluginHttpRouteParams) => void;
    /** Register a native messaging channel plugin (channel capability). */
    registerChannel: (registration: OpenClawPluginChannelRegistration | ChannelPlugin) => void;
    registerGatewayMethod: (method: string, handler: GatewayRequestHandler) => void;
    registerCli: (registrar: OpenClawPluginCliRegistrar, opts?: {
        commands?: string[];
    }) => void;
    registerService: (service: OpenClawPluginService) => void;
    /** Register a native model/provider plugin (text inference capability). */
    registerProvider: (provider: ProviderPlugin) => void;
    /** Register a speech synthesis provider (speech capability). */
    registerSpeechProvider: (provider: SpeechProviderPlugin) => void;
    /** Register a media understanding provider (media understanding capability). */
    registerMediaUnderstandingProvider: (provider: MediaUnderstandingProviderPlugin) => void;
    /** Register an image generation provider (image generation capability). */
    registerImageGenerationProvider: (provider: ImageGenerationProviderPlugin) => void;
    /** Register a web search provider (web search capability). */
    registerWebSearchProvider: (provider: WebSearchProviderPlugin) => void;
    registerInteractiveHandler: (registration: PluginInteractiveHandlerRegistration) => void;
    onConversationBindingResolved: (handler: (event: PluginConversationBindingResolvedEvent) => void | Promise<void>) => void;
    /**
     * Register a custom command that bypasses the LLM agent.
     * Plugin commands are processed before built-in commands and before agent invocation.
     * Use this for simple state-toggling or status commands that don't need AI reasoning.
     */
    registerCommand: (command: OpenClawPluginCommandDefinition) => void;
    /** Register a context engine implementation (exclusive slot — only one active at a time). */
    registerContextEngine: (id: string, factory: import("../context-engine/registry.js").ContextEngineFactory) => void;
    /** Register the system prompt section builder for this memory plugin (exclusive slot). */
    registerMemoryPromptSection: (builder: import("../memory/prompt-section.js").MemoryPromptSectionBuilder) => void;
    resolvePath: (input: string) => string;
    /** Register a lifecycle hook handler */
    on: <K extends PluginHookName>(hookName: K, handler: PluginHookHandlerMap[K], opts?: {
        priority?: number;
    }) => void;
};
export type PluginOrigin = "bundled" | "global" | "workspace" | "config";
export type PluginFormat = "openclaw" | "bundle";
export type PluginBundleFormat = "codex" | "claude" | "cursor";
export type PluginDiagnostic = {
    level: "warn" | "error";
    message: string;
    pluginId?: string;
    source?: string;
};
export type PluginHookName = "before_model_resolve" | "before_prompt_build" | "before_agent_start" | "llm_input" | "llm_output" | "agent_end" | "before_compaction" | "after_compaction" | "before_reset" | "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";
export declare const PLUGIN_HOOK_NAMES: readonly ["before_model_resolve", "before_prompt_build", "before_agent_start", "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"];
export declare const isPluginHookName: (hookName: unknown) => hookName is PluginHookName;
export declare const PROMPT_INJECTION_HOOK_NAMES: readonly ["before_prompt_build", "before_agent_start"];
export type PromptInjectionHookName = (typeof PROMPT_INJECTION_HOOK_NAMES)[number];
export declare const isPromptInjectionHookName: (hookName: PluginHookName) => boolean;
export type PluginHookAgentContext = {
    agentId?: string;
    sessionKey?: string;
    sessionId?: string;
    workspaceDir?: string;
    messageProvider?: string;
    /** What initiated this agent run: "user", "heartbeat", "cron", or "memory". */
    trigger?: string;
    /** Channel identifier (e.g. "telegram", "discord", "whatsapp"). */
    channelId?: string;
};
export type PluginHookBeforeModelResolveEvent = {
    /** User prompt for this run. No session messages are available yet in this phase. */
    prompt: string;
};
export type PluginHookBeforeModelResolveResult = {
    /** Override the model for this agent run. E.g. "llama3.3:8b" */
    modelOverride?: string;
    /** Override the provider for this agent run. E.g. "ollama" */
    providerOverride?: string;
};
export type PluginHookBeforePromptBuildEvent = {
    prompt: string;
    /** Session messages prepared for this run. */
    messages: unknown[];
};
export type PluginHookBeforePromptBuildResult = {
    systemPrompt?: string;
    prependContext?: string;
    /**
     * Prepended to the agent system prompt so providers can cache it (e.g. prompt caching).
     * Use for static plugin guidance instead of prependContext to avoid per-turn token cost.
     */
    prependSystemContext?: string;
    /**
     * Appended to the agent system prompt so providers can cache it (e.g. prompt caching).
     * Use for static plugin guidance instead of prependContext to avoid per-turn token cost.
     */
    appendSystemContext?: string;
};
export declare const PLUGIN_PROMPT_MUTATION_RESULT_FIELDS: readonly ["systemPrompt", "prependContext", "prependSystemContext", "appendSystemContext"];
export type PluginHookBeforeAgentStartEvent = {
    prompt: string;
    /** Optional because legacy hook can run in pre-session phase. */
    messages?: unknown[];
};
export type PluginHookBeforeAgentStartResult = PluginHookBeforePromptBuildResult & PluginHookBeforeModelResolveResult;
export type PluginHookBeforeAgentStartOverrideResult = Omit<PluginHookBeforeAgentStartResult, keyof PluginHookBeforePromptBuildResult>;
export declare const stripPromptMutationFieldsFromLegacyHookResult: (result: PluginHookBeforeAgentStartResult | void) => PluginHookBeforeAgentStartOverrideResult | void;
export type PluginHookLlmInputEvent = {
    runId: string;
    sessionId: string;
    provider: string;
    model: string;
    systemPrompt?: string;
    prompt: string;
    historyMessages: unknown[];
    imagesCount: number;
};
export type PluginHookLlmOutputEvent = {
    runId: string;
    sessionId: string;
    provider: string;
    model: string;
    assistantTexts: string[];
    lastAssistant?: unknown;
    usage?: {
        input?: number;
        output?: number;
        cacheRead?: number;
        cacheWrite?: number;
        total?: number;
    };
};
export type PluginHookAgentEndEvent = {
    messages: unknown[];
    success: boolean;
    error?: string;
    durationMs?: number;
};
export type PluginHookBeforeCompactionEvent = {
    /** Total messages in the session before any truncation or compaction */
    messageCount: number;
    /** Messages being fed to the compaction LLM (after history-limit truncation) */
    compactingCount?: number;
    tokenCount?: number;
    messages?: unknown[];
    /** Path to the session JSONL transcript. All messages are already on disk
     *  before compaction starts, so plugins can read this file asynchronously
     *  and process in parallel with the compaction LLM call. */
    sessionFile?: string;
};
export type PluginHookBeforeResetEvent = {
    sessionFile?: string;
    messages?: unknown[];
    reason?: string;
};
export type PluginHookAfterCompactionEvent = {
    messageCount: number;
    tokenCount?: number;
    compactedCount: number;
    /** Path to the session JSONL transcript. All pre-compaction messages are
     *  preserved on disk, so plugins can read and process them asynchronously
     *  without blocking the compaction pipeline. */
    sessionFile?: string;
};
export type PluginHookMessageContext = {
    channelId: string;
    accountId?: string;
    conversationId?: string;
};
export type PluginHookInboundClaimContext = PluginHookMessageContext & {
    parentConversationId?: string;
    senderId?: string;
    messageId?: string;
};
export type PluginHookInboundClaimEvent = {
    content: string;
    body?: string;
    bodyForAgent?: string;
    transcript?: string;
    timestamp?: number;
    channel: string;
    accountId?: string;
    conversationId?: string;
    parentConversationId?: string;
    senderId?: string;
    senderName?: string;
    senderUsername?: string;
    threadId?: string | number;
    messageId?: string;
    isGroup: boolean;
    commandAuthorized?: boolean;
    wasMentioned?: boolean;
    metadata?: Record<string, unknown>;
};
export type PluginHookInboundClaimResult = {
    handled: boolean;
};
export type PluginHookMessageReceivedEvent = {
    from: string;
    content: string;
    timestamp?: number;
    metadata?: Record<string, unknown>;
};
export type PluginHookMessageSendingEvent = {
    to: string;
    content: string;
    metadata?: Record<string, unknown>;
};
export type PluginHookMessageSendingResult = {
    content?: string;
    cancel?: boolean;
};
export type PluginHookMessageSentEvent = {
    to: string;
    content: string;
    success: boolean;
    error?: string;
};
export type PluginHookToolContext = {
    agentId?: string;
    sessionKey?: string;
    /** 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;
};
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 type PluginHookBeforeToolCallResult = {
    params?: Record<string, unknown>;
    block?: boolean;
    blockReason?: string;
};
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;
};
export type PluginHookToolResultPersistContext = {
    agentId?: string;
    sessionKey?: string;
    toolName?: string;
    toolCallId?: string;
};
export type PluginHookToolResultPersistEvent = {
    toolName?: string;
    toolCallId?: string;
    /**
     * The toolResult message about to be written to the session transcript.
     * Handlers may return a modified message (e.g. drop non-essential fields).
     */
    message: AgentMessage;
    /** True when the tool result was synthesized by a guard/repair step. */
    isSynthetic?: boolean;
};
export type PluginHookToolResultPersistResult = {
    message?: AgentMessage;
};
export type PluginHookBeforeMessageWriteEvent = {
    message: AgentMessage;
    sessionKey?: string;
    agentId?: string;
};
export type PluginHookBeforeMessageWriteResult = {
    block?: boolean;
    message?: AgentMessage;
};
export type PluginHookSessionContext = {
    agentId?: string;
    sessionId: string;
    sessionKey?: string;
};
export type PluginHookSessionStartEvent = {
    sessionId: string;
    sessionKey?: string;
    resumedFrom?: string;
};
export type PluginHookSessionEndEvent = {
    sessionId: string;
    sessionKey?: string;
    messageCount: number;
    durationMs?: number;
};
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 PluginHookHandlerMap = {
    before_model_resolve: (event: PluginHookBeforeModelResolveEvent, ctx: PluginHookAgentContext) => Promise<PluginHookBeforeModelResolveResult | void> | PluginHookBeforeModelResolveResult | void;
    before_prompt_build: (event: PluginHookBeforePromptBuildEvent, ctx: PluginHookAgentContext) => Promise<PluginHookBeforePromptBuildResult | void> | PluginHookBeforePromptBuildResult | void;
    before_agent_start: (event: PluginHookBeforeAgentStartEvent, ctx: PluginHookAgentContext) => Promise<PluginHookBeforeAgentStartResult | void> | PluginHookBeforeAgentStartResult | void;
    llm_input: (event: PluginHookLlmInputEvent, ctx: PluginHookAgentContext) => Promise<void> | void;
    llm_output: (event: PluginHookLlmOutputEvent, ctx: PluginHookAgentContext) => Promise<void> | void;
    agent_end: (event: PluginHookAgentEndEvent, ctx: PluginHookAgentContext) => Promise<void> | void;
    before_compaction: (event: PluginHookBeforeCompactionEvent, ctx: PluginHookAgentContext) => Promise<void> | void;
    after_compaction: (event: PluginHookAfterCompactionEvent, ctx: PluginHookAgentContext) => Promise<void> | void;
    before_reset: (event: PluginHookBeforeResetEvent, ctx: PluginHookAgentContext) => Promise<void> | void;
    inbound_claim: (event: PluginHookInboundClaimEvent, ctx: PluginHookInboundClaimContext) => Promise<PluginHookInboundClaimResult | void> | PluginHookInboundClaimResult | 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;
};
export type PluginHookRegistration<K extends PluginHookName = PluginHookName> = {
    pluginId: string;
    hookName: K;
    handler: PluginHookHandlerMap[K];
    priority?: number;
    source: string;
};
