or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

apis

agents.mdhooks.mdmcp.mdmessages.mdoptions.mdpermissions.mdquery-api.mdsandbox.mdtools.md
index.mdpatterns.mdquick-reference.mdtypes.md
tile.json

hooks.mddocs/apis/

Hook System

Intercept and modify agent behavior at key execution points.

Hook Events

type HookEvent =
  | 'PreToolUse'            // Before tool execution
  | 'PostToolUse'           // After successful execution
  | 'PostToolUseFailure'    // After execution failure
  | 'PermissionRequest'     // During permission request
  | 'UserPromptSubmit'      // User submits prompt
  | 'SessionStart'          // Session starts
  | 'SessionEnd'            // Session ends
  | 'Stop'                  // Execution stops
  | 'SubagentStart'         // Subagent starts
  | 'SubagentStop'          // Subagent stops
  | 'PreCompact'            // Before compaction
  | 'Notification';         // System notification

Hook Callback

type HookCallback = (
  input: HookInput,
  toolUseID: string | undefined,
  options: {signal: AbortSignal}
) => Promise<HookJSONOutput>;

interface HookCallbackMatcher {
  matcher?: string;
  hooks: HookCallback[];
  timeout?: number;
}

Hook Input Types

interface PreToolUseHookInput {
  hook_event_name: 'PreToolUse';
  tool_name: string;
  tool_input: unknown;
  tool_use_id: string;
  session_id: string;
  transcript_path: string;
  cwd: string;
  permission_mode?: string;
}

interface PostToolUseHookInput {
  hook_event_name: 'PostToolUse';
  tool_name: string;
  tool_input: unknown;
  tool_response: unknown;
  tool_use_id: string;
  // ... base fields
}

interface PostToolUseFailureHookInput {
  hook_event_name: 'PostToolUseFailure';
  tool_name: string;
  tool_input: unknown;
  tool_use_id: string;
  error: string;
  is_interrupt?: boolean;
  // ... base fields
}

// See types.md for complete list

Hook Output

interface SyncHookJSONOutput {
  continue?: boolean;
  suppressOutput?: boolean;
  stopReason?: string;
  decision?: 'approve' | 'block';
  systemMessage?: string;
  reason?: string;
  hookSpecificOutput?:
    | {hookEventName: 'PreToolUse'; permissionDecision?: 'allow' | 'deny' | 'ask'; permissionDecisionReason?: string; updatedInput?: Record<string, unknown>}
    | {hookEventName: 'UserPromptSubmit'; additionalContext?: string}
    | {hookEventName: 'SessionStart'; additionalContext?: string}
    | {hookEventName: 'PostToolUse'; additionalContext?: string; updatedMCPToolOutput?: unknown}
    | {hookEventName: 'PostToolUseFailure'; additionalContext?: string}
    | {hookEventName: 'PermissionRequest'; decision: {behavior: 'allow'; updatedInput?: Record<string, unknown>} | {behavior: 'deny'; message?: string; interrupt?: boolean}};
}

interface AsyncHookJSONOutput {
  async: true;
  asyncTimeout?: number;
}

type HookJSONOutput = SyncHookJSONOutput | AsyncHookJSONOutput;

Examples

Logging

hooks: {
  PreToolUse: [{
    hooks: [async (input) => {
      if (input.hook_event_name === 'PreToolUse') {
        console.log(`[${input.tool_name}]`, input.tool_input);
      }
      return {continue: true};
    }]
  }],
  PostToolUse: [{
    hooks: [async (input) => {
      if (input.hook_event_name === 'PostToolUse') {
        console.log(`[${input.tool_name} ✓]`);
      }
      return {continue: true};
    }]
  }]
}

Block Operations

hooks: {
  PreToolUse: [{
    hooks: [async (input) => {
      if (input.hook_event_name === 'PreToolUse') {
        if (input.tool_name === 'Bash' &&
            (input.tool_input as any).command?.includes('rm -rf')) {
          return {
            continue: false,
            stopReason: 'Dangerous command blocked',
            hookSpecificOutput: {
              hookEventName: 'PreToolUse',
              permissionDecision: 'deny',
              permissionDecisionReason: 'rm -rf not allowed'
            }
          };
        }
      }
      return {continue: true};
    }]
  }]
}

Add Context

hooks: {
  SessionStart: [{
    hooks: [async (input) => {
      if (input.hook_event_name === 'SessionStart') {
        return {
          continue: true,
          hookSpecificOutput: {
            hookEventName: 'SessionStart',
            additionalContext: 'Project uses TypeScript. Follow TS best practices.'
          }
        };
      }
      return {continue: true};
    }]
  }]
}

Complete Example

const result = query({
  prompt: 'Build project',
  options: {
    hooks: {
      PreToolUse: [{
        hooks: [async (input) => {
          if (input.hook_event_name === 'PreToolUse') {
            console.log(`[PRE] ${input.tool_name}:`, input.tool_input);
          }
          return {continue: true};
        }],
        timeout: 5
      }],

      PostToolUse: [{
        hooks: [async (input) => {
          if (input.hook_event_name === 'PostToolUse') {
            console.log(`[POST] ${input.tool_name} completed`);
          }
          return {continue: true};
        }]
      }],

      PostToolUseFailure: [{
        hooks: [async (input) => {
          if (input.hook_event_name === 'PostToolUseFailure') {
            console.error(`[FAIL] ${input.tool_name}:`, input.error);
          }
          return {continue: true};
        }]
      }],

      SessionStart: [{
        hooks: [async (input) => {
          if (input.hook_event_name === 'SessionStart') {
            console.log('Session started:', input.source);
            return {
              continue: true,
              hookSpecificOutput: {
                hookEventName: 'SessionStart',
                additionalContext: 'Use TypeScript best practices'
              }
            };
          }
          return {continue: true};
        }]
      }]
    }
  }
});

for await (const msg of result) {
  if (msg.type === 'system' && msg.subtype === 'hook_response') {
    console.log('Hook:', msg.hook_name, msg.hook_event);
    if (msg.exit_code !== 0) {
      console.error('Hook failed:', msg.stderr);
    }
  }
}

Constants

const HOOK_EVENTS: readonly ["PreToolUse", "PostToolUse", "PostToolUseFailure", "Notification", "UserPromptSubmit", "SessionStart", "SessionEnd", "Stop", "SubagentStart", "SubagentStop", "PreCompact", "PermissionRequest"];