export declare const SESSION_HOOK_EVENTS: readonly ["session.idle", "session.created", "session.deleted", "file.changed"]; /** * Reason values forwarded with `session.deleted` envelopes. * * PI emits both `session_shutdown` and `session_before_switch` with their own * `reason` field (e.g. "quit", "reload", "new", "resume", "fork"). The * adapter forwards that value verbatim into the runtime envelope so hook * authors can distinguish a graceful shutdown from a /new|/resume|/fork * transition. The string is opaque to the runtime: handlers should treat * unknown values as a forward-compatible extension. */ export type SessionDeletedReason = string; export declare const LEGACY_HOOK_CONDITIONS: readonly ["matchesCodeFiles"]; export declare const PATH_HOOK_CONDITION_KEYS: readonly ["matchesAnyPath", "matchesAllPaths"]; export declare const HOOK_SCOPES: readonly ["all", "main", "child"]; export declare const HOOK_RUN_IN: readonly ["current", "main"]; export declare const HOOK_BEHAVIORS: readonly ["stop"]; export type SessionHookEvent = (typeof SESSION_HOOK_EVENTS)[number]; export type ToolHookPhase = "before" | "after"; export type ToolHookEvent = `tool.${ToolHookPhase}.*` | `tool.${ToolHookPhase}.${string}`; export type HookEvent = SessionHookEvent | ToolHookEvent; export type HookLegacyCondition = (typeof LEGACY_HOOK_CONDITIONS)[number]; export type HookPathConditionKey = (typeof PATH_HOOK_CONDITION_KEYS)[number]; /** * Discriminated union for path conditions. The two members are mutually * exclusive: a single condition entry must specify either `matchesAnyPath` * or `matchesAllPaths`. The structural union already rejects values that * literally carry both keys at construction time; an excess-property check * `assertHookPathConditionMutex` is exposed for runtime call sites that * want a stronger guarantee against arbitrary record inputs. */ export type HookPathCondition = { readonly matchesAnyPath: readonly string[]; } | { readonly matchesAllPaths: readonly string[]; }; export type HookCondition = HookLegacyCondition | HookPathCondition; export type HookScope = (typeof HOOK_SCOPES)[number]; export type HookRunIn = (typeof HOOK_RUN_IN)[number]; export type HookBehavior = (typeof HOOK_BEHAVIORS)[number]; export interface HookAsyncConfig { readonly group?: string; readonly concurrency?: number; } export interface CreateFileChange { readonly operation: "create"; readonly path: string; } export interface ModifyFileChange { readonly operation: "modify"; readonly path: string; } export interface DeleteFileChange { readonly operation: "delete"; readonly path: string; } export interface RenameFileChange { readonly operation: "rename"; readonly fromPath: string; readonly toPath: string; } export type FileChange = CreateFileChange | ModifyFileChange | DeleteFileChange | RenameFileChange; export interface HookCommandActionConfig { readonly name: string; readonly args?: string; } export interface HookToolActionConfig { readonly name: string; readonly args?: Record; } export interface HookBashActionConfig { readonly command: string; readonly timeout?: number; } export interface HookCommandAction { readonly command: string | HookCommandActionConfig; } export interface HookToolAction { readonly tool: HookToolActionConfig; } export interface HookBashAction { readonly bash: string | HookBashActionConfig; } export type HookNotifyLevel = "info" | "success" | "warning" | "error"; export interface HookNotifyActionConfig { readonly text: string; readonly level?: HookNotifyLevel; } export interface HookNotifyAction { readonly notify: string | HookNotifyActionConfig; } export interface HookConfirmActionConfig { readonly title?: string; readonly message: string; } export interface HookConfirmAction { readonly confirm: HookConfirmActionConfig; } export interface HookSetStatusActionConfig { readonly text: string; } export interface HookSetStatusAction { readonly setStatus: string | HookSetStatusActionConfig; } export type HookAction = HookCommandAction | HookToolAction | HookBashAction | HookNotifyAction | HookConfirmAction | HookSetStatusAction; /** * P2-25: closed union of skip reasons emitted by the runtime when a hook is * evaluated. `matched` is included so a single decision shape can express * both the "ran" and "did not run" outcomes. New skip causes must extend * this union — the compile-time check forces every emit site to be * accounted for here. */ export type HookSkipReason = "matched" | "scope_mismatch" | "matchesCodeFiles_failed" | "matchesAnyPath_no_paths" | "matchesAnyPath_failed" | "matchesAllPaths_no_paths" | "matchesAllPaths_failed"; export interface HookConfigSource { readonly filePath: string; readonly index: number; } export interface HookConfig { readonly id?: string; readonly event: HookEvent; readonly action?: HookBehavior; readonly actions: HookAction[]; readonly scope: HookScope; readonly runIn: HookRunIn; readonly async?: true | HookAsyncConfig; readonly conditions?: HookCondition[]; readonly source: HookConfigSource; } export interface HookOverrideEntry { readonly targetId: string; readonly disable: boolean; readonly replacement?: HookConfig; readonly source: HookConfigSource; } export type HookMap = Map; export type HookValidationErrorCode = "invalid_frontmatter" | "invalid_imports" | "missing_hooks" | "invalid_hooks" | "invalid_hook" | "invalid_event" | "invalid_scope" | "invalid_run_in" | "invalid_hook_action" | "invalid_conditions" | "invalid_actions" | "invalid_action" | "duplicate_hook_id" | "override_target_not_found" | "invalid_override" | "invalid_async" | "unsupported_on_pi"; export interface HookValidationError { readonly code: HookValidationErrorCode; readonly filePath: string; readonly message: string; readonly path?: string; } export interface ParsedHooksFile { readonly hooks: HookMap; readonly overrides: HookOverrideEntry[]; readonly errors: HookValidationError[]; readonly advisories?: string[]; } /** * Host-supplied policy that flags hooks the host runtime cannot execute. * * The core loader is host-agnostic: it parses YAML, validates structure, and * caches results. Anything that depends on which host runtime will *execute* * the hook (e.g. PI lacks a slash-command API, so `command:` actions are * rejected on PI but might be valid on another host) lives behind this * interface. PI registers an implementation in `src/pi/unsupported.ts`; cores * loaded standalone simply receive an empty policy and skip those checks. * * `errors` are appended to `ParsedHooksFile.errors` and produced hooks are * dropped from the active hook map. * `advisories` are surfaced via `ParsedHooksFile.advisories` (load succeeds). * `invalidHooks` lists the hooks that produced load-blocking errors so the * loader can remove exactly those entries from the active hook map. */ export interface HookPolicyDiagnostics { readonly errors: string[]; readonly advisories: string[]; readonly invalidHooks: ReadonlySet; } export interface HookPolicy { readonly diagnose: (hookMap: HookMap) => HookPolicyDiagnostics; } export declare function isHookEvent(value: unknown): value is HookEvent; export declare function isHookLegacyCondition(value: unknown): value is HookLegacyCondition; export declare function isHookPathConditionKey(value: unknown): value is HookPathConditionKey; export declare function isHookScope(value: unknown): value is HookScope; export declare function isHookRunIn(value: unknown): value is HookRunIn; export declare function isHookBehavior(value: unknown): value is HookBehavior; import type { BashExecutionRequest, BashHookResult } from "./bash-types.js"; export interface HostDeliveryResult { /** * `accepted` means the host API accepted the request without throwing. * `degraded` means the action was intentionally skipped or downgraded. */ readonly status: "accepted" | "degraded"; readonly reason?: string; readonly details?: Record; } export interface HostAdapter { /** Abort the given session (best-effort). Errors must be handled by the adapter. */ abort(sessionId: string): void | Promise; /** Return the root/parent-less session id reachable from `sessionId`. */ getRootSessionId(sessionId: string): string | Promise; /** Execute a bash hook request; same contract as the node bash-executor. */ runBash(request: BashExecutionRequest): Promise; /** Queue a prompt in the current session; used as the fallback for `tool:` actions. */ sendPrompt(sessionId: string, text: string): void | HostDeliveryResult | Promise; /** * Show a user-visible notification. Optional: hosts that do not implement * a UI surface (e.g. headless tests, non-PI embedders) may omit this; the * runtime degrades to a log + skip in that case. */ notify?(text: string, level?: HookNotifyLevel): void | HostDeliveryResult | Promise; /** * Prompt the user for confirmation. Must resolve to a boolean: `true` = * user approved, `false` = user rejected (treated as a blocking result * for pre-tool hooks, same as exit-code-2 from a bash action). */ confirm?(options: { title?: string; message: string; }): boolean | Promise; /** * Set a status-bar entry for the given hookId. Pass an empty string to * clear; hosts without a status surface may omit this. */ setStatus?(hookId: string, text: string): void | HostDeliveryResult | Promise; }