fix: preserve cockpit steps across view toggles and show full step info
Backend: stop wiping pending_task_lane when AI response has no new [ACTIONS]/[QUESTIONS] markers — previous task lane state is still relevant until replaced by new markers. Frontend (selectChat): don't eagerly clear task lane before server response arrives; restore from sessionStorage as fallback when pending_task_lane is null (covers sessions before backend fix). StepsPanel: show description and command for all steps instead of hiding behind hover/active-only visibility. Commands render as inline code blocks. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -339,14 +339,12 @@ async def send_chat_message(
|
|||||||
except Exception:
|
except Exception:
|
||||||
logger.exception("Failed to create fork within branch for session %s", session.id)
|
logger.exception("Failed to create fork within branch for session %s", session.id)
|
||||||
|
|
||||||
# Persist task lane state on session
|
# Persist task lane state on session — only overwrite when new markers present
|
||||||
if branch_questions_data or branch_actions_data:
|
if branch_questions_data or branch_actions_data:
|
||||||
session.pending_task_lane = {
|
session.pending_task_lane = {
|
||||||
"questions": branch_questions_data or [],
|
"questions": branch_questions_data or [],
|
||||||
"actions": branch_actions_data or [],
|
"actions": branch_actions_data or [],
|
||||||
}
|
}
|
||||||
else:
|
|
||||||
session.pending_task_lane = None
|
|
||||||
|
|
||||||
suggested_flows = extract_suggested_flows(
|
suggested_flows = extract_suggested_flows(
|
||||||
await rag_search(query=message, account_id=account_id, db=db, limit=8)
|
await rag_search(query=message, account_id=account_id, db=db, limit=8)
|
||||||
@@ -472,14 +470,12 @@ async def send_chat_message(
|
|||||||
logger.exception("Failed to create fork for session %s", session_id)
|
logger.exception("Failed to create fork for session %s", session_id)
|
||||||
# Fork failed but chat message still sent — don't break the response
|
# Fork failed but chat message still sent — don't break the response
|
||||||
|
|
||||||
# Persist task lane state on session
|
# Persist task lane state on session — only overwrite when new markers present
|
||||||
if questions_data or actions_data:
|
if questions_data or actions_data:
|
||||||
session.pending_task_lane = {
|
session.pending_task_lane = {
|
||||||
"questions": questions_data or [],
|
"questions": questions_data or [],
|
||||||
"actions": actions_data or [],
|
"actions": actions_data or [],
|
||||||
}
|
}
|
||||||
else:
|
|
||||||
session.pending_task_lane = None
|
|
||||||
|
|
||||||
suggested_flows = extract_suggested_flows(rag_results)
|
suggested_flows = extract_suggested_flows(rag_results)
|
||||||
|
|
||||||
|
|||||||
@@ -101,15 +101,22 @@ export function StepsPanel({
|
|||||||
'transition-colors duration-200',
|
'transition-colors duration-200',
|
||||||
isCompleted && 'line-through opacity-70',
|
isCompleted && 'line-through opacity-70',
|
||||||
)}>{action.label}</span>
|
)}>{action.label}</span>
|
||||||
{/* Show description for active step and on hover for others */}
|
|
||||||
{action.description && (
|
{action.description && (
|
||||||
<p className={cn(
|
<p className={cn(
|
||||||
'text-xs text-muted-foreground mt-0.5 leading-relaxed transition-all duration-200',
|
'text-xs text-muted-foreground mt-0.5 leading-relaxed',
|
||||||
isActive ? 'opacity-100 max-h-20' : 'opacity-0 max-h-0 group-hover:opacity-70 group-hover:max-h-20 overflow-hidden',
|
isCompleted && 'opacity-60',
|
||||||
)}>
|
)}>
|
||||||
{action.description}
|
{action.description}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
{action.command && (
|
||||||
|
<code className={cn(
|
||||||
|
'block text-[11px] font-mono mt-1 px-2 py-1 rounded bg-white/[0.04] border border-white/[0.06] text-muted-foreground break-all',
|
||||||
|
isCompleted && 'opacity-60',
|
||||||
|
)}>
|
||||||
|
{action.command}
|
||||||
|
</code>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Active indicator */}
|
{/* Active indicator */}
|
||||||
|
|||||||
@@ -168,9 +168,7 @@ export function useAssistantSession() {
|
|||||||
const selectChat = useCallback(async (chatId: string) => {
|
const selectChat = useCallback(async (chatId: string) => {
|
||||||
currentChatRef.current = chatId
|
currentChatRef.current = chatId
|
||||||
setActiveChatId(chatId)
|
setActiveChatId(chatId)
|
||||||
setShowTaskLane(false)
|
// Don't clear task lane yet — wait for server response to decide
|
||||||
setActiveQuestions([])
|
|
||||||
setActiveActions([])
|
|
||||||
try {
|
try {
|
||||||
const detail = await aiSessionsApi.getSession(chatId)
|
const detail = await aiSessionsApi.getSession(chatId)
|
||||||
if (currentChatRef.current !== chatId) return
|
if (currentChatRef.current !== chatId) return
|
||||||
@@ -188,7 +186,7 @@ export function useAssistantSession() {
|
|||||||
triage_hypothesis: detail.triage_hypothesis ?? null,
|
triage_hypothesis: detail.triage_hypothesis ?? null,
|
||||||
evidence_items: detail.evidence_items ?? null,
|
evidence_items: detail.evidence_items ?? null,
|
||||||
})
|
})
|
||||||
// Restore task lane from persisted state
|
// Restore task lane from server state
|
||||||
if (detail.pending_task_lane) {
|
if (detail.pending_task_lane) {
|
||||||
const q = detail.pending_task_lane.questions || []
|
const q = detail.pending_task_lane.questions || []
|
||||||
const a = detail.pending_task_lane.actions || []
|
const a = detail.pending_task_lane.actions || []
|
||||||
@@ -202,10 +200,35 @@ export function useAssistantSession() {
|
|||||||
setActiveQuestions(q)
|
setActiveQuestions(q)
|
||||||
setActiveActions(a)
|
setActiveActions(a)
|
||||||
setShowTaskLane(true)
|
setShowTaskLane(true)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Fallback: restore from sessionStorage (covers view toggles before backend fix)
|
||||||
|
try {
|
||||||
|
const saved = sessionStorage.getItem('rf-tasklane-meta')
|
||||||
|
if (saved) {
|
||||||
|
const d = JSON.parse(saved)
|
||||||
|
if (d.chatId === chatId) {
|
||||||
|
const q = d.questions || []
|
||||||
|
const a = d.actions || []
|
||||||
|
if (q.length > 0 || a.length > 0) {
|
||||||
|
setActiveQuestions(q)
|
||||||
|
setActiveActions(a)
|
||||||
|
setShowTaskLane(d.show === true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch { /* ignore */ }
|
||||||
|
// No task lane data from either source — clear
|
||||||
|
setShowTaskLane(false)
|
||||||
|
setActiveQuestions([])
|
||||||
|
setActiveActions([])
|
||||||
} catch {
|
} catch {
|
||||||
setMessages([])
|
setMessages([])
|
||||||
|
setShowTaskLane(false)
|
||||||
|
setActiveQuestions([])
|
||||||
|
setActiveActions([])
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user