fix(ci): resolve frontend validation failures
This commit is contained in:
parent
01b8161f41
commit
46df757f49
10 changed files with 126 additions and 82 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
import { useCallback, useMemo, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
|
|
||||||
import { api } from '@renderer/api';
|
import { api } from '@renderer/api';
|
||||||
import { CreateTaskDialog } from '@renderer/components/team/dialogs/CreateTaskDialog';
|
import { CreateTaskDialog } from '@renderer/components/team/dialogs/CreateTaskDialog';
|
||||||
|
|
|
||||||
|
|
@ -6,29 +6,53 @@ import {
|
||||||
} from '@renderer/store/slices/teamSlice';
|
} from '@renderer/store/slices/teamSlice';
|
||||||
import { useShallow } from 'zustand/react/shallow';
|
import { useShallow } from 'zustand/react/shallow';
|
||||||
|
|
||||||
import type { TeamGraphData } from '../adapters/TeamGraphAdapter';
|
import type { AppState } from '@renderer/store/types';
|
||||||
|
|
||||||
export function useGraphMemberPopoverContext(teamName: string, memberName: string) {
|
interface GraphMemberPopoverContext {
|
||||||
|
teamData:
|
||||||
|
| (NonNullable<ReturnType<typeof selectTeamDataForName>> & {
|
||||||
|
members: ReturnType<typeof selectResolvedMembersForTeamName>;
|
||||||
|
messageFeed: [];
|
||||||
|
})
|
||||||
|
| null;
|
||||||
|
teamMembers: ReturnType<typeof selectResolvedMembersForTeamName>;
|
||||||
|
spawnEntry: AppState['memberSpawnStatusesByTeam'][string][string] | undefined;
|
||||||
|
leadActivity: AppState['leadActivityByTeam'][string] | undefined;
|
||||||
|
progress: ReturnType<typeof getCurrentProvisioningProgressForTeam> | null;
|
||||||
|
memberSpawnSnapshot: AppState['memberSpawnSnapshotsByTeam'][string] | undefined;
|
||||||
|
memberSpawnStatuses: AppState['memberSpawnStatusesByTeam'][string] | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectGraphMemberPopoverContext(
|
||||||
|
state: AppState,
|
||||||
|
teamName: string,
|
||||||
|
memberName: string
|
||||||
|
): GraphMemberPopoverContext {
|
||||||
|
const snapshot = teamName ? selectTeamDataForName(state, teamName) : null;
|
||||||
|
const teamMembers = teamName ? selectResolvedMembersForTeamName(state, teamName) : [];
|
||||||
|
|
||||||
|
return {
|
||||||
|
teamData: snapshot
|
||||||
|
? {
|
||||||
|
...snapshot,
|
||||||
|
members: teamMembers,
|
||||||
|
messageFeed: [],
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
teamMembers,
|
||||||
|
spawnEntry: teamName ? state.memberSpawnStatusesByTeam[teamName]?.[memberName] : undefined,
|
||||||
|
leadActivity: teamName ? state.leadActivityByTeam[teamName] : undefined,
|
||||||
|
progress: teamName ? getCurrentProvisioningProgressForTeam(state, teamName) : null,
|
||||||
|
memberSpawnSnapshot: teamName ? state.memberSpawnSnapshotsByTeam[teamName] : undefined,
|
||||||
|
memberSpawnStatuses: teamName ? state.memberSpawnStatusesByTeam[teamName] : undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useGraphMemberPopoverContext(
|
||||||
|
teamName: string,
|
||||||
|
memberName: string
|
||||||
|
): ReturnType<typeof selectGraphMemberPopoverContext> {
|
||||||
return useStore(
|
return useStore(
|
||||||
useShallow((state) => {
|
useShallow((state) => selectGraphMemberPopoverContext(state, teamName, memberName))
|
||||||
const snapshot = teamName ? selectTeamDataForName(state, teamName) : null;
|
|
||||||
const teamMembers = teamName ? selectResolvedMembersForTeamName(state, teamName) : [];
|
|
||||||
|
|
||||||
return {
|
|
||||||
teamData: snapshot
|
|
||||||
? {
|
|
||||||
...snapshot,
|
|
||||||
members: teamMembers,
|
|
||||||
messageFeed: [],
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
teamMembers,
|
|
||||||
spawnEntry: teamName ? state.memberSpawnStatusesByTeam[teamName]?.[memberName] : undefined,
|
|
||||||
leadActivity: teamName ? state.leadActivityByTeam[teamName] : undefined,
|
|
||||||
progress: teamName ? getCurrentProvisioningProgressForTeam(state, teamName) : null,
|
|
||||||
memberSpawnSnapshot: teamName ? state.memberSpawnSnapshotsByTeam[teamName] : undefined,
|
|
||||||
memberSpawnStatuses: teamName ? state.memberSpawnStatusesByTeam[teamName] : undefined,
|
|
||||||
};
|
|
||||||
})
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -289,6 +289,15 @@ export const GraphActivityHud = ({
|
||||||
const handleMessageClick = useCallback((item: TimelineItem) => {
|
const handleMessageClick = useCallback((item: TimelineItem) => {
|
||||||
setExpandedItem(item);
|
setExpandedItem(item);
|
||||||
}, []);
|
}, []);
|
||||||
|
const handleMessageKeyDown = useCallback(
|
||||||
|
(event: React.KeyboardEvent<HTMLDivElement>, item: TimelineItem): void => {
|
||||||
|
if (event.key === 'Enter' || event.key === ' ') {
|
||||||
|
event.preventDefault();
|
||||||
|
handleMessageClick(item);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[handleMessageClick]
|
||||||
|
);
|
||||||
|
|
||||||
const handleMemberClick = useCallback(
|
const handleMemberClick = useCallback(
|
||||||
(member: ResolvedTeamMember) => {
|
(member: ResolvedTeamMember) => {
|
||||||
|
|
@ -360,6 +369,52 @@ export const GraphActivityHud = ({
|
||||||
};
|
};
|
||||||
}, [enabled, forwardWheelToGraph, visibleLanes]);
|
}, [enabled, forwardWheelToGraph, visibleLanes]);
|
||||||
|
|
||||||
|
const renderLaneEntry = useCallback(
|
||||||
|
(entry: InlineActivityEntry, index: number): React.JSX.Element => {
|
||||||
|
const messageKey = toMessageKey(entry.message);
|
||||||
|
const timelineItem: TimelineItem = {
|
||||||
|
type: 'message',
|
||||||
|
message: entry.message,
|
||||||
|
};
|
||||||
|
const isUnread = !entry.message.read && !readSet.has(messageKey);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={entry.graphItem.id}
|
||||||
|
className="h-[72px] min-h-[72px] min-w-0 max-w-full cursor-pointer overflow-hidden"
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
|
onClick={() => handleMessageClick(timelineItem)}
|
||||||
|
onKeyDown={(event) => handleMessageKeyDown(event, timelineItem)}
|
||||||
|
>
|
||||||
|
<GraphActivityCard
|
||||||
|
message={entry.message}
|
||||||
|
teamName={teamName}
|
||||||
|
messageContext={messageContext}
|
||||||
|
teamNames={teamNames}
|
||||||
|
teamColorByName={teamColorByName}
|
||||||
|
isUnread={isUnread}
|
||||||
|
zebraShade={index % 2 === 1}
|
||||||
|
onClick={() => handleMessageClick(timelineItem)}
|
||||||
|
onOpenTaskDetail={onOpenTaskDetail}
|
||||||
|
onOpenMemberProfile={onOpenMemberProfile}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[
|
||||||
|
handleMessageClick,
|
||||||
|
handleMessageKeyDown,
|
||||||
|
messageContext,
|
||||||
|
onOpenMemberProfile,
|
||||||
|
onOpenTaskDetail,
|
||||||
|
readSet,
|
||||||
|
teamColorByName,
|
||||||
|
teamName,
|
||||||
|
teamNames,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
if (!enabled || !teamSnapshot || visibleLanes.length === 0) {
|
if (!enabled || !teamSnapshot || visibleLanes.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -421,43 +476,7 @@ export const GraphActivityHud = ({
|
||||||
No recent activity
|
No recent activity
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
{lane.entries.map((entry, index) => {
|
{lane.entries.map(renderLaneEntry)}
|
||||||
const messageKey = toMessageKey(entry.message);
|
|
||||||
const timelineItem: TimelineItem = {
|
|
||||||
type: 'message',
|
|
||||||
message: entry.message,
|
|
||||||
};
|
|
||||||
const isUnread = !entry.message.read && !readSet.has(messageKey);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
key={entry.graphItem.id}
|
|
||||||
className="h-[72px] min-h-[72px] min-w-0 max-w-full cursor-pointer overflow-hidden"
|
|
||||||
role="button"
|
|
||||||
tabIndex={0}
|
|
||||||
onClick={() => handleMessageClick(timelineItem)}
|
|
||||||
onKeyDown={(event) => {
|
|
||||||
if (event.key === 'Enter' || event.key === ' ') {
|
|
||||||
event.preventDefault();
|
|
||||||
handleMessageClick(timelineItem);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<GraphActivityCard
|
|
||||||
message={entry.message}
|
|
||||||
teamName={teamName}
|
|
||||||
messageContext={messageContext}
|
|
||||||
teamNames={teamNames}
|
|
||||||
teamColorByName={teamColorByName}
|
|
||||||
isUnread={isUnread}
|
|
||||||
zebraShade={index % 2 === 1}
|
|
||||||
onClick={() => handleMessageClick(timelineItem)}
|
|
||||||
onOpenTaskDetail={onOpenTaskDetail}
|
|
||||||
onOpenMemberProfile={onOpenMemberProfile}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
|
|
||||||
{lane.overflowCount > 0 ? (
|
{lane.overflowCount > 0 ? (
|
||||||
<button
|
<button
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,11 @@ import { GraphNodePopover } from './GraphNodePopover';
|
||||||
import { GraphProvisioningHud } from './GraphProvisioningHud';
|
import { GraphProvisioningHud } from './GraphProvisioningHud';
|
||||||
import { GraphTransientHandoffHud } from './GraphTransientHandoffHud';
|
import { GraphTransientHandoffHud } from './GraphTransientHandoffHud';
|
||||||
|
|
||||||
import type { GraphDomainRef, GraphEventPort } from '@claude-teams/agent-graph';
|
import type {
|
||||||
|
GraphDomainRef,
|
||||||
|
GraphEventPort,
|
||||||
|
TransientHandoffCard,
|
||||||
|
} from '@claude-teams/agent-graph';
|
||||||
import type {
|
import type {
|
||||||
MemberActivityFilter,
|
MemberActivityFilter,
|
||||||
MemberDetailTab,
|
MemberDetailTab,
|
||||||
|
|
@ -149,7 +153,7 @@ export const TeamGraphOverlay = ({
|
||||||
focusNodeIds?: ReadonlySet<string> | null;
|
focusNodeIds?: ReadonlySet<string> | null;
|
||||||
focusEdgeIds?: ReadonlySet<string> | null;
|
focusEdgeIds?: ReadonlySet<string> | null;
|
||||||
}) => {
|
}) => {
|
||||||
cards: import('@claude-teams/agent-graph').TransientHandoffCard[];
|
cards: TransientHandoffCard[];
|
||||||
time: number;
|
time: number;
|
||||||
};
|
};
|
||||||
worldToScreen?: (x: number, y: number) => { x: number; y: number };
|
worldToScreen?: (x: number, y: number) => { x: number; y: number };
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,11 @@ import { GraphNodePopover } from './GraphNodePopover';
|
||||||
import { GraphProvisioningHud } from './GraphProvisioningHud';
|
import { GraphProvisioningHud } from './GraphProvisioningHud';
|
||||||
import { GraphTransientHandoffHud } from './GraphTransientHandoffHud';
|
import { GraphTransientHandoffHud } from './GraphTransientHandoffHud';
|
||||||
|
|
||||||
import type { GraphDomainRef, GraphEventPort } from '@claude-teams/agent-graph';
|
import type {
|
||||||
|
GraphDomainRef,
|
||||||
|
GraphEventPort,
|
||||||
|
TransientHandoffCard,
|
||||||
|
} from '@claude-teams/agent-graph';
|
||||||
import type {
|
import type {
|
||||||
MemberActivityFilter,
|
MemberActivityFilter,
|
||||||
MemberDetailTab,
|
MemberDetailTab,
|
||||||
|
|
@ -169,7 +173,7 @@ export const TeamGraphTab = ({
|
||||||
focusNodeIds?: ReadonlySet<string> | null;
|
focusNodeIds?: ReadonlySet<string> | null;
|
||||||
focusEdgeIds?: ReadonlySet<string> | null;
|
focusEdgeIds?: ReadonlySet<string> | null;
|
||||||
}) => {
|
}) => {
|
||||||
cards: import('@claude-teams/agent-graph').TransientHandoffCard[];
|
cards: TransientHandoffCard[];
|
||||||
time: number;
|
time: number;
|
||||||
};
|
};
|
||||||
worldToScreen?: (x: number, y: number) => { x: number; y: number };
|
worldToScreen?: (x: number, y: number) => { x: number; y: number };
|
||||||
|
|
|
||||||
|
|
@ -15,15 +15,15 @@ import { BoardTaskExactLogSummarySelector } from '../exact/BoardTaskExactLogSumm
|
||||||
import { isBoardTaskExactLogsReadEnabled } from '../exact/featureGates';
|
import { isBoardTaskExactLogsReadEnabled } from '../exact/featureGates';
|
||||||
import { getBoardTaskExactLogFileVersions } from '../exact/fileVersions';
|
import { getBoardTaskExactLogFileVersions } from '../exact/fileVersions';
|
||||||
|
|
||||||
import { OpenCodeTaskLogStreamSource } from './OpenCodeTaskLogStreamSource';
|
|
||||||
import { CodexNativeTaskLogStreamSource } from './CodexNativeTaskLogStreamSource';
|
|
||||||
import { buildCodexNativeToolSignature } from './CodexNativeTraceProjector';
|
|
||||||
import { HistoricalBoardMcpRawProbe } from './HistoricalBoardMcpRawProbe';
|
|
||||||
import { TaskLogTranscriptCandidateSelector } from './TaskLogTranscriptCandidateSelector';
|
|
||||||
import {
|
import {
|
||||||
canonicalizeBoardTaskLogToolName,
|
canonicalizeBoardTaskLogToolName,
|
||||||
isBoardTaskLogMcpToolName,
|
isBoardTaskLogMcpToolName,
|
||||||
} from './boardTaskLogToolNames';
|
} from './boardTaskLogToolNames';
|
||||||
|
import { CodexNativeTaskLogStreamSource } from './CodexNativeTaskLogStreamSource';
|
||||||
|
import { buildCodexNativeToolSignature } from './CodexNativeTraceProjector';
|
||||||
|
import { HistoricalBoardMcpRawProbe } from './HistoricalBoardMcpRawProbe';
|
||||||
|
import { OpenCodeTaskLogStreamSource } from './OpenCodeTaskLogStreamSource';
|
||||||
|
import { TaskLogTranscriptCandidateSelector } from './TaskLogTranscriptCandidateSelector';
|
||||||
|
|
||||||
import type { BoardTaskActivityRecord } from '../activity/BoardTaskActivityRecord';
|
import type { BoardTaskActivityRecord } from '../activity/BoardTaskActivityRecord';
|
||||||
import type { BoardTaskExactLogDetailCandidate } from '../exact/BoardTaskExactLogTypes';
|
import type { BoardTaskExactLogDetailCandidate } from '../exact/BoardTaskExactLogTypes';
|
||||||
|
|
@ -1684,12 +1684,12 @@ export class BoardTaskLogStreamService {
|
||||||
const cacheKey = this.buildLayoutCacheKey(teamName, taskId);
|
const cacheKey = this.buildLayoutCacheKey(teamName, taskId);
|
||||||
const generation = this.getTranscriptDiscoveryGeneration(teamName);
|
const generation = this.getTranscriptDiscoveryGeneration(teamName);
|
||||||
const cached = this.layoutCache.get(cacheKey);
|
const cached = this.layoutCache.get(cacheKey);
|
||||||
if (cached && cached.generation === generation && cached.expiresAt > Date.now()) {
|
if (cached?.generation === generation && cached.expiresAt > Date.now()) {
|
||||||
return cached.layout;
|
return cached.layout;
|
||||||
}
|
}
|
||||||
|
|
||||||
const existingInFlight = this.layoutInFlight.get(cacheKey);
|
const existingInFlight = this.layoutInFlight.get(cacheKey);
|
||||||
if (existingInFlight && existingInFlight.generation === generation) {
|
if (existingInFlight?.generation === generation) {
|
||||||
return await existingInFlight.promise;
|
return await existingInFlight.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
|
import { getTeamsBasePath } from '@main/utils/pathDecoder';
|
||||||
import * as fs from 'fs/promises';
|
import * as fs from 'fs/promises';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
import { getTeamsBasePath } from '@main/utils/pathDecoder';
|
|
||||||
|
|
||||||
const TRACE_ROOT_SEGMENT = path.join('.member-work-sync', 'runtime-hooks', 'codex-native-traces');
|
const TRACE_ROOT_SEGMENT = path.join('.member-work-sync', 'runtime-hooks', 'codex-native-traces');
|
||||||
|
|
||||||
export interface CodexNativeTraceProjection {
|
export interface CodexNativeTraceProjection {
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
|
import { getTaskDisplayId } from '@shared/utils/taskIdentity';
|
||||||
import { createReadStream } from 'fs';
|
import { createReadStream } from 'fs';
|
||||||
import * as readline from 'readline';
|
import * as readline from 'readline';
|
||||||
|
|
||||||
import { getTaskDisplayId } from '@shared/utils/taskIdentity';
|
|
||||||
|
|
||||||
import type { TeamTask } from '@shared/types';
|
import type { TeamTask } from '@shared/types';
|
||||||
|
|
||||||
const RAW_PROBE_CONCURRENCY = process.platform === 'win32' ? 4 : 8;
|
const RAW_PROBE_CONCURRENCY = process.platform === 'win32' ? 4 : 8;
|
||||||
|
|
|
||||||
|
|
@ -45,10 +45,5 @@ export function isReadOnlyBoardTaskLogToolName(toolName: string | undefined): bo
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isRecoverableHistoricalBoardTaskLogToolName(toolName: string | undefined): boolean {
|
export function isRecoverableHistoricalBoardTaskLogToolName(toolName: string | undefined): boolean {
|
||||||
const canonical = canonicalizeBoardTaskLogToolName(toolName);
|
return isBoardTaskLogMcpToolName(toolName);
|
||||||
return (
|
|
||||||
canonical !== null &&
|
|
||||||
(HISTORICAL_BOARD_LIFECYCLE_TOOL_NAMES.has(canonical) ||
|
|
||||||
HISTORICAL_BOARD_ACTION_TOOL_NAMES.has(canonical))
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,9 @@ import { AlertCircle, Clock, FileText, Loader2 } from 'lucide-react';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
BoardTaskLogActor,
|
BoardTaskLogActor,
|
||||||
ResolvedTeamMember,
|
|
||||||
BoardTaskLogSegment,
|
BoardTaskLogSegment,
|
||||||
BoardTaskLogStreamResponse,
|
BoardTaskLogStreamResponse,
|
||||||
|
ResolvedTeamMember,
|
||||||
} from '@shared/types';
|
} from '@shared/types';
|
||||||
|
|
||||||
interface TaskLogStreamSectionProps {
|
interface TaskLogStreamSectionProps {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue