Format
The event log is NDJSON, one JSON object per line, emitted in chronological order. Every object has an"event" field identifying its type and a "ts" field with a Unix
timestamp in milliseconds.
Output destination
By default, events are written to stdout, interleaved with your agent’s own output. To separate them, configure file output:.nanny/logs/log.ndjson, a location Nanny always owns and auto-creates,
never something you point at a path yourself. Only set file if you want a different
name: a bare name, no extension. Nanny always appends .ndjson itself:
.nanny/logs/ is meant to stay out of git (Nanny adds it to .gitignore automatically
the first time it’s created): it’s a local audit trail, not source. If you later log this
NANNY_API_KEY, Cloud sync can back-fill from whatever accumulated in
that folder before sync was ever turned on, not just events going forward.
Or pipe stdout to a file at the shell level:
Guaranteed events
Every execution emits exactly these two events, in this order:ExecutionStarted
Always the first event. Emitted immediately before the child process is spawned.ExecutionStopped
Always the last event of a complete run. Emitted on every exit path: clean exit, a policy stop, an error, or a signal.reason is one of four: ToolDenied, RuleDenied, AgentCompleted, or
ManualStop. Only the first two are policy violations.
SDK events
When the Rust SDK macros or Python SDK decorators are active, additional events are emitted for each tool call. These appear betweenExecutionStarted and ExecutionStopped:
ToolDenied and RuleDenied are distinct denial events. ToolDenied means the tool was not permitted at all; RuleDenied means the tool was permitted but a rule blocked this specific call.
ToolFailed is different from both: the tool was allowed and called, but hit a runtime error. No tokens are recorded for a failure.