pub fn map_cursor_hook(payload: &Value) -> Option<(HookSignal, Option<String>)>Expand description
Map one cursor lifecycle-hook payload (the JSON the CLI pipes to the
hook command’s stdin) to a signal and an optional human-readable
detail. None = the event carries no status meaning for this lane and
is IGNORED (malformed or unmapped payloads are always ignorable — the
relay treats them as success and never retries).
The mapping is deliberately honest about what a headless
--print session can produce:
sessionStart→HookSignal::Running.stop→HookSignal::TurnFinished. In an INTERACTIVE cursor session a stop means “idle at the prompt”; kranz’s cursor backend is always headless single-shot (agent --print, seecrate::backend_cursormodule docs), where the agent stopping IS the turn finishing.sessionEnd→completedmaps toHookSignal::TurnFinished;aborted/error/window_close/user_closemap toHookSignal::Interruptedwith the reason (and anyerror_message) as detail. An absent reason is unmapped — never guessed at.postToolUseFailurewithfailure_type == "permission_denied"→HookSignal::NeedsInput: a headless session wanted a capability its posture refused, which is exactly the “a human should look” signal this lane exists to surface. Other failure types (timeout/error) are ordinary tool noise, not status.