PostToolUse vs PreToolUse Hooks in AgentMemory: Capture Pipeline Differences

PreToolUse hooks enrich the model's input context before tool execution (optional and opt-in), while PostToolUse hooks persist tool outputs as durable observations for indexing and replay (always active).

AgentMemory is an open-source observability framework for Claude Code that captures filesystem and tool interactions through a dual-stage hook pipeline. Understanding the difference between PostToolUse and PreToolUse hooks is critical for customizing how context is injected into model turns versus how execution history is recorded. This analysis examines the implementation in rohitg00/agentmemory to clarify their distinct roles in the capture architecture.

Execution Timing and Core Purpose

The capture pipeline processes every tool invocation—such as Edit, Write, Read, Glob, or Grep—through two sequential hooks that operate at different phases of the tool lifecycle.

PreToolUse: Context Enrichment Before Execution

The PreToolUse hook executes before a tool call is sent to Claude Code. Its primary function is to optionally inject relevant context—such as file lists or search terms—into the upcoming model turn.

In src/hooks/pre-tool-use.ts, the hook reads the tool payload from stdin and checks the AGENTMEMORY_INJECT_CONTEXT environment variable (lines 9–23). If disabled, the hook exits immediately (lines 34–37) without modifying the workflow. When enabled, it extracts file paths and patterns (lines 56–68), then sends a JSON payload to the /agentmemory/enrich REST endpoint. Any context string returned by the server is written to stdout (lines 90–94), which Claude Code reads and prepends to the next model turn.

PostToolUse: Observation Persistence After Execution

The PostToolUse hook executes after a tool call returns its result. Its purpose is to create a permanent record of the interaction for later retrieval, compression, and replay.

Located in src/hooks/post-tool-use.ts, this hook always runs regardless of environment settings. It extracts image data using extractImageData (lines 60–88), truncates large outputs to 8,000 characters using the truncate function (lines 93–101), and POSTs an observation to /agentmemory/observe with hookType: "post_tool_use" (lines 33–53). The payload includes the session ID, tool name, input parameters, and processed output.

Configuration and Activation Behavior

The two hooks differ fundamentally in their activation models and operational requirements.

PreToolUse is opt-in and disabled by default. It imposes token costs since injected context consumes model context window. Activate it by setting:

AGENTMEMORY_INJECT_CONTEXT=true

PostToolUse is always active and cannot be disabled via environment variables. The hook is registered unconditionally in the capture pipeline because AgentMemory requires these observations for core functionality like session replay and audit logging.

Payload Structures and Data Flow

Each hook communicates with different REST endpoints and produces distinct data structures.

The PreToolUse hook sends enrichment requests to /agentmemory/enrich:

const res = await fetch(`${REST_URL}/agentmemory/enrich`, {
  method: "POST",
  headers: authHeaders(),
  body: JSON.stringify({
    sessionId: "s-123",
    files: ["src/index.ts"],
    terms: [],
    toolName: "Read"
  })
});

The response context is streamed to stdout for Claude Code consumption, but no observation is created in the memory store.

The PostToolUse hook sends observation records to /agentmemory/observe:

await fetch(`${REST_URL}/agentmemory/observe`, {
  method: "POST",
  headers: authHeaders(),
  body: JSON.stringify({
    hookType: "post_tool_use",
    sessionId: "s-123",
    project: process.cwd(),
    cwd: process.cwd(),
    timestamp: new Date().toISOString(),
    data: {
      tool_name: "Write",
      tool_input: { "file_path": "src/util.ts", "content": "export const foo = 1;" },
      tool_output: "File written successfully."
    }
  })
});

The Observer function in src/functions/observe.ts processes this payload (checking hookType at lines 101–102) and stores it as a RawObservation entry defined in src/types.ts (lines 15–24).

Code Implementation Details

Key implementation files reveal the architectural separation between these hooks:

  • src/hooks/pre-tool-use.ts: Handles conditional logic for context injection. Lines 34–37 implement the early-exit guard, while lines 90–94 handle the stdout output that Claude Code consumes.

  • src/hooks/post-tool-use.ts: Implements mandatory observation capture. The extractImageData helper (lines 60–88) handles base64 image extraction, and the truncation logic (lines 93–101) ensures outputs stay within the 8,000-character limit.

  • src/replay/jsonl-parser.ts: Distinguishes between hook types during replay generation, mapping post_tool_use entries at line 118 and pre_tool_use entries at line 156 to timeline events.

  • src/triggers/api.ts: Registers both the /enrich endpoint (for PreToolUse) and /observe endpoint (for PostToolUse) that the hooks call via REST.

Summary

  • PreToolUse hooks run before tool execution to optionally inject context into the model's input stream, requiring AGENTMEMORY_INJECT_CONTEXT=true to activate.
  • PostToolUse hooks run after tool completion to permanently store tool outputs, images, and metadata, and cannot be disabled.
  • PreToolUse communicates via stdout to influence the immediate model turn, while PostToolUse persists data via /agentmemory/observe for long-term retrieval.
  • The hookType field distinguishes these observations in the storage layer (src/functions/observe.ts line 101) and replay parser (src/replay/jsonl-parser.ts).

Frequently Asked Questions

Can I disable PostToolUse hooks to reduce overhead?

No. According to the source code in src/hooks/post-tool-use.ts, PostToolUse hooks are always active in the capture pipeline. Unlike PreToolUse hooks, there is no environment variable to disable them because AgentMemory depends on these observations for core functionality like session replay and audit logging.

Why is PreToolUse disabled by default?

PreToolUse is disabled by default because it increases token consumption. When AGENTMEMORY_INJECT_CONTEXT is enabled, the hook retrieves contextual snippets from the AgentMemory server and injects them into the model's context window (lines 90–94 in src/hooks/pre-tool-use.ts). This enrichment improves model accuracy but costs additional tokens per turn.

How does AgentMemory handle large tool outputs in PostToolUse?

The PostToolUse hook implements automatic truncation. In src/hooks/post-tool-use.ts (lines 93–101), the truncate function limits tool_output to 8,000 characters before sending the payload to /agentmemory/observe. This prevents storage bloat while preserving the essential content of filesystem operations.

Can PreToolUse and PostToolUse hooks be used together?

Yes. They serve complementary purposes in the capture pipeline. PreToolUse enriches the model's input before execution (when enabled), while PostToolUse records the result after execution (always). During replay, src/replay/jsonl-parser.ts processes both hook types—post_tool_use at line 118 and pre_tool_use at line 156—to reconstruct the full session timeline.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →