Skip to content

Hooks

Hooks are event observers that react to things happening in the runtime — an inference run completing, a tool call failing, a scheduled task finishing. They run asynchronously and never block the main request flow.

The most common use for hooks is memory synthesis: after every inference run, hooks summarize the conversation and update your agent's working memory.

Built-In Hooks

Protege ships with two hooks that form the memory synthesis chain:

thread-memory-updater

Listens to harness.inference.completed. After each inference run, it generates a summary of the conversation thread and persists it.

active-memory-updater

Listens to memory.thread.updated (emitted by thread-memory-updater). It reads the latest thread summaries and refreshes memory/{persona_id}/active.md — the short-horizon working memory that's loaded into every inference run.

Together, they form a chain:

Inference completes
  → thread-memory-updater summarizes the thread
    → emits memory.thread.updated
      → active-memory-updater refreshes active.md

Default manifest configuration

json
{
  "hooks": [
    {
      "name": "thread-memory-updater",
      "events": ["harness.inference.completed"],
      "config": {
        "prompt_path": "prompts/thread-summary.md",
        "max_delta_items": 24,
        "max_output_tokens": 800
      }
    },
    {
      "name": "active-memory-updater",
      "events": ["memory.thread.updated"],
      "config": {
        "prompt_path": "prompts/active-summary.md",
        "max_recent_threads": 6,
        "max_output_tokens": 600,
        "debounce_ms": 0
      }
    }
  ]
}

Configuration keys

thread-memory-updater:

KeyDescriptionDefault
prompt_pathPath to the summarization promptprompts/thread-summary.md
max_delta_itemsMax thread entries to include24
max_output_tokensToken limit for the summary800

active-memory-updater:

KeyDescriptionDefault
prompt_pathPath to the synthesis promptprompts/active-summary.md
max_recent_threadsHow many recent threads to consider6
max_output_tokensToken limit for active memory600
debounce_msDebounce window for rapid updates0

Event System

Hooks subscribe to specific runtime events. The engine emits events at key moments across the gateway, harness, scheduler, and memory subsystems.

Hook dispatch behavior

  1. Hooks are called in manifest order
  2. Each hook only receives events listed in its events array
  3. Using "*" subscribes to all events (useful for logging/debugging hooks)
  4. Hook failures are isolated — a failing hook doesn't crash the run

Event reference

Events are organized by subsystem. Every event payload includes these base fields:

ts
{
  level: 'info' | 'error',   // Severity
  scope: string,              // Subsystem (e.g., "gateway", "scheduler")
  event: string,              // Event name
  timestamp: string,          // ISO 8601 timestamp
}

Plus event-specific fields like personaId, threadId, toolName, etc.

Gateway events

EventDescription
gateway.inbound.receivedRaw SMTP data received
gateway.inbound.parsedMessage parsed and persona routed
gateway.inbound.enqueuedMessage queued for inference
gateway.outbound.sendingSending outbound email
gateway.outbound.sentOutbound email delivered
gateway.outbound.queued_via_relayOutbound queued through relay
gateway.outbound.sent_via_relayOutbound delivered through relay
gateway.relay.authenticatedWebSocket auth succeeded
gateway.relay.disconnectedRelay connection lost
gateway.errorUnhandled gateway error

Harness events

EventDescription
harness.inbound.persistedInbound message stored
harness.inference.startedInference run beginning
harness.inference.completedInference run finished
harness.tool.calls.receivedLLM requested tool calls
harness.tool.call.startedSingle tool execution starting
harness.tool.call.completedSingle tool execution succeeded
harness.tool.call.failedSingle tool execution failed

Scheduler events

EventDescription
scheduler.sync.completedResponsibility sync finished
scheduler.cron.enqueuedCron tick queued a run
scheduler.cron.skipped_overlapSkipped — previous run still active
scheduler.run.claimedRun claimed by runner
scheduler.run.startedRun execution began
scheduler.run.completedRun succeeded
scheduler.run.failedRun failed

Memory events

EventDescription
memory.thread.updatedThread memory summary updated
memory.active.updatedActive memory file refreshed

Event payload example

Here's what a harness.tool.call.failed event looks like:

json
{
  "level": "error",
  "scope": "gateway",
  "event": "harness.tool.call.failed",
  "timestamp": "2026-03-01T12:00:00.000Z",
  "correlationId": "persona:thread:message",
  "toolName": "send_email",
  "toolCallId": "call_abc123",
  "message": "No recipients defined",
  "errorName": "Error"
}