> ## Documentation Index
> Fetch the complete documentation index at: https://polos.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Lifecycle Hooks

> Hook into agent execution events

Add hooks to log, validate, or modify agent execution at specific points.

## Available hooks

| Hook                  | Timing                | Use Cases                 |
| --------------------- | --------------------- | ------------------------- |
| `on_start`            | Before agent starts   | Input validation, logging |
| `on_end`              | After agent completes | Cleanup, metrics          |
| `on_agent_step_start` | Before each LLM call  | Rate limiting             |
| `on_agent_step_end`   | After each LLM call   | Logging responses         |
| `on_tool_start`       | Before tool execution | Modify inputs             |
| `on_tool_end`         | After tool execution  | Modify outputs            |

## Define hooks

<CodeGroup>
  ```python Python theme={null}
  from polos import hook, WorkflowContext, HookContext, HookResult

  @hook
  def log_start(ctx: WorkflowContext, hook_ctx: HookContext) -> HookResult:
      """Log when agent execution starts."""
      print(f"Agent started - workflow: {hook_ctx.workflow_id}")
      return HookResult.continue_with()

  @hook
  def log_end(ctx: WorkflowContext, hook_ctx: HookContext) -> HookResult:
      """Log when agent execution ends."""
      print(f"Agent completed - workflow: {hook_ctx.workflow_id}")
      return HookResult.continue_with()

  @hook
  def validate_input(ctx: WorkflowContext, hook_ctx: HookContext) -> HookResult:
      """Validate input before agent execution starts."""
      payload = hook_ctx.current_payload or {}
      prompt = payload.get("prompt", "")

      if not prompt or not prompt.strip():
          return HookResult.fail("Empty prompt not allowed")

      if len(prompt) > 10000:
          return HookResult.fail("Prompt too long (max 10000 characters)")

      return HookResult.continue_with()
  ```

  ```typescript TypeScript theme={null}
  import { defineHook, HookResult } from '@polos/sdk';
  import type { HookResultType } from '@polos/sdk';

  export const logStart = defineHook(
    async (ctx, _hookCtx): Promise<HookResultType> => {
      console.log(`Agent started - workflow: ${ctx.workflowId}`);
      return HookResult.continue();
    },
    { name: 'log_start' },
  );

  export const logEnd = defineHook(
    async (ctx, _hookCtx): Promise<HookResultType> => {
      console.log(`Agent completed - workflow: ${ctx.workflowId}`);
      return HookResult.continue();
    },
    { name: 'log_end' },
  );

  export const validateInput = defineHook(
    async (_ctx, hookCtx): Promise<HookResultType> => {
      const payload = hookCtx.currentPayload as Record<string, unknown> | undefined;
      const input = String(payload?.['input'] ?? '');

      if (!input || !input.trim()) {
        return HookResult.fail('Empty input not allowed');
      }
      if (input.length > 10000) {
        return HookResult.fail('Input too long (max 10000 characters)');
      }
      return HookResult.continue();
    },
    { name: 'validate_input' },
  );
  ```
</CodeGroup>

## Attach hooks to agent

<CodeGroup>
  ```python Python theme={null}
  from polos import Agent, max_steps, MaxStepsConfig

  logged_agent = Agent(
      id="logged_agent",
      provider="openai",
      model="gpt-4o-mini",
      system_prompt="You are a helpful assistant.",
      tools=[search, calculate],
      on_start=[validate_input, log_start],
      on_end=[log_end],
      on_agent_step_start=[log_step_start],
      on_agent_step_end=[log_step_end],
      on_tool_start=[log_tool_start],
      on_tool_end=[log_tool_end],
      stop_conditions=[
          max_steps(MaxStepsConfig(limit=5)),
      ],
  )
  ```

  ```typescript TypeScript theme={null}
  import { defineAgent, maxSteps } from '@polos/sdk';
  import { openai } from '@ai-sdk/openai';
  import { search, calculate } from './tools.js';
  import {
    logStart, logEnd, logStepStart, logStepEnd,
    logToolStart, logToolEnd, validateInput,
  } from './hooks.js';

  const loggedAgent = defineAgent({
    id: 'logged_agent',
    model: openai('gpt-4o-mini'),
    systemPrompt: 'You are a helpful assistant.',
    tools: [search, calculate],
    onStart: [validateInput, logStart],
    onEnd: [logEnd],
    onAgentStepStart: [logStepStart],
    onAgentStepEnd: [logStepEnd],
    onToolStart: [logToolStart],
    onToolEnd: [logToolEnd],
    stopConditions: [maxSteps({ count: 5 })],
  });
  ```
</CodeGroup>

## Run it

<CodeGroup>
  ```bash Python theme={null}
  git clone https://github.com/polos-dev/polos.git
  cd polos/python-examples/07-lifecycle-hooks
  cp .env.example .env  # Add your POLOS_PROJECT_ID and API key
  uv sync
  python main.py
  ```

  ```bash TypeScript theme={null}
  git clone https://github.com/polos-dev/polos.git
  cd polos/typescript-examples/07-lifecycle-hooks
  cp .env.example .env  # Add your POLOS_PROJECT_ID and API key
  npm install
  npx tsx main.ts
  ```
</CodeGroup>

Open [http://localhost:5173](http://localhost:5173) to view your agents and workflows, run them from the UI, and see execution traces.

[Python example on GitHub](https://github.com/polos-dev/polos/tree/main/python-examples/07-lifecycle-hooks) | [TypeScript example on GitHub](https://github.com/polos-dev/polos/tree/main/typescript-examples/07-lifecycle-hooks)
