TypeScript API: Hooks
Your code around the agent loop: before and after model and tool calls. Generated from the declarations and doc comments of criogenai 0.1.7. See also: TypeScript API overview, the same module in Python.
import { HookContext, HookAction, ToolCall, ToolResult, Hook } from "criogenai";Contents#
| Name | Kind | Summary |
|---|---|---|
HookContext | interface | What a hook sees. |
HookAction | interface | What a hook wants: save its state; replace the transcript (messages, beforeModel) or the new reply (message, afterModel); or end the run, with output. |
ToolCall | interface | A tool call going through beforeTool (change name or args and return it). |
ToolResult | class | Returned by beforeTool: use value as the result and skip the tool (e.g. a cached answer). |
Hook | interface | Code that runs around the agent loop: an object (or class instance) with any of the methods below, each of which may be async. |
HookContext#
interface HookContext {
runId: string;
messages: Record<string, unknown>[];
modelCalls: number;
hookState: unknown;
state: Record<string, unknown>;
}What a hook sees.
Fields
runId(string): The id of the run (result.runId).messages(Record<string, unknown>[]): The transcript so far (as inresult.messages).modelCalls(number): Model calls made so far in this run.hookState(unknown): What this hook saved with{ state }, if anything.state(Record<string, unknown>): The whole run state, read-only.
HookAction#
interface HookAction {
state?: unknown;
messages?: Record<string, unknown>[];
message?: Record<string, unknown>;
end?: boolean;
output?: unknown;
}What a hook wants: save its state; replace the transcript (messages,
beforeModel) or the new reply (message, afterModel); or end the
run, with output.
Fields
state(unknown, optional): A value to keep for this hook; later calls see it asctx.hookState.messages(Record<string, unknown>[], optional):beforeModelonly: the transcript to send instead (and to keep).message(Record<string, unknown>, optional):afterModelonly: the assistant message to use instead of the model's reply.end(boolean, optional):trueends the run now, with stop reason "hook_end".output(unknown, optional): Withend: the run's output.
ToolCall#
interface ToolCall {
callId: string;
name: string;
args: Record<string, unknown>;
}A tool call going through beforeTool (change name or args and return it).
Fields
callId(string): The call's id, as in the transcript.name(string): The tool the model called. Return a call with another name to run that tool instead.args(Record<string, unknown>): The arguments the model wrote.
ToolResult#
class ToolResult {
constructor(value: unknown);
}Returned by beforeTool: use value as the result and skip the tool (e.g. a cached answer).
Parameters
value(unknown): The tool result the model sees, as JSON.
Example
import { ToolResult, type Hook } from "criogenai";
const cache = new Map<string, unknown>([["get_weather:Paris", { forecast: "sunny" }]]);
const cached: Hook = {
beforeTool(call) {
const hit = cache.get(`${call.name}:${call.args.city}`);
if (hit !== undefined) return new ToolResult(hit);
},
};Hook#
interface Hook {
name?: string;
onStart?(ctx: HookContext): Maybe<HookAction>;
beforeModel?(ctx: HookContext): Maybe<HookAction>;
afterModel?(ctx: HookContext): Maybe<HookAction>;
beforeTool?(call: ToolCall): Maybe<ToolCall | ToolResult>;
afterTool?(call: ToolCall, result: unknown): unknown;
onEnd?(ctx: HookContext, outcome: "completed" | "suspended" | "failed"): void | Promise<void>;
}Code that runs around the agent loop: an object (or class instance) with
any of the methods below, each of which may be async. Pass hooks in the
agent's hooks option.
onStart, beforeModel and beforeTool run in list order, the others in
reverse. A throw in onStart, beforeModel or afterModel fails the run
(code hook_failed; what was thrown is the error's cause); a throw in
beforeTool or afterTool counts as the tool's (see the agent's
onToolException).
Fields
name(string, optional): Shown in errors and audit records; defaults to the class name.onStart(((ctx: HookContext) => Maybe<HookAction>), optional): Once per run segment (a run, or its continuation after approval or retry), before anything else; may return{ state }.beforeModel(((ctx: HookContext) => Maybe<HookAction>), optional): Before each model call; may replace the transcript ({ messages }) or end the run ({ end: true, output }).afterModel(((ctx: HookContext) => Maybe<HookAction>), optional): After each model reply (ctx.messagesends with it); may replace it ({ message }) or end the run.beforeTool(((call: ToolCall) => Maybe<ToolResult | ToolCall>), optional): Before each tool call: return a changedToolCallto run that instead, aToolResultto skip the tool, or nothing to go on. ThrowingToolErrorgives the model that error instead of running the tool.afterTool(((call: ToolCall, result: unknown) => unknown), optional): After each tool call; a value other thanundefinedreplaces the result the model sees.onEnd(((ctx: HookContext, outcome: "completed" | "suspended" | "failed") => void | Promise<void>), optional): Always, once per run segment, with how it ended: "completed", "suspended" (waiting for approval) or "failed". Cannot change the outcome.
Example
import { Agent, type Hook } from "criogenai";
const logTools: Hook = {
name: "log-tools",
beforeTool(call) {
console.log("calling", call.name);
},
onEnd(ctx, outcome) {
console.log(`run ${ctx.runId}: ${outcome} after ${ctx.modelCalls} model calls`);
},
};
const agent = new Agent({ model: "openai:gpt-5-mini", hooks: [logTools] });