Documentation
¶
Overview ¶
Package notify emits dep-free terminal notifications (BEL and/or OSC-9 desktop notifications) when Zero finishes a turn or needs user input.
Index ¶
- Constants
- func DefaultMessage(event Event) string
- func Enabled(mode Mode) bool
- func MaybeAddWebhookSink(n *Notifier, env func(string) string, logf func(format string, args ...any))
- type Config
- type Event
- type FocusMode
- type Mode
- type Notifier
- type Sink
- type WebhookConfig
- type WebhookLink
- type WebhookSink
Constants ¶
const ( // EnvWebhookURL holds the destination webhook/Slack URL. Empty disables it. EnvWebhookURL = "ZERO_NOTIFY_WEBHOOK_URL" // EnvWebhookSummary is an optional one-line run summary attached to every // payload (for example "nightly audit run"). EnvWebhookSummary = "ZERO_NOTIFY_WEBHOOK_SUMMARY" )
Webhook delivery is configured entirely from the environment. A webhook URL typically embeds a secret token, so sourcing it from the environment keeps it out of any on-disk config file. The sink is strictly opt-in: with EnvWebhookURL unset the wiring helper attaches nothing and the notifier behaves exactly as before.
Variables ¶
This section is empty.
Functions ¶
func DefaultMessage ¶
DefaultMessage is the generic OSC-9 body for an event (no prompt content).
func MaybeAddWebhookSink ¶
func MaybeAddWebhookSink(n *Notifier, env func(string) string, logf func(format string, args ...any))
MaybeAddWebhookSink attaches a webhook sink to n when a webhook URL is present in the environment, and is otherwise a no-op. env resolves an environment variable (pass os.Getenv); logf records one redacted line per failed delivery (pass nil to stay silent — for example a TUI that owns the screen). It is safe to call unconditionally: configuration alone decides whether the sink exists.
The attached sink is still subject to the notifier's Mode/focus policy, so a webhook only delivers when notifications are enabled (for example `--notify both`), matching the rest of the notification surface.
Types ¶
type Notifier ¶
type Notifier struct {
// contains filtered or unexported fields
}
Notifier emits notifications to w according to cfg, and fans them out to any attached Sinks. Safe for concurrent use.
func New ¶
New returns a Notifier. focused defaults to false so a headless caller (no focus signal) still emits under the default "unfocused" focus mode; an interactive caller should call SetFocused(true) at launch.
func (*Notifier) AddSink ¶
AddSink registers an additional destination that receives every eligible event (subject to the same mode/focus policy as the terminal). Sinks fire even when the Notifier has no terminal writer, so a headless CI run can still reach Slack. Safe to call concurrently with Notify.
func (*Notifier) Notify ¶
Notify emits a notification for event if policy allows. message is the OSC-9 body (ignored for bell) and is also forwarded verbatim to every sink. Write errors are intentionally ignored — a failed notification must never disrupt the run.
The terminal sequence is gated by Mode (bell vs OSC-9) and by the focus policy; sinks are gated only by "notifications enabled" plus the focus policy, because a sink is a separate channel and not bound to the terminal mechanism. Sinks are invoked outside the lock so a slow/blocking sink cannot stall a concurrent Notify or SetFocused.
func (*Notifier) SetFocused ¶
SetFocused records the terminal focus state (TUI FocusMsg/BlurMsg).
type Sink ¶
Sink is an additional notification destination beyond the terminal (bell / OSC-9). A webhook/Slack sink is the canonical implementation: it lets an unattended run report "finished / needs input / verify failed" to a chat channel. Emit is fire-and-forget — it must never return an error, panic out, or block the run for long, so an unreachable endpoint cannot disrupt the agent. Implementations are responsible for their own redaction and timeouts.
type WebhookConfig ¶
type WebhookConfig struct {
// URL is the destination. Empty disables the sink.
URL string
// Summary is an optional run summary attached to every emitted payload.
Summary string
// Links are optional labeled URLs attached to every emitted payload.
Links []WebhookLink
// Client is the HTTP client used for delivery. When nil a client with a
// conservative timeout is used; its default transport honors HTTP(S)_PROXY
// when a proxy is configured.
Client *http.Client
// Logf records a single line per failed delivery. Lines are passed through
// the repo redaction before being written, so a token in the URL or message
// is never logged in the clear. When nil, failures are silent.
Logf func(format string, args ...any)
// ExtraSecrets are literal values (for example the resolved API key) that must
// be masked in the payload and logs in addition to the built-in patterns.
ExtraSecrets []string
}
WebhookConfig configures a WebhookSink. URL is a Slack incoming-webhook URL or any generic endpoint that accepts a JSON POST. The zero value (empty URL) yields an inert sink whose Emit is a no-op, so callers can wire a sink unconditionally and let configuration decide whether it fires.
type WebhookLink ¶
WebhookLink is an optional labeled URL attached to a notification (for example a link to the CI run, the opened PR, or the session log).
type WebhookSink ¶
type WebhookSink struct {
// contains filtered or unexported fields
}
WebhookSink delivers notifications to a webhook/Slack endpoint. It implements Sink. Delivery is best-effort and fails soft: a non-2xx response or a transport error is logged (redacted) and swallowed so it can never disrupt the run.
func NewWebhookSink ¶
func NewWebhookSink(cfg WebhookConfig) *WebhookSink
NewWebhookSink builds a WebhookSink from cfg. A blank URL produces an inert sink (Emit is a no-op).
func (*WebhookSink) Emit ¶
func (s *WebhookSink) Emit(event Event, message string)
Emit builds the JSON payload for event/message and POSTs it to the webhook. It is fire-and-forget: every failure path logs (redacted) and returns nil-ish rather than propagating, satisfying the Sink contract that a notification must never crash the run.