import { z } from 'zod'; import type { RequestContextChange, RequestContextSnapshot } from '../../runtime/query/request-context.js'; import type { PluginId, SessionId, TurnId } from '../ids/index.js'; import type { Message, ToolResultContent } from '../message/index.js'; import type { CancelCause } from '../session/cancel-cause.js'; import type { ToolResult } from '../tool/index.js'; export type PluginScope = 'project' | 'user'; export declare function assertPluginScope(scope: PluginScope): void; export type PluginStatus = 'installed' | 'enabled' | 'disabled' | 'error'; export declare function assertPluginStatus(status: PluginStatus): void; export type PluginContributionType = 'tool' | 'skill' | 'hook' | 'mcp_server' | 'connector' | 'persona'; export declare function assertPluginContributionType(type: PluginContributionType): void; export type PluginHookEvent = /** * The operator's prompt, before the model sees it. Carries `prompt`. * The one event that can BLOCK a turn (`skip`) and the one that can add * to what the model is told (`annotate`). */ 'user_prompt_submit' /** A host's session opened or closed. Carries `sessionId` and no `turnId`: these hooks run outside any turn. */ | 'session_start' | 'session_end' /** A compaction pass is about to run / has run. Carries `compaction`. */ | 'pre_compact' | 'post_compact' /** A child session's turn ended. Fired after its own `turn_end`; carries `parentSessionId` and `parentTurnId`. */ | 'subagent_stop' | 'turn_start' | 'turn_end' | 'turn_interrupt' | 'pre_tool_use' | 'post_tool_use' | 'pre_llm_call' | 'post_llm_call' | 'iteration_start' | 'iteration_end'; /** * Hook event names that were renamed when turns replaced runs. A config that * still names one is refused, and the refusal names the replacement. */ export declare const RENAMED_PLUGIN_HOOK_EVENTS: Readonly>; export declare function assertPluginHookEvent(event: PluginHookEvent): void; /** * What an extension is shown about a model call it is about to make. * * A projection, not the live request object. The wire params carry driver * concerns an extension has no business depending on, and handing them * over would make every future field of that type part of the plugin * contract by accident. */ export interface PluginModelRequest { /** SDK provider-input content inventory; excludes adapter/server-private transformations. */ readonly context?: { readonly snapshot: RequestContextSnapshot; /** Compared with the preceding pre_llm_call in this turn; absent on the first. */ readonly change?: RequestContextChange; }; readonly model: string; readonly messages: readonly Message[]; /** Names only. A hook auditing tool exposure needs the set, not the schemas. */ readonly toolNames: readonly string[]; readonly temperature?: number; readonly maxTokens?: number; } /** What an extension is shown about the model's reply. */ export interface PluginModelResponse { readonly content: string | null; readonly toolNames: readonly string[]; readonly finishReason: string; readonly usage: { readonly promptTokens: number; readonly completionTokens: number; readonly totalTokens: number; }; } /** What a compaction hook is shown about the pass. */ export interface PluginCompactionInfo { /** `threshold`: the estimate crossed the line. `overflow`: the provider rejected the prompt. */ readonly reason: 'threshold' | 'overflow'; readonly tokensBefore: number; /** Present on `post_compact`. */ readonly tokensAfter?: number; readonly contextWindowTokens: number; } export interface PluginHookContext { /** The turn the hook fired in. Absent on `session_start` and `session_end`. */ readonly turnId?: TurnId; readonly pluginId: PluginId; readonly event: PluginHookEvent; /** The host's session. Always present. */ readonly sessionId: SessionId; /** The session that delegated, on `subagent_stop`. */ readonly parentSessionId?: SessionId; /** The turn whose tool call spawned the child, on `subagent_stop`. */ readonly parentTurnId?: TurnId; /** The operator's prompt, on `user_prompt_submit`. */ readonly prompt?: string; /** The pass, on `pre_compact` and `post_compact`. */ readonly compaction?: PluginCompactionInfo; readonly toolName?: string; readonly toolInput?: unknown; readonly toolResult?: ToolResult; readonly iteration?: number; /** * Why the turn was stopped, on `turn_interrupt`. * * That hook is emitted only for a root session's turn carrying the explicit `user` * cause. Keeping the field typed as the complete cause vocabulary lets a * host narrow normally and leaves room for a future, deliberate expansion * without overloading `event` or an error sentence. */ readonly cancelCause?: CancelCause; /** * The request about to be sent, on `pre_llm_call`. * * Both model-call hooks fired directly beside this data and were handed * none of it — only a turn id and an iteration number — so an extension * could observe THAT a call was happening and nothing about what it * was. A redaction pass, a prompt audit, a per-tenant token ledger: all * of them needed the one thing the hook did not carry. * * Read-only. Frozen before fan-out for the same reason probe events * are: a hook that mutated the live request would change what every * later hook sees, and the last one registered would silently win. * Shaping the request stays the job of the single-slot host callback * that owns it, where one writer is the contract rather than an * accident of registration order. * * The freeze is one level deep, as elsewhere: each message is a frozen * copy, so writing to one is inert and cannot reach the turn's history, * but a nested array inside a message is still the turn's own. */ readonly request?: Readonly; /** * What came back, on `post_llm_call`. Read-only, same reasoning. */ readonly response?: Readonly; /** * Aborts when this hook's run is cancelled or its deadline expires. * `turn_interrupt` is the exception: the turn is already cancelled, so its * handler receives a fresh signal that represents only the bounded cleanup * deadline. The original verdict is available as `cancelCause`. * * The runtime stops waiting on a slow hook either way, but in-process * JavaScript cannot be forcibly stopped. Without a signal the hook itself * never learns it was abandoned: an HTTP request inside it keeps a socket * open and its eventual side effects can happen after the turn moved on. A * hook doing I/O must forward this signal and stop publishing when it * aborts. */ readonly signal?: AbortSignal; } export type PluginHookResult = { action: 'continue'; } /** * Add to what the model is told, without changing anything else. * * The answer a `user_prompt_submit` hook gives when it has context the * model should have — the branch, the time, a ticket — and no opinion * on the prompt. Accepted on that event only: a tool hook that returns * it is rejected, because a tool result is not a place for context. */ | { action: 'annotate'; text: string; } | { action: 'skip'; reason: string; } | { action: 'modify'; input: unknown; } | { action: 'error'; message: string; } | { action: 'retry'; } /** * Replace what the model sees, WITHOUT reporting the call as failed. * * The substitution seam already existed and was typed as a failure channel: * the only way a `post_tool_use` hook could change the output was * `action: 'error'`, which prefixes `Error: ` and sets the error flag. So * redacting a credential out of a successful result was delivered to the * model as a tool failure, and the model routed around a call that had * worked — retrying it, or reporting to the user that it had failed. * * That is the difference this variant exists for. `error` says the call went * wrong; this says the call went right and the model may not see all of it. * * `modify` is not this. It carries `input` and belongs to the pre-call * hooks, which is why `post_tool_use` rejects it — a result is not an input, * and reusing the variant would have made one action mean two things * depending on where it was returned. * * Rich content blocks SURVIVE a replace unless `content` is given, because * the common case is redacting text from a result whose image or resource * is unaffected. A hook that needs to drop them passes `content: []`, and a * hook redacting a secret that also appears in an image must — this variant * cannot inspect what it is preserving. */ | { action: 'replace'; output: string; content?: ToolResultContent; }; export declare function assertPluginHookResult(result: PluginHookResult): asserts result; export interface PluginHookDefinition { readonly event: PluginHookEvent; readonly handler: (context: PluginHookContext) => Promise; /** * Lower runs first. Default 100. * * Order was install order, which is neither declared nor stable — it * depends on when each plugin happened to be installed. That is fine * for hooks that only observe, and wrong for the ones that decide: * `executeHooks` SHORT-CIRCUITS on `skip` and `error`, so a hook that * denies a dangerous command only gets to deny it if it runs before * whatever else stops the chain. A guard that fires depending on * installation history is not a guard. * * Ties keep registration order, so plugins that never set a priority * behave exactly as before. Convention: guards below 100, observers * above. */ readonly priority?: number; } export interface PluginMCPServerConfig { readonly name: string; readonly command: string; readonly args?: readonly string[]; readonly env?: Readonly>; } export interface PluginManifest { readonly name: string; readonly version: string; readonly description: string; readonly author?: string; readonly tools?: readonly string[]; readonly skills?: readonly string[]; readonly hooks?: readonly string[]; readonly mcpServers?: readonly PluginMCPServerConfig[]; readonly connectors?: readonly string[]; readonly personas?: readonly string[]; } export declare const PluginMCPServerConfigSchema: z.ZodObject<{ name: z.ZodString; command: z.ZodString; args: z.ZodOptional>; env: z.ZodOptional>; }, "strip", z.ZodTypeAny, { name: string; command: string; env?: Record | undefined; args?: string[] | undefined; }, { name: string; command: string; env?: Record | undefined; args?: string[] | undefined; }>; export declare const PluginManifestSchema: z.ZodObject<{ name: z.ZodString; version: z.ZodString; description: z.ZodString; author: z.ZodOptional; tools: z.ZodOptional>; skills: z.ZodOptional>; hooks: z.ZodOptional>; mcpServers: z.ZodOptional>; env: z.ZodOptional>; }, "strip", z.ZodTypeAny, { name: string; command: string; env?: Record | undefined; args?: string[] | undefined; }, { name: string; command: string; env?: Record | undefined; args?: string[] | undefined; }>, "many">>; connectors: z.ZodOptional>; personas: z.ZodOptional>; }, "strip", z.ZodTypeAny, { name: string; version: string; description: string; skills?: string[] | undefined; tools?: string[] | undefined; author?: string | undefined; hooks?: string[] | undefined; mcpServers?: { name: string; command: string; env?: Record | undefined; args?: string[] | undefined; }[] | undefined; connectors?: string[] | undefined; personas?: string[] | undefined; }, { name: string; version: string; description: string; skills?: string[] | undefined; tools?: string[] | undefined; author?: string | undefined; hooks?: string[] | undefined; mcpServers?: { name: string; command: string; env?: Record | undefined; args?: string[] | undefined; }[] | undefined; connectors?: string[] | undefined; personas?: string[] | undefined; }>; export interface PluginDefinition { readonly id: PluginId; readonly manifest: PluginManifest; readonly scope: PluginScope; readonly status: PluginStatus; readonly rootDir: string; readonly installedAt: number; readonly enabledAt?: number; readonly error?: string; } export type PluginLifecycleEvent = { type: 'plugin_installed'; pluginId: PluginId; name: string; scope: PluginScope; } | { type: 'plugin_enabled'; pluginId: PluginId; name: string; } | { type: 'plugin_disabled'; pluginId: PluginId; name: string; } | { type: 'plugin_uninstalled'; pluginId: PluginId; name: string; } | { type: 'plugin_error'; pluginId: PluginId; name: string; error: string; } | { type: 'plugin_hook_executed'; pluginId: PluginId; hookEvent: PluginHookEvent; durationMs: number; }; export type PluginEventListener = (event: PluginLifecycleEvent) => void; //# sourceMappingURL=index.d.ts.map