fix(ci): restore workspace validation
This commit is contained in:
parent
0eb38387a3
commit
f536e7e715
28 changed files with 86 additions and 86 deletions
|
|
@ -115,9 +115,7 @@ export function useTeamGraphAdapter(teamName: string): GraphDataPort {
|
||||||
const currentAssignment = slotAssignments[stableOwnerId];
|
const currentAssignment = slotAssignments[stableOwnerId];
|
||||||
const defaultAssignment = defaultSeed.assignments[stableOwnerId];
|
const defaultAssignment = defaultSeed.assignments[stableOwnerId];
|
||||||
return (
|
return (
|
||||||
currentAssignment &&
|
currentAssignment?.ringIndex === defaultAssignment?.ringIndex &&
|
||||||
defaultAssignment &&
|
|
||||||
currentAssignment.ringIndex === defaultAssignment.ringIndex &&
|
|
||||||
currentAssignment.sectorIndex === defaultAssignment.sectorIndex
|
currentAssignment.sectorIndex === defaultAssignment.sectorIndex
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
* into ui/, hooks/, or core/ directly.
|
* into ui/, hooks/, or core/ directly.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
export type { InlineActivityEntry } from '../core/domain/buildInlineActivityEntries';
|
||||||
export { buildInlineActivityEntries } from '../core/domain/buildInlineActivityEntries';
|
export { buildInlineActivityEntries } from '../core/domain/buildInlineActivityEntries';
|
||||||
export { buildGraphMemberNodeIdForMember } from '../core/domain/graphOwnerIdentity';
|
export { buildGraphMemberNodeIdForMember } from '../core/domain/graphOwnerIdentity';
|
||||||
export { TeamGraphAdapter } from './adapters/TeamGraphAdapter';
|
export { TeamGraphAdapter } from './adapters/TeamGraphAdapter';
|
||||||
|
|
|
||||||
|
|
@ -157,7 +157,7 @@ function resolveHistoryOpenedAt(lookup: HistoryLookup, projectPath: string): num
|
||||||
}
|
}
|
||||||
|
|
||||||
const foldedMatch = lookup.folded.get(foldHistoryPath(normalizedPath));
|
const foldedMatch = lookup.folded.get(foldHistoryPath(normalizedPath));
|
||||||
if (!foldedMatch || foldedMatch.exactPaths.size !== 1) {
|
if (foldedMatch?.exactPaths.size !== 1) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -471,7 +471,7 @@ export class TmuxWslService {
|
||||||
['-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command', POWERSHELL_FEATURE_QUERY],
|
['-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command', POWERSHELL_FEATURE_QUERY],
|
||||||
6_000
|
6_000
|
||||||
);
|
);
|
||||||
if (!result || result.exitCode !== 0 || !result.stdout.trim()) {
|
if (result?.exitCode !== 0 || !result.stdout.trim()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,7 @@ import {
|
||||||
} from './services/extensions';
|
} from './services/extensions';
|
||||||
import { startEventLoopLagMonitor } from './services/infrastructure/EventLoopLagMonitor';
|
import { startEventLoopLagMonitor } from './services/infrastructure/EventLoopLagMonitor';
|
||||||
import { HttpServer } from './services/infrastructure/HttpServer';
|
import { HttpServer } from './services/infrastructure/HttpServer';
|
||||||
|
import { clearAutoResumeService } from './services/team/AutoResumeService';
|
||||||
import {
|
import {
|
||||||
buildTeamControlApiBaseUrl,
|
buildTeamControlApiBaseUrl,
|
||||||
clearTeamControlApiState,
|
clearTeamControlApiState,
|
||||||
|
|
@ -100,7 +101,6 @@ import {
|
||||||
type TeamReconcileTrigger,
|
type TeamReconcileTrigger,
|
||||||
} from './services/team/TeamReconcileDrainScheduler';
|
} from './services/team/TeamReconcileDrainScheduler';
|
||||||
import { TeamSentMessagesStore } from './services/team/TeamSentMessagesStore';
|
import { TeamSentMessagesStore } from './services/team/TeamSentMessagesStore';
|
||||||
import { clearAutoResumeService } from './services/team/AutoResumeService';
|
|
||||||
import { getAppIconPath } from './utils/appIcon';
|
import { getAppIconPath } from './utils/appIcon';
|
||||||
import { getProjectsBasePath, getTeamsBasePath, getTodosBasePath } from './utils/pathDecoder';
|
import { getProjectsBasePath, getTeamsBasePath, getTodosBasePath } from './utils/pathDecoder';
|
||||||
import {
|
import {
|
||||||
|
|
|
||||||
|
|
@ -839,7 +839,7 @@ async function handleGetData(
|
||||||
const projectPath = data.config.projectPath;
|
const projectPath = data.config.projectPath;
|
||||||
const live = provisioning.getLiveLeadProcessMessages(tn);
|
const live = provisioning.getLiveLeadProcessMessages(tn);
|
||||||
const durableMessages = Array.isArray((data as { messages?: unknown }).messages)
|
const durableMessages = Array.isArray((data as { messages?: unknown }).messages)
|
||||||
? (((data as { messages?: typeof live }).messages ?? []) as typeof live)
|
? ((data as { messages?: typeof live }).messages ?? [])
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
if (live.length === 0) {
|
if (live.length === 0) {
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ export class ApiKeyService {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (!request.value) throw new Error('Key value is required');
|
if (!request.value) throw new Error('Key value is required');
|
||||||
if (request.scope === 'project' && (!request.projectPath || !request.projectPath.trim())) {
|
if (request.scope === 'project' && !request.projectPath?.trim()) {
|
||||||
throw new Error('Project-scoped API keys require a project path');
|
throw new Error('Project-scoped API keys require a project path');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -161,7 +161,7 @@ function classifyFailedProbe(
|
||||||
|
|
||||||
export class CliProviderModelAvailabilityService {
|
export class CliProviderModelAvailabilityService {
|
||||||
private readonly cache = new Map<string, ProviderModelAvailabilityCacheEntry>();
|
private readonly cache = new Map<string, ProviderModelAvailabilityCacheEntry>();
|
||||||
private readonly queue: Array<() => void> = [];
|
private readonly queue: (() => void)[] = [];
|
||||||
private activeProbeCount = 0;
|
private activeProbeCount = 0;
|
||||||
|
|
||||||
constructor(private readonly onUpdate?: ProviderAvailabilityUpdateHandler) {}
|
constructor(private readonly onUpdate?: ProviderAvailabilityUpdateHandler) {}
|
||||||
|
|
|
||||||
|
|
@ -223,8 +223,7 @@ export function createPersistedLaunchSnapshot(params: {
|
||||||
for (const name of expectedMembers) {
|
for (const name of expectedMembers) {
|
||||||
const member = members[name];
|
const member = members[name];
|
||||||
if (
|
if (
|
||||||
member &&
|
member?.launchState === 'starting' &&
|
||||||
member.launchState === 'starting' &&
|
|
||||||
!member.agentToolAccepted &&
|
!member.agentToolAccepted &&
|
||||||
!member.runtimeAlive &&
|
!member.runtimeAlive &&
|
||||||
!member.bootstrapConfirmed &&
|
!member.bootstrapConfirmed &&
|
||||||
|
|
|
||||||
|
|
@ -390,7 +390,7 @@ export class TeamMessageFeedService {
|
||||||
|
|
||||||
const feedRevision = toFeedRevision(messages);
|
const feedRevision = toFeedRevision(messages);
|
||||||
const nextEntry =
|
const nextEntry =
|
||||||
cached && cached.feedRevision === feedRevision
|
cached?.feedRevision === feedRevision
|
||||||
? cached
|
? cached
|
||||||
: {
|
: {
|
||||||
feedRevision,
|
feedRevision,
|
||||||
|
|
|
||||||
|
|
@ -82,6 +82,7 @@ import { resolveTeamProviderId } from '../runtime/providerRuntimeEnv';
|
||||||
|
|
||||||
import { buildActionModeProtocol } from './actionModeInstructions';
|
import { buildActionModeProtocol } from './actionModeInstructions';
|
||||||
import { atomicWriteAsync } from './atomicWrite';
|
import { atomicWriteAsync } from './atomicWrite';
|
||||||
|
import { peekAutoResumeService } from './AutoResumeService';
|
||||||
import { ClaudeBinaryResolver } from './ClaudeBinaryResolver';
|
import { ClaudeBinaryResolver } from './ClaudeBinaryResolver';
|
||||||
import { withFileLock } from './fileLock';
|
import { withFileLock } from './fileLock';
|
||||||
import {
|
import {
|
||||||
|
|
@ -112,7 +113,6 @@ import { TeamMembersMetaStore } from './TeamMembersMetaStore';
|
||||||
import { TeamMetaStore } from './TeamMetaStore';
|
import { TeamMetaStore } from './TeamMetaStore';
|
||||||
import { TeamSentMessagesStore } from './TeamSentMessagesStore';
|
import { TeamSentMessagesStore } from './TeamSentMessagesStore';
|
||||||
import { TeamTaskReader } from './TeamTaskReader';
|
import { TeamTaskReader } from './TeamTaskReader';
|
||||||
import { peekAutoResumeService } from './AutoResumeService';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Kill a team CLI process using SIGKILL (uncatchable).
|
* Kill a team CLI process using SIGKILL (uncatchable).
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { atomicWriteAsync } from '@main/utils/atomicWrite';
|
||||||
import { extractCwd } from '@main/utils/jsonl';
|
import { extractCwd } from '@main/utils/jsonl';
|
||||||
import {
|
import {
|
||||||
encodePath,
|
encodePath,
|
||||||
|
|
@ -5,7 +6,6 @@ import {
|
||||||
getProjectsBasePath,
|
getProjectsBasePath,
|
||||||
getTeamsBasePath,
|
getTeamsBasePath,
|
||||||
} from '@main/utils/pathDecoder';
|
} from '@main/utils/pathDecoder';
|
||||||
import { atomicWriteAsync } from '@main/utils/atomicWrite';
|
|
||||||
import { isLeadMember } from '@shared/utils/leadDetection';
|
import { isLeadMember } from '@shared/utils/leadDetection';
|
||||||
import { createLogger } from '@shared/utils/logger';
|
import { createLogger } from '@shared/utils/logger';
|
||||||
import { createReadStream, type Dirent } from 'fs';
|
import { createReadStream, type Dirent } from 'fs';
|
||||||
|
|
@ -219,7 +219,8 @@ export class TeamTranscriptProjectResolver {
|
||||||
...config,
|
...config,
|
||||||
projectPath: resolution.effectiveProjectPath,
|
projectPath: resolution.effectiveProjectPath,
|
||||||
projectPathHistory: this.buildRepairedProjectPathHistory(
|
projectPathHistory: this.buildRepairedProjectPathHistory(
|
||||||
config,
|
config.projectPath,
|
||||||
|
config.projectPathHistory,
|
||||||
resolution.effectiveProjectPath
|
resolution.effectiveProjectPath
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
@ -598,25 +599,11 @@ export class TeamTranscriptProjectResolver {
|
||||||
|
|
||||||
parsed.projectPath = normalizedNextPath;
|
parsed.projectPath = normalizedNextPath;
|
||||||
|
|
||||||
const history: string[] = [];
|
parsed.projectPathHistory = this.buildRepairedProjectPathHistory(
|
||||||
const seen = new Set<string>();
|
rawProjectPath,
|
||||||
const pushHistory = (value: unknown): void => {
|
parsed.projectPathHistory,
|
||||||
const normalized = normalizeProjectPathCandidate(value);
|
normalizedNextPath
|
||||||
if (!normalized || normalized === normalizedNextPath || seen.has(normalized)) {
|
);
|
||||||
return;
|
|
||||||
}
|
|
||||||
seen.add(normalized);
|
|
||||||
history.push(normalized);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (Array.isArray(parsed.projectPathHistory)) {
|
|
||||||
for (const value of parsed.projectPathHistory) {
|
|
||||||
pushHistory(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pushHistory(rawProjectPath);
|
|
||||||
|
|
||||||
parsed.projectPathHistory = history.slice(-500);
|
|
||||||
await atomicWriteAsync(configPath, JSON.stringify(parsed, null, 2));
|
await atomicWriteAsync(configPath, JSON.stringify(parsed, null, 2));
|
||||||
logger.info(
|
logger.info(
|
||||||
`[${teamName}] Repaired transcript projectPath via exact session match: ${normalizedNextPath}`
|
`[${teamName}] Repaired transcript projectPath via exact session match: ${normalizedNextPath}`
|
||||||
|
|
@ -663,7 +650,11 @@ export class TeamTranscriptProjectResolver {
|
||||||
return orderedSessionIds;
|
return orderedSessionIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
private buildRepairedProjectPathHistory(config: TeamConfig, nextProjectPath: string): string[] {
|
private buildRepairedProjectPathHistory(
|
||||||
|
currentProjectPath: unknown,
|
||||||
|
rawProjectPathHistory: unknown,
|
||||||
|
nextProjectPath: string
|
||||||
|
): string[] {
|
||||||
const normalizedNextPath = normalizeProjectPathCandidate(nextProjectPath);
|
const normalizedNextPath = normalizeProjectPathCandidate(nextProjectPath);
|
||||||
const history: string[] = [];
|
const history: string[] = [];
|
||||||
const seen = new Set<string>();
|
const seen = new Set<string>();
|
||||||
|
|
@ -676,12 +667,12 @@ export class TeamTranscriptProjectResolver {
|
||||||
history.push(normalized);
|
history.push(normalized);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (Array.isArray(config.projectPathHistory)) {
|
if (Array.isArray(rawProjectPathHistory)) {
|
||||||
for (const value of config.projectPathHistory) {
|
for (const value of rawProjectPathHistory) {
|
||||||
pushHistory(value);
|
pushHistory(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pushHistory(config.projectPath);
|
pushHistory(currentProjectPath);
|
||||||
|
|
||||||
return history.slice(-500);
|
return history.slice(-500);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,9 @@
|
||||||
|
export {
|
||||||
|
AutoResumeService,
|
||||||
|
clearAutoResumeService,
|
||||||
|
getAutoResumeService,
|
||||||
|
initializeAutoResumeService,
|
||||||
|
} from './AutoResumeService';
|
||||||
export { BranchStatusService } from './BranchStatusService';
|
export { BranchStatusService } from './BranchStatusService';
|
||||||
export { CascadeGuard } from './CascadeGuard';
|
export { CascadeGuard } from './CascadeGuard';
|
||||||
export { ChangeExtractorService } from './ChangeExtractorService';
|
export { ChangeExtractorService } from './ChangeExtractorService';
|
||||||
|
|
@ -16,12 +22,6 @@ export { BoardTaskActivityService } from './taskLogs/activity/BoardTaskActivityS
|
||||||
export { BoardTaskExactLogDetailService } from './taskLogs/exact/BoardTaskExactLogDetailService';
|
export { BoardTaskExactLogDetailService } from './taskLogs/exact/BoardTaskExactLogDetailService';
|
||||||
export { BoardTaskExactLogsService } from './taskLogs/exact/BoardTaskExactLogsService';
|
export { BoardTaskExactLogsService } from './taskLogs/exact/BoardTaskExactLogsService';
|
||||||
export { BoardTaskLogStreamService } from './taskLogs/stream/BoardTaskLogStreamService';
|
export { BoardTaskLogStreamService } from './taskLogs/stream/BoardTaskLogStreamService';
|
||||||
export {
|
|
||||||
AutoResumeService,
|
|
||||||
clearAutoResumeService,
|
|
||||||
getAutoResumeService,
|
|
||||||
initializeAutoResumeService,
|
|
||||||
} from './AutoResumeService';
|
|
||||||
export { TeamAttachmentStore } from './TeamAttachmentStore';
|
export { TeamAttachmentStore } from './TeamAttachmentStore';
|
||||||
export { TeamBackupService } from './TeamBackupService';
|
export { TeamBackupService } from './TeamBackupService';
|
||||||
export { TeamConfigReader } from './TeamConfigReader';
|
export { TeamConfigReader } from './TeamConfigReader';
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,12 @@ import { Button } from '@renderer/components/ui/button';
|
||||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
|
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
|
||||||
import { useStore } from '@renderer/store';
|
import { useStore } from '@renderer/store';
|
||||||
import { formatCompactNumber, formatRelativeTime } from '@renderer/utils/formatters';
|
import { formatCompactNumber, formatRelativeTime } from '@renderer/utils/formatters';
|
||||||
import { getDefaultMcpSharedScope } from '@shared/utils/mcpScopes';
|
|
||||||
import {
|
import {
|
||||||
getMcpInstallationSummaryLabel,
|
getMcpInstallationSummaryLabel,
|
||||||
getMcpOperationKey,
|
getMcpOperationKey,
|
||||||
sanitizeMcpServerName,
|
sanitizeMcpServerName,
|
||||||
} from '@shared/utils/extensionNormalizers';
|
} from '@shared/utils/extensionNormalizers';
|
||||||
|
import { getDefaultMcpSharedScope } from '@shared/utils/mcpScopes';
|
||||||
import { Clock, Cloud, Globe, KeyRound, Lock, Monitor, Star, Tag, Wrench } from 'lucide-react';
|
import { Clock, Cloud, Globe, KeyRound, Lock, Monitor, Star, Tag, Wrench } from 'lucide-react';
|
||||||
import { Github as GithubIcon } from 'lucide-react';
|
import { Github as GithubIcon } from 'lucide-react';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,18 +25,18 @@ import {
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from '@renderer/components/ui/select';
|
} from '@renderer/components/ui/select';
|
||||||
import { useStore } from '@renderer/store';
|
import { useStore } from '@renderer/store';
|
||||||
import {
|
|
||||||
getDefaultMcpSharedScope,
|
|
||||||
getMcpScopeLabel,
|
|
||||||
isProjectScopedMcpScope,
|
|
||||||
isSharedMcpScope,
|
|
||||||
} from '@shared/utils/mcpScopes';
|
|
||||||
import {
|
import {
|
||||||
getMcpInstallationSummaryLabel,
|
getMcpInstallationSummaryLabel,
|
||||||
getMcpOperationKey,
|
getMcpOperationKey,
|
||||||
getPreferredMcpInstallationEntry,
|
getPreferredMcpInstallationEntry,
|
||||||
sanitizeMcpServerName,
|
sanitizeMcpServerName,
|
||||||
} from '@shared/utils/extensionNormalizers';
|
} from '@shared/utils/extensionNormalizers';
|
||||||
|
import {
|
||||||
|
getDefaultMcpSharedScope,
|
||||||
|
getMcpScopeLabel,
|
||||||
|
isProjectScopedMcpScope,
|
||||||
|
isSharedMcpScope,
|
||||||
|
} from '@shared/utils/mcpScopes';
|
||||||
import { ExternalLink, Lock, Plus, Star, Trash2, Wrench } from 'lucide-react';
|
import { ExternalLink, Lock, Plus, Star, Trash2, Wrench } from 'lucide-react';
|
||||||
|
|
||||||
import { InstallButton } from '../common/InstallButton';
|
import { InstallButton } from '../common/InstallButton';
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,8 @@ import {
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from '@renderer/components/ui/select';
|
} from '@renderer/components/ui/select';
|
||||||
import { useStore } from '@renderer/store';
|
import { useStore } from '@renderer/store';
|
||||||
import { getCliProviderExtensionCapability } from '@shared/utils/providerExtensionCapabilities';
|
|
||||||
import { inferCapabilities, normalizeCategory } from '@shared/utils/extensionNormalizers';
|
import { inferCapabilities, normalizeCategory } from '@shared/utils/extensionNormalizers';
|
||||||
|
import { getCliProviderExtensionCapability } from '@shared/utils/providerExtensionCapabilities';
|
||||||
import { ArrowUpDown, Filter, Puzzle, Search } from 'lucide-react';
|
import { ArrowUpDown, Filter, Puzzle, Search } from 'lucide-react';
|
||||||
import { useShallow } from 'zustand/react/shallow';
|
import { useShallow } from 'zustand/react/shallow';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -41,8 +41,8 @@ import { validateSkillFolderName } from './skillValidationUtils';
|
||||||
import type {
|
import type {
|
||||||
SkillDetail,
|
SkillDetail,
|
||||||
SkillInvocationMode,
|
SkillInvocationMode,
|
||||||
SkillRootKind,
|
|
||||||
SkillReviewPreview,
|
SkillReviewPreview,
|
||||||
|
SkillRootKind,
|
||||||
} from '@shared/types/extensions';
|
} from '@shared/types/extensions';
|
||||||
|
|
||||||
type EditorMode = 'create' | 'edit';
|
type EditorMode = 'create' | 'edit';
|
||||||
|
|
|
||||||
|
|
@ -127,7 +127,7 @@ function formatRuntimeAudienceLabel(providerNames: readonly string[]): string {
|
||||||
return 'the configured runtime';
|
return 'the configured runtime';
|
||||||
}
|
}
|
||||||
if (providerNames.length === 1) {
|
if (providerNames.length === 1) {
|
||||||
return providerNames[0]!;
|
return providerNames[0];
|
||||||
}
|
}
|
||||||
if (providerNames.length === 2) {
|
if (providerNames.length === 2) {
|
||||||
return `${providerNames[0]} and ${providerNames[1]}`;
|
return `${providerNames[0]} and ${providerNames[1]}`;
|
||||||
|
|
|
||||||
|
|
@ -72,8 +72,8 @@ import { AdvancedCliSection } from './AdvancedCliSection';
|
||||||
import { EffortLevelSelector } from './EffortLevelSelector';
|
import { EffortLevelSelector } from './EffortLevelSelector';
|
||||||
import { resolveLaunchDialogPrefill } from './launchDialogPrefill';
|
import { resolveLaunchDialogPrefill } from './launchDialogPrefill';
|
||||||
import { OptionalSettingsSection } from './OptionalSettingsSection';
|
import { OptionalSettingsSection } from './OptionalSettingsSection';
|
||||||
import { buildProviderPrepareModelCacheKey } from './providerPrepareCacheKey';
|
|
||||||
import { ProjectPathSelector } from './ProjectPathSelector';
|
import { ProjectPathSelector } from './ProjectPathSelector';
|
||||||
|
import { buildProviderPrepareModelCacheKey } from './providerPrepareCacheKey';
|
||||||
import {
|
import {
|
||||||
buildReusableProviderPrepareModelResults,
|
buildReusableProviderPrepareModelResults,
|
||||||
getProviderPrepareCachedSnapshot,
|
getProviderPrepareCachedSnapshot,
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,8 @@ export const MemberCard = ({
|
||||||
!isRemoved &&
|
!isRemoved &&
|
||||||
presenceLabel === 'starting' &&
|
presenceLabel === 'starting' &&
|
||||||
spawnLaunchState !== 'failed_to_start' &&
|
spawnLaunchState !== 'failed_to_start' &&
|
||||||
!activityTask;
|
!activityTask &&
|
||||||
|
!runtimeSummary;
|
||||||
const showStartingBadge = !isRemoved && presenceLabel === 'starting' && !activityTask;
|
const showStartingBadge = !isRemoved && presenceLabel === 'starting' && !activityTask;
|
||||||
const showRuntimeAdvisoryBadge =
|
const showRuntimeAdvisoryBadge =
|
||||||
!isRemoved &&
|
!isRemoved &&
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,8 @@ import { selectMemberMessagesForTeamMember } from '@renderer/store/slices/teamSl
|
||||||
import { isLeadMember } from '@shared/utils/leadDetection';
|
import { isLeadMember } from '@shared/utils/leadDetection';
|
||||||
import { BarChart3, FileText, ListPlus, MessageSquare, UserMinus } from 'lucide-react';
|
import { BarChart3, FileText, ListPlus, MessageSquare, UserMinus } from 'lucide-react';
|
||||||
|
|
||||||
import { MemberDetailHeader } from './MemberDetailHeader';
|
|
||||||
import { buildMemberActivityEntries } from './memberActivityEntries';
|
import { buildMemberActivityEntries } from './memberActivityEntries';
|
||||||
|
import { MemberDetailHeader } from './MemberDetailHeader';
|
||||||
import { MemberDetailStats } from './MemberDetailStats';
|
import { MemberDetailStats } from './MemberDetailStats';
|
||||||
import { type MemberActivityFilter, type MemberDetailTab } from './memberDetailTypes';
|
import { type MemberActivityFilter, type MemberDetailTab } from './memberDetailTypes';
|
||||||
import { MemberLogsTab } from './MemberLogsTab';
|
import { MemberLogsTab } from './MemberLogsTab';
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import { buildInlineActivityEntries } from '@features/agent-graph/renderer';
|
||||||
import { filterTeamMessages } from '@renderer/utils/teamMessageFiltering';
|
import { filterTeamMessages } from '@renderer/utils/teamMessageFiltering';
|
||||||
import { isLeadMember } from '@shared/utils/leadDetection';
|
import { isLeadMember } from '@shared/utils/leadDetection';
|
||||||
|
|
||||||
import type { InlineActivityEntry } from '@features/agent-graph/core/domain/buildInlineActivityEntries';
|
import type { InlineActivityEntry } from '@features/agent-graph/renderer';
|
||||||
import type { InboxMessage, ResolvedTeamMember, TeamTaskWithKanban } from '@shared/types';
|
import type { InboxMessage, ResolvedTeamMember, TeamTaskWithKanban } from '@shared/types';
|
||||||
|
|
||||||
export function buildMemberActivityEntries({
|
export function buildMemberActivityEntries({
|
||||||
|
|
|
||||||
|
|
@ -267,7 +267,7 @@ export function initializeNotificationListeners(): () => void {
|
||||||
const headResult = await current.refreshTeamMessagesHead(teamName);
|
const headResult = await current.refreshTeamMessagesHead(teamName);
|
||||||
const latest = useStore.getState();
|
const latest = useStore.getState();
|
||||||
const meta = latest.memberActivityMetaByTeam[teamName];
|
const meta = latest.memberActivityMetaByTeam[teamName];
|
||||||
if (headResult.feedChanged || !meta || meta.feedRevision !== headResult.feedRevision) {
|
if (headResult.feedChanged || meta?.feedRevision !== headResult.feedRevision) {
|
||||||
await latest.refreshMemberActivityMeta(teamName);
|
await latest.refreshMemberActivityMeta(teamName);
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
|
|
|
||||||
|
|
@ -1656,7 +1656,7 @@ export function selectResolvedMembersForTeamName(
|
||||||
const meta = state.memberActivityMetaByTeam[teamName];
|
const meta = state.memberActivityMetaByTeam[teamName];
|
||||||
const metaMembers = meta?.members;
|
const metaMembers = meta?.members;
|
||||||
const cached = resolvedMembersSelectorCache.get(teamName);
|
const cached = resolvedMembersSelectorCache.get(teamName);
|
||||||
if (cached && cached.snapshotRef === snapshot.members && cached.metaMembersRef === metaMembers) {
|
if (cached?.snapshotRef === snapshot.members && cached.metaMembersRef === metaMembers) {
|
||||||
return cached.result;
|
return cached.result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1690,7 +1690,7 @@ export function selectResolvedMemberForTeamName(
|
||||||
const metaEntry = state.memberActivityMetaByTeam[teamName]?.members[memberName];
|
const metaEntry = state.memberActivityMetaByTeam[teamName]?.members[memberName];
|
||||||
const cacheKey = `${teamName}:${memberName}`;
|
const cacheKey = `${teamName}:${memberName}`;
|
||||||
const cached = resolvedMemberSelectorCache.get(cacheKey);
|
const cached = resolvedMemberSelectorCache.get(cacheKey);
|
||||||
if (cached && cached.snapshotMemberRef === snapshotMember && cached.metaEntryRef === metaEntry) {
|
if (cached?.snapshotMemberRef === snapshotMember && cached.metaEntryRef === metaEntry) {
|
||||||
return cached.result;
|
return cached.result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1735,8 +1735,7 @@ export function selectTeamMessages(
|
||||||
const entry = getTeamMessagesCacheEntry(state, teamName);
|
const entry = getTeamMessagesCacheEntry(state, teamName);
|
||||||
const cached = mergedMessagesSelectorCache.get(teamName);
|
const cached = mergedMessagesSelectorCache.get(teamName);
|
||||||
if (
|
if (
|
||||||
cached &&
|
cached?.canonicalRef === entry.canonicalMessages &&
|
||||||
cached.canonicalRef === entry.canonicalMessages &&
|
|
||||||
cached.optimisticRef === entry.optimisticMessages
|
cached.optimisticRef === entry.optimisticMessages
|
||||||
) {
|
) {
|
||||||
return cached.result;
|
return cached.result;
|
||||||
|
|
@ -1763,7 +1762,7 @@ export function selectMemberMessagesForTeamMember(
|
||||||
const messages = selectTeamMessages(state, teamName);
|
const messages = selectTeamMessages(state, teamName);
|
||||||
const cacheKey = `${teamName}:${memberName}`;
|
const cacheKey = `${teamName}:${memberName}`;
|
||||||
const cached = memberMessagesSelectorCache.get(cacheKey);
|
const cached = memberMessagesSelectorCache.get(cacheKey);
|
||||||
if (cached && cached.messagesRef === messages) {
|
if (cached?.messagesRef === messages) {
|
||||||
return cached.result;
|
return cached.result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3530,8 +3529,10 @@ export const createTeamSlice: StateCreator<AppState, [], [], TeamSlice> = (set,
|
||||||
return queuedRequest;
|
return queuedRequest;
|
||||||
}
|
}
|
||||||
|
|
||||||
let request!: Promise<RefreshTeamMessagesHeadResult>;
|
const requestRef: { current: Promise<RefreshTeamMessagesHeadResult> | null } = {
|
||||||
request = (async (): Promise<RefreshTeamMessagesHeadResult> => {
|
current: null,
|
||||||
|
};
|
||||||
|
requestRef.current = (async (): Promise<RefreshTeamMessagesHeadResult> => {
|
||||||
const teamStateEpoch = captureTeamLocalStateEpoch(teamName);
|
const teamStateEpoch = captureTeamLocalStateEpoch(teamName);
|
||||||
set((state) => ({
|
set((state) => ({
|
||||||
teamMessagesByName: {
|
teamMessagesByName: {
|
||||||
|
|
@ -3621,7 +3622,7 @@ export const createTeamSlice: StateCreator<AppState, [], [], TeamSlice> = (set,
|
||||||
}));
|
}));
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
if (inFlightTeamMessagesHeadRequests.get(teamName) === request) {
|
if (inFlightTeamMessagesHeadRequests.get(teamName) === requestRef.current) {
|
||||||
inFlightTeamMessagesHeadRequests.delete(teamName);
|
inFlightTeamMessagesHeadRequests.delete(teamName);
|
||||||
if (pendingFreshTeamMessagesHeadRefreshes.delete(teamName)) {
|
if (pendingFreshTeamMessagesHeadRefreshes.delete(teamName)) {
|
||||||
void get().refreshTeamMessagesHead(teamName);
|
void get().refreshTeamMessagesHead(teamName);
|
||||||
|
|
@ -3630,6 +3631,7 @@ export const createTeamSlice: StateCreator<AppState, [], [], TeamSlice> = (set,
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
const request = requestRef.current;
|
||||||
inFlightTeamMessagesHeadRequests.set(teamName, request);
|
inFlightTeamMessagesHeadRequests.set(teamName, request);
|
||||||
return request;
|
return request;
|
||||||
},
|
},
|
||||||
|
|
@ -3662,8 +3664,8 @@ export const createTeamSlice: StateCreator<AppState, [], [], TeamSlice> = (set,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let request!: Promise<void>;
|
const requestRef: { current: Promise<void> | null } = { current: null };
|
||||||
request = (async (): Promise<void> => {
|
requestRef.current = (async (): Promise<void> => {
|
||||||
const teamStateEpoch = captureTeamLocalStateEpoch(teamName);
|
const teamStateEpoch = captureTeamLocalStateEpoch(teamName);
|
||||||
set((state) => ({
|
set((state) => ({
|
||||||
teamMessagesByName: {
|
teamMessagesByName: {
|
||||||
|
|
@ -3747,12 +3749,13 @@ export const createTeamSlice: StateCreator<AppState, [], [], TeamSlice> = (set,
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
} finally {
|
} finally {
|
||||||
if (inFlightTeamMessagesOlderRequests.get(teamName) === request) {
|
if (inFlightTeamMessagesOlderRequests.get(teamName) === requestRef.current) {
|
||||||
inFlightTeamMessagesOlderRequests.delete(teamName);
|
inFlightTeamMessagesOlderRequests.delete(teamName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
const request = requestRef.current;
|
||||||
inFlightTeamMessagesOlderRequests.set(teamName, request);
|
inFlightTeamMessagesOlderRequests.set(teamName, request);
|
||||||
return request;
|
return request;
|
||||||
},
|
},
|
||||||
|
|
@ -3769,8 +3772,8 @@ export const createTeamSlice: StateCreator<AppState, [], [], TeamSlice> = (set,
|
||||||
return existingRequest;
|
return existingRequest;
|
||||||
}
|
}
|
||||||
|
|
||||||
let request!: Promise<void>;
|
const requestRef: { current: Promise<void> | null } = { current: null };
|
||||||
request = (async (): Promise<void> => {
|
requestRef.current = (async (): Promise<void> => {
|
||||||
const teamStateEpoch = captureTeamLocalStateEpoch(teamName);
|
const teamStateEpoch = captureTeamLocalStateEpoch(teamName);
|
||||||
try {
|
try {
|
||||||
const meta = await unwrapIpc('team:getMemberActivityMeta', () =>
|
const meta = await unwrapIpc('team:getMemberActivityMeta', () =>
|
||||||
|
|
@ -3786,7 +3789,7 @@ export const createTeamSlice: StateCreator<AppState, [], [], TeamSlice> = (set,
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
const existing = state.memberActivityMetaByTeam[teamName];
|
const existing = state.memberActivityMetaByTeam[teamName];
|
||||||
if (existing && existing.feedRevision === meta.feedRevision) {
|
if (existing?.feedRevision === meta.feedRevision) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
const sharedMembers = structurallyShareMemberActivityFacts(
|
const sharedMembers = structurallyShareMemberActivityFacts(
|
||||||
|
|
@ -3794,8 +3797,7 @@ export const createTeamSlice: StateCreator<AppState, [], [], TeamSlice> = (set,
|
||||||
meta.members
|
meta.members
|
||||||
);
|
);
|
||||||
const nextMeta =
|
const nextMeta =
|
||||||
existing &&
|
existing?.members === sharedMembers &&
|
||||||
existing.members === sharedMembers &&
|
|
||||||
existing.feedRevision === meta.feedRevision &&
|
existing.feedRevision === meta.feedRevision &&
|
||||||
existing.computedAt === meta.computedAt
|
existing.computedAt === meta.computedAt
|
||||||
? existing
|
? existing
|
||||||
|
|
@ -3816,7 +3818,7 @@ export const createTeamSlice: StateCreator<AppState, [], [], TeamSlice> = (set,
|
||||||
}
|
}
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
if (inFlightTeamMemberActivityMetaRequests.get(teamName) === request) {
|
if (inFlightTeamMemberActivityMetaRequests.get(teamName) === requestRef.current) {
|
||||||
inFlightTeamMemberActivityMetaRequests.delete(teamName);
|
inFlightTeamMemberActivityMetaRequests.delete(teamName);
|
||||||
if (pendingFreshTeamMemberActivityMetaRefreshes.delete(teamName)) {
|
if (pendingFreshTeamMemberActivityMetaRefreshes.delete(teamName)) {
|
||||||
void get().refreshMemberActivityMeta(teamName);
|
void get().refreshMemberActivityMeta(teamName);
|
||||||
|
|
@ -3825,6 +3827,7 @@ export const createTeamSlice: StateCreator<AppState, [], [], TeamSlice> = (set,
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
const request = requestRef.current;
|
||||||
inFlightTeamMemberActivityMetaRequests.set(teamName, request);
|
inFlightTeamMemberActivityMetaRequests.set(teamName, request);
|
||||||
return request;
|
return request;
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -148,11 +148,7 @@ export function buildDisplayItems(
|
||||||
// Build display items
|
// Build display items
|
||||||
for (const step of steps) {
|
for (const step of steps) {
|
||||||
// Skip the last output step
|
// Skip the last output step
|
||||||
if (
|
if (step.id === lastOutputStepRef?.id && step.type === lastOutputStepRef.type) {
|
||||||
lastOutputStepRef &&
|
|
||||||
step.id === lastOutputStepRef.id &&
|
|
||||||
step.type === lastOutputStepRef.type
|
|
||||||
) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -104,10 +104,10 @@ function parseRelativeResetDuration(text: string): number | null {
|
||||||
const match = LEADING_TIME_VALUE_RE.exec(tail);
|
const match = LEADING_TIME_VALUE_RE.exec(tail);
|
||||||
if (!match) return null;
|
if (!match) return null;
|
||||||
|
|
||||||
const amount = Number.parseFloat(match[1]!);
|
const amount = Number.parseFloat(match[1]);
|
||||||
if (!Number.isFinite(amount) || amount < 0) return null;
|
if (!Number.isFinite(amount) || amount < 0) return null;
|
||||||
|
|
||||||
const unit = match[2]!.toLowerCase();
|
const unit = match[2].toLowerCase();
|
||||||
if (['second', 'seconds', 'sec', 'secs', 's'].includes(unit)) {
|
if (['second', 'seconds', 'sec', 'secs', 's'].includes(unit)) {
|
||||||
return Math.round(amount * 1000);
|
return Math.round(amount * 1000);
|
||||||
}
|
}
|
||||||
|
|
@ -157,7 +157,7 @@ function parseAbsoluteResetClockTime(text: string, now: Date): Date | null {
|
||||||
const afterMatch = tail.slice(tzTokenLength);
|
const afterMatch = tail.slice(tzTokenLength);
|
||||||
if (DAY_SHIFT_QUALIFIER_RE.test(afterMatch)) return null;
|
if (DAY_SHIFT_QUALIFIER_RE.test(afterMatch)) return null;
|
||||||
|
|
||||||
const hourRaw = Number.parseInt(match[1]!, 10);
|
const hourRaw = Number.parseInt(match[1], 10);
|
||||||
const minuteRaw = match[2] ? Number.parseInt(match[2], 10) : 0;
|
const minuteRaw = match[2] ? Number.parseInt(match[2], 10) : 0;
|
||||||
const ampm = match[3]?.toLowerCase() ?? null;
|
const ampm = match[3]?.toLowerCase() ?? null;
|
||||||
const parenthesizedTz = parenthesizedTzMatch?.[1]?.toUpperCase() ?? '';
|
const parenthesizedTz = parenthesizedTzMatch?.[1]?.toUpperCase() ?? '';
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ export interface TeamGraphDefaultLayoutSeed {
|
||||||
assignments: Record<string, GraphOwnerSlotAssignment>;
|
assignments: Record<string, GraphOwnerSlotAssignment>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SMALL_TEAM_CARDINAL_SLOT_PRESETS: ReadonlyArray<ReadonlyArray<GraphOwnerSlotAssignment>> = [
|
const SMALL_TEAM_CARDINAL_SLOT_PRESETS: readonly (readonly GraphOwnerSlotAssignment[])[] = [
|
||||||
[],
|
[],
|
||||||
[{ ringIndex: 0, sectorIndex: 0 }],
|
[{ ringIndex: 0, sectorIndex: 0 }],
|
||||||
[
|
[
|
||||||
|
|
@ -83,7 +83,7 @@ export function buildTeamGraphDefaultLayoutSeed(
|
||||||
const preset = SMALL_TEAM_CARDINAL_SLOT_PRESETS[orderedVisibleOwnerIds.length];
|
const preset = SMALL_TEAM_CARDINAL_SLOT_PRESETS[orderedVisibleOwnerIds.length];
|
||||||
const assignments: Record<string, GraphOwnerSlotAssignment> = {};
|
const assignments: Record<string, GraphOwnerSlotAssignment> = {};
|
||||||
|
|
||||||
if (preset && preset.length === orderedVisibleOwnerIds.length) {
|
if (preset?.length === orderedVisibleOwnerIds.length) {
|
||||||
orderedVisibleOwnerIds.forEach((stableOwnerId, index) => {
|
orderedVisibleOwnerIds.forEach((stableOwnerId, index) => {
|
||||||
assignments[stableOwnerId] = preset[index]!;
|
assignments[stableOwnerId] = preset[index]!;
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -308,6 +308,17 @@ describe('MessagesPanel idle summary invariants', () => {
|
||||||
];
|
];
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
|
storeState.teamMessagesByName['atlas-hq'] = {
|
||||||
|
canonicalMessages: messages,
|
||||||
|
optimisticMessages: [],
|
||||||
|
feedRevision: 'rev-1',
|
||||||
|
nextCursor: null,
|
||||||
|
hasMore: false,
|
||||||
|
lastFetchedAt: Date.now(),
|
||||||
|
loadingHead: false,
|
||||||
|
loadingOlder: false,
|
||||||
|
headHydrated: true,
|
||||||
|
};
|
||||||
root.render(
|
root.render(
|
||||||
React.createElement(MessagesPanel, {
|
React.createElement(MessagesPanel, {
|
||||||
teamName: 'atlas-hq',
|
teamName: 'atlas-hq',
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue