Pi Agent Integration: Message Parsing, Retry, and Cancellation A developer integrating the pi CLI-based AI coding agent into an AI coding assistant project encountered three major challenges: parsing a private JSON event stream, handling ambiguous failure semantics with retry logic, and managing long-running interruptible processes. The solution involved splitting integration into two layers—a provider layer to normalize pi's output into a shared message stream and a core layer to translate business requests—with specific components for message parsing, retry, and cancellation. When integrating a CLI-based AI agent, you can't avoid three things: how to translate its private event stream into stable messages, who's responsible for retry after failures, and how to cleanly stop the process when users click cancel. These three things essentially boil down to "clarifying responsibilities"—it's simple in theory, but you only realize how deep the water is when you actually do it. Recently, I've been working on an AI coding assistant project, and one of the agents to integrate is pi https://github.com/earendil-works/pi-coding-agent . It's a TUI/CLI coding agent that outputs JSON events line by line to stdout when running. Sounds simple—spawn the process, read output, parse it—but when you actually start, you realize "integrating an agent CLI" is completely different from "integrating a regular CLI." With a regular CLI, you read stdout, get an exit code, and that's it. But agent CLIs have three particularly headache-inducing characteristics: First, its event stream is a private protocol . turn start , session , message update , message end , turn end , agent end —these are defined by pi itself, not any industry standard. Every upper layer that wants to consume it has to handle it separately, effectively leaking pi's internal details everywhere. It's like looking at someone from a distance—you think you see them clearly, but you're only seeing the side they want to show you. Second, its failure semantics are particularly ambiguous . The agent might encounter network jitter, model rate limiting, or process crashes while running. Should it retry? Where to retry? Will retry mess up the session state that's already half-output? This is an architectural decision, not something you can solve by casually writing a for loop. Third, it's long-running and interruptible . A single turn might run for tens of seconds or even minutes, and users might want to cancel at any time. When canceled, the process can't become an orphan, tool calls can't be left half-finished, and already output content can't be lost. The water here is much deeper than imagined. To solve these pain points, we spent some time straightening out the integration path. I'll get into specifics later, but here's a spoiler: the real difficulty isn't in "spawning the process," but in "clarifying responsibilities." The solution shared in this article comes from the HagiCode project—an AI coding assistant that supports multiple models and multiple agent CLI backends. GitHub repository: HagiCode-org/site , feel free to star it. All the code and all the pitfalls mentioned below are actually running in this project. Writing this out is just leaving myself a memory. HagiCode splits AI capability integration into two layers: Hagicode.Libs , providing reusable provider primitives ICliProvider