Skip to main content

Lifecycle Hooks

Lifecycle hooks let you run custom logic at key points during an agent's execution. The most commonly used hook is beforeExecute, which fires before each execution step and lets you modify the message parts the agent sees.


beforeExecute

The beforeExecute hook fires before each agent execution step. It receives the current message and can modify it — adding parts, injecting context, or transforming existing data.

This is the primary way to inject dynamic application state into an agent's context at runtime.

How It Works

  1. Your agent starts a run
  2. Before each execution step, the server fires beforeExecute
  3. If you have an inline hook registered, it sends the request to your client
  4. Your handler returns a HookMutation with dynamic_values
  5. These values are merged into the agent's context as template variables
import type { HookHandler } from '@distri/core';

const beforeExecute: HookHandler = async (req) => {
// req.message contains the current message parts
// req.context has agent_id, thread_id, task_id, run_id

const appState = await getApplicationState();

return {
dynamic_values: {
current_user: appState.userName,
active_items: appState.items.length,
last_updated: appState.updatedAt,
},
};
};

The dynamic_values are injected into the agent's prompt and become available as Handlebars template variables:

# CURRENT CONTEXT
User: {{current_user}}
Active items: {{active_items}}
Last updated: {{last_updated}}

Configuring Hooks in Agent Definitions

Reference hooks in your agent's TOML frontmatter:

---
name = "my_agent"
description = "Agent with lifecycle hooks"
hooks = ["my_hook"]
---

The hook name maps to a handler on the client side that responds to inline_hook_requested events during the agent's streaming execution.


Parts Metadata (save: false)

When tool handlers return DistriPart[], you can attach __metadata: { save: false } to any part. This tells Distri to include the part in the current agent context but not persist it to conversation history.

This is critical for keeping context lean — screenshots, DOM snapshots, and other large ephemeral data should use save: false to avoid bloating the thread.

Pattern

import type { DistriPart } from '@distri/core';

// Return parts with __metadata to prevent saving
handler: async (input): Promise<DistriPart[]> => {
const screenshot = await captureScreenshot();
const pageState = await extractPageState();

return [
{
part_type: 'text',
data: `=== BROWSER STATE ===\nURL: ${pageState.url}`,
__metadata: { save: false },
},
{
part_type: 'image',
data: screenshot,
__metadata: { save: false },
},
{
part_type: 'text',
data: `=== PAGE STATE ===\n${pageState.stateText}`,
__metadata: { save: false },
},
];
}

Building Observation Parts

A common pattern is a helper that builds multiple observation parts and marks them all as transient:

function buildObservationParts(observation: {
url: string;
screenshot?: string;
stateText?: string;
}): DistriPart[] {
const parts: DistriPart[] = [];

// URL and browser state
parts.push({
part_type: 'text',
data: `=== BROWSER STATE ===\nURL: ${observation.url}`,
__metadata: { save: false },
});

// Screenshot (large, never save)
if (observation.screenshot) {
parts.push({
part_type: 'image',
data: {
bytes: observation.screenshot,
mime_type: 'image/png',
name: 'viewport.png',
},
__metadata: { save: false },
});
}

// Page state text
if (observation.stateText) {
parts.push({
part_type: 'text',
data: `=== PAGE STATE ===\n${observation.stateText}`,
__metadata: { save: false },
});
}

return parts;
}

When to Use save: false

Use CaseWhy
Browser screenshotsLarge binary data, only relevant for current step
DOM snapshotsChanges every step, stale data wastes context
Intermediate observationsAgent needs them now but not in history
Temporary tool outputVerbose data that should not clutter the thread
Keep context lean

Without save: false, every tool response is persisted to the thread. For multi-step agents (e.g., browser automation with 30+ steps), this quickly fills the context window. Mark ephemeral parts as save: false to keep only the data the agent needs right now.


Hook Kinds Reference

Distri provides four hook points. beforeExecute is the most commonly used:

HookWhen It FiresUse Case
beforeExecuteBefore each execution stepInject fresh context, modify parts
onPlanStartAgent begins planningLog start, inject planning context
onPlanEndPlanning completesReview plan before execution
onStepEndAfter each step completesLog progress, update UI

References