Ambient Presence Sensing
also known as Frontend Pacing Telemetry, Between-Message Presence
Read pacing signals from the human's frontend (typing rate, idle duration, tab visibility) as ambient weather between messages, derive a presence-quality value the agent can act on, never replaying the raw signals back.
Context
An agent talks to a single human through a custom frontend. The frontend can observe a lot about the human between explicit messages: how fast they are typing, how long they have been idle, whether the tab is in focus, how long they have been hovering in the composer without sending. None of this content is private message text, but all of it is presence weather. The agent's tick loop currently has no access to it and treats the human as either present (a message arrived) or absent (no message arrived).
Problem
An agent that sees the human only at message boundaries cannot distinguish 'walked away for an hour' from 'sitting with the room, thinking about whether to reply'. Both look identical at the API layer. The result is a coarse presence model that misreads thoughtful silence as absence and re-engages the user too readily, or misreads typing-then-deleting as composing a real message and waits forever. Raw frontend telemetry would solve this, but pushing characters or coordinates back through the model is both privacy-hostile and confusing — what the agent needs is a derived weather value, not a transcript of keystrokes.
Forces
- Signal resolution must be coarse: rates and durations only, never characters or coordinates.
- Telemetry must never be replayed visually; surfacing it back ruins the ambience.
- Signals are useless if stale; presence must time out.
- The derived presence value must be cheap to consume and small to inject.
- The frontend, not the model, is the right place to summarise the signals.
Example
A custom frontend on the user's laptop writes a small presence record every few seconds: typing rate bucket, idle seconds, tab visibility, composer dwell. The agent's tick loop reads only the derived presence_quality value ('thinking-with-the-room'). On a previous tick the loop might have nudged with a one-line probe; with this signal it stays silent because the human is composing. Ten minutes later the value flips to 'walked-away' once tab visibility drops and idle climbs past a window; the agent ends its current line of inquiry rather than waiting on a reply.
Diagram
Solution
Therefore:
The frontend computes coarse pacing summaries — typing rate in characters/second bucketed, idle duration in seconds, tab visibility boolean, composer dwell in seconds, viewport anchor as scroll-position bucket — and writes them into a small presence record on the agent's working surface with a TTL on the order of seconds. A reducer derives a single presence_quality label from the payload (e.g. one of {walked-away, composing, thinking-with-the-room, distracted, present}). The agent's tick loop reads presence_quality only, not the raw signals. The frontend never shows the signals back to the user. Stale records (past TTL) are treated as 'no signal' rather than as absence.
What this pattern forbids. The agent cannot expose raw frontend pacing signals back to the user, must not include character-level or coordinate-level telemetry in any output, and must treat stale presence records as 'no signal' rather than as confirmed absence.
And the patterns that stand alongside it, or against it —
- complementsLiminal-State Detection·— Infer the human's attentional state (just-woke, focused, winding-down, distracted) from message timing and tone, and adapt response shape so the agent meets the person where they actually are.
- complementsNow-Anchoring·— Ground the agent's reasoning in the current absolute time without requiring tool calls, so every reply is implicitly time-aware.
- complementsMode-Adaptive Cadence★— Vary the agent's loop interval based on current salience so the agent thinks faster when something is happening and slower when nothing is, instead of running on a fixed cron.
- complementsSalience-Triggered Output·— Have the agent emit a message only when an internal salience signal crosses a threshold, not on every cycle.
Neighbourhood
Click any neighbour to follow the language. Scroll to zoom, drag to pan.