Agent Attention Prompts
Status: Open — design proposal
Problem
Section titled “Problem”The biggest hidden cost of running AI coding agents today is idle wall-clock. Agents finish work, hit a confirmation prompt, run into an ambiguous decision, or stop on a long-running command and silently wait for the operator to come back to the terminal. The operator, meanwhile, is heads-down on something else — reviewing a PR, in a meeting, on another machine — with no signal that anything wants their attention.
Every minute the agent waits is a minute of compounding throughput loss across however many agents the operator is running in parallel. The point of jackin’ is to make running many agents safe and convenient; “many agents” is only a real win if the operator can stay in flow and only context-switch when an agent actually needs them.
This roadmap item proposes agent attention prompts: an opt-in, per-workspace, per-role notification channel that fires a host-side OS notification when an agent inside a jackin’ container is waiting on operator input, with optional escalation to sound and a click action that focuses the right terminal tab.
What the operator gets
Section titled “What the operator gets”The end state, from the operator’s seat:
- A passive notification, on the host, when an agent stops to wait. macOS Notification Center / Linux desktop notification / Windows toast. Title names the workspace, role, and (optionally) the question the agent is asking. No visible jackin window required — the operator can be anywhere on the host.
- Optional escalation. When the operator has set a per-workspace policy “if I don’t respond in N seconds, start playing a sound”, jackin’ escalates from a silent toast to an audible cue. Configurable per workspace and per role; off by default.
- Click-to-focus. Clicking the notification opens or focuses the terminal tab the agent is running in. The Desktop Agent Hub v1 should wire this for Ghostty first; kitty, iTerm2, WezTerm, gnome-terminal, and other adapters are later additions once the Ghostty contract is solid. Operators don’t have to remember which window the agent is in.
- Per-workspace mute. Operators in deep-focus mode can mute notifications for a workspace temporarily without losing the launch-time policy.
- Per-axis toggle. Notify on idle waiting, on agent finished work, on sensitive-mount confirmation requested, etc. — operators choose which classes of attention prompts they care about.
How the agent expresses “I’m waiting”
Section titled “How the agent expresses “I’m waiting””The agent inside the container needs a way to tell jackin’ “I’m idle on operator input.” Three sources, in priority order:
-
An MCP server jackin’ auto-registers in every agent session. Each agent runtime jackin’ supports (Claude Code, Codex; Amp forthcoming) integrates MCP servers via its own config. jackin’ already wires
tirithandshellfirmfor the Claude runtime viadocker/runtime/entrypoint.sh; the equivalent registration for Codex / Amp is part of this proposal’s scope. The newjackin-attentionMCP server adds two tools the agent can call:attention.waiting(reason: string, urgency?: low | normal | high)— declare the agent is waiting for operator input.attention.resolved()— clear the waiting state when the operator responds.
The agent runtime’s tool-use loop already calls these tools when the runtime decides it’s hit a confirmation or an open-ended ambiguity; jackin’ bundles the MCP server, registers it at launch, and never asks the operator to wire it manually.
-
Inferred from the agent’s PTY activity. Even without explicit MCP calls, jackin’ can detect “the agent stopped emitting output and is sitting on a prompt” by watching the container’s TTY (no output for N seconds while the process is
S+foreground). This catches the long tail of confirmation flows the agent runtime hasn’t instrumented yet. -
Inferred from the agent’s exit code / state file. When the agent runtime writes a state file or exits with a “waiting for input” code, jackin’ reads it.
Path 1 is the primary, path 2 the fallback, path 3 a future hook for runtimes that publish their own state.
How the notification reaches the host
Section titled “How the notification reaches the host”The pieces:
jackin-attentionMCP server. Static binary baked into the construct image. Registered automatically in each agent’s MCP config at container launch — extending the registration patterndocker/runtime/entrypoint.shuses today for the Claude runtime’stirith/shellfirmto every supported runtime. When the agent callsattention.waiting, the MCP server pushes the event over the existing per-container control channel (the same bind-mount or pipe used for daemon ↔ container coordination — see jackin daemon for the channel design).- The jackin daemon dispatches the host-side notification. Per the jackin daemon plug-in surface, this feature is one adapter that listens for attention events from any running container and translates them to OS-native notifications:
- macOS —
osascript -e 'display notification …'or theUserNotificationsframework via a small Swift helper. Sound escalation viaafplay/NSSound. - Linux —
notify-send(libnotify) or the org.freedesktop.Notifications D-Bus API. Sound viapaplay/aplay. - Windows — out of scope for the first phase; revisit when jackin’ supports Windows hosts.
- macOS —
- Click-to-focus adapter. The notification carries an action that, when activated, runs a host-side command to focus the right terminal tab. The first Desktop slice should implement Ghostty only, matching the Jackin Desktop Agent Hub v1 launch contract. Other terminal emulators get a notification without click focus until their adapters are deliberately added.
Per-workspace / per-role policy
Section titled “Per-workspace / per-role policy”The configuration knobs (managed through jackin console → workspace editor → a new Notifications tab, parallel to the existing General / Mounts / Roles / Secrets / Auth tabs):
| Knob | Default | What it does |
|---|---|---|
attention.enabled | true | Master switch for this workspace. |
attention.urgency_filter | normal+high | Which urgency levels notify (low/normal/high). |
attention.sound_escalation_after | off | If unset, silent forever. Set to a duration (e.g. 60s, 5m) to start playing sound after that wait. |
attention.click_action | focus_tab | What clicking the notification does (focus_tab / none). |
attention.mute_until | unset | Temporary mute set from the TUI (m on the workspace row); auto-clears at the named timestamp. |
Every knob is configurable globally, per workspace, and per (workspace × role × agent), with the same most-specific-wins resolution rule the rest of jackin’ already uses.
Why a daemon
Section titled “Why a daemon”Same answer as live auth sync: this feature can’t run from a CLI that exits between commands. The daemon adapter:
- Subscribes to the per-container control channels for attention events.
- Tracks per-event timers (sound-escalation deadlines).
- Holds the per-workspace mute state.
- Talks to the OS notification API.
- Receives click events back from the OS and runs the focus-tab action.
The lifecycle, install, and security posture this needs are the same ones the jackin daemon item is decided. This feature is an adapter against that daemon; it does not introduce its own long-running shape.
Open design questions
Section titled “Open design questions”MCP server contract
Section titled “MCP server contract”- Tool surface. Just
attention.waiting/attention.resolved? Or a richer set (attention.progress(message)for “I’m still working, here’s what I’m doing”)? - Auto-resolve vs explicit resolve. Does the next tool call from the agent count as “operator unblocked the agent” (auto-resolve), or does the agent have to call
attention.resolvedexplicitly? - Urgency taxonomy.
low / normal / highis a starting point; do we need anerrorlevel for crashes vsinteractivefor confirmations? The smaller the taxonomy, the better.
Inference fallback
Section titled “Inference fallback”- PTY-idle threshold. How many seconds of TTY silence before jackin’ assumes the agent is waiting? Too short = false positives every time an agent is thinking; too long = operator waits longer than necessary. Configurable per workspace?
- False-positive backoff. When the operator confirms a notification was a false positive (clicks “this wasn’t really waiting”), the daemon should debounce that container for a while.
Click-to-focus across terminals
Section titled “Click-to-focus across terminals”- Detection. The v1 contract is Ghostty-first. What if the operator switches terminals after launch, closes Ghostty, or starts a session outside the Desktop launcher? The click action should degrade gracefully to a notification with no click action.
- Adapter scope. Ghostty is the first supported adapter. Other terminals are explicitly deferred until the Desktop Agent Hub proves the product shape.
Notification de-duplication
Section titled “Notification de-duplication”- Multiple agents waiting at once. Five agents idle in five containers — five notifications? One grouped notification? A digest the operator can drill into?
- Notification fatigue. What’s the rate-limit policy? After N notifications in M seconds, fall back to a digest?
Cross-host
Section titled “Cross-host”- Remote operator. When jackin’ grows to support remote agents (Kubernetes phase, see the Why jackin’ page), notifications need to reach the operator wherever they are. Out of scope for v1; revisit when remote-agent work lands.
What ships in phase 1
Section titled “What ships in phase 1”A minimal cut to validate the shape:
jackin-attentionMCP server, baked into construct, auto-registered for every agent runtime jackin’ supports (Claude Code first, Codex / Amp as part of the same phase to close the runtime-coverage gap left by today’s Claude-onlytirith/shellfirmregistration).- The two-tool surface
attention.waiting/attention.resolved. - Daemon adapter listening on the per-container control channel, dispatching macOS Notification Center notifications.
- Click action wired for Ghostty first; other terminals get the notification with no click action and a one-line “open this terminal manually” hint.
Notificationstab injackin consoleworkspace editor — enable / mute / sound-escalation duration. No per-role overrides in v1.- Linux
notify-sendadapter as part of the same phase (it’s small and operators on Linux deserve parity from day one).
Not in phase 1: PTY-idle inference fallback, sound files beyond a single default, urgency-based sound differentiation, cross-terminal click-to-focus matrix, Windows support.
Why this matters
Section titled “Why this matters”Concretely: an operator running four agents in parallel today loses ~20–30% of total work time to “I didn’t notice the agent was waiting.” Even a 50% reduction in idle-wait time pays for the implementation effort within a few weeks of operator usage, and that’s before counting the qualitative win of staying in flow on the work the operator actually came to do.
The bet of jackin’ is that running many agents safely and conveniently changes how operators work. Attention prompts are the difference between “I have ten agents running, but I have to babysit them” and “I have ten agents running, and they ping me when they need me.”
Related work
Section titled “Related work”- jackin daemon — host-side process this feature plugs into. Phase 1 daemon must ship before this adapter can.
- Live bidirectional auth sync — sibling feature plugging into the same daemon; sets the precedent for per-axis adapters.
- Jackin Desktop Agent Hub — native macOS surface that should display attention/ready-for-review state and owns the first Ghostty click-to-focus UX.
- Agent Orchestrator Research Program → Live agent observability — adjacent work on surfacing what the agent is doing in real time. Attention prompts close the loop the other direction (when the agent stops doing things and waits).
docker/runtime/entrypoint.sh— current MCP-registration pattern fortirith/shellfirm. Thejackin-attentionMCP server registers through the same code path.