--- id: infra/nudger-a-control-in-the-chat-composer-beside title: "Nudger — a control in the chat composer, beside the background-job button" theme: infra status: open created: 2026-08-20 date_source: stated order: 4 --- **Nudger — a control in the chat composer, beside the background-job button.** Wanted 2026-08-20 while leaving an agent to work overnight: "continue every hour if you have stalled". `scripts/nudge-agent.ts` does it from outside tonight — it polls `GET /api/chat/sessions?all=1` and speaks over the chat websocket, exactly as a browser does, so the platform needed no changes. That is the workaround, not the feature. **Both things this said the platform lacked have since been built** (verified 2026-08-21): the recurring trigger is `startNudger()` at `server.tsx:182`, ticking once a minute over a `nudges` table; the door into a session is `startClaudeTurn({ ws: null })` at `nudger.ts:102`. What is left is the UI — a control in the chat composer, beside the background-job button — plus generalising delivery beyond the nudger's own schedule. See "Speaking into a session from the backend". Design notes worth keeping: - **Never interrupt work.** Check `isGenerating` immediately before speaking. The point is to restart a stopped agent, not to hurry a busy one — the script already gets this right. - **It must outlive what it nudges.** A chat background job is owned by its session, so a nudge job launched from the session being nudged dies with it — the 30-minute idle GC, a clear, or a restart takes the watchdog with the agent. Whatever owns the schedule cannot be the session. - **Bound it.** The script stops after `--max` (12). A loop nobody is watching should end. - The same trigger would serve `COMMS/command-hq/watcher-loop-proposal.md`, which wants infrastructure to own a loop rather than a model holding a protocol across turns.