Skip to main content
What’s New β†’ Current release: 0.8.2. 0.7.0: Built on Spring AI 2.0.0, Spring Boot 4.0.7, and Claude Agent SDK 1.5.0. This release keeps the core-plus-adapters architecture and public dispatch API while correcting standalone dependency resolution, packaging, diagnostics, and Java compatibility claims. Every agent framework implements hooks differently. Claude Code has shell-based hooks. Strands has steering callbacks. Spring AI has advisors. Your safety policy, logging, and steering logic gets rewritten for each one. Agent Hooks is a portable Java API that lets you write hook logic once and run it on any runtime that has an adapter. The core module has no framework dependency β€” it defines the event model, decision types, and registry, with org.jspecify annotations as its only compile dependency. Adapters (Spring AI, Claude Agent SDK, and Gemini CLI) wire the core into their runtime’s tool-call lifecycle. Your hooks move with you when your agent infrastructure changes.

Why Hooks

LLMs are probabilistic β€” prompt-based instructions drift under token pressure. Agents skip steps, forget constraints, and ignore guardrails. Hooks solve this by moving critical logic out of the prompt and into deterministic code that intercepts every tool call, the same way servlet filters intercept HTTP requests:
  • Safety β€” Block dangerous operations before they execute. A Block decision short-circuits immediately and cannot be overridden by later hooks.
  • Observability β€” Log every tool call, capture timing data, and feed traces into Agent Journal for behavioral analysis.
  • Steering β€” Modify tool inputs in flight. Subsequent hooks see the modified input, so transformations chain cleanly.

How It Works

Hooks intercept at two points in the tool-call lifecycle:
Register hooks with type-safe generics and optional tool-name filtering:

Decision Model

HookDecision is a sealed type with four variants: Multiple hooks execute in priority order (default: 100, lower = earlier). AfterToolCall hooks fire in reverse priority order for proper cleanup semantics. Two behaviors are worth knowing before you rely on them. Dispatch matches an event’s exact runtime class, so a hook registered against a supertype such as ToolEvent.class compiles but never fires β€” register against the concrete record. And a hook that throws is logged at WARNING and treated as Proceed: Block short-circuits, but a failing hook does not fail closed.

Modules

agent-hooks-claude requires Java 21 because every published claude-code-sdk version is Java 21 bytecode. The other three modules are unaffected on Java 17.

Event Hierarchy

The event system is open (unsealed) β€” you can define custom events for your runtime:

Quick Start

Write Once, Run Anywhere

The same AgentHookProvider works on all three runtimes β€” this is the core value proposition.
Spring AI β€” register as a @Component bean, auto-configuration handles the rest:
The auto-configured HookContext is an application-wide singleton: every hooked tool call in the application shares one state map and one tool-call history. That suits a single-user CLI or desktop app. A multi-user server should define its own request- or session-scoped HookContext bean, or build one per conversation and call HookedTools.wrap at that point. Claude Agent SDK β€” bridge into the Claude HookRegistry:
The bridge registers callbacks for all six Claude hook events (PreToolUse, PostToolUse, UserPromptSubmit, Stop, SubagentStop, PreCompact). It converts Claude SDK types to core events, dispatches through your hooks, and maps decisions back to Claude’s HookOutput. Tool call duration is tracked via wall-clock timing across the pre/post hook boundary. Each Claude session gets its own HookContext for isolated state and history. The bridge retains those contexts for its own lifetime β€” fine for one bridge per CLI process; a long-lived bridge shared across sessions should call evictSession(sessionId) when a session finishes. Gemini CLI β€” stateless subprocess dispatcher reads JSON from stdin:
Gemini CLI spawns the hook process per event. The dispatcher maps all 11 Gemini events to core and Gemini-specific HookEvent records. HookContext is fresh per invocation β€” stateless hooks (security gates, audit logging) work out of the box. Note: Gemini BeforeTool can only allow or block β€” Modify is downgraded to allow with a warning.

Documentation

Source Code

Core API, Spring AI adapter, Claude adapter, and Gemini adapter

Design Notes

Architecture decisions, event hierarchy, dispatch semantics

Suite integration

Agent Hooks is managed by the AgentWorks BOM, but it is standalone today: no other published AgentWorks module depends on it. Bridges to Agent Journal (a hook provider that logs tool-call events to a journal Run) and to workflow steps are intended directions, not shipped code.