fix(ci): restore workspace validation
This commit is contained in:
parent
d8672c32f6
commit
bb60bbb0ec
42 changed files with 208 additions and 201 deletions
|
|
@ -6,8 +6,8 @@ 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 { TeamSummary } from '@shared/types/team';
|
|
||||||
import type { TeamGraphData } from '../adapters/TeamGraphAdapter';
|
import type { TeamGraphData } from '../adapters/TeamGraphAdapter';
|
||||||
|
import type { TeamSummary } from '@shared/types/team';
|
||||||
|
|
||||||
export function useGraphActivityContext(teamName: string): {
|
export function useGraphActivityContext(teamName: string): {
|
||||||
teamData: TeamGraphData | null;
|
teamData: TeamGraphData | null;
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,8 @@ import { getSnapshot, subscribe } from '@renderer/services/commentReadStorage';
|
||||||
import { useStore } from '@renderer/store';
|
import { useStore } from '@renderer/store';
|
||||||
import {
|
import {
|
||||||
getCurrentProvisioningProgressForTeam,
|
getCurrentProvisioningProgressForTeam,
|
||||||
selectResolvedMembersForTeamName,
|
|
||||||
isTeamGraphSlotPersistenceDisabled,
|
isTeamGraphSlotPersistenceDisabled,
|
||||||
|
selectResolvedMembersForTeamName,
|
||||||
selectTeamDataForName,
|
selectTeamDataForName,
|
||||||
selectTeamMessages,
|
selectTeamMessages,
|
||||||
} from '@renderer/store/slices/teamSlice';
|
} from '@renderer/store/slices/teamSlice';
|
||||||
|
|
@ -19,8 +19,8 @@ import { useShallow } from 'zustand/react/shallow';
|
||||||
|
|
||||||
import { TeamGraphAdapter } from '../adapters/TeamGraphAdapter';
|
import { TeamGraphAdapter } from '../adapters/TeamGraphAdapter';
|
||||||
|
|
||||||
import type { GraphDataPort } from '@claude-teams/agent-graph';
|
|
||||||
import type { TeamGraphData } from '../adapters/TeamGraphAdapter';
|
import type { TeamGraphData } from '../adapters/TeamGraphAdapter';
|
||||||
|
import type { GraphDataPort } from '@claude-teams/agent-graph';
|
||||||
|
|
||||||
export function useTeamGraphAdapter(teamName: string): GraphDataPort {
|
export function useTeamGraphAdapter(teamName: string): GraphDataPort {
|
||||||
const adapterRef = useRef<TeamGraphAdapter>(TeamGraphAdapter.create());
|
const adapterRef = useRef<TeamGraphAdapter>(TeamGraphAdapter.create());
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
import { useStore } from '@renderer/store';
|
import { useStore } from '@renderer/store';
|
||||||
|
import { isTeamGraphSlotPersistenceDisabled } from '@renderer/store/slices/teamSlice';
|
||||||
|
|
||||||
import { parseGraphMemberNodeId } from '../../core/domain/graphOwnerIdentity';
|
import { parseGraphMemberNodeId } from '../../core/domain/graphOwnerIdentity';
|
||||||
|
|
||||||
|
|
@ -8,6 +9,7 @@ import type { GraphOwnerSlotAssignment } from '@claude-teams/agent-graph';
|
||||||
|
|
||||||
export function useTeamGraphSurfaceActions(teamName: string): {
|
export function useTeamGraphSurfaceActions(teamName: string): {
|
||||||
openTeamPage: () => void;
|
openTeamPage: () => void;
|
||||||
|
resetOwnerSlotAssignmentsToDefaults: () => void;
|
||||||
commitOwnerSlotDrop: (payload: {
|
commitOwnerSlotDrop: (payload: {
|
||||||
nodeId: string;
|
nodeId: string;
|
||||||
assignment: GraphOwnerSlotAssignment;
|
assignment: GraphOwnerSlotAssignment;
|
||||||
|
|
@ -19,6 +21,13 @@ export function useTeamGraphSurfaceActions(teamName: string): {
|
||||||
useStore.getState().openTeamTab(teamName);
|
useStore.getState().openTeamTab(teamName);
|
||||||
}, [teamName]);
|
}, [teamName]);
|
||||||
|
|
||||||
|
const resetOwnerSlotAssignmentsToDefaults = useCallback(() => {
|
||||||
|
if (!isTeamGraphSlotPersistenceDisabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
useStore.getState().resetTeamGraphSlotAssignmentsToDefaults(teamName);
|
||||||
|
}, [teamName]);
|
||||||
|
|
||||||
const commitOwnerSlotDrop = useCallback(
|
const commitOwnerSlotDrop = useCallback(
|
||||||
(payload: {
|
(payload: {
|
||||||
nodeId: string;
|
nodeId: string;
|
||||||
|
|
@ -51,6 +60,7 @@ export function useTeamGraphSurfaceActions(teamName: string): {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
openTeamPage,
|
openTeamPage,
|
||||||
|
resetOwnerSlotAssignmentsToDefaults,
|
||||||
commitOwnerSlotDrop,
|
commitOwnerSlotDrop,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,6 @@ import { useCallback, useLayoutEffect, useMemo } from 'react';
|
||||||
|
|
||||||
import { GraphView } from '@claude-teams/agent-graph';
|
import { GraphView } from '@claude-teams/agent-graph';
|
||||||
import { TeamSidebarHost } from '@renderer/components/team/sidebar/TeamSidebarHost';
|
import { TeamSidebarHost } from '@renderer/components/team/sidebar/TeamSidebarHost';
|
||||||
import { useStore } from '@renderer/store';
|
|
||||||
import { isTeamGraphSlotPersistenceDisabled } from '@renderer/store/slices/teamSlice';
|
|
||||||
|
|
||||||
import { useGraphCreateTaskDialog } from '../hooks/useGraphCreateTaskDialog';
|
import { useGraphCreateTaskDialog } from '../hooks/useGraphCreateTaskDialog';
|
||||||
import { useGraphSidebarVisibility } from '../hooks/useGraphSidebarVisibility';
|
import { useGraphSidebarVisibility } from '../hooks/useGraphSidebarVisibility';
|
||||||
|
|
@ -54,10 +52,11 @@ export const TeamGraphOverlay = ({
|
||||||
onOpenMemberProfile,
|
onOpenMemberProfile,
|
||||||
}: TeamGraphOverlayProps): React.JSX.Element => {
|
}: TeamGraphOverlayProps): React.JSX.Element => {
|
||||||
const graphData = useTeamGraphAdapter(teamName);
|
const graphData = useTeamGraphAdapter(teamName);
|
||||||
const { openTeamPage: openTeamTab, commitOwnerSlotDrop } = useTeamGraphSurfaceActions(teamName);
|
const {
|
||||||
const resetTeamGraphSlotAssignmentsToDefaults = useStore(
|
openTeamPage: openTeamTab,
|
||||||
(s) => s.resetTeamGraphSlotAssignmentsToDefaults
|
resetOwnerSlotAssignmentsToDefaults,
|
||||||
);
|
commitOwnerSlotDrop,
|
||||||
|
} = useTeamGraphSurfaceActions(teamName);
|
||||||
const { sidebarVisible: persistedSidebarVisible, toggleSidebarVisible } =
|
const { sidebarVisible: persistedSidebarVisible, toggleSidebarVisible } =
|
||||||
useGraphSidebarVisibility();
|
useGraphSidebarVisibility();
|
||||||
const { dialog: createTaskDialog, openCreateTaskDialog } = useGraphCreateTaskDialog(teamName);
|
const { dialog: createTaskDialog, openCreateTaskDialog } = useGraphCreateTaskDialog(teamName);
|
||||||
|
|
@ -92,11 +91,8 @@ export const TeamGraphOverlay = ({
|
||||||
}, [openCreateTaskDialog]);
|
}, [openCreateTaskDialog]);
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
if (!isTeamGraphSlotPersistenceDisabled()) {
|
resetOwnerSlotAssignmentsToDefaults();
|
||||||
return;
|
}, [resetOwnerSlotAssignmentsToDefaults]);
|
||||||
}
|
|
||||||
resetTeamGraphSlotAssignmentsToDefaults(teamName);
|
|
||||||
}, [resetTeamGraphSlotAssignmentsToDefaults, teamName]);
|
|
||||||
|
|
||||||
const events: GraphEventPort = {
|
const events: GraphEventPort = {
|
||||||
onNodeDoubleClick: useCallback(
|
onNodeDoubleClick: useCallback(
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,6 @@ import { lazy, Suspense, useCallback, useLayoutEffect, useMemo, useState } from
|
||||||
|
|
||||||
import { GraphView } from '@claude-teams/agent-graph';
|
import { GraphView } from '@claude-teams/agent-graph';
|
||||||
import { TeamSidebarHost } from '@renderer/components/team/sidebar/TeamSidebarHost';
|
import { TeamSidebarHost } from '@renderer/components/team/sidebar/TeamSidebarHost';
|
||||||
import { useStore } from '@renderer/store';
|
|
||||||
import { isTeamGraphSlotPersistenceDisabled } from '@renderer/store/slices/teamSlice';
|
|
||||||
|
|
||||||
import { useGraphCreateTaskDialog } from '../hooks/useGraphCreateTaskDialog';
|
import { useGraphCreateTaskDialog } from '../hooks/useGraphCreateTaskDialog';
|
||||||
import { useGraphSidebarVisibility } from '../hooks/useGraphSidebarVisibility';
|
import { useGraphSidebarVisibility } from '../hooks/useGraphSidebarVisibility';
|
||||||
|
|
@ -47,10 +45,8 @@ export const TeamGraphTab = ({
|
||||||
isPaneFocused = false,
|
isPaneFocused = false,
|
||||||
}: TeamGraphTabProps): React.JSX.Element => {
|
}: TeamGraphTabProps): React.JSX.Element => {
|
||||||
const graphData = useTeamGraphAdapter(teamName);
|
const graphData = useTeamGraphAdapter(teamName);
|
||||||
const { openTeamPage, commitOwnerSlotDrop } = useTeamGraphSurfaceActions(teamName);
|
const { openTeamPage, resetOwnerSlotAssignmentsToDefaults, commitOwnerSlotDrop } =
|
||||||
const resetTeamGraphSlotAssignmentsToDefaults = useStore(
|
useTeamGraphSurfaceActions(teamName);
|
||||||
(s) => s.resetTeamGraphSlotAssignmentsToDefaults
|
|
||||||
);
|
|
||||||
const [fullscreen, setFullscreen] = useState(false);
|
const [fullscreen, setFullscreen] = useState(false);
|
||||||
const { sidebarVisible, toggleSidebarVisible } = useGraphSidebarVisibility();
|
const { sidebarVisible, toggleSidebarVisible } = useGraphSidebarVisibility();
|
||||||
const { dialog: createTaskDialog, openCreateTaskDialog } = useGraphCreateTaskDialog(teamName);
|
const { dialog: createTaskDialog, openCreateTaskDialog } = useGraphCreateTaskDialog(teamName);
|
||||||
|
|
@ -82,11 +78,11 @@ export const TeamGraphTab = ({
|
||||||
}, [openCreateTaskDialog]);
|
}, [openCreateTaskDialog]);
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
if (!isTeamGraphSlotPersistenceDisabled() || !isActive) {
|
if (!isActive) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
resetTeamGraphSlotAssignmentsToDefaults(teamName);
|
resetOwnerSlotAssignmentsToDefaults();
|
||||||
}, [isActive, resetTeamGraphSlotAssignmentsToDefaults, teamName]);
|
}, [isActive, resetOwnerSlotAssignmentsToDefaults]);
|
||||||
|
|
||||||
// Task action dispatchers
|
// Task action dispatchers
|
||||||
const dispatchTaskAction = useCallback(
|
const dispatchTaskAction = useCallback(
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import {
|
import {
|
||||||
DASHBOARD_RECENT_PROJECTS_ROUTE,
|
DASHBOARD_RECENT_PROJECTS_ROUTE,
|
||||||
normalizeDashboardRecentProjectsPayload,
|
|
||||||
type DashboardRecentProjectsPayload,
|
type DashboardRecentProjectsPayload,
|
||||||
|
normalizeDashboardRecentProjectsPayload,
|
||||||
} from '@features/recent-projects/contracts';
|
} from '@features/recent-projects/contracts';
|
||||||
import { createLogger } from '@shared/utils/logger';
|
import { createLogger } from '@shared/utils/logger';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
import {
|
||||||
|
type DashboardRecentProjectsPayload,
|
||||||
|
normalizeDashboardRecentProjectsPayload,
|
||||||
|
} from '@features/recent-projects/contracts';
|
||||||
|
|
||||||
import { ListDashboardRecentProjectsUseCase } from '../../core/application/use-cases/ListDashboardRecentProjectsUseCase';
|
import { ListDashboardRecentProjectsUseCase } from '../../core/application/use-cases/ListDashboardRecentProjectsUseCase';
|
||||||
import { DashboardRecentProjectsPresenter } from '../adapters/output/presenters/DashboardRecentProjectsPresenter';
|
import { DashboardRecentProjectsPresenter } from '../adapters/output/presenters/DashboardRecentProjectsPresenter';
|
||||||
import { ClaudeRecentProjectsSourceAdapter } from '../adapters/output/sources/ClaudeRecentProjectsSourceAdapter';
|
import { ClaudeRecentProjectsSourceAdapter } from '../adapters/output/sources/ClaudeRecentProjectsSourceAdapter';
|
||||||
|
|
@ -10,10 +15,6 @@ import { RecentProjectIdentityResolver } from '../infrastructure/identity/Recent
|
||||||
|
|
||||||
import type { ClockPort } from '../../core/application/ports/ClockPort';
|
import type { ClockPort } from '../../core/application/ports/ClockPort';
|
||||||
import type { LoggerPort } from '../../core/application/ports/LoggerPort';
|
import type { LoggerPort } from '../../core/application/ports/LoggerPort';
|
||||||
import {
|
|
||||||
normalizeDashboardRecentProjectsPayload,
|
|
||||||
type DashboardRecentProjectsPayload,
|
|
||||||
} from '@features/recent-projects/contracts';
|
|
||||||
import type { ServiceContext } from '@main/services';
|
import type { ServiceContext } from '@main/services';
|
||||||
|
|
||||||
export interface RecentProjectsFeatureFacade {
|
export interface RecentProjectsFeatureFacade {
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
import type {
|
|
||||||
DashboardRecentProjectsPayloadLike,
|
|
||||||
DashboardRecentProjectsPayload,
|
|
||||||
} from '@features/recent-projects/contracts';
|
|
||||||
import { normalizeDashboardRecentProjectsPayload } from '@features/recent-projects/contracts';
|
import { normalizeDashboardRecentProjectsPayload } from '@features/recent-projects/contracts';
|
||||||
|
|
||||||
|
import type {
|
||||||
|
DashboardRecentProjectsPayload,
|
||||||
|
DashboardRecentProjectsPayloadLike,
|
||||||
|
} from '@features/recent-projects/contracts';
|
||||||
|
|
||||||
const RECENT_PROJECTS_CLIENT_CACHE_TTL_MS = 15_000;
|
const RECENT_PROJECTS_CLIENT_CACHE_TTL_MS = 15_000;
|
||||||
const RECENT_PROJECTS_CLIENT_DEGRADED_CACHE_TTL_MS = 1_500;
|
const RECENT_PROJECTS_CLIENT_DEGRADED_CACHE_TTL_MS = 1_500;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -180,8 +180,8 @@ import type {
|
||||||
TeamSummary,
|
TeamSummary,
|
||||||
TeamTask,
|
TeamTask,
|
||||||
TeamTaskStatus,
|
TeamTaskStatus,
|
||||||
TeamViewSnapshot,
|
|
||||||
TeamUpdateConfigRequest,
|
TeamUpdateConfigRequest,
|
||||||
|
TeamViewSnapshot,
|
||||||
ToolApprovalFileContent,
|
ToolApprovalFileContent,
|
||||||
ToolApprovalSettings,
|
ToolApprovalSettings,
|
||||||
UpdateKanbanPatch,
|
UpdateKanbanPatch,
|
||||||
|
|
|
||||||
|
|
@ -50,8 +50,8 @@ import type {
|
||||||
CliInstallationStatus,
|
CliInstallationStatus,
|
||||||
CliInstallerProgress,
|
CliInstallerProgress,
|
||||||
CliPlatform,
|
CliPlatform,
|
||||||
CliProviderModelAvailability,
|
|
||||||
CliProviderId,
|
CliProviderId,
|
||||||
|
CliProviderModelAvailability,
|
||||||
CliProviderStatus,
|
CliProviderStatus,
|
||||||
} from '@shared/types';
|
} from '@shared/types';
|
||||||
import type { BrowserWindow } from 'electron';
|
import type { BrowserWindow } from 'electron';
|
||||||
|
|
@ -596,7 +596,7 @@ export class CliInstallerService {
|
||||||
private updateLatestProviderStatus(providerStatus: CliProviderStatus): void {
|
private updateLatestProviderStatus(providerStatus: CliProviderStatus): void {
|
||||||
if (
|
if (
|
||||||
providerStatus.modelVerificationState !== 'verifying' &&
|
providerStatus.modelVerificationState !== 'verifying' &&
|
||||||
!((providerStatus.modelAvailability?.length ?? 0) > 0)
|
(providerStatus.modelAvailability?.length ?? 0) <= 0
|
||||||
) {
|
) {
|
||||||
this.latestProviderSignatures.set(providerStatus.providerId, null);
|
this.latestProviderSignatures.set(providerStatus.providerId, null);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,8 +47,8 @@ import { TeamInboxWriter } from './TeamInboxWriter';
|
||||||
import { TeamKanbanManager } from './TeamKanbanManager';
|
import { TeamKanbanManager } from './TeamKanbanManager';
|
||||||
import { TeamMemberResolver } from './TeamMemberResolver';
|
import { TeamMemberResolver } from './TeamMemberResolver';
|
||||||
import { TeamMemberRuntimeAdvisoryService } from './TeamMemberRuntimeAdvisoryService';
|
import { TeamMemberRuntimeAdvisoryService } from './TeamMemberRuntimeAdvisoryService';
|
||||||
import { TeamMessageFeedService } from './TeamMessageFeedService';
|
|
||||||
import { TeamMembersMetaStore } from './TeamMembersMetaStore';
|
import { TeamMembersMetaStore } from './TeamMembersMetaStore';
|
||||||
|
import { TeamMessageFeedService } from './TeamMessageFeedService';
|
||||||
import { TeamMetaStore } from './TeamMetaStore';
|
import { TeamMetaStore } from './TeamMetaStore';
|
||||||
import { TeamSentMessagesStore } from './TeamSentMessagesStore';
|
import { TeamSentMessagesStore } from './TeamSentMessagesStore';
|
||||||
import { TeamTaskCommentNotificationJournal } from './TeamTaskCommentNotificationJournal';
|
import { TeamTaskCommentNotificationJournal } from './TeamTaskCommentNotificationJournal';
|
||||||
|
|
@ -75,8 +75,8 @@ import type {
|
||||||
TaskRef,
|
TaskRef,
|
||||||
TeamConfig,
|
TeamConfig,
|
||||||
TeamCreateConfigRequest,
|
TeamCreateConfigRequest,
|
||||||
TeamMemberActivityMeta,
|
|
||||||
TeamMember,
|
TeamMember,
|
||||||
|
TeamMemberActivityMeta,
|
||||||
TeamProcess,
|
TeamProcess,
|
||||||
TeamSummary,
|
TeamSummary,
|
||||||
TeamTask,
|
TeamTask,
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
|
import { getMemberColorByName } from '@shared/constants/memberColors';
|
||||||
import {
|
import {
|
||||||
createCliAutoSuffixNameGuard,
|
createCliAutoSuffixNameGuard,
|
||||||
createCliProvisionerNameGuard,
|
createCliProvisionerNameGuard,
|
||||||
} from '@shared/utils/teamMemberName';
|
} from '@shared/utils/teamMemberName';
|
||||||
import { getStableTeamOwnerId } from '@shared/utils/teamStableOwnerId';
|
import { getStableTeamOwnerId } from '@shared/utils/teamStableOwnerId';
|
||||||
import { getMemberColorByName } from '@shared/constants/memberColors';
|
|
||||||
|
|
||||||
import type { TeamConfig, TeamMember, TeamMemberSnapshot, TeamTaskWithKanban } from '@shared/types';
|
import type { TeamConfig, TeamMember, TeamMemberSnapshot, TeamTaskWithKanban } from '@shared/types';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import { createHash } from 'crypto';
|
|
||||||
|
|
||||||
import { buildStandaloneSlashCommandMeta } from '@shared/utils/slashCommands';
|
|
||||||
import { classifyIdleNotificationText } from '@shared/utils/idleNotificationSemantics';
|
import { classifyIdleNotificationText } from '@shared/utils/idleNotificationSemantics';
|
||||||
|
import { buildStandaloneSlashCommandMeta } from '@shared/utils/slashCommands';
|
||||||
|
import { createHash } from 'crypto';
|
||||||
|
|
||||||
import { getEffectiveInboxMessageId } from './inboxMessageIdentity';
|
import { getEffectiveInboxMessageId } from './inboxMessageIdentity';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ import {
|
||||||
import { getMemberColorByName } from '@shared/constants/memberColors';
|
import { getMemberColorByName } from '@shared/constants/memberColors';
|
||||||
import { DEFAULT_TOOL_APPROVAL_SETTINGS } from '@shared/types/team';
|
import { DEFAULT_TOOL_APPROVAL_SETTINGS } from '@shared/types/team';
|
||||||
import { resolveLanguageName } from '@shared/utils/agentLanguage';
|
import { resolveLanguageName } from '@shared/utils/agentLanguage';
|
||||||
|
import { getAnthropicDefaultTeamModel } from '@shared/utils/anthropicModelDefaults';
|
||||||
import { parseCliArgs } from '@shared/utils/cliArgsParser';
|
import { parseCliArgs } from '@shared/utils/cliArgsParser';
|
||||||
import {
|
import {
|
||||||
isInboxNoiseMessage,
|
isInboxNoiseMessage,
|
||||||
|
|
@ -42,14 +43,13 @@ import {
|
||||||
} from '@shared/utils/inboxNoise';
|
} from '@shared/utils/inboxNoise';
|
||||||
import { isLeadAgentType, isLeadMember } from '@shared/utils/leadDetection';
|
import { isLeadAgentType, isLeadMember } from '@shared/utils/leadDetection';
|
||||||
import { createLogger } from '@shared/utils/logger';
|
import { createLogger } from '@shared/utils/logger';
|
||||||
import { getAnthropicDefaultTeamModel } from '@shared/utils/anthropicModelDefaults';
|
import { isDefaultProviderModelSelection } from '@shared/utils/providerModelSelection';
|
||||||
import { formatTaskDisplayLabel } from '@shared/utils/taskIdentity';
|
import { formatTaskDisplayLabel } from '@shared/utils/taskIdentity';
|
||||||
import {
|
import {
|
||||||
parseAllTeammateMessages,
|
parseAllTeammateMessages,
|
||||||
type ParsedTeammateContent,
|
type ParsedTeammateContent,
|
||||||
} from '@shared/utils/teammateMessageParser';
|
} from '@shared/utils/teammateMessageParser';
|
||||||
import { createCliAutoSuffixNameGuard, parseNumericSuffixName } from '@shared/utils/teamMemberName';
|
import { createCliAutoSuffixNameGuard, parseNumericSuffixName } from '@shared/utils/teamMemberName';
|
||||||
import { isDefaultProviderModelSelection } from '@shared/utils/providerModelSelection';
|
|
||||||
import { normalizeOptionalTeamProviderId } from '@shared/utils/teamProvider';
|
import { normalizeOptionalTeamProviderId } from '@shared/utils/teamProvider';
|
||||||
import {
|
import {
|
||||||
extractToolPreview,
|
extractToolPreview,
|
||||||
|
|
@ -68,16 +68,16 @@ import {
|
||||||
type GeminiRuntimeAuthState,
|
type GeminiRuntimeAuthState,
|
||||||
resolveGeminiRuntimeAuth,
|
resolveGeminiRuntimeAuth,
|
||||||
} from '../runtime/geminiRuntimeAuth';
|
} from '../runtime/geminiRuntimeAuth';
|
||||||
|
import { buildProviderAwareCliEnv } from '../runtime/providerAwareCliEnv';
|
||||||
import {
|
import {
|
||||||
buildProviderPreflightPingArgs,
|
|
||||||
buildProviderModelProbeArgs,
|
buildProviderModelProbeArgs,
|
||||||
|
buildProviderPreflightPingArgs,
|
||||||
classifyProviderModelProbeFailure,
|
classifyProviderModelProbeFailure,
|
||||||
getProviderModelProbeExpectedOutput,
|
getProviderModelProbeExpectedOutput,
|
||||||
getProviderModelProbeTimeoutMs,
|
getProviderModelProbeTimeoutMs,
|
||||||
isProviderModelProbeSuccessOutput,
|
isProviderModelProbeSuccessOutput,
|
||||||
normalizeProviderModelProbeFailureReason,
|
normalizeProviderModelProbeFailureReason,
|
||||||
} from '../runtime/providerModelProbe';
|
} from '../runtime/providerModelProbe';
|
||||||
import { buildProviderAwareCliEnv } from '../runtime/providerAwareCliEnv';
|
|
||||||
import { resolveTeamProviderId } from '../runtime/providerRuntimeEnv';
|
import { resolveTeamProviderId } from '../runtime/providerRuntimeEnv';
|
||||||
|
|
||||||
import { buildActionModeProtocol } from './actionModeInstructions';
|
import { buildActionModeProtocol } from './actionModeInstructions';
|
||||||
|
|
|
||||||
|
|
@ -265,7 +265,6 @@ import type {
|
||||||
LeadContextUsageSnapshot,
|
LeadContextUsageSnapshot,
|
||||||
MemberFullStats,
|
MemberFullStats,
|
||||||
MemberLogSummary,
|
MemberLogSummary,
|
||||||
TeamMemberActivityMeta,
|
|
||||||
MemberSpawnStatusesSnapshot,
|
MemberSpawnStatusesSnapshot,
|
||||||
MessagesPage,
|
MessagesPage,
|
||||||
NotificationTrigger,
|
NotificationTrigger,
|
||||||
|
|
@ -297,14 +296,15 @@ import type {
|
||||||
TeamCreateResponse,
|
TeamCreateResponse,
|
||||||
TeamLaunchRequest,
|
TeamLaunchRequest,
|
||||||
TeamLaunchResponse,
|
TeamLaunchResponse,
|
||||||
|
TeamMemberActivityMeta,
|
||||||
TeamMessageNotificationData,
|
TeamMessageNotificationData,
|
||||||
TeamViewSnapshot,
|
|
||||||
TeamProvisioningPrepareResult,
|
TeamProvisioningPrepareResult,
|
||||||
TeamProvisioningProgress,
|
TeamProvisioningProgress,
|
||||||
TeamSummary,
|
TeamSummary,
|
||||||
TeamTask,
|
TeamTask,
|
||||||
TeamTaskStatus,
|
TeamTaskStatus,
|
||||||
TeamUpdateConfigRequest,
|
TeamUpdateConfigRequest,
|
||||||
|
TeamViewSnapshot,
|
||||||
ToolApprovalEvent,
|
ToolApprovalEvent,
|
||||||
ToolApprovalFileContent,
|
ToolApprovalFileContent,
|
||||||
ToolApprovalSettings,
|
ToolApprovalSettings,
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,6 @@ import type {
|
||||||
SshLastConnection,
|
SshLastConnection,
|
||||||
SubagentDetail,
|
SubagentDetail,
|
||||||
TeamChangeEvent,
|
TeamChangeEvent,
|
||||||
UpdateSchedulePatch,
|
|
||||||
TeamClaudeLogsQuery,
|
TeamClaudeLogsQuery,
|
||||||
TeamClaudeLogsResponse,
|
TeamClaudeLogsResponse,
|
||||||
TeamCreateRequest,
|
TeamCreateRequest,
|
||||||
|
|
@ -74,6 +73,7 @@ import type {
|
||||||
TriggerTestResult,
|
TriggerTestResult,
|
||||||
UpdateKanbanPatch,
|
UpdateKanbanPatch,
|
||||||
UpdaterAPI,
|
UpdaterAPI,
|
||||||
|
UpdateSchedulePatch,
|
||||||
WaterfallData,
|
WaterfallData,
|
||||||
WslClaudeRootCandidate,
|
WslClaudeRootCandidate,
|
||||||
} from '@shared/types';
|
} from '@shared/types';
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { api, isElectronMode } from '@renderer/api';
|
import { api, isElectronMode } from '@renderer/api';
|
||||||
import { confirm } from '@renderer/components/common/ConfirmDialog';
|
import { confirm } from '@renderer/components/common/ConfirmDialog';
|
||||||
import { ProviderBrandLogo } from '@renderer/components/common/ProviderBrandLogo';
|
import { ProviderBrandLogo } from '@renderer/components/common/ProviderBrandLogo';
|
||||||
import { ProviderModelBadges } from '@renderer/components/runtime/ProviderModelBadges';
|
|
||||||
import {
|
import {
|
||||||
formatProviderStatusText,
|
formatProviderStatusText,
|
||||||
getProviderConnectionModeSummary,
|
getProviderConnectionModeSummary,
|
||||||
|
|
@ -23,6 +22,7 @@ import {
|
||||||
isConnectionManagedRuntimeProvider,
|
isConnectionManagedRuntimeProvider,
|
||||||
shouldShowProviderConnectAction,
|
shouldShowProviderConnectAction,
|
||||||
} from '@renderer/components/runtime/providerConnectionUi';
|
} from '@renderer/components/runtime/providerConnectionUi';
|
||||||
|
import { ProviderModelBadges } from '@renderer/components/runtime/ProviderModelBadges';
|
||||||
import { getProviderRuntimeBackendSummary } from '@renderer/components/runtime/ProviderRuntimeBackendSelector';
|
import { getProviderRuntimeBackendSummary } from '@renderer/components/runtime/ProviderRuntimeBackendSelector';
|
||||||
import { ProviderRuntimeSettingsDialog } from '@renderer/components/runtime/ProviderRuntimeSettingsDialog';
|
import { ProviderRuntimeSettingsDialog } from '@renderer/components/runtime/ProviderRuntimeSettingsDialog';
|
||||||
import { SettingsToggle } from '@renderer/components/settings/components';
|
import { SettingsToggle } from '@renderer/components/settings/components';
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@
|
||||||
import { Badge } from '@renderer/components/ui/badge';
|
import { Badge } from '@renderer/components/ui/badge';
|
||||||
import { useStore } from '@renderer/store';
|
import { useStore } from '@renderer/store';
|
||||||
import {
|
import {
|
||||||
getInstallationSummaryLabel,
|
|
||||||
getCapabilityLabel,
|
getCapabilityLabel,
|
||||||
|
getInstallationSummaryLabel,
|
||||||
hasInstallationInScope,
|
hasInstallationInScope,
|
||||||
inferCapabilities,
|
inferCapabilities,
|
||||||
normalizeCategory,
|
normalizeCategory,
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,8 @@ import {
|
||||||
} from '@renderer/components/ui/select';
|
} from '@renderer/components/ui/select';
|
||||||
import { useStore } from '@renderer/store';
|
import { useStore } from '@renderer/store';
|
||||||
import {
|
import {
|
||||||
getInstallationSummaryLabel,
|
|
||||||
getCapabilityLabel,
|
getCapabilityLabel,
|
||||||
|
getInstallationSummaryLabel,
|
||||||
hasInstallationInScope,
|
hasInstallationInScope,
|
||||||
inferCapabilities,
|
inferCapabilities,
|
||||||
normalizeCategory,
|
normalizeCategory,
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
|
import { cn } from '@renderer/lib/utils';
|
||||||
import {
|
import {
|
||||||
getTeamModelBadgeLabel,
|
getTeamModelBadgeLabel,
|
||||||
getVisibleTeamProviderModels,
|
getVisibleTeamProviderModels,
|
||||||
} from '@renderer/utils/teamModelCatalog';
|
} from '@renderer/utils/teamModelCatalog';
|
||||||
import { cn } from '@renderer/lib/utils';
|
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
CliProviderId,
|
CliProviderId,
|
||||||
|
|
@ -43,7 +43,7 @@ function getAvailabilityChip(status: CliProviderModelAvailabilityStatus | null):
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ProviderModelBadges({
|
export const ProviderModelBadges = ({
|
||||||
providerId,
|
providerId,
|
||||||
models,
|
models,
|
||||||
modelAvailability,
|
modelAvailability,
|
||||||
|
|
@ -53,7 +53,7 @@ export function ProviderModelBadges({
|
||||||
readonly models: string[];
|
readonly models: string[];
|
||||||
readonly modelAvailability?: CliProviderModelAvailability[];
|
readonly modelAvailability?: CliProviderModelAvailability[];
|
||||||
readonly providerStatus?: Pick<CliProviderStatus, 'providerId' | 'authMethod' | 'backend'> | null;
|
readonly providerStatus?: Pick<CliProviderStatus, 'providerId' | 'authMethod' | 'backend'> | null;
|
||||||
}): React.JSX.Element {
|
}): React.JSX.Element => {
|
||||||
const visibleModels = getVisibleTeamProviderModels(providerId, models, providerStatus);
|
const visibleModels = getVisibleTeamProviderModels(providerId, models, providerStatus);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -94,4 +94,4 @@ export function ProviderModelBadges({
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { isElectronMode } from '@renderer/api';
|
import { isElectronMode } from '@renderer/api';
|
||||||
import { confirm } from '@renderer/components/common/ConfirmDialog';
|
import { confirm } from '@renderer/components/common/ConfirmDialog';
|
||||||
import { ProviderBrandLogo } from '@renderer/components/common/ProviderBrandLogo';
|
import { ProviderBrandLogo } from '@renderer/components/common/ProviderBrandLogo';
|
||||||
import { ProviderModelBadges } from '@renderer/components/runtime/ProviderModelBadges';
|
|
||||||
import {
|
import {
|
||||||
formatProviderStatusText,
|
formatProviderStatusText,
|
||||||
getProviderConnectionModeSummary,
|
getProviderConnectionModeSummary,
|
||||||
|
|
@ -21,6 +20,7 @@ import {
|
||||||
isConnectionManagedRuntimeProvider,
|
isConnectionManagedRuntimeProvider,
|
||||||
shouldShowProviderConnectAction,
|
shouldShowProviderConnectAction,
|
||||||
} from '@renderer/components/runtime/providerConnectionUi';
|
} from '@renderer/components/runtime/providerConnectionUi';
|
||||||
|
import { ProviderModelBadges } from '@renderer/components/runtime/ProviderModelBadges';
|
||||||
import { getProviderRuntimeBackendSummary } from '@renderer/components/runtime/ProviderRuntimeBackendSelector';
|
import { getProviderRuntimeBackendSummary } from '@renderer/components/runtime/ProviderRuntimeBackendSelector';
|
||||||
import { ProviderRuntimeSettingsDialog } from '@renderer/components/runtime/ProviderRuntimeSettingsDialog';
|
import { ProviderRuntimeSettingsDialog } from '@renderer/components/runtime/ProviderRuntimeSettingsDialog';
|
||||||
import { SettingsToggle } from '@renderer/components/settings/components';
|
import { SettingsToggle } from '@renderer/components/settings/components';
|
||||||
|
|
|
||||||
|
|
@ -56,10 +56,10 @@ import {
|
||||||
import type { ActiveTeamRef, TeamCopyData } from './dialogs/CreateTeamDialog';
|
import type { ActiveTeamRef, TeamCopyData } from './dialogs/CreateTeamDialog';
|
||||||
import type { TeamListFilterState } from './TeamListFilterPopover';
|
import type { TeamListFilterState } from './TeamListFilterPopover';
|
||||||
import type {
|
import type {
|
||||||
TeamMemberSnapshot,
|
|
||||||
ResolvedTeamMember,
|
ResolvedTeamMember,
|
||||||
TeamCreateRequest,
|
TeamCreateRequest,
|
||||||
TeamLaunchRequest,
|
TeamLaunchRequest,
|
||||||
|
TeamMemberSnapshot,
|
||||||
TeamSummary,
|
TeamSummary,
|
||||||
TeamSummaryMember,
|
TeamSummaryMember,
|
||||||
} from '@shared/types';
|
} from '@shared/types';
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ import {
|
||||||
import { normalizePath } from '@renderer/utils/pathNormalize';
|
import { normalizePath } from '@renderer/utils/pathNormalize';
|
||||||
import {
|
import {
|
||||||
getTeamModelSelectionError,
|
getTeamModelSelectionError,
|
||||||
|
normalizeExplicitTeamModelForUi,
|
||||||
normalizeTeamModelForUi,
|
normalizeTeamModelForUi,
|
||||||
} from '@renderer/utils/teamModelAvailability';
|
} from '@renderer/utils/teamModelAvailability';
|
||||||
import { getTeamProviderLabel as getCatalogTeamProviderLabel } from '@renderer/utils/teamModelCatalog';
|
import { getTeamProviderLabel as getCatalogTeamProviderLabel } from '@renderer/utils/teamModelCatalog';
|
||||||
|
|
@ -53,22 +54,22 @@ import { AlertTriangle, CheckCircle2, Info, Loader2, X } from 'lucide-react';
|
||||||
import { AdvancedCliSection } from './AdvancedCliSection';
|
import { AdvancedCliSection } from './AdvancedCliSection';
|
||||||
import { OptionalSettingsSection } from './OptionalSettingsSection';
|
import { OptionalSettingsSection } from './OptionalSettingsSection';
|
||||||
import { ProjectPathSelector } from './ProjectPathSelector';
|
import { ProjectPathSelector } from './ProjectPathSelector';
|
||||||
|
import {
|
||||||
|
getProviderPrepareCachedSnapshot,
|
||||||
|
type ProviderPrepareDiagnosticsModelResult,
|
||||||
|
runProviderPrepareDiagnostics,
|
||||||
|
} from './providerPrepareDiagnostics';
|
||||||
|
import { getProvisioningModelIssue } from './provisioningModelIssues';
|
||||||
import {
|
import {
|
||||||
failIncompleteProviderChecks,
|
failIncompleteProviderChecks,
|
||||||
getProvisioningFailureHint,
|
|
||||||
getPrimaryProvisioningFailureDetail,
|
getPrimaryProvisioningFailureDetail,
|
||||||
|
getProvisioningFailureHint,
|
||||||
getProvisioningProviderBackendSummary,
|
getProvisioningProviderBackendSummary,
|
||||||
type ProvisioningProviderCheck,
|
type ProvisioningProviderCheck,
|
||||||
ProvisioningProviderStatusList,
|
ProvisioningProviderStatusList,
|
||||||
shouldHideProvisioningProviderStatusList,
|
shouldHideProvisioningProviderStatusList,
|
||||||
updateProviderCheck,
|
updateProviderCheck,
|
||||||
} from './ProvisioningProviderStatusList';
|
} from './ProvisioningProviderStatusList';
|
||||||
import { getProvisioningModelIssue } from './provisioningModelIssues';
|
|
||||||
import {
|
|
||||||
getProviderPrepareCachedSnapshot,
|
|
||||||
runProviderPrepareDiagnostics,
|
|
||||||
type ProviderPrepareDiagnosticsModelResult,
|
|
||||||
} from './providerPrepareDiagnostics';
|
|
||||||
import { SkipPermissionsCheckbox } from './SkipPermissionsCheckbox';
|
import { SkipPermissionsCheckbox } from './SkipPermissionsCheckbox';
|
||||||
import { computeEffectiveTeamModel } from './TeamModelSelector';
|
import { computeEffectiveTeamModel } from './TeamModelSelector';
|
||||||
import { getNextSuggestedTeamName } from './teamNameSets';
|
import { getNextSuggestedTeamName } from './teamNameSets';
|
||||||
|
|
@ -108,7 +109,7 @@ function getStoredTeamModel(providerId: TeamProviderId): string {
|
||||||
if (stored === null) {
|
if (stored === null) {
|
||||||
return providerId === 'anthropic' ? 'opus' : '';
|
return providerId === 'anthropic' ? 'opus' : '';
|
||||||
}
|
}
|
||||||
return normalizeTeamModelForUi(providerId, stored === '__default__' ? '' : stored);
|
return normalizeExplicitTeamModelForUi(providerId, stored === '__default__' ? '' : stored);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isEphemeralRenderedProjectPath(projectPath: string | null | undefined): boolean {
|
function isEphemeralRenderedProjectPath(projectPath: string | null | undefined): boolean {
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ import { normalizePath } from '@renderer/utils/pathNormalize';
|
||||||
import { nameColorSet } from '@renderer/utils/projectColor';
|
import { nameColorSet } from '@renderer/utils/projectColor';
|
||||||
import {
|
import {
|
||||||
getTeamModelSelectionError,
|
getTeamModelSelectionError,
|
||||||
|
normalizeExplicitTeamModelForUi,
|
||||||
normalizeTeamModelForUi,
|
normalizeTeamModelForUi,
|
||||||
} from '@renderer/utils/teamModelAvailability';
|
} from '@renderer/utils/teamModelAvailability';
|
||||||
import { getTeamProviderLabel as getCatalogTeamProviderLabel } from '@renderer/utils/teamModelCatalog';
|
import { getTeamProviderLabel as getCatalogTeamProviderLabel } from '@renderer/utils/teamModelCatalog';
|
||||||
|
|
@ -72,22 +73,22 @@ import { EffortLevelSelector } from './EffortLevelSelector';
|
||||||
import { resolveLaunchDialogPrefill } from './launchDialogPrefill';
|
import { resolveLaunchDialogPrefill } from './launchDialogPrefill';
|
||||||
import { OptionalSettingsSection } from './OptionalSettingsSection';
|
import { OptionalSettingsSection } from './OptionalSettingsSection';
|
||||||
import { ProjectPathSelector } from './ProjectPathSelector';
|
import { ProjectPathSelector } from './ProjectPathSelector';
|
||||||
|
import {
|
||||||
|
getProviderPrepareCachedSnapshot,
|
||||||
|
type ProviderPrepareDiagnosticsModelResult,
|
||||||
|
runProviderPrepareDiagnostics,
|
||||||
|
} from './providerPrepareDiagnostics';
|
||||||
|
import { getProvisioningModelIssue } from './provisioningModelIssues';
|
||||||
import {
|
import {
|
||||||
failIncompleteProviderChecks,
|
failIncompleteProviderChecks,
|
||||||
getProvisioningFailureHint,
|
|
||||||
getPrimaryProvisioningFailureDetail,
|
getPrimaryProvisioningFailureDetail,
|
||||||
|
getProvisioningFailureHint,
|
||||||
getProvisioningProviderBackendSummary,
|
getProvisioningProviderBackendSummary,
|
||||||
type ProvisioningProviderCheck,
|
type ProvisioningProviderCheck,
|
||||||
ProvisioningProviderStatusList,
|
ProvisioningProviderStatusList,
|
||||||
shouldHideProvisioningProviderStatusList,
|
shouldHideProvisioningProviderStatusList,
|
||||||
updateProviderCheck,
|
updateProviderCheck,
|
||||||
} from './ProvisioningProviderStatusList';
|
} from './ProvisioningProviderStatusList';
|
||||||
import { getProvisioningModelIssue } from './provisioningModelIssues';
|
|
||||||
import {
|
|
||||||
getProviderPrepareCachedSnapshot,
|
|
||||||
runProviderPrepareDiagnostics,
|
|
||||||
type ProviderPrepareDiagnosticsModelResult,
|
|
||||||
} from './providerPrepareDiagnostics';
|
|
||||||
import {
|
import {
|
||||||
computeEffectiveTeamModel,
|
computeEffectiveTeamModel,
|
||||||
formatTeamModelSummary,
|
formatTeamModelSummary,
|
||||||
|
|
@ -195,7 +196,7 @@ function getStoredTeamModel(providerId: TeamProviderId): string {
|
||||||
if (stored === null) {
|
if (stored === null) {
|
||||||
return providerId === 'anthropic' ? 'opus' : '';
|
return providerId === 'anthropic' ? 'opus' : '';
|
||||||
}
|
}
|
||||||
return normalizeTeamModelForUi(providerId, stored === '__default__' ? '' : stored);
|
return normalizeExplicitTeamModelForUi(providerId, stored === '__default__' ? '' : stored);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getProviderLabel(providerId: TeamProviderId): string {
|
function getProviderLabel(providerId: TeamProviderId): string {
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ import {
|
||||||
} from '@renderer/components/ui/tooltip';
|
} from '@renderer/components/ui/tooltip';
|
||||||
import { cn } from '@renderer/lib/utils';
|
import { cn } from '@renderer/lib/utils';
|
||||||
import { useStore } from '@renderer/store';
|
import { useStore } from '@renderer/store';
|
||||||
import { getAnthropicDefaultTeamModel } from '@shared/utils/anthropicModelDefaults';
|
|
||||||
import {
|
import {
|
||||||
GEMINI_UI_DISABLED_BADGE_LABEL,
|
GEMINI_UI_DISABLED_BADGE_LABEL,
|
||||||
GEMINI_UI_DISABLED_REASON,
|
GEMINI_UI_DISABLED_REASON,
|
||||||
|
|
@ -30,6 +29,7 @@ import {
|
||||||
getTeamProviderLabel as getCatalogTeamProviderLabel,
|
getTeamProviderLabel as getCatalogTeamProviderLabel,
|
||||||
} from '@renderer/utils/teamModelCatalog';
|
} from '@renderer/utils/teamModelCatalog';
|
||||||
import { extractProviderScopedBaseModel } from '@renderer/utils/teamModelContext';
|
import { extractProviderScopedBaseModel } from '@renderer/utils/teamModelContext';
|
||||||
|
import { getAnthropicDefaultTeamModel } from '@shared/utils/anthropicModelDefaults';
|
||||||
import { AlertTriangle, Info } from 'lucide-react';
|
import { AlertTriangle, Info } from 'lucide-react';
|
||||||
|
|
||||||
export { getProviderScopedTeamModelLabel } from '@renderer/utils/teamModelCatalog';
|
export { getProviderScopedTeamModelLabel } from '@renderer/utils/teamModelCatalog';
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { normalizeCreateLaunchProviderForUi } from '@renderer/utils/geminiUiFreeze';
|
import { normalizeCreateLaunchProviderForUi } from '@renderer/utils/geminiUiFreeze';
|
||||||
import { normalizeTeamModelForUi } from '@renderer/utils/teamModelAvailability';
|
import { normalizeExplicitTeamModelForUi } from '@renderer/utils/teamModelAvailability';
|
||||||
import { extractProviderScopedBaseModel } from '@renderer/utils/teamModelContext';
|
import { extractProviderScopedBaseModel } from '@renderer/utils/teamModelContext';
|
||||||
import { isLeadMember } from '@shared/utils/leadDetection';
|
import { isLeadMember } from '@shared/utils/leadDetection';
|
||||||
import { normalizeOptionalTeamProviderId } from '@shared/utils/teamProvider';
|
import { normalizeOptionalTeamProviderId } from '@shared/utils/teamProvider';
|
||||||
|
|
@ -102,7 +102,7 @@ export function resolveLaunchDialogPrefill({
|
||||||
return {
|
return {
|
||||||
providerId,
|
providerId,
|
||||||
model: matchingModel
|
model: matchingModel
|
||||||
? normalizeTeamModelForUi(providerId, matchingModel)
|
? normalizeExplicitTeamModelForUi(providerId, matchingModel)
|
||||||
: getStoredModel(providerId),
|
: getStoredModel(providerId),
|
||||||
effort,
|
effort,
|
||||||
limitContext,
|
limitContext,
|
||||||
|
|
|
||||||
|
|
@ -5,15 +5,13 @@ import type { TeamProviderId, TeamProvisioningPrepareResult } from '@shared/type
|
||||||
|
|
||||||
export type ProviderPrepareCheckStatus = 'ready' | 'notes' | 'failed';
|
export type ProviderPrepareCheckStatus = 'ready' | 'notes' | 'failed';
|
||||||
|
|
||||||
interface PrepareProvisioningFn {
|
type PrepareProvisioningFn = (
|
||||||
(
|
cwd?: string,
|
||||||
cwd?: string,
|
providerId?: TeamProviderId,
|
||||||
providerId?: TeamProviderId,
|
providerIds?: TeamProviderId[],
|
||||||
providerIds?: TeamProviderId[],
|
selectedModels?: string[],
|
||||||
selectedModels?: string[],
|
limitContext?: boolean
|
||||||
limitContext?: boolean
|
) => Promise<TeamProvisioningPrepareResult>;
|
||||||
): Promise<TeamProvisioningPrepareResult>;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ProviderPrepareDiagnosticsProgress {
|
interface ProviderPrepareDiagnosticsProgress {
|
||||||
details: string[];
|
details: string[];
|
||||||
|
|
@ -156,15 +154,15 @@ function normalizeModelReason(rawReason: string | null | undefined): string | nu
|
||||||
return 'Model verification timed out';
|
return 'Model verification timed out';
|
||||||
}
|
}
|
||||||
|
|
||||||
const detailMatch = trimmed.match(/"detail":"((?:\\"|[^"])*)"/i);
|
const detailMatch = /"detail":"((?:\\"|[^"])*)"/i.exec(trimmed);
|
||||||
if (detailMatch?.[1]) {
|
if (detailMatch?.[1]) {
|
||||||
return normalizeModelReason(detailMatch[1].replace(/\\"/g, '"').trim());
|
return normalizeModelReason(detailMatch[1].replace(/\\"/g, '"').trim());
|
||||||
}
|
}
|
||||||
|
|
||||||
const messageMatch = trimmed.match(/"message":"((?:\\"|[^"])*)"/i);
|
const messageMatch = /"message":"((?:\\"|[^"])*)"/i.exec(trimmed);
|
||||||
if (messageMatch?.[1]) {
|
if (messageMatch?.[1]) {
|
||||||
const decodedMessage = messageMatch[1].replace(/\\"/g, '"');
|
const decodedMessage = messageMatch[1].replace(/\\"/g, '"');
|
||||||
const nestedDetailMatch = decodedMessage.match(/"detail":"([^"]+)"/i);
|
const nestedDetailMatch = /"detail":"([^"]+)"/i.exec(decodedMessage);
|
||||||
if (nestedDetailMatch?.[1]) {
|
if (nestedDetailMatch?.[1]) {
|
||||||
return normalizeModelReason(nestedDetailMatch[1].trim());
|
return normalizeModelReason(nestedDetailMatch[1].trim());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,8 @@ import { getTeamColorSet } from '@renderer/constants/teamColors';
|
||||||
import { useDraftPersistence } from '@renderer/hooks/useDraftPersistence';
|
import { useDraftPersistence } from '@renderer/hooks/useDraftPersistence';
|
||||||
import { useFileListCacheWarmer } from '@renderer/hooks/useFileListCacheWarmer';
|
import { useFileListCacheWarmer } from '@renderer/hooks/useFileListCacheWarmer';
|
||||||
import { useTheme } from '@renderer/hooks/useTheme';
|
import { useTheme } from '@renderer/hooks/useTheme';
|
||||||
import { reconcileChips, removeChipTokenFromText } from '@renderer/utils/chipUtils';
|
|
||||||
import { cn } from '@renderer/lib/utils';
|
import { cn } from '@renderer/lib/utils';
|
||||||
|
import { reconcileChips, removeChipTokenFromText } from '@renderer/utils/chipUtils';
|
||||||
import { getMemberColorByName } from '@shared/constants/memberColors';
|
import { getMemberColorByName } from '@shared/constants/memberColors';
|
||||||
import { AlertTriangle, ChevronDown, ChevronRight, Info, RotateCcw, Trash2 } from 'lucide-react';
|
import { AlertTriangle, ChevronDown, ChevronRight, Info, RotateCcw, Trash2 } from 'lucide-react';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
|
|
||||||
import { resolveMemberRuntimeSummary } from '@renderer/utils/memberRuntimeSummary';
|
|
||||||
import { buildMemberColorMap } from '@renderer/utils/memberHelpers';
|
import { buildMemberColorMap } from '@renderer/utils/memberHelpers';
|
||||||
|
import { resolveMemberRuntimeSummary } from '@renderer/utils/memberRuntimeSummary';
|
||||||
import { isLeadMember } from '@shared/utils/leadDetection';
|
import { isLeadMember } from '@shared/utils/leadDetection';
|
||||||
|
|
||||||
import { MemberCard } from './MemberCard';
|
import { MemberCard } from './MemberCard';
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import { CUSTOM_ROLE, NO_ROLE, PRESET_ROLES } from '@renderer/constants/teamRole
|
||||||
import { serializeChipsWithText } from '@renderer/types/inlineChip';
|
import { serializeChipsWithText } from '@renderer/types/inlineChip';
|
||||||
import { normalizeCreateLaunchProviderForUi } from '@renderer/utils/geminiUiFreeze';
|
import { normalizeCreateLaunchProviderForUi } from '@renderer/utils/geminiUiFreeze';
|
||||||
import { buildMemberColorMap } from '@renderer/utils/memberHelpers';
|
import { buildMemberColorMap } from '@renderer/utils/memberHelpers';
|
||||||
import { normalizeTeamModelForUi } from '@renderer/utils/teamModelAvailability';
|
import { normalizeExplicitTeamModelForUi } from '@renderer/utils/teamModelAvailability';
|
||||||
import { isLeadMember } from '@shared/utils/leadDetection';
|
import { isLeadMember } from '@shared/utils/leadDetection';
|
||||||
import { normalizeOptionalTeamProviderId } from '@shared/utils/teamProvider';
|
import { normalizeOptionalTeamProviderId } from '@shared/utils/teamProvider';
|
||||||
|
|
||||||
|
|
@ -39,7 +39,7 @@ export function createMemberDraft(initial?: Partial<MemberDraft>): MemberDraft {
|
||||||
customRole: initial?.customRole ?? '',
|
customRole: initial?.customRole ?? '',
|
||||||
workflow: initial?.workflow,
|
workflow: initial?.workflow,
|
||||||
providerId,
|
providerId,
|
||||||
model: normalizeTeamModelForUi(providerId, initial?.model ?? ''),
|
model: normalizeExplicitTeamModelForUi(providerId, initial?.model ?? ''),
|
||||||
effort: initial?.effort,
|
effort: initial?.effort,
|
||||||
removedAt: initial?.removedAt,
|
removedAt: initial?.removedAt,
|
||||||
};
|
};
|
||||||
|
|
@ -218,7 +218,7 @@ export function buildMembersFromDrafts(members: MemberDraft[]): TeamProvisioning
|
||||||
}
|
}
|
||||||
const model = member.model?.trim();
|
const model = member.model?.trim();
|
||||||
if (model) {
|
if (model) {
|
||||||
result.model = normalizeTeamModelForUi(providerId, model);
|
result.model = normalizeExplicitTeamModelForUi(providerId, model);
|
||||||
}
|
}
|
||||||
const effort = normalizeDraftEffort(member.effort);
|
const effort = normalizeDraftEffort(member.effort);
|
||||||
if (effort) {
|
if (effort) {
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ import { selectTeamMessages } from '@renderer/store/slices/teamSlice';
|
||||||
import { filterTeamMessages } from '@renderer/utils/teamMessageFiltering';
|
import { filterTeamMessages } from '@renderer/utils/teamMessageFiltering';
|
||||||
import { toMessageKey } from '@renderer/utils/teamMessageKey';
|
import { toMessageKey } from '@renderer/utils/teamMessageKey';
|
||||||
import { shouldExcludeInboxTextFromReplyCandidates } from '@shared/utils/idleNotificationSemantics';
|
import { shouldExcludeInboxTextFromReplyCandidates } from '@shared/utils/idleNotificationSemantics';
|
||||||
import { createLogger } from '@shared/utils/logger';
|
|
||||||
import {
|
import {
|
||||||
CheckCheck,
|
CheckCheck,
|
||||||
ChevronsDownUp,
|
ChevronsDownUp,
|
||||||
|
|
@ -52,9 +51,6 @@ interface TimeWindow {
|
||||||
end: number;
|
end: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const logger = createLogger('Component:MessagesPanel');
|
|
||||||
const MESSAGES_PANEL_FILTER_WARN_MS = 8;
|
|
||||||
const MESSAGES_PANEL_EXPANDED_ITEM_WARN_MS = 6;
|
|
||||||
const BOTTOM_SHEET_HEADER_HEIGHT = 40;
|
const BOTTOM_SHEET_HEADER_HEIGHT = 40;
|
||||||
const BOTTOM_SHEET_COLLAPSED_SNAP_INDEX = 1;
|
const BOTTOM_SHEET_COLLAPSED_SNAP_INDEX = 1;
|
||||||
const BOTTOM_SHEET_COMPOSER_SNAP_INDEX = 2;
|
const BOTTOM_SHEET_COMPOSER_SNAP_INDEX = 2;
|
||||||
|
|
@ -272,41 +268,21 @@ export const MessagesPanel = memo(function MessagesPanel({
|
||||||
}, [position, mountPoint]);
|
}, [position, mountPoint]);
|
||||||
|
|
||||||
const filteredMessages = useMemo(() => {
|
const filteredMessages = useMemo(() => {
|
||||||
const startedAt = performance.now();
|
return filterTeamMessages(effectiveMessages, {
|
||||||
const result = filterTeamMessages(effectiveMessages, {
|
|
||||||
timeWindow,
|
timeWindow,
|
||||||
filter: messagesFilter,
|
filter: messagesFilter,
|
||||||
searchQuery: messagesSearchQuery,
|
searchQuery: messagesSearchQuery,
|
||||||
});
|
});
|
||||||
const ms = performance.now() - startedAt;
|
}, [effectiveMessages, messagesFilter, messagesSearchQuery, timeWindow]);
|
||||||
if (ms >= MESSAGES_PANEL_FILTER_WARN_MS) {
|
|
||||||
logger.warn(
|
|
||||||
`[perf] filter team=${teamName} stage=messages ms=${ms.toFixed(1)} input=${effectiveMessages.length} output=${result.length} searchLen=${messagesSearchQuery.trim().length} noise=${
|
|
||||||
messagesFilter.showNoise ? 'on' : 'off'
|
|
||||||
}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}, [effectiveMessages, messagesFilter, messagesSearchQuery, teamName, timeWindow]);
|
|
||||||
|
|
||||||
const activityTimelineMessages = useMemo(() => {
|
const activityTimelineMessages = useMemo(() => {
|
||||||
const startedAt = performance.now();
|
return filterTeamMessages(effectiveMessages, {
|
||||||
const result = filterTeamMessages(effectiveMessages, {
|
|
||||||
includePassiveIdlePeerSummariesWhenNoiseHidden: true,
|
includePassiveIdlePeerSummariesWhenNoiseHidden: true,
|
||||||
timeWindow,
|
timeWindow,
|
||||||
filter: messagesFilter,
|
filter: messagesFilter,
|
||||||
searchQuery: messagesSearchQuery,
|
searchQuery: messagesSearchQuery,
|
||||||
});
|
});
|
||||||
const ms = performance.now() - startedAt;
|
}, [effectiveMessages, messagesFilter, messagesSearchQuery, timeWindow]);
|
||||||
if (ms >= MESSAGES_PANEL_FILTER_WARN_MS) {
|
|
||||||
logger.warn(
|
|
||||||
`[perf] filter team=${teamName} stage=timeline ms=${ms.toFixed(1)} input=${effectiveMessages.length} output=${result.length} searchLen=${messagesSearchQuery.trim().length} noise=${
|
|
||||||
messagesFilter.showNoise ? 'on' : 'off'
|
|
||||||
}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}, [effectiveMessages, messagesFilter, messagesSearchQuery, teamName, timeWindow]);
|
|
||||||
|
|
||||||
const replyCandidateMessages = useMemo(
|
const replyCandidateMessages = useMemo(
|
||||||
() =>
|
() =>
|
||||||
|
|
@ -320,33 +296,21 @@ export const MessagesPanel = memo(function MessagesPanel({
|
||||||
|
|
||||||
// Resolve the expanded item from filtered messages
|
// Resolve the expanded item from filtered messages
|
||||||
const expandedItem = useMemo<TimelineItem | null>(() => {
|
const expandedItem = useMemo<TimelineItem | null>(() => {
|
||||||
const startedAt = performance.now();
|
if (!expandedItemKey) {
|
||||||
if (!expandedItemKey) return null;
|
return null;
|
||||||
|
}
|
||||||
if (!expandedItemKey.startsWith('thoughts-')) {
|
if (!expandedItemKey.startsWith('thoughts-')) {
|
||||||
const msg = activityTimelineMessages.find((m) => toMessageKey(m) === expandedItemKey);
|
const msg = activityTimelineMessages.find((m) => toMessageKey(m) === expandedItemKey);
|
||||||
const result: TimelineItem | null = msg ? { type: 'message', message: msg } : null;
|
return msg ? { type: 'message', message: msg } : null;
|
||||||
const ms = performance.now() - startedAt;
|
|
||||||
if (ms >= MESSAGES_PANEL_EXPANDED_ITEM_WARN_MS) {
|
|
||||||
logger.warn(
|
|
||||||
`[perf] expandedItem team=${teamName} ms=${ms.toFixed(1)} mode=message timelineMessages=${activityTimelineMessages.length}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
const allItems = groupTimelineItems(activityTimelineMessages);
|
const allItems = groupTimelineItems(activityTimelineMessages);
|
||||||
const result =
|
return (
|
||||||
allItems.find(
|
allItems.find(
|
||||||
(item) =>
|
(item) =>
|
||||||
item.type === 'lead-thoughts' && getThoughtGroupKey(item.group) === expandedItemKey
|
item.type === 'lead-thoughts' && getThoughtGroupKey(item.group) === expandedItemKey
|
||||||
) ?? null;
|
) ?? null
|
||||||
const ms = performance.now() - startedAt;
|
);
|
||||||
if (ms >= MESSAGES_PANEL_EXPANDED_ITEM_WARN_MS) {
|
}, [expandedItemKey, activityTimelineMessages]);
|
||||||
logger.warn(
|
|
||||||
`[perf] expandedItem team=${teamName} ms=${ms.toFixed(1)} mode=thoughts timelineMessages=${activityTimelineMessages.length} groups=${allItems.length}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}, [expandedItemKey, activityTimelineMessages, teamName]);
|
|
||||||
|
|
||||||
// Auto-clear stale expanded key
|
// Auto-clear stale expanded key
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
|
||||||
|
|
@ -127,7 +127,7 @@ export interface ExtensionsSlice {
|
||||||
// Slice Creator
|
// Slice Creator
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
let pluginFetchInFlight: { key: string; promise: Promise<void> } | null = null;
|
let pluginFetchInFlight: { key: string; promise: Promise<void>; token: symbol } | null = null;
|
||||||
let pluginCatalogRequestSeq = 0;
|
let pluginCatalogRequestSeq = 0;
|
||||||
const pluginSuccessResetTimers = new Map<string, ReturnType<typeof setTimeout>>();
|
const pluginSuccessResetTimers = new Map<string, ReturnType<typeof setTimeout>>();
|
||||||
let mcpDiagnosticsInFlight: Promise<void> | null = null;
|
let mcpDiagnosticsInFlight: Promise<void> | null = null;
|
||||||
|
|
@ -286,6 +286,7 @@ export const createExtensionsSlice: StateCreator<AppState, [], [], ExtensionsSli
|
||||||
}
|
}
|
||||||
|
|
||||||
const requestSeq = ++pluginCatalogRequestSeq;
|
const requestSeq = ++pluginCatalogRequestSeq;
|
||||||
|
const requestToken = Symbol('pluginCatalogRequest');
|
||||||
set({ pluginCatalogLoading: true, pluginCatalogError: null });
|
set({ pluginCatalogLoading: true, pluginCatalogError: null });
|
||||||
|
|
||||||
const promise = (async () => {
|
const promise = (async () => {
|
||||||
|
|
@ -344,13 +345,13 @@ export const createExtensionsSlice: StateCreator<AppState, [], [], ExtensionsSli
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
if (pluginFetchInFlight?.promise === promise) {
|
if (pluginFetchInFlight?.token === requestToken) {
|
||||||
pluginFetchInFlight = null;
|
pluginFetchInFlight = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
pluginFetchInFlight = { key: requestKey, promise };
|
pluginFetchInFlight = { key: requestKey, promise, token: requestToken };
|
||||||
await promise;
|
await promise;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { api } from '@renderer/api';
|
import { api } from '@renderer/api';
|
||||||
|
import { mergeTeamMessages } from '@renderer/utils/mergeTeamMessages';
|
||||||
import { normalizePath } from '@renderer/utils/pathNormalize';
|
import { normalizePath } from '@renderer/utils/pathNormalize';
|
||||||
import {
|
import {
|
||||||
buildTaskChangePresenceKey,
|
buildTaskChangePresenceKey,
|
||||||
|
|
@ -6,10 +7,9 @@ import {
|
||||||
canDisplayTaskChangesForOptions,
|
canDisplayTaskChangesForOptions,
|
||||||
type TaskChangeRequestOptions,
|
type TaskChangeRequestOptions,
|
||||||
} from '@renderer/utils/taskChangeRequest';
|
} from '@renderer/utils/taskChangeRequest';
|
||||||
import { extractProviderScopedBaseModel } from '@renderer/utils/teamModelContext';
|
|
||||||
import { toMessageKey } from '@renderer/utils/teamMessageKey';
|
import { toMessageKey } from '@renderer/utils/teamMessageKey';
|
||||||
|
import { extractProviderScopedBaseModel } from '@renderer/utils/teamModelContext';
|
||||||
import { IpcError, unwrapIpc } from '@renderer/utils/unwrapIpc';
|
import { IpcError, unwrapIpc } from '@renderer/utils/unwrapIpc';
|
||||||
import { mergeTeamMessages } from '@renderer/utils/mergeTeamMessages';
|
|
||||||
import { stripAgentBlocks } from '@shared/constants/agentBlocks';
|
import { stripAgentBlocks } from '@shared/constants/agentBlocks';
|
||||||
import { DEFAULT_TOOL_APPROVAL_SETTINGS } from '@shared/types/team';
|
import { DEFAULT_TOOL_APPROVAL_SETTINGS } from '@shared/types/team';
|
||||||
import { isLeadMember } from '@shared/utils/leadDetection';
|
import { isLeadMember } from '@shared/utils/leadDetection';
|
||||||
|
|
@ -111,10 +111,10 @@ type TeamGraphConfigMemberSeedInput = Pick<
|
||||||
NonNullable<TeamViewSnapshot['config']['members']>[number],
|
NonNullable<TeamViewSnapshot['config']['members']>[number],
|
||||||
'name' | 'agentId' | 'removedAt'
|
'name' | 'agentId' | 'removedAt'
|
||||||
>;
|
>;
|
||||||
type TeamGraphLayoutSessionState = {
|
interface TeamGraphLayoutSessionState {
|
||||||
mode: 'default' | 'manual';
|
mode: 'default' | 'manual';
|
||||||
signature: string | null;
|
signature: string | null;
|
||||||
};
|
}
|
||||||
|
|
||||||
export function isTeamDataRefreshPending(teamName: string): boolean {
|
export function isTeamDataRefreshPending(teamName: string): boolean {
|
||||||
return (
|
return (
|
||||||
|
|
@ -3202,8 +3202,7 @@ export const createTeamSlice: StateCreator<AppState, [], [], TeamSlice> = (set,
|
||||||
|
|
||||||
const existingOlderRequest = inFlightTeamMessagesOlderRequests.get(teamName);
|
const existingOlderRequest = inFlightTeamMessagesOlderRequests.get(teamName);
|
||||||
if (existingOlderRequest) {
|
if (existingOlderRequest) {
|
||||||
let queuedRequest: Promise<RefreshTeamMessagesHeadResult>;
|
const queuedRequest: Promise<RefreshTeamMessagesHeadResult> = existingOlderRequest
|
||||||
queuedRequest = existingOlderRequest
|
|
||||||
.then(() => {
|
.then(() => {
|
||||||
if (queuedTeamMessagesHeadRefreshesAfterOlder.get(teamName) === queuedRequest) {
|
if (queuedTeamMessagesHeadRefreshesAfterOlder.get(teamName) === queuedRequest) {
|
||||||
queuedTeamMessagesHeadRefreshesAfterOlder.delete(teamName);
|
queuedTeamMessagesHeadRefreshesAfterOlder.delete(teamName);
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { formatTeamModelSummary } from '@renderer/components/team/dialogs/TeamModelSelector';
|
import { formatTeamModelSummary } from '@renderer/components/team/dialogs/TeamModelSelector';
|
||||||
|
import { inferTeamProviderIdFromModel } from '@shared/utils/teamProvider';
|
||||||
|
|
||||||
import type { TeamLaunchParams } from '@renderer/store/slices/teamSlice';
|
import type { TeamLaunchParams } from '@renderer/store/slices/teamSlice';
|
||||||
import type { MemberSpawnStatusEntry, ResolvedTeamMember, TeamProviderId } from '@shared/types';
|
import type { MemberSpawnStatusEntry, ResolvedTeamMember, TeamProviderId } from '@shared/types';
|
||||||
import { inferTeamProviderIdFromModel } from '@shared/utils/teamProvider';
|
|
||||||
|
|
||||||
function isMemberLaunchPending(spawnEntry: MemberSpawnStatusEntry | undefined): boolean {
|
function isMemberLaunchPending(spawnEntry: MemberSpawnStatusEntry | undefined): boolean {
|
||||||
if (!spawnEntry) {
|
if (!spawnEntry) {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,23 @@
|
||||||
|
import {
|
||||||
|
getProviderScopedTeamModelLabel,
|
||||||
|
getRuntimeAwareTeamModelUiDisabledReason,
|
||||||
|
getTeamProviderLabel,
|
||||||
|
getTeamProviderModelOptions,
|
||||||
|
getVisibleTeamProviderModels,
|
||||||
|
GPT_5_1_CODEX_MAX_CHATGPT_UI_DISABLED_REASON,
|
||||||
|
GPT_5_1_CODEX_MINI_UI_DISABLED_MODEL,
|
||||||
|
GPT_5_1_CODEX_MINI_UI_DISABLED_REASON,
|
||||||
|
GPT_5_2_CODEX_UI_DISABLED_MODEL,
|
||||||
|
GPT_5_2_CODEX_UI_DISABLED_REASON,
|
||||||
|
GPT_5_3_CODEX_SPARK_UI_DISABLED_MODEL,
|
||||||
|
GPT_5_3_CODEX_SPARK_UI_DISABLED_REASON,
|
||||||
|
normalizeTeamModelForUi as normalizeCatalogTeamModelForUi,
|
||||||
|
sortTeamProviderModels,
|
||||||
|
TEAM_MODEL_UI_DISABLED_BADGE_LABEL,
|
||||||
|
type TeamProviderModelOption,
|
||||||
|
} from './teamModelCatalog';
|
||||||
|
import { extractProviderScopedBaseModel } from './teamModelContext';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
CliProviderId,
|
CliProviderId,
|
||||||
CliProviderModelAvailability,
|
CliProviderModelAvailability,
|
||||||
|
|
@ -6,29 +26,10 @@ import type {
|
||||||
TeamProviderId,
|
TeamProviderId,
|
||||||
} from '@shared/types';
|
} from '@shared/types';
|
||||||
|
|
||||||
import {
|
|
||||||
getProviderScopedTeamModelLabel,
|
|
||||||
getRuntimeAwareTeamModelUiDisabledReason,
|
|
||||||
getTeamProviderLabel,
|
|
||||||
getTeamProviderModelOptions,
|
|
||||||
sortTeamProviderModels,
|
|
||||||
getVisibleTeamProviderModels,
|
|
||||||
normalizeTeamModelForUi as normalizeCatalogTeamModelForUi,
|
|
||||||
GPT_5_1_CODEX_MINI_UI_DISABLED_MODEL,
|
|
||||||
GPT_5_1_CODEX_MINI_UI_DISABLED_REASON,
|
|
||||||
GPT_5_1_CODEX_MAX_CHATGPT_UI_DISABLED_REASON,
|
|
||||||
GPT_5_2_CODEX_UI_DISABLED_MODEL,
|
|
||||||
GPT_5_2_CODEX_UI_DISABLED_REASON,
|
|
||||||
GPT_5_3_CODEX_SPARK_UI_DISABLED_MODEL,
|
|
||||||
GPT_5_3_CODEX_SPARK_UI_DISABLED_REASON,
|
|
||||||
TEAM_MODEL_UI_DISABLED_BADGE_LABEL,
|
|
||||||
type TeamProviderModelOption,
|
|
||||||
} from './teamModelCatalog';
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
GPT_5_1_CODEX_MAX_CHATGPT_UI_DISABLED_REASON,
|
||||||
GPT_5_1_CODEX_MINI_UI_DISABLED_MODEL,
|
GPT_5_1_CODEX_MINI_UI_DISABLED_MODEL,
|
||||||
GPT_5_1_CODEX_MINI_UI_DISABLED_REASON,
|
GPT_5_1_CODEX_MINI_UI_DISABLED_REASON,
|
||||||
GPT_5_1_CODEX_MAX_CHATGPT_UI_DISABLED_REASON,
|
|
||||||
GPT_5_2_CODEX_UI_DISABLED_MODEL,
|
GPT_5_2_CODEX_UI_DISABLED_MODEL,
|
||||||
GPT_5_2_CODEX_UI_DISABLED_REASON,
|
GPT_5_2_CODEX_UI_DISABLED_REASON,
|
||||||
GPT_5_3_CODEX_SPARK_UI_DISABLED_MODEL,
|
GPT_5_3_CODEX_SPARK_UI_DISABLED_MODEL,
|
||||||
|
|
@ -236,6 +237,14 @@ export function isTeamModelAvailableForUi(
|
||||||
return getRuntimeModelAvailability(providerId, trimmed, providerStatus) === 'available';
|
return getRuntimeModelAvailability(providerId, trimmed, providerStatus) === 'available';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function normalizeExplicitTeamModelForUi(
|
||||||
|
providerId: SupportedProviderId | undefined,
|
||||||
|
model: string | undefined
|
||||||
|
): string {
|
||||||
|
const normalized = extractProviderScopedBaseModel(model, providerId) ?? '';
|
||||||
|
return normalizeCatalogTeamModelForUi(providerId, normalized).trim();
|
||||||
|
}
|
||||||
|
|
||||||
export function normalizeTeamModelForUi(
|
export function normalizeTeamModelForUi(
|
||||||
providerId: SupportedProviderId | undefined,
|
providerId: SupportedProviderId | undefined,
|
||||||
model: string | undefined,
|
model: string | undefined,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import type { CliProviderId, CliProviderStatus, TeamProviderId } from '@shared/types';
|
|
||||||
import {
|
import {
|
||||||
filterVisibleProviderRuntimeModels,
|
filterVisibleProviderRuntimeModels,
|
||||||
GPT_5_1_CODEX_MINI_UI_DISABLED_MODEL,
|
GPT_5_1_CODEX_MINI_UI_DISABLED_MODEL,
|
||||||
|
|
@ -6,6 +5,8 @@ import {
|
||||||
GPT_5_3_CODEX_SPARK_UI_DISABLED_MODEL,
|
GPT_5_3_CODEX_SPARK_UI_DISABLED_MODEL,
|
||||||
} from '@shared/utils/providerModelVisibility';
|
} from '@shared/utils/providerModelVisibility';
|
||||||
|
|
||||||
|
import type { CliProviderId, CliProviderStatus, TeamProviderId } from '@shared/types';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
GPT_5_1_CODEX_MINI_UI_DISABLED_MODEL,
|
GPT_5_1_CODEX_MINI_UI_DISABLED_MODEL,
|
||||||
GPT_5_2_CODEX_UI_DISABLED_MODEL,
|
GPT_5_2_CODEX_UI_DISABLED_MODEL,
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,6 @@ import type {
|
||||||
KanbanColumnId,
|
KanbanColumnId,
|
||||||
LeadActivitySnapshot,
|
LeadActivitySnapshot,
|
||||||
LeadContextUsageSnapshot,
|
LeadContextUsageSnapshot,
|
||||||
TeamMemberActivityMeta,
|
|
||||||
MemberFullStats,
|
MemberFullStats,
|
||||||
MemberLogSummary,
|
MemberLogSummary,
|
||||||
MemberSpawnStatusesSnapshot,
|
MemberSpawnStatusesSnapshot,
|
||||||
|
|
@ -74,14 +73,15 @@ import type {
|
||||||
TeamCreateResponse,
|
TeamCreateResponse,
|
||||||
TeamLaunchRequest,
|
TeamLaunchRequest,
|
||||||
TeamLaunchResponse,
|
TeamLaunchResponse,
|
||||||
|
TeamMemberActivityMeta,
|
||||||
TeamMessageNotificationData,
|
TeamMessageNotificationData,
|
||||||
TeamViewSnapshot,
|
|
||||||
TeamProvisioningPrepareResult,
|
TeamProvisioningPrepareResult,
|
||||||
TeamProvisioningProgress,
|
TeamProvisioningProgress,
|
||||||
TeamSummary,
|
TeamSummary,
|
||||||
TeamTask,
|
TeamTask,
|
||||||
TeamTaskStatus,
|
TeamTaskStatus,
|
||||||
TeamUpdateConfigRequest,
|
TeamUpdateConfigRequest,
|
||||||
|
TeamViewSnapshot,
|
||||||
ToolApprovalEvent,
|
ToolApprovalEvent,
|
||||||
ToolApprovalFileContent,
|
ToolApprovalFileContent,
|
||||||
ToolApprovalSettings,
|
ToolApprovalSettings,
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
CliInstallationStatus,
|
CliInstallationStatus,
|
||||||
InstallScope,
|
|
||||||
InstalledPluginEntry,
|
InstalledPluginEntry,
|
||||||
|
InstallScope,
|
||||||
PluginCapability,
|
PluginCapability,
|
||||||
PluginCatalogItem,
|
PluginCatalogItem,
|
||||||
} from '@shared/types';
|
} from '@shared/types';
|
||||||
|
|
|
||||||
|
|
@ -288,11 +288,11 @@ describe('CliInstallerService', () => {
|
||||||
expect(status.providers.find((provider) => provider.providerId === 'codex')?.modelAvailability).toEqual([]);
|
expect(status.providers.find((provider) => provider.providerId === 'codex')?.modelAvailability).toEqual([]);
|
||||||
|
|
||||||
const verifiedProvider = await service.verifyProviderModels('codex');
|
const verifiedProvider = await service.verifyProviderModels('codex');
|
||||||
expect(verifiedProvider?.modelAvailability).toEqual(
|
expect(verifiedProvider?.modelAvailability).toEqual([
|
||||||
expect.arrayContaining([
|
expect.objectContaining({ modelId: 'gpt-5.4', status: 'checking' }),
|
||||||
expect.objectContaining({ modelId: 'gpt-5.4', status: 'checking' }),
|
]);
|
||||||
expect.objectContaining({ modelId: 'gpt-5.2-codex', status: 'checking' }),
|
expect(verifiedProvider?.modelAvailability).not.toEqual(
|
||||||
])
|
expect.arrayContaining([expect.objectContaining({ modelId: 'gpt-5.2-codex' })])
|
||||||
);
|
);
|
||||||
|
|
||||||
await vi.waitFor(() => {
|
await vi.waitFor(() => {
|
||||||
|
|
@ -302,13 +302,15 @@ describe('CliInstallerService', () => {
|
||||||
|
|
||||||
expect(latestCodexProvider?.modelAvailability).toEqual([
|
expect(latestCodexProvider?.modelAvailability).toEqual([
|
||||||
expect.objectContaining({ modelId: 'gpt-5.4', status: 'available' }),
|
expect.objectContaining({ modelId: 'gpt-5.4', status: 'available' }),
|
||||||
expect.objectContaining({
|
|
||||||
modelId: 'gpt-5.2-codex',
|
|
||||||
status: 'unavailable',
|
|
||||||
}),
|
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
expect(execCli).not.toHaveBeenCalledWith(
|
||||||
|
'/usr/local/bin/claude',
|
||||||
|
expect.arrayContaining(['--model', 'gpt-5.2-codex']),
|
||||||
|
expect.anything()
|
||||||
|
);
|
||||||
|
|
||||||
const statusEvents = mockWindow.webContents.send.mock.calls
|
const statusEvents = mockWindow.webContents.send.mock.calls
|
||||||
.filter((call: unknown[]) => call[0] === 'cliInstaller:progress')
|
.filter((call: unknown[]) => call[0] === 'cliInstaller:progress')
|
||||||
.map((call: unknown[]) => call[1] as { type?: string; status?: { providers?: unknown[] } })
|
.map((call: unknown[]) => call[1] as { type?: string; status?: { providers?: unknown[] } })
|
||||||
|
|
@ -325,12 +327,29 @@ describe('CliInstallerService', () => {
|
||||||
'modelAvailability' in provider &&
|
'modelAvailability' in provider &&
|
||||||
(provider as { providerId?: string }).providerId === 'codex' &&
|
(provider as { providerId?: string }).providerId === 'codex' &&
|
||||||
Array.isArray((provider as { modelAvailability?: unknown[] }).modelAvailability) &&
|
Array.isArray((provider as { modelAvailability?: unknown[] }).modelAvailability) &&
|
||||||
(provider as { modelAvailability: Array<{ status?: string }> }).modelAvailability.some(
|
(provider as { modelAvailability: Array<{ modelId?: string; status?: string }> })
|
||||||
(item) => item.status === 'unavailable'
|
.modelAvailability.some(
|
||||||
)
|
(item) => item.modelId === 'gpt-5.4' && item.status === 'available'
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
).toBe(true);
|
).toBe(true);
|
||||||
|
expect(
|
||||||
|
statusEvents.some((event) =>
|
||||||
|
event.status?.providers?.some(
|
||||||
|
(provider) =>
|
||||||
|
typeof provider === 'object' &&
|
||||||
|
provider !== null &&
|
||||||
|
'providerId' in provider &&
|
||||||
|
'modelAvailability' in provider &&
|
||||||
|
(provider as { providerId?: string }).providerId === 'codex' &&
|
||||||
|
Array.isArray((provider as { modelAvailability?: unknown[] }).modelAvailability) &&
|
||||||
|
(provider as { modelAvailability: Array<{ modelId?: string }> }).modelAvailability.some(
|
||||||
|
(item) => item.modelId === 'gpt-5.2-codex'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -72,18 +72,20 @@ describe('CliProviderModelAvailabilityService', () => {
|
||||||
expect(execCliMock).toHaveBeenCalledTimes(2);
|
expect(execCliMock).toHaveBeenCalledTimes(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('marks unsupported models as unavailable with the runtime reason', async () => {
|
it('marks visible unsupported models as unavailable with the runtime reason', async () => {
|
||||||
buildProviderAwareCliEnvMock.mockResolvedValue({
|
buildProviderAwareCliEnvMock.mockResolvedValue({
|
||||||
env: { HOME: '/Users/tester' },
|
env: { HOME: '/Users/tester' },
|
||||||
connectionIssues: {},
|
connectionIssues: {},
|
||||||
});
|
});
|
||||||
execCliMock.mockRejectedValue(
|
execCliMock.mockRejectedValue(
|
||||||
new Error("The 'gpt-5.2-codex' model is not supported when using Codex with a ChatGPT account.")
|
new Error(
|
||||||
|
"The 'gpt-5.1-codex-max' model is not supported when using Codex with a ChatGPT account."
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
const onUpdate = vi.fn();
|
const onUpdate = vi.fn();
|
||||||
const service = new CliProviderModelAvailabilityService(onUpdate);
|
const service = new CliProviderModelAvailabilityService(onUpdate);
|
||||||
service.getSnapshot(createContext(['gpt-5.2-codex']));
|
service.getSnapshot(createContext(['gpt-5.1-codex-max']));
|
||||||
|
|
||||||
await vi.waitFor(() => {
|
await vi.waitFor(() => {
|
||||||
expect(onUpdate).toHaveBeenCalledWith(
|
expect(onUpdate).toHaveBeenCalledWith(
|
||||||
|
|
@ -92,7 +94,7 @@ describe('CliProviderModelAvailabilityService', () => {
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
modelAvailability: [
|
modelAvailability: [
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
modelId: 'gpt-5.2-codex',
|
modelId: 'gpt-5.1-codex-max',
|
||||||
status: 'unavailable',
|
status: 'unavailable',
|
||||||
reason: 'Not available with Codex ChatGPT subscription',
|
reason: 'Not available with Codex ChatGPT subscription',
|
||||||
}),
|
}),
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,13 @@ function createDeferred<T>(): Deferred<T> {
|
||||||
return { promise, resolve, reject };
|
return { promise, resolve, reject };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createLogsFinderMock(listAttributedMemberFiles: ReturnType<typeof vi.fn>) {
|
||||||
|
return {
|
||||||
|
listAttributedMemberFiles,
|
||||||
|
listAttributedSubagentFiles: listAttributedMemberFiles,
|
||||||
|
} as never;
|
||||||
|
}
|
||||||
|
|
||||||
describe('TeammateToolTracker', () => {
|
describe('TeammateToolTracker', () => {
|
||||||
const tempDirs: string[] = [];
|
const tempDirs: string[] = [];
|
||||||
|
|
||||||
|
|
@ -90,7 +97,7 @@ describe('TeammateToolTracker', () => {
|
||||||
const events: TeamChangeEvent[] = [];
|
const events: TeamChangeEvent[] = [];
|
||||||
|
|
||||||
const tracker = new TeammateToolTracker(
|
const tracker = new TeammateToolTracker(
|
||||||
{ listAttributedSubagentFiles } as never,
|
createLogsFinderMock(listAttributedSubagentFiles),
|
||||||
{ enableTracking, disableTracking } as never,
|
{ enableTracking, disableTracking } as never,
|
||||||
(event) => events.push(event)
|
(event) => events.push(event)
|
||||||
);
|
);
|
||||||
|
|
@ -136,7 +143,7 @@ describe('TeammateToolTracker', () => {
|
||||||
]);
|
]);
|
||||||
const events: TeamChangeEvent[] = [];
|
const events: TeamChangeEvent[] = [];
|
||||||
const tracker = new TeammateToolTracker(
|
const tracker = new TeammateToolTracker(
|
||||||
{ listAttributedSubagentFiles } as never,
|
createLogsFinderMock(listAttributedSubagentFiles),
|
||||||
{ enableTracking: vi.fn(async () => ({ projectFingerprint: null, logSourceGeneration: null })), disableTracking: vi.fn(async () => ({ projectFingerprint: null, logSourceGeneration: null })) } as never,
|
{ enableTracking: vi.fn(async () => ({ projectFingerprint: null, logSourceGeneration: null })), disableTracking: vi.fn(async () => ({ projectFingerprint: null, logSourceGeneration: null })) } as never,
|
||||||
(event) => events.push(event)
|
(event) => events.push(event)
|
||||||
);
|
);
|
||||||
|
|
@ -193,7 +200,7 @@ describe('TeammateToolTracker', () => {
|
||||||
const listAttributedSubagentFiles = vi.fn(async () => [...attributedFiles]);
|
const listAttributedSubagentFiles = vi.fn(async () => [...attributedFiles]);
|
||||||
const events: TeamChangeEvent[] = [];
|
const events: TeamChangeEvent[] = [];
|
||||||
const tracker = new TeammateToolTracker(
|
const tracker = new TeammateToolTracker(
|
||||||
{ listAttributedSubagentFiles } as never,
|
createLogsFinderMock(listAttributedSubagentFiles),
|
||||||
{ enableTracking: vi.fn(async () => ({ projectFingerprint: null, logSourceGeneration: null })), disableTracking: vi.fn(async () => ({ projectFingerprint: null, logSourceGeneration: null })) } as never,
|
{ enableTracking: vi.fn(async () => ({ projectFingerprint: null, logSourceGeneration: null })), disableTracking: vi.fn(async () => ({ projectFingerprint: null, logSourceGeneration: null })) } as never,
|
||||||
(event) => events.push(event)
|
(event) => events.push(event)
|
||||||
);
|
);
|
||||||
|
|
@ -242,7 +249,7 @@ describe('TeammateToolTracker', () => {
|
||||||
]);
|
]);
|
||||||
const events: TeamChangeEvent[] = [];
|
const events: TeamChangeEvent[] = [];
|
||||||
const tracker = new TeammateToolTracker(
|
const tracker = new TeammateToolTracker(
|
||||||
{ listAttributedSubagentFiles } as never,
|
createLogsFinderMock(listAttributedSubagentFiles),
|
||||||
{ enableTracking: vi.fn(async () => ({ projectFingerprint: null, logSourceGeneration: null })), disableTracking: vi.fn(async () => ({ projectFingerprint: null, logSourceGeneration: null })) } as never,
|
{ enableTracking: vi.fn(async () => ({ projectFingerprint: null, logSourceGeneration: null })), disableTracking: vi.fn(async () => ({ projectFingerprint: null, logSourceGeneration: null })) } as never,
|
||||||
(event) => events.push(event)
|
(event) => events.push(event)
|
||||||
);
|
);
|
||||||
|
|
@ -299,7 +306,7 @@ describe('TeammateToolTracker', () => {
|
||||||
]);
|
]);
|
||||||
const events: TeamChangeEvent[] = [];
|
const events: TeamChangeEvent[] = [];
|
||||||
const tracker = new TeammateToolTracker(
|
const tracker = new TeammateToolTracker(
|
||||||
{ listAttributedSubagentFiles } as never,
|
createLogsFinderMock(listAttributedSubagentFiles),
|
||||||
{ enableTracking: vi.fn(async () => ({ projectFingerprint: null, logSourceGeneration: null })), disableTracking: vi.fn(async () => ({ projectFingerprint: null, logSourceGeneration: null })) } as never,
|
{ enableTracking: vi.fn(async () => ({ projectFingerprint: null, logSourceGeneration: null })), disableTracking: vi.fn(async () => ({ projectFingerprint: null, logSourceGeneration: null })) } as never,
|
||||||
(event) => events.push(event)
|
(event) => events.push(event)
|
||||||
);
|
);
|
||||||
|
|
@ -353,7 +360,7 @@ describe('TeammateToolTracker', () => {
|
||||||
const disableTracking = vi.fn(async () => ({ projectFingerprint: null, logSourceGeneration: null }));
|
const disableTracking = vi.fn(async () => ({ projectFingerprint: null, logSourceGeneration: null }));
|
||||||
const events: TeamChangeEvent[] = [];
|
const events: TeamChangeEvent[] = [];
|
||||||
const tracker = new TeammateToolTracker(
|
const tracker = new TeammateToolTracker(
|
||||||
{ listAttributedSubagentFiles } as never,
|
createLogsFinderMock(listAttributedSubagentFiles),
|
||||||
{ enableTracking, disableTracking } as never,
|
{ enableTracking, disableTracking } as never,
|
||||||
(event) => events.push(event)
|
(event) => events.push(event)
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ describe('MemberCard starting-state visuals', () => {
|
||||||
document.body.innerHTML = '';
|
document.body.innerHTML = '';
|
||||||
});
|
});
|
||||||
|
|
||||||
it('shows starting skeleton treatment even after provisioning is no longer active', async () => {
|
it('shows runtime summary while keeping the starting treatment after provisioning stops', async () => {
|
||||||
vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true);
|
vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true);
|
||||||
const host = document.createElement('div');
|
const host = document.createElement('div');
|
||||||
document.body.appendChild(host);
|
document.body.appendChild(host);
|
||||||
|
|
@ -84,8 +84,9 @@ describe('MemberCard starting-state visuals', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(host.textContent).toContain('starting');
|
expect(host.textContent).toContain('starting');
|
||||||
|
expect(host.textContent).toContain('Anthropic · haiku · Medium');
|
||||||
expect(host.querySelector('.member-waiting-shimmer')).not.toBeNull();
|
expect(host.querySelector('.member-waiting-shimmer')).not.toBeNull();
|
||||||
expect(host.querySelectorAll('.skeleton-shimmer').length).toBeGreaterThan(0);
|
expect(host.querySelectorAll('.skeleton-shimmer').length).toBe(0);
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
root.unmount();
|
root.unmount();
|
||||||
|
|
@ -173,7 +174,7 @@ describe('MemberCard starting-state visuals', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('keeps the starting skeleton visible while a runtime is alive but still joining', async () => {
|
it('keeps the starting treatment and runtime summary visible while a runtime is still joining', async () => {
|
||||||
vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true);
|
vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true);
|
||||||
const host = document.createElement('div');
|
const host = document.createElement('div');
|
||||||
document.body.appendChild(host);
|
document.body.appendChild(host);
|
||||||
|
|
@ -197,9 +198,10 @@ describe('MemberCard starting-state visuals', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(host.textContent).toContain('starting');
|
expect(host.textContent).toContain('starting');
|
||||||
|
expect(host.textContent).toContain('Anthropic · sonnet · Medium');
|
||||||
expect(host.textContent).not.toContain('online');
|
expect(host.textContent).not.toContain('online');
|
||||||
expect(host.querySelector('.member-waiting-shimmer')).not.toBeNull();
|
expect(host.querySelector('.member-waiting-shimmer')).not.toBeNull();
|
||||||
expect(host.querySelectorAll('.skeleton-shimmer').length).toBeGreaterThan(0);
|
expect(host.querySelectorAll('.skeleton-shimmer').length).toBe(0);
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
root.unmount();
|
root.unmount();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue