import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import type { RoleWorkItemSummary, Session, TaskPreferredAgent } from '../types/kanban' import type { ChatMessage, CheckpointReplyMetadata, OutgoingAttachmentPayload } from '../types/chat' import type { AgentInfo, OrgInfoPayload, SavedOrgSummary } from '../types/visual' import type { CommsStatePayload, CommsMessagePayload } from '../lib/wsClient' import { CommsPanel } from './CommsPanel' import { ProjectCockpit } from './ProjectCockpit' import { PRIORITY_META, type TaskPriority } from '../types/kanban' import { TaskHeaderBar } from '../chat/TaskHeaderBar' import { MarkdownBody, MessageList } from '../chat/MessageList' import { MessageComposer } from '../chat/MessageComposer' import { analyzeCheckpointMessages, checkpointReplyMetadataForComposer } from '../chat/checkpointUtils' import { AgentWorkPanel } from '../chat/AgentWorkPanel' import { WorkItemProgressCard } from '../chat/WorkItemProgressCard' import { IconTimeline, IconHandoff, IconTool } from '../chat/SvgIcons' import { isSessionWorking } from '../lib/sessionRuntime' import { getWorkItemRoleLabel } from '../lib/workItemIdentity' import { getLinkedRuntimeTaskId } from '../lib/workItemRuntimeIds' import { TaskDetailView } from './TaskDetailView' import { getConversationPeerSessions, getConversationHeaderSession, getConversationSessionView, getConversationMessageCount, getWorkItemRoleSessions, mergeConversationProgressLog, projectSessionConversation, } from '../lib/workItemSessions' type ActiveView = | { kind: 'session'; taskId: string } | { kind: 'task-detail'; taskId: string } | { kind: 'activity' } | { kind: 'secretary' } | { kind: 'child-detail' } interface ContextPanelProps { panelState: 'collapsed' | 'open' | 'maximized' width: number onResizeMouseDown: (e: React.MouseEvent) => void isResizing: boolean activeView: ActiveView activeSession: Session | null activeTask?: import('../types/kanban').KanbanTask | null linkedTaskSession?: Session | null linkedTaskSessionMessages?: ChatMessage[] childDetailSession: Session | null messages: ChatMessage[] childDetailMessages: ChatMessage[] allSessions: Session[] openSessions: Session[] openSessionMessages: Record openSessionChildren: Record agents: AgentInfo[] childSessions: Session[] execMode?: string taskPreferredAgent: TaskPreferredAgent savedOrgsList?: SavedOrgSummary[] | null activeSavedOrg?: string | null onSavedOrgsList?: () => void onSavedOrgLoad?: (name: string) => void canShowAgentsTab?: boolean channelId: string channelName: string secretaryChannelId: string unreadCounts?: Record multiSessionView?: boolean panelTab: 'chat' | 'agents' | 'info' | 'comms' | 'team' onPanelTabChange: (tab: 'chat' | 'agents' | 'info' | 'comms' | 'team') => void commsState?: CommsStatePayload | null commsMessage?: CommsMessagePayload | null onCommsRefresh?: () => void onCommsReadMessage?: (path: string) => void orgInfoData?: OrgInfoPayload | null canShowTeamTab?: boolean onTeamStopRun?: () => void onTitleChange: (taskId: string, title: string) => void onSessionConfigChange?: (taskId: string, execMode: string, companyProfile?: string, orgId?: string) => void onSessionTaskAgentChange?: (taskId: string, preferredAgent: TaskPreferredAgent) => void /** * User asked to "continue this conversation in a different mode" from the * locked-mode chip popover. We expect the host to spin up a fresh chat in * the requested mode (inside the same project). */ onContinueInNewChat?: (mode: 'task' | 'company' | 'org' | 'custom', companyProfile?: 'corporate' | 'custom', orgId?: string) => void onStop?: () => void onComplete?: () => void onResume?: () => void onResumeTask?: (taskId: string) => void onStopTask?: (taskId: string) => void onCompleteTask?: (taskId: string) => void onLocateOnBoard?: (taskId: string) => void onBackToParent?: () => void onCloseTaskDetail?: () => void onOpenChildDetail?: (taskId: string) => void onOpenExecutionPanel?: (taskId: string) => void onSelectSessionTab?: (taskId: string) => void onCloseSessionTab?: (taskId: string) => void onToggleMultiSessionView?: () => void onCollapse: () => void onExpand: () => void onMaximize: () => void onComposerSend: (content: string, attachments?: OutgoingAttachmentPayload[]) => void onMessageSend: (content: string, taskId?: string, metadata?: CheckpointReplyMetadata) => void onSessionSend?: ( taskId: string, content: string, attachments?: OutgoingAttachmentPayload[], metadata?: CheckpointReplyMetadata, ) => void onWorkItemClick: (executionTurnId: string) => void onWorkItemOpenSession?: (executionTurnId: string) => void onMarkRead: () => void onSessionMarkRead?: (taskId: string) => void onLoadSessionHistory?: ( taskId: string, oldestMessage?: ChatMessage, detailLevel?: 'summary' | 'full', ) => Promise | void isSessionHistoryLoading?: (taskId: string) => boolean } function relativeTime(ts: number): string { const diff = Date.now() - ts if (diff < 60_000) return 'just now' if (diff < 3_600_000) return `${Math.floor(diff / 60_000)}m` if (diff < 86_400_000) return `${Math.floor(diff / 3_600_000)}h` return `${Math.floor(diff / 86_400_000)}d` } function sessionRuntimeLabel(session: Session, activeChildCount: number): string | null { if (activeChildCount > 1) return `${activeChildCount} agents working` const displayTool = session.displayTool || session.currentTool if (displayTool) return `Running ${displayTool}` if (session.agentStatus === 'reflecting') return 'Thinking...' if (session.status === 'running') return 'Running' return null } function activeRoleWorkItemCount(roleWorkItems?: Record): number | undefined { if (!roleWorkItems || Object.keys(roleWorkItems).length === 0) return undefined return Object.values(roleWorkItems).filter(summary => summary.aggregatedStatus === 'active').length } function distinctWorkingRoleCount(sessions: Session[]): number { const roles = new Set( sessions .filter(isSessionWorking) .map(s => String(s.workItemRoleId ?? s.assigneeIds[0] ?? s.taskId).trim()) .filter(Boolean), ) return roles.size } function activeAgentCountFor( roleWorkItems: Record | undefined, sessions: Session[], ): number | undefined { const roleCount = activeRoleWorkItemCount(roleWorkItems) if (roleCount !== undefined) return roleCount || undefined return distinctWorkingRoleCount(sessions) || undefined } function sessionModeLabel(session: Session): string { const execMode = composerExecModeForSession(session) if (execMode === 'org' || execMode === 'custom') return `company/${session.orgId ?? 'org'}` if (execMode === 'company') return `company/${session.companyProfile ?? 'corporate'}` return execMode } function normalizePanelExecMode(value?: string): 'task' | 'company' | 'org' { const normalized = String(value ?? '').trim().toLowerCase() if (normalized === 'company') return 'company' if (normalized === 'org' || normalized === 'custom') return 'org' return 'task' } function hasCompanyRuntimeIdentity(session: Session): boolean { return !!( session.isCompanyRuntime || session.parentSessionId || session.workItemProjectionId || session.roleWorkItems || session.executorRoleWorkItems ) } function isCompanyRuntimeSession( session: Session | null | undefined, relatedSessionCount = 0, ): boolean { if (!session) return false const mode = String(session.execMode ?? '').trim().toLowerCase() return relatedSessionCount > 0 || hasCompanyRuntimeIdentity(session) || mode === 'company' || mode === 'org' || mode === 'custom' } export function sessionHasMoreForDetail( session: Session, detailLevel: 'summary' | 'full', ): boolean | undefined { const scoped = detailLevel === 'full' ? session.fullHasMore : session.summaryHasMore if (scoped !== undefined) return scoped // Old snapshots only expose the unscoped value. Once either scoped cursor // has been observed, the generic field may describe the other policy. if (session.summaryHasMore === undefined && session.fullHasMore === undefined) { return session.hasMore } return undefined } export function conversationHasOlderHistory( sessions: Session[], displayedMessageCount: number, detailLevel: 'summary' | 'full', allowMessageCountFallback = true, ): boolean { const pagination = sessions.map(session => sessionHasMoreForDetail(session, detailLevel)) if (pagination.some(hasMore => hasMore === true)) return true if (sessions.length !== 1 || pagination[0] === false) return false return allowMessageCountFallback && sessions[0].messageCount > displayedMessageCount } function hasCustomRuntimeIdentity(session: Session): boolean { const rawMode = String(session.execMode ?? '').trim().toLowerCase() const normalizedMode = normalizePanelExecMode(session.execMode) const profile = String(session.companyProfile ?? '').trim().toLowerCase() if (normalizedMode === 'company') return false if (normalizedMode === 'org') return true if (rawMode) return false return profile === 'custom' || !!session.orgId } function isSessionConfigLocked(session: Session | undefined, visibleMessageCount = 0): boolean { if (!session) return visibleMessageCount > 0 const messageCount = Math.max(session.messageCount ?? 0, visibleMessageCount) if (messageCount > 0) return true const status = String(session.status ?? '').trim().toLowerCase() if (status && status !== 'pending') return true if ( session.parentSessionId || session.workItemProjectionId || session.workItemRoleId || session.workItemTurnType || session.pendingRuntimeCheckpointId ) { return true } if (session.runtimeControlState && session.runtimeControlState !== 'idle') return true if ((session.progressLog?.length ?? 0) > 0 || (session.workItemLog?.length ?? 0) > 0) return true if (session.roleWorkItems && Object.keys(session.roleWorkItems).length > 0) return true if (session.executorRoleWorkItems && Object.keys(session.executorRoleWorkItems).length > 0) return true return false } export function composerExecModeForSession(session: Session, fallbackExecMode?: string): string { const rawMode = String(session.execMode ?? '').trim().toLowerCase() const hasExplicitMode = rawMode.length > 0 const normalized = normalizePanelExecMode(session.execMode) if (hasCustomRuntimeIdentity(session)) { return 'org' } if (!hasExplicitMode && normalized === 'task' && hasCompanyRuntimeIdentity(session)) { return 'company' } return session.execMode ?? fallbackExecMode ?? 'task' } function composerTaskAgentForSession( session: Session, locked: boolean, fallbackAgent: TaskPreferredAgent, ): TaskPreferredAgent { if (locked) { if (session.selectedExecutionAgent && session.selectedExecutionAgent !== 'native') { return session.selectedExecutionAgent } return session.preferredAgent ?? session.selectedExecutionAgent ?? fallbackAgent } return session.preferredAgent ?? session.selectedExecutionAgent ?? fallbackAgent } function sessionDetailLevel( session: Session | null | undefined, options?: { childDetail?: boolean }, ): 'summary' | 'full' { const childDetail = !!options?.childDetail if (!session) return 'summary' if (childDetail) return 'full' return session.execMode === 'company' || session.execMode === 'org' || session.execMode === 'custom' ? 'summary' : 'full' } const HUMAN_REVIEW_STATUSES = new Set([ 'awaiting_human', 'awaiting_manager_review', 'awaiting_review', 'awaiting_peer', ]) function canShowContinue(session: Session): boolean { const status = String(session.status ?? '').trim() const fallback = session.runtimeControlState === 'suspended' || (!HUMAN_REVIEW_STATUSES.has(status) && status !== 'running' && status !== 'done') return Boolean(session.canResume ?? fallback) } function InfoTabView({ task, agents, roleLabel, }: { task: Session agents: AgentInfo[] roleLabel: string | null }) { const [showDev, setShowDev] = useState(false) const priorityKey = task.priority as TaskPriority | undefined const priorityMeta = priorityKey && (task.priority as string) in PRIORITY_META ? PRIORITY_META[priorityKey] : null const assigneeNames = task.assigneeIds .map(id => agents.find(a => a.agent_id === id)?.name ?? id) .filter(Boolean) const createdAt = new Date(task.createdAt) const employeeLabel = task.employeeAssignment?.name ? `${task.employeeAssignment.name}${task.employeeAssignment.category ? ` · ${task.employeeAssignment.category}` : ''}` : null // Field-level "fact" rendering helper — uniform spacing + treatment. const Fact = ({ label, value }: { label: string; value: React.ReactNode }) => (
{label} {value}
) return (

Status

{task.status} {priorityMeta && ( {priorityMeta.label} )}
{task.tags && task.tags.length > 0 && (
{task.tags.map(tag => ( {tag} ))}
)}

People & agent

{assigneeNames.length > 0 && ( {assigneeNames.map(name => ( {name} ))}
} /> )} {roleLabel && } {employeeLabel && } {task.selectedExecutionAgent && ( {task.selectedExecutionAgent} } /> )}

Timing

{createdAt.toLocaleString(undefined, { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit', })} } />
{showDev && (
{task.workItemProjectionId && ( {task.workItemProjectionId} } /> )} {task.taskId} } /> {task.channelId} } /> {task.sessionId && ( {task.sessionId} } /> )}
)}
) } export function ContextPanel({ panelState, width, onResizeMouseDown, isResizing, activeView, activeSession, activeTask, linkedTaskSession: linkedTaskSessionProp, linkedTaskSessionMessages, childDetailSession, messages, childDetailMessages, allSessions, openSessions, openSessionMessages, openSessionChildren, agents, childSessions, execMode, taskPreferredAgent, savedOrgsList, activeSavedOrg, onSavedOrgsList, onSavedOrgLoad, canShowAgentsTab = false, channelId, channelName, secretaryChannelId, unreadCounts, multiSessionView = false, panelTab, onPanelTabChange, commsState, commsMessage, onCommsRefresh, onCommsReadMessage, orgInfoData, canShowTeamTab = false, onTeamStopRun, onTitleChange, onSessionConfigChange, onSessionTaskAgentChange, onContinueInNewChat, onStop, onComplete, onResume, onResumeTask, onStopTask, onCompleteTask, onLocateOnBoard, onBackToParent, onCloseTaskDetail, onOpenChildDetail, onOpenExecutionPanel, onSelectSessionTab, onCloseSessionTab, onToggleMultiSessionView, onCollapse, onExpand, onMaximize, onComposerSend, onMessageSend, onSessionSend, onWorkItemClick, onWorkItemOpenSession, onMarkRead, onSessionMarkRead, onLoadSessionHistory, isSessionHistoryLoading, }: ContextPanelProps) { const isSecretary = activeView.kind === 'secretary' const isActivity = activeView.kind === 'activity' const isChildDetail = activeView.kind === 'child-detail' const isTaskDetail = activeView.kind === 'task-detail' const isCompanyRuntime = isCompanyRuntimeSession(activeSession, childSessions.length) const showTabs = activeView.kind === 'session' && activeSession const canSend = isSecretary ? true : !!activeSession const showSessionStrip = !isChildDetail && openSessions.length > 0 const canShowMultiSessionView = openSessions.length > 1 const showMultiSessionGrid = !!(multiSessionView && activeView.kind === 'session' && activeSession && canShowMultiSessionView && panelTab === 'chat') const childDetailScrollRef = useRef(null) const activeConversation = useMemo(() => { const conversationPeers = getConversationPeerSessions(activeSession, allSessions) return projectSessionConversation(activeSession, [...conversationPeers, ...childSessions]) }, [activeSession, childSessions, allSessions]) const activeDisplaySession = activeConversation.displaySession ?? activeSession const activeDetailMode = sessionDetailLevel(activeDisplaySession) const activeConversationSession = useMemo(() => { return getConversationSessionView(activeSession, activeConversation.runtimeSession, activeConversation.timelineSessions) }, [activeSession, activeConversation.runtimeSession, activeConversation.timelineSessions]) const activeHeaderSession = useMemo(() => { return getConversationHeaderSession(activeSession, activeConversation.runtimeSession, activeConversation.timelineSessions) }, [activeSession, activeConversation.runtimeSession, activeConversation.timelineSessions]) const activeConversationProgress = useMemo(() => { return mergeConversationProgressLog(activeConversation.timelineSessions) }, [activeConversation.timelineSessions]) const activeWorkItemLog = useMemo(() => ( activeConversationSession?.workItemLog ?? activeSession?.workItemLog ?? [] ), [activeConversationSession?.workItemLog, activeSession?.workItemLog]) const activeWorkItemRoleSessions = useMemo(() => ( getWorkItemRoleSessions(activeConversationSession ?? activeSession, allSessions) ), [activeConversationSession, activeSession, allSessions]) const activeRoleWorkItems = useMemo(() => ( activeConversationSession?.roleWorkItems ?? activeSession?.roleWorkItems ), [activeConversationSession?.roleWorkItems, activeSession?.roleWorkItems]) const activeExecutorRoleWorkItems = useMemo(() => ( activeConversationSession?.executorRoleWorkItems ?? activeSession?.executorRoleWorkItems ), [ activeConversationSession?.executorRoleWorkItems, activeSession?.executorRoleWorkItems, ]) const hasRoleWorkItems = !!( (activeRoleWorkItems && Object.keys(activeRoleWorkItems).length > 0) || (activeExecutorRoleWorkItems && Object.keys(activeExecutorRoleWorkItems).length > 0) ) const visibleAgentSessions = activeWorkItemRoleSessions.length > 0 ? activeWorkItemRoleSessions : childSessions const activeConversationMessageCount = useMemo(() => { return getConversationMessageCount(activeConversation.timelineSessions) }, [activeConversation.timelineSessions]) const activeConversationLoading = useMemo(() => ( activeConversation.timelineSessions.some((session) => isSessionHistoryLoading?.(session.taskId) ?? false) ), [activeConversation.timelineSessions, isSessionHistoryLoading]) const resolveConversationHistoryTarget = useCallback((oldestMessage?: ChatMessage) => { if (oldestMessage) { const matched = activeConversation.timelineSessions.find( (session) => session.channelId === oldestMessage.channelId, ) if (matched && sessionHasMoreForDetail(matched, activeDetailMode) !== false) return matched } const knownTarget = activeConversation.timelineSessions.find( session => sessionHasMoreForDetail(session, activeDetailMode) === true, ) if (knownTarget) return knownTarget return activeDisplaySession ?? activeSession }, [activeConversation.timelineSessions, activeDetailMode, activeDisplaySession, activeSession]) // Child detail: find the agent for this session const childDetailAgent = useMemo(() => { if (!childDetailSession) return undefined const id = childDetailSession.assigneeIds[0] return id ? agents.find(a => a.agent_id === id) : undefined }, [childDetailSession, agents]) // Use prop-provided linked session (computed + lazy-loaded by WorkspacePage) // with a local fallback for backwards compat const linkedTaskSession = linkedTaskSessionProp ?? (() => { const linkedRuntimeTaskId = getLinkedRuntimeTaskId(activeTask) if (!linkedRuntimeTaskId) return null return allSessions.find(session => session.taskId === linkedRuntimeTaskId || session.runtimeTaskId === linkedRuntimeTaskId || session.executionTurnId === linkedRuntimeTaskId ) ?? null })() useEffect(() => { if (!isChildDetail || !childDetailSession) return childDetailScrollRef.current?.scrollTo({ top: 0, behavior: 'auto' }) }, [isChildDetail, childDetailSession?.taskId]) // Task for Info tab const taskForInfo = activeSession // Collapsed state: render a thin strip if (panelState === 'collapsed') { return (
onExpand()} title="Open panel" > ◀
) } return ( <>
{/* Task detail view */} {isTaskDetail && activeTask ? ( ) : isChildDetail && childDetailSession ? ( /* Child detail view */
{(childDetailSession.canStop ?? childDetailSession.status === 'running') && childDetailSession.runtimeControlState !== 'suspending' && onStopTask && ( )} {childDetailSession.runtimeControlState === 'suspending' && ( )} {canShowContinue(childDetailSession) && onResumeTask && ( )}
{childDetailAgent && (
{childDetailAgent.name.charAt(0).toUpperCase()}
)}
{childDetailAgent?.name ?? childDetailSession.title} {childDetailSession.workItemRoleName && ( {childDetailSession.workItemRoleName} )} {!childDetailSession.workItemRoleName && childDetailSession.workItemProjectionId && ( {childDetailSession.workItemProjectionId.replace(/_/g, ' ')} )}
{childDetailSession.handoffContext && (
Received From
)}
Transcript
onLoadSessionHistory?.(childDetailSession.taskId, oldestMessage, 'full')} loadingOlderHistory={isSessionHistoryLoading?.(childDetailSession.taskId) ?? false} scrollPolicy="initial-bottom" scrollScope={childDetailSession.channelId} showRuntimeProgress renderUserMarkdown />
{childDetailSession.artifacts && childDetailSession.artifacts.length > 0 && (
Artifacts
    {childDetailSession.artifacts.map((a, i) => (
  • {a}
  • ))}
)} {childDetailSession.handoffTo && (
Passed To
)}
) : ( <> {showSessionStrip && (
{openSessions.map((session) => { const unreadCount = unreadCounts?.[session.channelId] ?? 0 const isActiveTab = activeSession?.taskId === session.taskId return (
) })}
{canShowMultiSessionView && ( )}
)} {/* Header with tabs or title */}
{showMultiSessionGrid ? ( {isCompanyRuntime ? 'Open Runtime Sessions' : 'Open Sessions'} ) : showTabs ? (
{canShowAgentsTab && ( )} {onCommsRefresh && ( )} {canShowTeamTab && ( )}
) : isSecretary ? ( Secretary ) : ( Activity )}
{panelState === 'open' && ( )} {panelState === 'maximized' && ( )}
{/* Body */}
{/* No session selected */} {!activeSession && !isSecretary && !isActivity && (
📋 {isCompanyRuntime ? 'Select a Work Item to see details' : 'Select a task to see details'}
)} {showMultiSessionGrid && (
{openSessions.map((session) => { const sessionMessages = openSessionMessages[session.taskId] ?? [] const sessionChildren = openSessionChildren[session.taskId] ?? [] const sessionPeers = getConversationPeerSessions(session, allSessions) const sessionConversation = projectSessionConversation(session, [...sessionPeers, ...sessionChildren]) const sessionConversationSession = getConversationSessionView( session, sessionConversation.runtimeSession, sessionConversation.timelineSessions, ) const sessionWorkItemRoleSessions = getWorkItemRoleSessions( sessionConversationSession ?? session, allSessions, ) const sessionRoleWorkItems = sessionConversationSession?.roleWorkItems ?? session.roleWorkItems const activeChildCount = activeAgentCountFor(sessionRoleWorkItems, sessionChildren) ?? 0 const assigneeNames = session.assigneeIds .map(id => agents.find(agent => agent.agent_id === id)?.name ?? id) .filter(Boolean) const runtimeLabel = sessionRuntimeLabel(sessionConversationSession ?? session, activeChildCount) const sessionIsCompanyRuntime = isCompanyRuntimeSession(session, sessionChildren.length) const sessionDisplaySession = sessionConversation.displaySession ?? session const sessionProgressLog = mergeConversationProgressLog(sessionConversation.timelineSessions) const sessionMessageCount = getConversationMessageCount(sessionConversation.timelineSessions) const sessionLockedMode = isSessionConfigLocked( sessionConversationSession ?? sessionDisplaySession, Math.max(sessionMessageCount, sessionMessages.length), ) const sessionHistoryLoading = sessionConversation.timelineSessions.some( (timelineSession) => isSessionHistoryLoading?.(timelineSession.taskId) ?? false, ) return (
{((sessionConversationSession ?? session).canStop ?? (sessionConversationSession ?? session).status === 'running') && (sessionConversationSession ?? session).runtimeControlState !== 'suspending' && onStopTask && ( )} {(sessionConversationSession ?? session).runtimeControlState === 'suspending' && ( )} {canShowContinue(sessionConversationSession ?? session) && onResumeTask && ( )} {(sessionConversationSession ?? session).status !== 'done' && (sessionConversationSession ?? session).status !== 'cancelled' && onCompleteTask && ( )}
{sessionModeLabel(session)} {session.status} {runtimeLabel && {runtimeLabel}} {assigneeNames.length > 0 && {assigneeNames.join(', ')}} {relativeTime(session.updatedAt)}
onSessionSend?.(session.taskId, content, undefined, metadata)} onWorkItemClick={onWorkItemClick} onWorkItemOpenSession={onWorkItemOpenSession} onMarkRead={() => onSessionMarkRead?.(session.taskId)} scrollScope={session.channelId} hasOlderHistory={ // Keep known cursors available during live work; // suppress only the count-based fallback. conversationHasOlderHistory( sessionConversation.timelineSessions, sessionMessages.length, sessionDetailLevel(sessionDisplaySession ?? session), !sessionConversation.timelineSessions.some(isSessionWorking), ) } totalMessageCount={sessionMessageCount} onLoadOlderHistory={(oldestMessage) => { const detailLevel = sessionDetailLevel(sessionDisplaySession ?? session) const matchedSession = sessionConversation.timelineSessions.find( (timelineSession) => timelineSession.channelId === oldestMessage?.channelId, ) const targetSession = ( matchedSession && sessionHasMoreForDetail(matchedSession, detailLevel) !== false ? matchedSession : undefined ) ?? sessionConversation.timelineSessions.find( timelineSession => sessionHasMoreForDetail(timelineSession, detailLevel) === true, ) ?? sessionDisplaySession ?? session return onLoadSessionHistory?.( targetSession.taskId, oldestMessage, detailLevel, ) }} loadingOlderHistory={sessionHistoryLoading} showRuntimeProgress={sessionDetailLevel(sessionDisplaySession) === 'full'} />
onSessionSend?.( session.taskId, content, attachments, checkpointReplyMetadataForComposer( analyzeCheckpointMessages(sessionMessages).latestPendingReplyMetadata, ), )} onModeChange={(mode, profile, orgId) => onSessionConfigChange?.(session.taskId, mode, profile, orgId)} onTaskAgentChange={(preferredAgent) => onSessionTaskAgentChange?.(session.taskId, preferredAgent)} onContinueInNewChat={onContinueInNewChat} onSavedOrgsRefresh={onSavedOrgsList} onSavedOrgLoad={onSavedOrgLoad} onStop={() => onStopTask?.(sessionConversation.runtimeSession?.taskId ?? session.taskId)} />
) })}
)} {/* Activity view */} {isActivity && ( <>
Recent activity across all sessions
)} {/* Secretary view */} {isSecretary && ( <> )} {/* Session view — Chat tab */} {activeSession && !isSecretary && !isActivity && !showMultiSessionGrid && panelTab === 'chat' && ( <> onLocateOnBoard(activeSession.taskId) : undefined} onStop={onStop} onComplete={(activeHeaderSession ?? activeSession).status !== 'done' && (activeHeaderSession ?? activeSession).status !== 'cancelled' ? onComplete : undefined} onResume={onResume} /> {isCompanyRuntime && (
)} { const targetSession = resolveConversationHistoryTarget(oldestMessage) if (!targetSession) return return onLoadSessionHistory?.( targetSession.taskId, oldestMessage, activeDetailMode, ) }} loadingOlderHistory={activeConversationLoading} showWorkItemRuntimeCard={false} showRuntimeProgress={activeDetailMode === 'full'} /> onSessionConfigChange?.(activeSession.taskId, mode, profile, orgId)} onTaskAgentChange={(preferredAgent) => onSessionTaskAgentChange?.(activeSession.taskId, preferredAgent)} onContinueInNewChat={onContinueInNewChat} onSavedOrgsRefresh={onSavedOrgsList} onSavedOrgLoad={onSavedOrgLoad} onStop={onStop} /> )} {/* Session view — Agents tab */} {activeSession && !isSecretary && !showMultiSessionGrid && panelTab === 'agents' && canShowAgentsTab && ( (visibleAgentSessions.length > 0 || hasRoleWorkItems) ? ( ) : (
🧠 No child agents have started for this session yet
) )} {/* Session view — Info tab. Reorganised into semantic cards (Overview / Identity / Timing) instead of a flat key-value list. Internal-only debug fields (projection id, channel id) are tucked into a collapsed "Developer details" section so they don't dominate the user-facing summary. */} {activeSession && !isSecretary && !showMultiSessionGrid && panelTab === 'info' && taskForInfo && ( )} {/* Comms tab */} {panelTab === 'comms' && onCommsRefresh && onCommsReadMessage && (
)} {panelTab === 'team' && canShowTeamTab && (
)}
)}
) }