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

# Event Schema

> Every event type emitted by Nanny, with full field definitions.

## Format

All events are JSON objects emitted one per line (NDJSON). Every event has:

| Field   | Type    | Description                       |
| ------- | ------- | --------------------------------- |
| `event` | string  | Event type identifier (see below) |
| `ts`    | integer | Unix timestamp in milliseconds    |

***

## ExecutionStarted

Emitted immediately before the child process is spawned.
Always the first event in any log.

```json theme={null}
{
  "event": "ExecutionStarted",
  "ts": 1711234567000,
  "limits_set": "[limits]",
  "command": "python agent.py",
  "limits": {
    "steps": 100,
    "tokens": 1000,
    "timeout": 30000
  }
}
```

| Field            | Type    | Description                                              |
| ---------------- | ------- | -------------------------------------------------------- |
| `limits_set`     | string  | The active limit set name. `"[limits]"` = base defaults. |
| `command`        | string  | The full command string as passed to `nanny run`.        |
| `limits.steps`   | integer | Active step limit.                                       |
| `limits.tokens`  | integer | Active token limit.                                      |
| `limits.timeout` | integer | Active timeout in milliseconds.                          |

***

## ExecutionStopped

Emitted on every exit path — clean completion, limit breach, spawn failure, or internal error.
Always the last event in any log.

```json theme={null}
{
  "event": "ExecutionStopped",
  "ts": 1711234572000,
  "reason": "AgentCompleted",
  "steps": 42,
  "tokens_spent": 380,
  "elapsed_ms": 4823
}
```

| Field          | Type    | Description                               |
| -------------- | ------- | ----------------------------------------- |
| `reason`       | string  | Why execution stopped. See reasons below. |
| `steps`        | integer | Total steps completed.                    |
| `tokens_spent` | integer | Total tokens spent.                       |
| `elapsed_ms`   | integer | Total wall-clock time in milliseconds.    |

### Stop reasons

| Reason              | Description                                                                                                                                                       |
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `AgentCompleted`    | Process exited cleanly on its own.                                                                                                                                |
| `TimeoutExpired`    | Wall-clock timeout was reached. Process was killed.                                                                                                               |
| `MaxStepsReached`   | Step limit was reached. Process was killed.                                                                                                                       |
| `BudgetExhausted`   | Token budget was exhausted. Process was killed.                                                                                                                   |
| `ToolDenied`        | A tool call was blocked because the tool is not in the allowlist.                                                                                                 |
| `RuleDenied`        | A tool call was blocked by a custom rule or a per-tool `max_calls` limit.                                                                                         |
| `ManualStop`        | Execution was stopped programmatically.                                                                                                                           |
| `ProcessCrashed`    | The child process exited with a non-zero code unexpectedly.                                                                                                       |
| `BridgeUnavailable` | The bridge was active but unreachable during rule evaluation or a tool call. Nanny fails closed — silently continuing with ungoverned execution is never allowed. |

***

## AgentScopeEntered

Emitted when a function annotated with `#[nanny::agent("name")]` is entered. Records the limits active for that scope.

```json theme={null}
{
  "event": "AgentScopeEntered",
  "ts": 1711234567200,
  "name": "researcher",
  "limits": {
    "steps": 200,
    "tokens": 5000,
    "timeout": 120000
  }
}
```

| Field            | Type    | Description                                                              |
| ---------------- | ------- | ------------------------------------------------------------------------ |
| `name`           | string  | The name of the agent scope (matches `[limits.<name>]` in `nanny.toml`). |
| `limits.steps`   | integer | Step limit active for this scope.                                        |
| `limits.tokens`  | integer | Token limit active for this scope.                                       |
| `limits.timeout` | integer | Timeout active for this scope in milliseconds.                           |

***

## AgentScopeExited

Emitted when a function annotated with `#[nanny::agent("name")]` returns. Records usage during that scope.

```json theme={null}
{
  "event": "AgentScopeExited",
  "ts": 1711234571800,
  "name": "researcher",
  "steps_used": 12,
  "tokens_used": 240
}
```

| Field         | Type    | Description                        |
| ------------- | ------- | ---------------------------------- |
| `name`        | string  | The name of the agent scope.       |
| `steps_used`  | integer | Steps consumed during this scope.  |
| `tokens_used` | integer | Tokens consumed during this scope. |

***

## StepCompleted

Emitted after each agent step when using the Rust SDK or Python SDK.

```json theme={null}
{
  "event": "StepCompleted",
  "ts": 1711234568000,
  "step": 1
}
```

| Field  | Type    | Description                       |
| ------ | ------- | --------------------------------- |
| `step` | integer | The step number, starting from 1. |

***

## ToolAllowed

Emitted when Nanny permits a tool call to proceed.

```json theme={null}
{
  "event": "ToolAllowed",
  "ts": 1711234568101,
  "tool": "http_get"
}
```

| Field  | Type   | Description                            |
| ------ | ------ | -------------------------------------- |
| `tool` | string | The name of the tool that was allowed. |

***

## ToolDenied

Emitted when Nanny blocks a tool call because the tool is not in the `[tools] allowed` list in `nanny.toml`.

```json theme={null}
{
  "event": "ToolDenied",
  "ts": 1711234568101,
  "tool": "write_file"
}
```

| Field  | Type   | Description                            |
| ------ | ------ | -------------------------------------- |
| `tool` | string | The name of the tool that was blocked. |

***

## RuleDenied

Emitted when a custom rule or a per-tool `max_calls` limit blocks a tool call. The tool was on the allowlist but a rule returned a denial before the call executed.

```json theme={null}
{
  "event": "RuleDenied",
  "ts": 1711234568101,
  "tool": "http_get",
  "rule_name": "no_loop"
}
```

| Field       | Type   | Description                                                                                                          |
| ----------- | ------ | -------------------------------------------------------------------------------------------------------------------- |
| `tool`      | string | The name of the tool that triggered the rule.                                                                        |
| `rule_name` | string | The name of the rule that fired (e.g. `"no_loop"`) or the per-tool limit that was hit (e.g. `"http_get.max_calls"`). |

***

## ToolFailed

Emitted when a permitted tool fails at runtime. Distinct from `ToolDenied` — the tool was allowed but encountered an error (network failure, bad arguments, timeout). No tokens are charged on failure.

```json theme={null}
{
  "event": "ToolFailed",
  "ts": 1711234568200,
  "tool": "http_get",
  "error": "connection refused"
}
```

| Field   | Type   | Description                       |
| ------- | ------ | --------------------------------- |
| `tool`  | string | The name of the tool that failed. |
| `error` | string | A description of the error.       |

***

## LlmUsageRecorded

Emitted when LLM token usage is reported to Nanny — via the Python SDK's `instrument()` or the Rust SDK's `report_usage()`. Records the measured input and output tokens debited from the budget, plus optional model and provider labels.

```json theme={null}
{
  "event": "LlmUsageRecorded",
  "ts": 1711234568150,
  "input": 1200,
  "output": 340,
  "model": "gpt-4o",
  "provider": "openai"
}
```

| Field      | Type    | Description                                                                                     |
| ---------- | ------- | ----------------------------------------------------------------------------------------------- |
| `input`    | integer | Prompt / input tokens consumed by the LLM call.                                                 |
| `output`   | integer | Completion / output tokens produced by the LLM call.                                            |
| `model`    | string  | Optional. Model identifier reported by the SDK (e.g. `"gpt-4o"`). Omitted when not provided.    |
| `provider` | string  | Optional. Provider identifier reported by the SDK (e.g. `"openai"`). Omitted when not provided. |
