Hooks

Intercept and customize tool execution, session lifecycle, permissions, file writes, compaction, and more with 18 hook events.

Hooks let you intercept key moments in the agent lifecycle — before and after tool calls, at session and turn boundaries, when permissions are requested or denied, when files are written, when models switch, around compaction, retry, memory extraction, and on errors. They can modify tool inputs/outputs, deny tool calls, or run side effects like logging and analytics.

Defining hooks

Pass hooks through Agent options:

import { Agent, type HookDefinition } from "noumen";

const hooks: HookDefinition[] = [
  {
    event: "SessionStart",
    handler: async (input) => {
      console.log(`Session ${input.sessionId} started (resume: ${input.isResume})`);
    },
  },
  {
    event: "PreToolUse",
    matcher: "Bash",
    handler: async (input) => {
      console.log(`Running bash: ${input.toolInput.command}`);
      return { decision: "allow" };
    },
  },
  {
    event: "FileWrite",
    handler: async (input) => {
      console.log(`${input.toolName} wrote ${input.filePath} (new: ${input.isNew})`);
    },
  },
];

const code = new Agent({
  provider,
  sandbox,
  options: { hooks },
});

Hook events

noumen provides 18 hook events across six categories.

Session lifecycle

EventTypeDescription
SessionStartNotificationFires at the start of thread.run(), after session restore. Includes prompt and isResume.
SessionEndNotificationFires when thread.run() completes. Includes reason: "complete", "abort", "maxTurns", or "error".
TurnStartNotificationFires at the beginning of each provider call (before the LLM request).
TurnEndNotificationFires after the agent turn completes with a final text response.
ErrorNotificationFires when an error occurs during the agent loop.

Tool execution

EventTypeDescription
PreToolUseInterceptorFires before a tool executes. Can allow, deny, or modify input.
PostToolUseInterceptorFires after a tool executes. Can modify output or stop the loop.
PostToolUseFailureInterceptorFires after a tool execution that returned an error. Can modify the error output.
FileWriteNotificationFires after WriteFile or EditFile successfully writes to disk. Includes filePath and isNew.

Permissions

EventTypeDescription
PermissionRequestNotificationFires before the user/handler is asked for permission. Includes toolName, input, and mode.
PermissionDeniedNotificationFires when a tool call is denied by rules, the user, or missing handler. Includes reason.

Subagents

EventTypeDescription
SubagentStartNotificationFires when a subagent is spawned.
SubagentStopNotificationFires when a subagent finishes.

Compaction

EventTypeDescription
PreCompactNotificationFires before conversation compaction (proactive or reactive).
PostCompactNotificationFires after compaction completes.

System

EventTypeDescription
ModelSwitchNotificationFires when setModel() or setProvider() changes the active model. Includes previousModel and newModel.
RetryAttemptNotificationFires before each retry attempt. Includes attempt, maxAttempts, error, and delay.
MemoryUpdateNotificationFires after memory extraction creates, updates, or deletes entries.

PreToolUse hooks

Pre-tool hooks run before tool execution. They receive the tool name, input, and session ID. Return a decision to control whether the tool runs:

{
  event: "PreToolUse",
  matcher: "WriteFile",
  handler: async (input) => {
    const path = input.toolInput.file_path as string;
    if (path.includes("node_modules")) {
      return { decision: "deny", message: "Cannot write to node_modules" };
    }
    return { decision: "allow" };
  },
}

PreToolUseHookOutput

FieldTypeDescription
decision"allow" | "deny" | "passthrough"Whether to allow, deny, or defer to the next hook.
updatedInputRecord<string, unknown>Replace the tool input before execution.
messagestringMessage to include in the deny response.
preventContinuationbooleanStop the agent loop after this tool call.

PostToolUse hooks

Post-tool hooks run after execution. They can modify the output returned to the model:

{
  event: "PostToolUse",
  matcher: "ReadFile",
  handler: async (input) => {
    if (input.toolOutput.length > 50000) {
      return { updatedOutput: input.toolOutput.slice(0, 50000) + "\n[truncated]" };
    }
  },
}

PostToolUseFailure hooks

Failure hooks fire only when a tool returns an error (isError: true). Use them for error logging, alerting, or modifying the error message:

{
  event: "PostToolUseFailure",
  handler: async (input) => {
    console.error(`Tool ${input.toolName} failed: ${input.errorMessage}`);
  },
}

Session lifecycle hooks

Track when sessions start and end:

{
  event: "SessionStart",
  handler: async (input) => {
    await analytics.track("session_started", {
      sessionId: input.sessionId,
      isResume: input.isResume,
    });
  },
}
{
  event: "SessionEnd",
  handler: async (input) => {
    await analytics.track("session_ended", {
      sessionId: input.sessionId,
      reason: input.reason, // "complete" | "abort" | "maxTurns" | "error"
    });
  },
}

Permission hooks

Observe permission decisions for audit trails and alerting:

{
  event: "PermissionDenied",
  handler: async (input) => {
    await audit.log(`Permission denied for ${input.toolName}: ${input.reason}`);
  },
}

File write hooks

Get notified whenever the agent writes to the filesystem:

{
  event: "FileWrite",
  handler: async (input) => {
    console.log(`[audit] ${input.toolName} wrote ${input.filePath} (new: ${input.isNew})`);
  },
}

Model switch hooks

Track model changes during a session:

{
  event: "ModelSwitch",
  handler: async (input) => {
    console.log(`Model changed: ${input.previousModel} → ${input.newModel}`);
  },
}

Matcher patterns

The optional matcher field filters which tools a hook applies to. It supports simple glob patterns:

  • "Bash" — exact match
  • "mcp__*" — prefix match (all MCP tools)
  • "*File" — suffix match (ReadFile, WriteFile, EditFile)
  • "*" — match all tools

Omitting matcher means the hook applies to all tools (for PreToolUse/PostToolUse/PostToolUseFailure) or all events of that type (for notifications).

Notification hooks

Notification hooks are fire-and-forget — they have no return value and errors are silently swallowed. Use them for logging, analytics, or side effects. All events except PreToolUse, PostToolUse, and PostToolUseFailure are notification hooks.

HookDefinition type

interface HookDefinition {
  event: HookEvent;
  matcher?: string;
  handler: (input: HookInput) => Promise<HookOutput> | HookOutput;
}

type HookEvent =
  | "PreToolUse" | "PostToolUse" | "PostToolUseFailure"
  | "TurnStart" | "TurnEnd"
  | "SessionStart" | "SessionEnd"
  | "SubagentStart" | "SubagentStop"
  | "PreCompact" | "PostCompact"
  | "PermissionRequest" | "PermissionDenied"
  | "FileWrite" | "ModelSwitch"
  | "RetryAttempt" | "MemoryUpdate"
  | "Error";