6.8 KiB
Task Lane Minimize/Reopen + Resolve Documentation Streaming
Date: 2026-03-28 Status: Approved
Problem
Two UX issues in the Assistant Chat page:
-
Task Lane close destroys state. The X button calls
clearTaskState()and hides the panel. There's no way to bring it back without getting a new AI response with markers. Engineers lose in-progress task responses. -
Conclude → Resolved hangs. The resolve flow blocks on an LLM call to generate documentation. The modal shows "Generating..." for 2-5s+ with no progressive feedback. The generated output needs to be structured ticket notes engineers can copy into their PSA.
Feature 1: Task Lane Minimize/Reopen
Close Button Change
- Replace the X icon (
Xfrom Lucide) withPanelRightCloseto signal "collapse" not "destroy." - On click: set
showTaskLane = false. Do NOT callclearTaskState(sessionId). - Task state (questions, actions, user responses) remains in sessionStorage and backend
pending_task_lane.
Reopen Pill on Input Toolbar
- New pill button in the chat input toolbar row, alongside Attach / Paste Logs / Conclude.
- Visibility condition:
(activeQuestions.length > 0 || activeActions.length > 0) && !showTaskLane - Label:
Tasks (N)where N =activeQuestions.length + activeActions.length - Icon:
ListChecksfrom Lucide - Action:
setShowTaskLane(true) - Style: Same ghost button style as Attach/Paste Logs — muted text, hover highlight.
Files Changed
| File | Change |
|---|---|
frontend/src/components/assistant/TaskLane.tsx |
Replace X icon with PanelRightClose in header |
frontend/src/pages/AssistantChatPage.tsx |
Remove clearTaskState() from onClose handler; add Tasks pill to input toolbar |
Feature 2: Resolve with Streaming Documentation
Two-Phase Resolve Flow
Phase 1 — Instant resolve:
- User clicks Resolve in
ConcludeSessionModal, enters optional notes, confirms. - Frontend calls
POST /ai-sessions/{id}/resolvewith{ resolution_summary }. - Backend sets session status to
resolved, saves summary, returns immediately. No LLM call on this path. - Modal transitions to the summary step instantly, showing a skeleton loading state for "Ticket Notes."
Phase 2 — Streamed doc generation:
- Immediately after phase 1, frontend opens an SSE connection to
GET /ai-sessions/{id}/documentation/stream. - Backend streams the structured ticket notes as they generate, token by token.
- Frontend renders chunks progressively into the summary step using
MarkdownContent. - When stream completes, show a "Copy to Clipboard" button.
Ticket Notes Format
The LLM generates four structured sections:
## Problem Summary
[What the engineer reported / intake context]
## Steps Taken
[Key diagnostic steps and findings from conversation]
## Resolution
[What fixed it / final action taken]
## Next Steps
[Follow-up items, if any — or "None"]
Fallback Behavior
- If SSE stream fails or times out (30s): fall back to non-streaming
GET /ai-sessions/{id}/documentation. - If that also fails: show "Documentation unavailable" with a "Copy Conversation" button that formats the raw
conversation_messagesinto a basic structured summary (no LLM, pure template).
Copy to Clipboard
- Prominent button below the rendered ticket notes.
- Copies the full markdown text.
- Toast confirmation: "Ticket notes copied."
AI Optimizations
1. Streaming (SSE endpoint):
- New endpoint:
GET /ai-sessions/{id}/documentation/stream - Returns
text/event-streamvia FastAPIStreamingResponse. - Uses Anthropic's
client.messages.stream()for token-level streaming. - Time-to-first-byte drops from ~2-5s to ~200ms.
2. Prompt caching:
- Apply
cache_control: {"type": "ephemeral"}to the system prompt and conversation context prefix in the documentation generation call. - Pattern already exists in
assistant_chat_service.py(_call_anthropic_cached). - Repeat calls for the same session (regenerate, different doc types) hit cache — up to 85% faster, 90% cheaper on input tokens.
3. Client reuse:
- Singleton
AsyncAnthropicclient instead of creating a new one per call. - Eliminates connection setup overhead.
Model tier: Haiku (already configured as quick_action tier) — fastest model, appropriate for summarization.
Backend Changes
| File | Change |
|---|---|
backend/app/api/endpoints/ai_sessions.py |
Modify resolve_session to only set status + save summary (remove the blocking get_session_documentation call); add new SSE streaming endpoint for doc generation |
backend/app/services/flowpilot_engine.py |
Add stream_session_documentation() generator function using client.messages.stream() |
backend/app/core/ai_provider.py |
Add generate_text_stream() method returning async iterator; singleton client |
backend/app/services/flowpilot_engine.py |
Add prompt caching to _build_status_update_prompt call path |
Frontend Changes
| File | Change |
|---|---|
frontend/src/components/assistant/ConcludeSessionModal.tsx |
Two-phase flow: instant resolve → streaming doc render with skeleton → copy button |
frontend/src/api/aiSessions.ts |
Add streamDocumentation(sessionId) using fetch + ReadableStream |
frontend/src/pages/AssistantChatPage.tsx |
Update handleConclude to not await documentation |
ConcludeSessionModal Summary Step Layout
┌─────────────────────────────────────┐
│ Session Resolved │
│ │
│ ┌─ Ticket Notes ─────────────────┐ │
│ │ ## Problem Summary │ │
│ │ [streaming text...] │ │
│ │ │ │
│ │ ## Steps Taken │ │
│ │ [streaming text...] │ │
│ │ │ │
│ │ ## Resolution │ │
│ │ [streaming text...] │ │
│ │ │ │
│ │ ## Next Steps │ │
│ │ [streaming text...] │ │
│ └────────────────────────────────┘ │
│ │
│ [ Copy to Clipboard ] [ Done ] │
└─────────────────────────────────────┘
Out of Scope
- Client-facing update generation (future feature, different audience prompt)
- Direct PSA posting (requires ConnectWise integration to be wired to sessions)
- Task lane drag-to-reorder
- Task lane persistence across browser tabs (sessionStorage is per-tab by design)