Hook API
Programmatic access to the hook system. For what each hook does and when it fires, see Hook System. This page covers the HookBus registration API itself.
AgentHooks
Each hook point is an optional handler on the AgentHooks interface, imported from @logician/agent-core:
import type { AgentHooks } from '@logician/agent-core'
const hooks: AgentHooks = {
beforeToolCall(ctx, signal) {
console.log(`Calling ${ctx.toolCall.name}`)
},
afterToolCall(ctx, signal) {
if (ctx.isError) console.error(`${ctx.toolCall.name} failed: ${ctx.result}`)
},
}Handlers may be synchronous or return a Promise, and receive an optional AbortSignal as the second argument. See Hook System for the full list of hook points and their context/result shapes.
Registering hooks
Hooks are registered on a HookBus instance, not via a global function. HookBus.register() takes a whole AgentHooks object and wires up every handler it defines in one call, returning a single unsubscribe function:
import { HookBus } from '@logician/agent-core/hooks/native'
const bus = new HookBus({ errorMode: 'continue' })
const unregister = bus.register(hooks, {
id: 'my-plugin', // stable identity for diagnostics/dedup
source: 'my-plugin', // used to attribute errors to a source
priority: 0, // higher runs first; ties keep registration order
timeoutMs: 5000, // per-handler timeout override
})
// Later, to remove all handlers registered above:
unregister()Individual hook points can also be registered one at a time with bus.on():
bus.on('beforeToolCall', hooks.beforeToolCall!, { priority: 10 })Composition semantics
Multiple registrants can hook the same event. Each event type composes handlers deterministically rather than just "last one wins":
beforeToolCall— early-block: the first handler to return{ content }short-circuits tool execution; a returned{ args }rewrites arguments for later handlers.afterToolCall— patch-accumulate: each handler sees the prior patch; later non-undefinedfields win.prepareNextTurn— transform: messages thread through every handler in order.shouldStopAfterTurn— firsttruewins.
Priority and error isolation
- Priority: handlers with a higher
priorityrun first; equal priorities preserve registration order. - Timeouts:
HookBusOptions.defaultTimeoutMssets a default per-handler timeout (0 disables it); a per-registrationtimeoutMsoverrides it. A timed-out handler is treated like a thrown error — skipped and reported. - Error mode:
HookBusOptions.errorModecontrols whether a thrown handler aborts the rest of the chain ("throw") or is skipped and reported viaonError("continue", the default).
Observing without hooking
bus.observe(observer) subscribes a read-only firehose over every event — useful for logging or metrics without participating in the hook chain's return-value semantics:
bus.observe((event, ctx) => {
console.log(`[hook] ${event} fired`)
})