fix(logs): restore member process log source

This commit is contained in:
777genius 2026-05-25 17:17:11 +03:00
parent 5513531053
commit 7e0520cb4c
2 changed files with 72 additions and 26 deletions

View file

@ -1,13 +1,15 @@
import { useEffect, useMemo } from 'react'; import { useCallback, useEffect, useMemo, useState } from 'react';
import { useAppTranslation } from '@features/localization/renderer'; import { useAppTranslation } from '@features/localization/renderer';
import { api } from '@renderer/api';
import { useStore } from '@renderer/store'; import { useStore } from '@renderer/store';
import { selectResolvedMembersForTeamName } from '@renderer/store/slices/teamSlice'; import { selectResolvedMembersForTeamName } from '@renderer/store/slices/teamSlice';
import { useMemberLogStream } from '../hooks/useMemberLogStream'; import { useMemberLogStream } from '../hooks/useMemberLogStream';
import { ExecutionLogStreamView } from '../ui/ExecutionLogStreamView'; import { ExecutionLogStreamView } from '../ui/ExecutionLogStreamView';
import { MemberRuntimeProcessLogsPanel } from '../ui/MemberRuntimeProcessLogsPanel';
import type { MemberLogStreamSegment } from '../../contracts'; import type { MemberLogStreamSegment, MemberRuntimeLogKind } from '../../contracts';
import type { ResolvedTeamMember } from '@shared/types'; import type { ResolvedTeamMember } from '@shared/types';
interface MemberLogStreamSectionProps { interface MemberLogStreamSectionProps {
@ -17,6 +19,10 @@ interface MemberLogStreamSectionProps {
onInitialLoadErrorChange?: (hasError: boolean) => void; onInitialLoadErrorChange?: (hasError: boolean) => void;
} }
function describeMemberStream(): string {
return 'Member-scoped transcript and runtime logs rendered with the same execution-log components used in Task Log Stream.';
}
function getSegmentMetaLabel(segment: MemberLogStreamSegment): string { function getSegmentMetaLabel(segment: MemberLogStreamSegment): string {
const details = [segment.source.label]; const details = [segment.source.label];
if (segment.source.laneId) { if (segment.source.laneId) {
@ -39,8 +45,17 @@ export const MemberLogStreamSection = ({
onInitialLoadErrorChange, onInitialLoadErrorChange,
}: Readonly<MemberLogStreamSectionProps>): React.JSX.Element => { }: Readonly<MemberLogStreamSectionProps>): React.JSX.Element => {
const { t } = useAppTranslation('team'); const { t } = useAppTranslation('team');
const [selectedLogView, setSelectedLogView] = useState<'execution' | 'process'>('execution');
const teamMembers = useStore((s) => selectResolvedMembersForTeamName(s, teamName)); const teamMembers = useStore((s) => selectResolvedMembersForTeamName(s, teamName));
const { stream, loading, error } = useMemberLogStream({ teamName, member, enabled }); const { stream, loading, error } = useMemberLogStream({ teamName, member, enabled });
const loadRuntimeLogTail = useCallback(
(input: {
readonly kind: MemberRuntimeLogKind;
readonly maxBytes: number;
readonly forceRefresh?: boolean;
}) => api.memberLogStream.getMemberRuntimeLogTail(teamName, member.name, input),
[member.name, teamName]
);
const hasInitialLoadError = Boolean(error && !stream && !loading); const hasInitialLoadError = Boolean(error && !stream && !loading);
const boundedHistoryNote = useMemo(() => { const boundedHistoryNote = useMemo(() => {
if (!stream) return null; if (!stream) return null;
@ -55,24 +70,57 @@ export const MemberLogStreamSection = ({
}, [hasInitialLoadError, onInitialLoadErrorChange]); }, [hasInitialLoadError, onInitialLoadErrorChange]);
return ( return (
<ExecutionLogStreamView <div className="space-y-3">
title="" <div className="inline-flex rounded-md bg-[var(--color-surface-subtle)] p-0.5">
description="" <button
stream={stream} type="button"
loading={loading} className={`rounded px-2.5 py-1 text-xs font-medium transition-colors ${
error={error} selectedLogView === 'execution'
teamName={teamName} ? 'bg-[var(--color-surface)] text-[var(--color-text)] shadow-sm'
teamMembers={teamMembers} : 'text-[var(--color-text-muted)] hover:text-[var(--color-text)]'
loadingText={t('memberLogStream.logs.loading')} }`}
emptyTitle={t('memberLogStream.logs.emptyTitle')} onClick={() => setSelectedLogView('execution')}
emptyDescription={t('memberLogStream.logs.emptyDescription')} >
selectionResetKey={`${teamName}:${member.name}`} {t('memberLogStream.tabs.execution')}
boundedHistoryNote={boundedHistoryNote} </button>
forceSegmentHeaders <button
showIntro={false} type="button"
showSegmentParticipantBadge={false} className={`rounded px-2.5 py-1 text-xs font-medium transition-colors ${
buildSegmentRenderKey={buildMemberSegmentRenderKey} selectedLogView === 'process'
getSegmentMetaLabel={getSegmentMetaLabel} ? 'bg-[var(--color-surface)] text-[var(--color-text)] shadow-sm'
/> : 'text-[var(--color-text-muted)] hover:text-[var(--color-text)]'
}`}
onClick={() => setSelectedLogView('process')}
>
{t('memberLogStream.tabs.process')}
</button>
</div>
{selectedLogView === 'execution' ? (
<ExecutionLogStreamView
title={t('memberLogStream.logs.title')}
description={describeMemberStream()}
stream={stream}
loading={loading}
error={error}
teamName={teamName}
teamMembers={teamMembers}
loadingText={t('memberLogStream.logs.loading')}
emptyTitle={t('memberLogStream.logs.emptyTitle')}
emptyDescription={t('memberLogStream.logs.emptyDescription')}
selectionResetKey={`${teamName}:${member.name}`}
boundedHistoryNote={boundedHistoryNote}
forceSegmentHeaders
showSegmentParticipantBadge={false}
buildSegmentRenderKey={buildMemberSegmentRenderKey}
getSegmentMetaLabel={getSegmentMetaLabel}
/>
) : (
<MemberRuntimeProcessLogsPanel
enabled={enabled && selectedLogView === 'process'}
loadRuntimeLogTail={loadRuntimeLogTail}
/>
)}
</div>
); );
}; };

View file

@ -733,11 +733,9 @@ describe('CLI status visibility during completed install state', () => {
vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true); vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true);
storeState.cliInstallerState = 'idle'; storeState.cliInstallerState = 'idle';
storeState.codexRuntimeStatus = { storeState.codexRuntimeStatus = {
installed: true, installed: false,
source: 'path', source: 'missing',
state: 'ready', state: 'idle',
binaryPath: '/usr/local/bin/codex',
version: 'codex-cli 0.125.0',
}; };
storeState.cliStatus = createInstalledCliStatus({ storeState.cliStatus = createInstalledCliStatus({
flavor: 'agent_teams_orchestrator', flavor: 'agent_teams_orchestrator',