Functions
AgentRuntime
Alias of ::rt/AgentRuntime
forget-notification
fn (rt: ::ai::agent::runtime/AgentRuntime, recipient-id: Str, id: Str): Bool
Permanently delete a notification by id. Returns true when removed.
hydrate
fn (raw: Map): Notification
Lift a serialized notification map back into a Notification value.
list-notifications
fn (rt: ::ai::agent::runtime/AgentRuntime, recipient-id: Str, opts: Map?): Vec
List notifications for a recipient, oldest first.
opts:
unread-only(Bool, default false): drop records withread-at.kind(Str?): if set, only matchingkindrecords.limit(Int?): cap the result length (after filtering).
mark-all-read
fn (rt: ::ai::agent::runtime/AgentRuntime, recipient-id: Str): Int
Mark every unread notification for a recipient as read. Returns the count touched.
mark-read
fn (rt: ::ai::agent::runtime/AgentRuntime, recipient-id: Str, id: Str): Notification?
Mark a single notification (looked up by id) read for the given
recipient. Returns the updated Notification, or null when the
id is unknown for that recipient.
now-secs
fn (): Int
ns
Alias of ::ai::agent::notify/
Notification ledger for scheduled (and ad-hoc) agent output.
Scheduled handlers (daily recaps, stale-task nudges, standups, …) run
without a client subscribed to their reply stream, so they need a
durable place to land their output. notify is that place: a
runtime-scoped store of Notification records the active UI can
surface via an /inbox command or a "new since you were here" badge.
Design notes
- Storage:
rt.notify-store(a::hot::store/Map). One key per notification, value is a serializedNotificationmap. Keys are${recipient-id}:${created-at}:${id}so they sort newest-last when listed. - Recipient: a free-form string. PersonalAgents typically pass the
user id (
u1); TeamAgents passchannel:<session-id>so a channel-wide inbox is keyed independently from any one user. - Kind: short tag for the emitter (e.g.
daily-recap,stale-task-nudge,daily-standup). Lets UIs filter or group. - Read state: a single
read-atepoch-seconds field.null= unread.
This is intentionally a small primitive. Production agents will
likely add external delivery (Slack post, email) by tailing this
store from a separate channel — see record-notification for the
write path you'd reuse.
record-notification
fn (rt: ::ai::agent::runtime/AgentRuntime, opts: Map): Notification
Append a new notification to rt.notify-store and return the
typed record. opts shape:
recipient-id(Str, required): user id for personal agents,channel:<session-id>for team agents.kind(Str, required): emitter tag, e.g.daily-recap.body(Str, required): the message body. Markdown is fine.session-id(Str?): the originating session if any.source(Str?): a free-form provenance string (cron job name).meta(Map?): extra structured metadata.
render-inbox
fn (notifications: Vec): Str
Format a list of notifications as a single markdown block suitable
for an /inbox reply. Empty input yields a friendly "no
notifications" line.
storage-key
fn (recipient-id: Str, created-at: Int, id: Str): Str
Compose the store key for a notification. Sortable by recipient,
then time, then id — so ::store/list returns chronologically
grouped entries per recipient.
unread-count
fn (rt: ::ai::agent::runtime/AgentRuntime, recipient-id: Str): Int
Number of unread notifications for a recipient.
Types
Notification
Notification type {
id: Str,
recipient-id: Str,
session-id: Str?,
kind: Str,
body: Str,
source: Str?,
created-at: Int,
read-at: Int?,
meta: Map?
}
One notification record. recipient-id is opaque (user id for
personal, channel:<sid> for team). kind identifies the cron
handler that wrote it; UIs can group/badge by kind.