Swiping away shouldn't cancel the answer
I asked Pepper a question from my phone, swiped to another app for a few seconds, and came back to “Pepper is unreachable.” On the other end, the agent had worked the question for 57 seconds across seven tool calls and then thrown the answer away, because the connection it was streaming into had died while I was looking at something else.
Pepper is the chief-of-staff agent I’m standing up in front of my fleet, and the phone end of it lives in Dispatch, an Android app I’ve been building as a single front door to the parts of my estate I check every day. Asking Pepper something from the app was the newest thing in it, and this was the first sharp edge I hit.
Nothing in the app’s own lifecycle code cancels anything. The process ID was identical before and after the swipe, so the app had not been killed and relaunched, and the system had logged no freeze event either. Battery saver, data saver and the per-app restrictions were all off. The answer was one layer down, in the phone’s connectivity state. On this phone, about five seconds after the app left the screen, the system moved it into a background firewall chain, and instead of pausing the app’s network it destroyed its live connections. That is why the request failed at once instead of stalling until the app’s read timeout.
AI got the first version working quickly. I found the missing behavior by using it on my phone. I build and ship software for a living, and I’m blessed with a world-class engineering team, which creates a blind spot of its own: dozens of standard patterns my work software simply inherits don’t exist in my own software until I build them, and I notice the gap the first time it bites.
There were two standard ways around this one. A foreground service would have kept the app out of that chain for the length of the request, at the price of a notification in the tray while Pepper works. Retrying when I came back could hit the same wall on the next swipe. What I wanted was simpler to state: once I’ve asked, the work should finish whether or not the phone is still watching.
So the ask moved server-side. The phone now starts a job and gets an ID back, and Pepper’s work belongs to that job, so losing the phone’s stream no longer cancels the request. The phone follows the job and reconnects after a swipe. If it loses the thread entirely, a worker on the phone polls the job and posts the answer as a notification with a link straight into it. The record is bounded: the server keeps the newest 50 jobs, and a job still running when the server restarts ends as interrupted instead of finishing.

Testing it immediately broke it in a new way. The first version canceled the “answer ready” notification the moment the app returned to the foreground, which is precisely the moment I would be coming back to read it, so I never saw one. The system’s own notification log named the culprit: the app had canceled its notification 51 seconds after posting it. It now cancels only when the sheet that would show the answer is open and the app is on screen.
I had already separated a session’s lifetime from the device watching it in the terminal in my pocket. I’ve now added the same requirement to the instructions my agents read before building: once a request starts, it should survive the phone disconnecting. In Dispatch, swiping away mid-question now costs me a notification instead of the answer.