Skip to main content

Format

All events are JSON objects emitted one per line (NDJSON). Every event has:
FieldTypeDescription
eventstringEvent type identifier (see below)
tsintegerUnix timestamp in milliseconds

ExecutionStarted

Emitted immediately before the child process is spawned. Always the first event in any log.
{
  "event": "ExecutionStarted",
  "ts": 1711234567000,
  "limits_set": "[limits]",
  "command": "python agent.py",
  "limits": {
    "steps": 100,
    "cost": 1000,
    "timeout": 30000
  }
}
FieldTypeDescription
limits_setstringThe active limit set name. "[limits]" = base defaults.
commandstringThe full command string as passed to nanny run.
limits.stepsintegerActive step limit.
limits.costintegerActive cost limit.
limits.timeoutintegerActive 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.
{
  "event": "ExecutionStopped",
  "ts": 1711234572000,
  "reason": "AgentCompleted",
  "steps": 42,
  "cost_spent": 380,
  "elapsed_ms": 4823
}
FieldTypeDescription
reasonstringWhy execution stopped. See reasons below.
stepsintegerTotal steps completed.
cost_spentintegerTotal cost units spent.
elapsed_msintegerTotal wall-clock time in milliseconds.

Stop reasons

ReasonDescription
AgentCompletedProcess exited cleanly on its own.
TimeoutExpiredWall-clock timeout was reached. Process was killed.
MaxStepsReachedStep limit was reached. Process was killed.
BudgetExhaustedCost budget was exhausted. Process was killed.
ToolDeniedA tool call was blocked by the allowlist or a rule.
RuleDeniedA custom rule returned a denial.
ManualStopExecution was stopped programmatically.
ProcessCrashedThe child process exited with a non-zero code unexpectedly.

AgentScopeEntered

Emitted when a function annotated with #[nanny::agent("name")] is entered. Records the limits active for that scope.
{
  "event": "AgentScopeEntered",
  "ts": 1711234567200,
  "name": "researcher",
  "limits": {
    "steps": 200,
    "cost": 5000,
    "timeout": 120000
  }
}
FieldTypeDescription
namestringThe name of the agent scope (matches [limits.<name>] in nanny.toml).
limits.stepsintegerStep limit active for this scope.
limits.costintegerCost limit active for this scope.
limits.timeoutintegerTimeout active for this scope in milliseconds.

AgentScopeExited

Emitted when a function annotated with #[nanny::agent("name")] returns. Records usage during that scope.
{
  "event": "AgentScopeExited",
  "ts": 1711234571800,
  "name": "researcher",
  "steps_used": 12,
  "cost_used": 240
}
FieldTypeDescription
namestringThe name of the agent scope.
steps_usedintegerSteps consumed during this scope.
cost_usedintegerCost units consumed during this scope.

StepCompleted

Emitted after each agent step when using the Rust SDK or Python SDK.
{
  "event": "StepCompleted",
  "ts": 1711234568000,
  "step": 1
}
FieldTypeDescription
stepintegerThe step number, starting from 1.

ToolAllowed

Emitted when Nanny permits a tool call to proceed.
{
  "event": "ToolAllowed",
  "ts": 1711234568101,
  "tool": "http_get"
}
FieldTypeDescription
toolstringThe name of the tool that was allowed.

ToolDenied

Emitted when Nanny blocks a tool call — either the tool is not in the allowlist, or a rule returned a denial.
{
  "event": "ToolDenied",
  "ts": 1711234568101,
  "tool": "write_file",
  "reason": "ToolDenied"
}
FieldTypeDescription
toolstringThe name of the tool that was blocked.
reasonstringThe stop reason that triggered the denial.

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 cost is charged on failure.
{
  "event": "ToolFailed",
  "ts": 1711234568200,
  "tool": "http_get",
  "error": "connection refused"
}
FieldTypeDescription
toolstringThe name of the tool that failed.
errorstringA description of the error.