ChirpySessionHolder
Global Chirpy conversation state shared across all docs panes.
Registered as a Koin singleton so both the list and detail entries access the same conversation. Uses Compose snapshot state so reads trigger recomposition automatically.
The holder also runs the requests, on its own scope. Callers must not launch them from a rememberCoroutineScope(): on-device inference takes seconds, and the pane that submitted a question is routinely disposed before the answer lands — the adaptive docs layout swaps the list pane for a detail pane as soon as the user opens a page. A composition scope is cancelled at that point, so the answer would be dropped mid-stream and AIDocAssistantSessionState.isLoading would stay true forever in this singleton, permanently blocking the next question. The session outlives any one pane, so the scope that fills it has to as well.
Functions
Submits the trimmed draft question and streams the answer into sessionState. No-ops on a blank draft or while a request is already running.