# `Nous.Plugin`
[🔗](https://github.com/nyo16/nous/blob/v0.17.1/lib/nous/plugin.ex#L1)

Behaviour for composable agent extensions.

Plugins allow you to extend agent capabilities without modifying the core agent
or creating monolithic behaviour modules. Multiple plugins can be composed together.

## Example

    defmodule MyApp.Plugins.Logging do
      @behaviour Nous.Plugin

      @impl true
      def init(_agent, ctx) do
        ctx
      end

      @impl true
      def before_request(_agent, ctx, _tools) do
        IO.puts("Making LLM request with #{length(ctx.messages)} messages")
        {ctx, []}
      end

      @impl true
      def after_response(_agent, response, ctx) do
        IO.puts("Got response: #{inspect(response.content)}")
        ctx
      end
    end

## Usage

    agent = Agent.new("openai:gpt-4",
      plugins: [MyApp.Plugins.Logging, Nous.Plugins.TodoTracking]
    )

## Callback Execution Order

Plugins are executed in list order. The context flows through each plugin
sequentially for all hooks:

1. `init/2` — once at run start
2. `system_prompt/2` — once, fragments joined into system message
3. `tools/2` — once per iteration, tools collected
4. `before_request/3` — before each LLM call
5. `after_response/3` — after each LLM response
6. `after_run/3` — once after the entire run completes (post-loop)

## Built-in Plugins

- `Nous.Plugins.TodoTracking` - Automatic todo/task management
- `Nous.Plugins.HumanInTheLoop` - Human approval for tool calls
- `Nous.Plugins.Summarization` - Context window management

# `after_response`
*optional* 

```elixir
@callback after_response(
  agent :: Nous.Agent.t(),
  response :: Nous.Message.t(),
  ctx :: Nous.Agent.Context.t()
) :: Nous.Agent.Context.t()
```

Post-process after each LLM response.

Receives the LLM response message and current context.
Return the updated context.

# `after_run`
*optional* 

```elixir
@callback after_run(
  agent :: Nous.Agent.t(),
  result :: map(),
  ctx :: Nous.Agent.Context.t()
) ::
  Nous.Agent.Context.t()
```

Post-process after the entire agent run completes.

Receives the agent, the final result map, and the final context.
Return the updated context. Use this for end-of-run housekeeping
like auto-updating memory.

# `before_request`
*optional* 

```elixir
@callback before_request(
  agent :: Nous.Agent.t(),
  ctx :: Nous.Agent.Context.t(),
  tools :: [Nous.Tool.t()]
) ::
  {Nous.Agent.Context.t(), [Nous.Tool.t()]}
```

Pre-process before each LLM call.

Receives the current context and tools list. Return the updated context
and the updated tools list.

# `init`
*optional* 

```elixir
@callback init(agent :: Nous.Agent.t(), ctx :: Nous.Agent.Context.t()) ::
  Nous.Agent.Context.t()
```

Initialize the plugin when the agent run starts.

Use this to set up initial state in `ctx.deps`, register callbacks, etc.

Called once at the start of each `Nous.Agent.run/3`.

# `system_prompt`
*optional* 

```elixir
@callback system_prompt(agent :: Nous.Agent.t(), ctx :: Nous.Agent.Context.t()) ::
  String.t() | nil
```

Contribute system prompt fragments.

Return a string to append to the system prompt, or nil for no contribution.
Fragments from all plugins are joined with newlines.

# `tools`
*optional* 

```elixir
@callback tools(agent :: Nous.Agent.t(), ctx :: Nous.Agent.Context.t()) :: [Nous.Tool.t()]
```

Contribute additional tools for this agent run.

Return a list of `Nous.Tool` structs to add to the agent's tool set.
Called once per iteration before the LLM request.

# `collect_system_prompts`

```elixir
@spec collect_system_prompts([module()], Nous.Agent.t(), Nous.Agent.Context.t()) ::
  String.t() | nil
```

Collect system prompt fragments from all plugins.

# `collect_tools`

```elixir
@spec collect_tools([module()], Nous.Agent.t(), Nous.Agent.Context.t()) :: [
  Nous.Tool.t()
]
```

Collect tools from all plugins.

# `run_after_response`

```elixir
@spec run_after_response(
  [module()],
  Nous.Agent.t(),
  Nous.Message.t(),
  Nous.Agent.Context.t()
) ::
  Nous.Agent.Context.t()
```

Run `after_response/3` across all plugins, threading context.

# `run_after_run`

```elixir
@spec run_after_run([module()], Nous.Agent.t(), map(), Nous.Agent.Context.t()) ::
  Nous.Agent.Context.t()
```

Run `after_run/3` across all plugins, threading context.

# `run_before_request`

```elixir
@spec run_before_request([module()], Nous.Agent.t(), Nous.Agent.Context.t(), [
  Nous.Tool.t()
]) ::
  {Nous.Agent.Context.t(), [Nous.Tool.t()]}
```

Run `before_request/3` across all plugins, threading context and tools.

Each plugin receives the current tools and returns the updated tools list.

# `run_init`

```elixir
@spec run_init([module()], Nous.Agent.t(), Nous.Agent.Context.t()) ::
  Nous.Agent.Context.t()
```

Run `init/2` across all plugins, threading context through each.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
