Skip to content
Night Owls.dev
Jump to a page

@nightowlsdev/hooks

Engine

The @mastra-free decision-hook substrate: the plain types and dispatcher the platform's billing, approval, and mutation gates hang off.

What it does

@nightowlsdev/hooks is the deliberately tiny, @mastra-free substrate that carries Night Owls' decision-hook contracts AND the runtime that applies them, kept engine-vendor-free by design so @nightowlsdev/core can depend on it without leaking engine types into core's public API. It defines the declarative decision shapes (HookDecision allow/deny, the three-way ToolDecision allow/deny/ask) and the SwarmHooks bundle of optional decision hooks: preGeneration (per-model-launch gate, e.g. billing reserve), preToolCall (action-approval gate honoring a non-removable ToolApprovalPolicy of flag vs all-side-effecting), and guardDefinitionMutation (who may publish/rollback an agent definition). It ships real runtime code too, not just types: the HookDispatcher / createHookDispatcher that applies the bundle uniformly and fail-closed (a hook that throws becomes a deny, so safety/billing vetoes never silently allow on error), the defineHook identity helper, the decision constructors deny()/ask(), and the canonical constants ALLOW / ALLOW_TOOL / DEFAULT_READ_ONLY_TOOLS. Hosts normally configure these via SwarmConfig.hooks; the values and types are re-exported through @nightowlsdev/core so most consumers never import this package directly.

Install

pnpm add @nightowlsdev/hooks

Key exports

  • HookDispatcher / createHookDispatcher
  • defineHook
  • deny / ask / ALLOW / ALLOW_TOOL
  • DEFAULT_READ_ONLY_TOOLS
  • types: HookDecision, ToolDecision, SwarmHooks, PreGenerationHook, PreToolCallHook, GuardMutationHook, ToolApprovalPolicy

Usage

hooks.ts
import { defineHook, deny, ask, ALLOW } from "@nightowlsdev/hooks";
import type { SwarmHooks } from "@nightowlsdev/hooks";

const hooks: SwarmHooks = {
  // Gate every side-effecting tool behind a human decision.
  preToolCall: defineHook(async ({ tool }) =>
    tool.sideEffecting ? ask("Approve this action?") : ALLOW,
  ),
  // Veto a model launch when out of budget.
  preGeneration: defineHook(async ({ budgetRemaining }) =>
    budgetRemaining > 0 ? ALLOW : deny("out of budget"),
  ),
};