Hook System

Overview

Hooks intercept actions at defined points. Unlike events (which are notifications after the fact), hooks run before an action and can abort or modify it.

Hook Points

PointTimingCan AbortCan Modify
pre:messageBefore sending to LLMYesYes (modify prompt)
post:messageAfter LLM responseNoYes (modify output)
pre:toolBefore tool executionYesYes (modify args)
post:toolAfter tool returnsNoYes (modify result)
pre:agent:spawnBefore creating agentYesYes (modify config)
post:agent:completeAfter agent finishesNoNo
pre:session:phaseBefore phase transitionYesNo
post:session:phaseAfter phase transitionNoNo
pre:git:writeBefore file write (shadow git)YesYes (modify content)
post:git:writeAfter file writeNoNo
pre:view:renderBefore rendering frameNoYes (modify render data)

Registration

import { HookSystem } from 'armament/core';

const hooks = new HookSystem();

// Basic hook
hooks.register('pre:message', async (context) => {
  console.log('About to send:', context.prompt);
});

// With priority (lower = earlier, default 100)
hooks.register('pre:message', handler, { priority: 10 });

// Named hook (for enable/disable)
hooks.register('pre:tool', handler, { name: 'safety-check' });

Abort Capability

Pre-hooks can abort the action:

hooks.register('pre:tool', async (context) => {
  if (context.tool === 'shell' && context.args.includes('rm')) {
    return { abort: true, reason: 'Destructive command blocked by hook' };
  }
});

hooks.register('pre:message', async (context) => {
  if (context.prompt.length > 100000) {
    return { abort: true, reason: 'Prompt too large' };
  }
});

Modify Capability

Hooks can transform the action’s data:

// Add system context to all prompts
hooks.register('pre:message', async (context) => {
  return {
    modify: {
      prompt: `[Project: ${projectName}]\n${context.prompt}`
    }
  };
});

// Redact secrets from output
hooks.register('post:message', async (context) => {
  return {
    modify: {
      output: redactSecrets(context.output)
    }
  };
});

Execution Order

Hooks at the same point execute in priority order:

  1. Priority 1 hooks (system-critical)
  2. Priority 10 hooks (security)
  3. Priority 50 hooks (user scripts)
  4. Priority 100 hooks (default)

If any hook aborts, subsequent hooks and the action itself don’t run.

Error Isolation

A failing hook doesn’t crash the system:

hooks.register('pre:message', async () => {
  throw new Error('hook bug');
  // Error is logged, other hooks still run
});

Configure error behavior per hook:

hooks.register('pre:tool', handler, {
  onError: 'skip'    // skip | abort | log
});

Enable/Disable

Toggle hooks by name without unregistering:

hooks.disable('safety-check');
hooks.enable('safety-check');
hooks.isEnabled('safety-check'); // boolean

Scripting Integration

Register hooks from .arma scripts:

hook pre:message /log debug "Sending: $prompt"
hook pre:tool:shell /perms check shellExec
hook post:agent:complete /notify "Agent $agent finished"

Configuration

hooks:
  errorBehavior: log    # skip | abort | log
  timeout: 5000         # Max hook execution time (ms)
  maxHooksPerPoint: 50  # Safety limit