diff --git a/src/main/index.ts b/src/main/index.ts index 91667982..c9ee0254 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -120,6 +120,7 @@ import { import { startEventLoopLagMonitor } from './services/infrastructure/EventLoopLagMonitor'; import { HttpServer } from './services/infrastructure/HttpServer'; import { clearAutoResumeService } from './services/team/AutoResumeService'; +import { LaunchIoGovernor } from './services/team/LaunchIoGovernor'; import { OpenCodeBridgeCommandClient } from './services/team/opencode/bridge/OpenCodeBridgeCommandClient'; import { createOpenCodeBridgeCommandLeaseStore, @@ -136,16 +137,15 @@ import { clearTeamControlApiState, writeTeamControlApiState, } from './services/team/TeamControlApiState'; -import { TeamInboxReader } from './services/team/TeamInboxReader'; import { getTeamDataWorkerClient } from './services/team/TeamDataWorkerClient'; import { getTeamFsWorkerClient } from './services/team/TeamFsWorkerClient'; +import { TeamInboxReader } from './services/team/TeamInboxReader'; import { TeamMemberRuntimeAdvisoryService } from './services/team/TeamMemberRuntimeAdvisoryService'; import { createTeamReconcileDrainScheduler, type TeamReconcileTrigger, } from './services/team/TeamReconcileDrainScheduler'; import { TeamSentMessagesStore } from './services/team/TeamSentMessagesStore'; -import { LaunchIoGovernor } from './services/team/LaunchIoGovernor'; import { getAppIconPath } from './utils/appIcon'; import { getClaudeBasePath, @@ -647,7 +647,7 @@ async function runStartupJobsBounded( if (isShutdownStarted()) { return; } - await run(items[index]!); + await run(items[index]); } }); await Promise.allSettled(workers); diff --git a/src/main/ipc/teams.ts b/src/main/ipc/teams.ts index 765193b5..4dd234aa 100644 --- a/src/main/ipc/teams.ts +++ b/src/main/ipc/teams.ts @@ -125,6 +125,10 @@ import { initializeAutoResumeService, planRateLimitAutoResume, } from '../services/team/AutoResumeService'; +import { + cloneLaunchIoGovernorPayload, + type LaunchIoGovernor, +} from '../services/team/LaunchIoGovernor'; import { buildReplaceMembersDiff, buildReplaceMembersSummaryMessage, @@ -136,10 +140,6 @@ import { TeamMetaStore } from '../services/team/TeamMetaStore'; import { buildAddMemberSpawnMessage } from '../services/team/TeamProvisioningService'; import { TeamTaskAttachmentStore } from '../services/team/TeamTaskAttachmentStore'; import { TeamWorktreeGitService } from '../services/team/TeamWorktreeGitService'; -import { - cloneLaunchIoGovernorPayload, - type LaunchIoGovernor, -} from '../services/team/LaunchIoGovernor'; import { validateFromField, @@ -983,7 +983,6 @@ async function handleGetData( } else { noteHeavyTeamDataWorkerFallback('teams:getData'); data = await getTeamDataService().getTeamData(tn); - dataSource = 'main-unavailable'; } } catch (error) { const message = error instanceof Error ? error.message : String(error); diff --git a/src/main/services/infrastructure/NotificationManager.ts b/src/main/services/infrastructure/NotificationManager.ts index a0b29ebb..1d6633e4 100644 --- a/src/main/services/infrastructure/NotificationManager.ts +++ b/src/main/services/infrastructure/NotificationManager.ts @@ -23,7 +23,7 @@ import { stripAgentBlocks } from '@shared/constants/agentBlocks'; import { getMemberColorByName, MEMBER_COLOR_HUE } from '@shared/constants/memberColors'; import { isLeadMember } from '@shared/utils/leadDetection'; import { createLogger } from '@shared/utils/logger'; -import { Notification as ElectronNotification, nativeImage } from 'electron'; +import { nativeImage, Notification as ElectronNotification } from 'electron'; import { EventEmitter } from 'events'; import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs'; import * as fsp from 'fs/promises'; @@ -279,7 +279,7 @@ function truncateNotificationText(value: string, maxLength: number): string { } function extractTaskRef(summary: string): string | null { - const match = summary.match(/#([A-Za-z0-9][A-Za-z0-9-]*)/); + const match = /#([A-Za-z0-9][A-Za-z0-9-]*)/.exec(summary); return match ? `#${match[1]}` : null; } diff --git a/src/main/services/runtime/teamRuntimeSettingsBundle.ts b/src/main/services/runtime/teamRuntimeSettingsBundle.ts index 3a893530..99bda9d9 100644 --- a/src/main/services/runtime/teamRuntimeSettingsBundle.ts +++ b/src/main/services/runtime/teamRuntimeSettingsBundle.ts @@ -1,6 +1,6 @@ +import { randomUUID } from 'crypto'; import fs from 'fs'; import path from 'path'; -import { randomUUID } from 'crypto'; import { mergeJsonSettingsObjects, parseJsonSettingsObject } from './cliSettingsArgs'; @@ -24,7 +24,8 @@ export function splitSettingsJsonArgs(args: string[]): SplitSettingsJsonArgsResu const settingsFragments: TeamRuntimeSettingsJson[] = []; const passthroughArgs: string[] = []; - for (let index = 0; index < args.length; index += 1) { + let index = 0; + while (index < args.length) { const arg = args[index]; if (arg === '--settings') { const value = args[index + 1]; @@ -32,11 +33,11 @@ export function splitSettingsJsonArgs(args: string[]): SplitSettingsJsonArgsResu const parsed = parseJsonSettingsObject(value); if (parsed) { settingsFragments.push(parsed); - index += 1; + index += 2; continue; } passthroughArgs.push(arg, value); - index += 1; + index += 2; continue; } } @@ -52,6 +53,7 @@ export function splitSettingsJsonArgs(args: string[]): SplitSettingsJsonArgsResu } passthroughArgs.push(arg); + index += 1; } return { settingsFragments, passthroughArgs }; @@ -119,7 +121,7 @@ async function writeSettingsFile( export async function materializeTeamRuntimeSettingsBundle(input: { teamName: string; providerId: TeamProviderId; - baseSettings?: Array; + baseSettings?: (TeamRuntimeSettingsJson | null | undefined)[]; anthropicHelper?: AnthropicTeamApiKeyHelperMaterial | null; }): Promise { const fragments = [...(input.baseSettings ?? [])].filter( diff --git a/src/main/services/team/TeamFsWorkerClient.ts b/src/main/services/team/TeamFsWorkerClient.ts index f9991efb..fd98fb89 100644 --- a/src/main/services/team/TeamFsWorkerClient.ts +++ b/src/main/services/team/TeamFsWorkerClient.ts @@ -198,12 +198,8 @@ export class TeamFsWorkerClient { )}` ); this.failWorker(worker, timeoutError); - try { - // Terminate and recreate on next call - worker may be stuck in native IO. - worker.terminate().catch(() => undefined); - } catch { - // ignore - } + // Terminate and recreate on next call - worker may be stuck in native IO. + void worker.terminate().catch(() => undefined); reject(timeoutError); }, WORKER_CALL_TIMEOUT_MS); diff --git a/src/main/services/team/TeamLogSourceTracker.ts b/src/main/services/team/TeamLogSourceTracker.ts index c2d84055..6435907f 100644 --- a/src/main/services/team/TeamLogSourceTracker.ts +++ b/src/main/services/team/TeamLogSourceTracker.ts @@ -13,13 +13,13 @@ import { BOARD_TASK_CHANGES_DIRNAME, BOARD_TASK_LOG_FRESHNESS_DIRNAME, BOARD_TASK_LOG_FRESHNESS_FILE_SUFFIX, - MAX_PENDING_UNKNOWN_ROOT_REFRESH_ATTEMPTS, - MAX_PENDING_UNKNOWN_ROOT_SESSIONS, - PENDING_UNKNOWN_ROOT_SESSION_TTL_MS, classifyLogSourceWatcherEvent, getRelativeLogSourceParts, isAgentTranscriptFileName, + MAX_PENDING_UNKNOWN_ROOT_REFRESH_ATTEMPTS, + MAX_PENDING_UNKNOWN_ROOT_SESSIONS, normalizeLogSourceSessionId, + PENDING_UNKNOWN_ROOT_SESSION_TTL_MS, } from './teamLogSourceWatchScope'; import type { TeamLogSourceLiveContext, TeamMemberLogsFinder } from './TeamMemberLogsFinder'; @@ -420,18 +420,21 @@ export class TeamLogSourceTracker { }); if (action.kind === 'task-freshness') { - this.handleTaskFreshnessSignalChange( - teamName, - current.projectDir, - changedPath, - BOARD_TASK_LOG_FRESHNESS_DIRNAME - ) || + if ( + !this.handleTaskFreshnessSignalChange( + teamName, + current.projectDir, + changedPath, + BOARD_TASK_LOG_FRESHNESS_DIRNAME + ) + ) { this.handleTaskFreshnessSignalChange( teamName, current.projectDir, changedPath, BOARD_TASK_CHANGE_FRESHNESS_DIRNAME ); + } return; } diff --git a/src/main/services/team/TeamMemberLogsFinder.ts b/src/main/services/team/TeamMemberLogsFinder.ts index 5896a1a5..3e1828b9 100644 --- a/src/main/services/team/TeamMemberLogsFinder.ts +++ b/src/main/services/team/TeamMemberLogsFinder.ts @@ -11,15 +11,15 @@ import { lineHasAgentTeamsTaskBoundaryToolName, } from './agentTeamsToolNames'; import { - readBootstrapLaunchSnapshot, choosePreferredLaunchSnapshot, + readBootstrapLaunchSnapshot, } from './TeamBootstrapStateReader'; import { TeamConfigReader } from './TeamConfigReader'; import { TeamInboxReader } from './TeamInboxReader'; import { TeamLaunchStateStore } from './TeamLaunchStateStore'; +import { buildTeamLogWatchSessionIds, extractRuntimeSessionIds } from './teamLogSourceWatchScope'; import { TeamMembersMetaStore } from './TeamMembersMetaStore'; import { TeamTranscriptProjectResolver } from './TeamTranscriptProjectResolver'; -import { buildTeamLogWatchSessionIds, extractRuntimeSessionIds } from './teamLogSourceWatchScope'; import type { MemberLogSummary, diff --git a/src/main/services/team/TeamProvisioningService.ts b/src/main/services/team/TeamProvisioningService.ts index 50eda0a4..c5d5edf5 100644 --- a/src/main/services/team/TeamProvisioningService.ts +++ b/src/main/services/team/TeamProvisioningService.ts @@ -109,20 +109,20 @@ import * as path from 'path'; import pidusage from 'pidusage'; import * as readline from 'readline'; -import { mergeJsonSettingsArgs, parseJsonSettingsObject } from '../runtime/cliSettingsArgs'; import { ANTHROPIC_HELPER_MODE_COMPETING_AUTH_ENV_KEYS, + type AnthropicTeamApiKeyHelperMaterial, CLAUDE_TEAM_ANTHROPIC_API_KEY_HELPER_SETTINGS_PATH_ENV, CLAUDE_TEAM_ANTHROPIC_AUTH_MODE_API_KEY_HELPER, CLAUDE_TEAM_ANTHROPIC_AUTH_MODE_ENV, - DISABLE_ANTHROPIC_TEAM_API_KEY_HELPER_ENV, cleanupAnthropicTeamApiKeyHelperForTeam, cleanupAnthropicTeamApiKeyHelperMaterial, cleanupStaleAnthropicTeamApiKeyHelpers, + DISABLE_ANTHROPIC_TEAM_API_KEY_HELPER_ENV, materializeAnthropicTeamApiKeyHelper, verifyAnthropicTeamApiKeyHelperMaterial, - type AnthropicTeamApiKeyHelperMaterial, } from '../runtime/anthropicTeamApiKeyHelper'; +import { mergeJsonSettingsArgs, parseJsonSettingsObject } from '../runtime/cliSettingsArgs'; import { type GeminiRuntimeAuthState, resolveGeminiRuntimeAuth, @@ -359,9 +359,9 @@ import type { LeadContextUsage, MemberLaunchState, MemberSpawnLivenessSource, - MemberSpawnStatusesSnapshot, MemberSpawnStatus, MemberSpawnStatusEntry, + MemberSpawnStatusesSnapshot, PersistedTeamLaunchMemberState, PersistedTeamLaunchPhase, PersistedTeamLaunchSnapshot, @@ -1100,22 +1100,26 @@ function filterOutSettingsPathArgs( return [...args]; } const filtered: string[] = []; - for (let index = 0; index < args.length; index += 1) { + let index = 0; + while (index < args.length) { const arg = args[index]; if (arg === '--settings' && args[index + 1] === settingsPath) { - index += 1; + index += 2; continue; } if (arg === `--settings=${settingsPath}`) { + index += 1; continue; } filtered.push(arg); + index += 1; } return filtered; } function hasPathBasedSettingsArgs(args: string[]): boolean { - for (let index = 0; index < args.length; index += 1) { + let index = 0; + while (index < args.length) { const arg = args[index]; if (arg === '--settings') { const value = args[index + 1]; @@ -1123,17 +1127,20 @@ function hasPathBasedSettingsArgs(args: string[]): boolean { if (!parseJsonSettingsObject(value)) { return true; } - index += 1; + index += 2; + continue; } if (typeof value !== 'string') { return true; } + index += 1; continue; } const prefix = '--settings='; if (arg.startsWith(prefix) && !parseJsonSettingsObject(arg.slice(prefix.length))) { return true; } + index += 1; } return false; } @@ -4989,6 +4996,10 @@ export class TeamProvisioningService { this.transcriptProjectResolver = new TeamTranscriptProjectResolver({ getConfig: (teamName) => this.configReader.getConfigSnapshot(teamName), }); + this.scheduleStaleAnthropicTeamApiKeyHelperCleanup(); + } + + private scheduleStaleAnthropicTeamApiKeyHelperCleanup(): void { void cleanupStaleAnthropicTeamApiKeyHelpers({ baseClaudeDir: getClaudeBasePath(), maxAgeMs: 14 * 24 * 60 * 60 * 1000, @@ -11090,8 +11101,7 @@ export class TeamProvisioningService { const existingRequest = this.memberSpawnStatusesInFlightByTeam.get(teamName); if ( - existingRequest && - existingRequest.generationAtStart === generationAtStart && + existingRequest?.generationAtStart === generationAtStart && existingRequest.runIdAtStart === run.runId ) { const snapshot = await existingRequest.promise; @@ -11201,8 +11211,7 @@ export class TeamProvisioningService { const generationAtStart = this.getRuntimeSnapshotCacheGeneration(teamName); const existingRequest = this.agentRuntimeSnapshotInFlightByTeam.get(teamName); if ( - existingRequest && - existingRequest.generationAtStart === generationAtStart && + existingRequest?.generationAtStart === generationAtStart && existingRequest.runIdAtStart === runId ) { return existingRequest.promise; @@ -18732,8 +18741,7 @@ export class TeamProvisioningService { const generationAtStart = this.getRuntimeSnapshotCacheGeneration(teamName); const existingRequest = this.liveTeamAgentRuntimeMetadataInFlightByTeam.get(teamName); if ( - existingRequest && - existingRequest.generationAtStart === generationAtStart && + existingRequest?.generationAtStart === generationAtStart && existingRequest.runIdAtStart === runId ) { return this.cloneLiveTeamAgentRuntimeMetadata(await existingRequest.promise); diff --git a/src/main/services/team/opencode/store/RuntimeStoreManifest.ts b/src/main/services/team/opencode/store/RuntimeStoreManifest.ts index be3b2e24..7b1d36d7 100644 --- a/src/main/services/team/opencode/store/RuntimeStoreManifest.ts +++ b/src/main/services/team/opencode/store/RuntimeStoreManifest.ts @@ -3,10 +3,10 @@ import { createHash, randomUUID } from 'crypto'; import { promises as fs } from 'fs'; import * as path from 'path'; -import type { FileLockOptions } from '../../fileLock'; - import { VersionedJsonStore, VersionedJsonStoreError } from './VersionedJsonStore'; +import type { FileLockOptions } from '../../fileLock'; + export const OPENCODE_RUNTIME_STORE_MANIFEST_SCHEMA_VERSION = 1; export const OPENCODE_RUNTIME_STORE_RECEIPT_SCHEMA_VERSION = 1; diff --git a/src/main/workers/team-fs-worker.ts b/src/main/workers/team-fs-worker.ts index b4eb215f..d3afb416 100644 --- a/src/main/workers/team-fs-worker.ts +++ b/src/main/workers/team-fs-worker.ts @@ -148,8 +148,8 @@ interface TeamSummaryCacheEntry { } type CachedTaskReadResult = - | { task: Record; skipReason?: undefined } - | { task?: undefined; skipReason: string }; + | { task: Record; skipReason?: never } + | { task?: never; skipReason: string }; interface TaskFileCacheEntry { fingerprint: string; diff --git a/src/renderer/components/chat/items/BaseItem.tsx b/src/renderer/components/chat/items/BaseItem.tsx index eb4795e3..34b3933d 100644 --- a/src/renderer/components/chat/items/BaseItem.tsx +++ b/src/renderer/components/chat/items/BaseItem.tsx @@ -216,3 +216,5 @@ export const BaseItem = memo( ); } ); + +BaseItem.displayName = 'BaseItem'; diff --git a/src/renderer/components/chat/items/ExecutionTrace.tsx b/src/renderer/components/chat/items/ExecutionTrace.tsx index e81ebe20..aaf39889 100644 --- a/src/renderer/components/chat/items/ExecutionTrace.tsx +++ b/src/renderer/components/chat/items/ExecutionTrace.tsx @@ -282,3 +282,5 @@ export const ExecutionTrace: React.FC = React.memo( ); } ); + +ExecutionTrace.displayName = 'ExecutionTrace'; diff --git a/src/renderer/components/chat/items/LinkedToolItem.tsx b/src/renderer/components/chat/items/LinkedToolItem.tsx index 1dac357c..2b9af00e 100644 --- a/src/renderer/components/chat/items/LinkedToolItem.tsx +++ b/src/renderer/components/chat/items/LinkedToolItem.tsx @@ -236,3 +236,5 @@ export const LinkedToolItem = memo( ); } ); + +LinkedToolItem.displayName = 'LinkedToolItem'; diff --git a/src/renderer/components/chat/items/MetricsPill.tsx b/src/renderer/components/chat/items/MetricsPill.tsx index 9f276c45..e6a31c61 100644 --- a/src/renderer/components/chat/items/MetricsPill.tsx +++ b/src/renderer/components/chat/items/MetricsPill.tsx @@ -215,3 +215,5 @@ export const MetricsPill = memo( ); } ); + +MetricsPill.displayName = 'MetricsPill'; diff --git a/src/renderer/components/chat/items/SlashItem.tsx b/src/renderer/components/chat/items/SlashItem.tsx index df24caf6..306b27c8 100644 --- a/src/renderer/components/chat/items/SlashItem.tsx +++ b/src/renderer/components/chat/items/SlashItem.tsx @@ -81,3 +81,5 @@ export const SlashItem = memo( ); } ); + +SlashItem.displayName = 'SlashItem'; diff --git a/src/renderer/components/chat/items/SubagentItem.tsx b/src/renderer/components/chat/items/SubagentItem.tsx index 0c79394e..3125e62b 100644 --- a/src/renderer/components/chat/items/SubagentItem.tsx +++ b/src/renderer/components/chat/items/SubagentItem.tsx @@ -598,3 +598,5 @@ export const SubagentItem: React.FC = React.memo( ); } ); + +SubagentItem.displayName = 'SubagentItem'; diff --git a/src/renderer/components/chat/items/TeammateMessageItem.tsx b/src/renderer/components/chat/items/TeammateMessageItem.tsx index 6e2af8e4..4bc8d309 100644 --- a/src/renderer/components/chat/items/TeammateMessageItem.tsx +++ b/src/renderer/components/chat/items/TeammateMessageItem.tsx @@ -264,3 +264,5 @@ export const TeammateMessageItem = memo( ); } ); + +TeammateMessageItem.displayName = 'TeammateMessageItem'; diff --git a/src/renderer/components/chat/items/TextItem.tsx b/src/renderer/components/chat/items/TextItem.tsx index ed53418d..f9b87a2d 100644 --- a/src/renderer/components/chat/items/TextItem.tsx +++ b/src/renderer/components/chat/items/TextItem.tsx @@ -82,3 +82,5 @@ export const TextItem: React.FC = React.memo( ); } ); + +TextItem.displayName = 'TextItem'; diff --git a/src/renderer/components/chat/items/ThinkingItem.tsx b/src/renderer/components/chat/items/ThinkingItem.tsx index 5a681ff5..03c2c1ee 100644 --- a/src/renderer/components/chat/items/ThinkingItem.tsx +++ b/src/renderer/components/chat/items/ThinkingItem.tsx @@ -82,3 +82,5 @@ export const ThinkingItem: React.FC = React.memo( ); } ); + +ThinkingItem.displayName = 'ThinkingItem'; diff --git a/src/renderer/components/chat/items/linkedTool/CollapsibleOutputSection.tsx b/src/renderer/components/chat/items/linkedTool/CollapsibleOutputSection.tsx index f0d9bc9b..232043fa 100644 --- a/src/renderer/components/chat/items/linkedTool/CollapsibleOutputSection.tsx +++ b/src/renderer/components/chat/items/linkedTool/CollapsibleOutputSection.tsx @@ -59,3 +59,5 @@ export const CollapsibleOutputSection = memo( ); } ); + +CollapsibleOutputSection.displayName = 'CollapsibleOutputSection'; diff --git a/src/renderer/components/sidebar/DateGroupedSessions.tsx b/src/renderer/components/sidebar/DateGroupedSessions.tsx index 5efd93bf..5c539408 100644 --- a/src/renderer/components/sidebar/DateGroupedSessions.tsx +++ b/src/renderer/components/sidebar/DateGroupedSessions.tsx @@ -1112,3 +1112,5 @@ export const DateGroupedSessions = memo((): React.JSX.Element => { ); }); + +DateGroupedSessions.displayName = 'DateGroupedSessions'; diff --git a/src/renderer/components/team/MemberBadge.tsx b/src/renderer/components/team/MemberBadge.tsx index 15d8d479..7031ce99 100644 --- a/src/renderer/components/team/MemberBadge.tsx +++ b/src/renderer/components/team/MemberBadge.tsx @@ -117,3 +117,5 @@ export const MemberBadge = memo( ); } ); + +MemberBadge.displayName = 'MemberBadge'; diff --git a/src/renderer/components/team/TeamDetailView.tsx b/src/renderer/components/team/TeamDetailView.tsx index 504f7a7d..2c04dd15 100644 --- a/src/renderer/components/team/TeamDetailView.tsx +++ b/src/renderer/components/team/TeamDetailView.tsx @@ -128,12 +128,12 @@ import { } from './sidebar/teamSidebarUiState'; import { ClaudeLogsSection } from './ClaudeLogsSection'; import { CollapsibleTeamSection } from './CollapsibleTeamSection'; -import { ProcessesSection } from './ProcessesSection'; -import { getLaunchJoinMilestonesFromMembers, getLaunchJoinState } from './provisioningSteps'; -import { TeamProvisioningBanner } from './TeamProvisioningBanner'; import { deriveLeadContextButtonLabel } from './leadContextLoadGuards'; import { LeadSessionDetailGate } from './LeadSessionDetailGate'; import { LiveRuntimeStatusBridge } from './LiveRuntimeStatusBridge'; +import { ProcessesSection } from './ProcessesSection'; +import { getLaunchJoinMilestonesFromMembers, getLaunchJoinState } from './provisioningSteps'; +import { TeamProvisioningBanner } from './TeamProvisioningBanner'; import { loadTeamSessionMetadata } from './teamSessionFetchGuards'; import { TeamSessionsSection } from './TeamSessionsSection'; @@ -1847,7 +1847,7 @@ export const TeamDetailView = memo(function TeamDetailView({ const pendingTeamSectionFocus = useStore((s) => s.pendingTeamSectionFocus); const clearTeamSectionFocus = useStore((s) => s.clearTeamSectionFocus); useEffect(() => { - if (!pendingTeamSectionFocus || pendingTeamSectionFocus.teamName !== teamName) return; + if (pendingTeamSectionFocus?.teamName !== teamName) return; const sectionId = pendingTeamSectionFocus.section === 'members' diff --git a/src/renderer/components/team/activity/ReplyQuoteBlock.tsx b/src/renderer/components/team/activity/ReplyQuoteBlock.tsx index 417fe683..e70271cf 100644 --- a/src/renderer/components/team/activity/ReplyQuoteBlock.tsx +++ b/src/renderer/components/team/activity/ReplyQuoteBlock.tsx @@ -79,3 +79,5 @@ export const ReplyQuoteBlock = memo( ); } ); + +ReplyQuoteBlock.displayName = 'ReplyQuoteBlock'; diff --git a/src/renderer/components/team/kanban/KanbanGridLayout.tsx b/src/renderer/components/team/kanban/KanbanGridLayout.tsx index 9b30de1a..2bc527b5 100644 --- a/src/renderer/components/team/kanban/KanbanGridLayout.tsx +++ b/src/renderer/components/team/kanban/KanbanGridLayout.tsx @@ -458,5 +458,7 @@ export const KanbanGridLayout = memo( } ); +KanbanGridLayout.displayName = 'KanbanGridLayout'; + export { SKELETON_HIDE_DELAY_MS, SKELETON_HIDE_DELAY_MS_ON_MODE_SWITCH }; /* eslint-enable tailwindcss/no-custom-classname -- stable class hooks remain scoped to this file. */ diff --git a/src/renderer/components/team/members/CurrentTaskIndicator.tsx b/src/renderer/components/team/members/CurrentTaskIndicator.tsx index 4a8394f3..594eabaf 100644 --- a/src/renderer/components/team/members/CurrentTaskIndicator.tsx +++ b/src/renderer/components/team/members/CurrentTaskIndicator.tsx @@ -58,3 +58,5 @@ export const CurrentTaskIndicator = memo( ); } ); + +CurrentTaskIndicator.displayName = 'CurrentTaskIndicator'; diff --git a/src/renderer/components/team/members/MemberPresenceDot.tsx b/src/renderer/components/team/members/MemberPresenceDot.tsx index bb2f8a3f..dde4e132 100644 --- a/src/renderer/components/team/members/MemberPresenceDot.tsx +++ b/src/renderer/components/team/members/MemberPresenceDot.tsx @@ -27,3 +27,5 @@ export const MemberPresenceDot = memo( ); } ); + +MemberPresenceDot.displayName = 'MemberPresenceDot'; diff --git a/src/renderer/components/team/teamRuntimeDisplayRows.ts b/src/renderer/components/team/teamRuntimeDisplayRows.ts index d1463db4..0e3d5eb4 100644 --- a/src/renderer/components/team/teamRuntimeDisplayRows.ts +++ b/src/renderer/components/team/teamRuntimeDisplayRows.ts @@ -36,11 +36,11 @@ export interface TeamRuntimeDisplayRow { actionsAllowed: false; } -type SpawnDegradation = { +interface SpawnDegradation { reason: string; diagnostic?: string; diagnosticSeverity: TeamAgentRuntimeDiagnosticSeverity; -}; +} const ACTIVE_SPAWN_STATUSES = new Set(['waiting', 'spawning']); diff --git a/src/renderer/store/index.ts b/src/renderer/store/index.ts index 102fa631..622c601a 100644 --- a/src/renderer/store/index.ts +++ b/src/renderer/store/index.ts @@ -48,6 +48,8 @@ import { isTeamDataRefreshPending, selectTeamDataForName, } from './slices/teamSlice'; +import { createUISlice } from './slices/uiSlice'; +import { createUpdateSlice } from './slices/updateSlice'; import { decideProcessFanoutDryRun, decideProcessFanoutMode, @@ -58,8 +60,6 @@ import { noteTeamRefreshFanout, type TeamRefreshFanoutOperation, } from './teamRefreshFanoutDiagnostics'; -import { createUISlice } from './slices/uiSlice'; -import { createUpdateSlice } from './slices/updateSlice'; import type { DetectedError } from '../types/data'; import type { AppState } from './types'; diff --git a/src/renderer/store/teamRefreshFanoutDiagnostics.ts b/src/renderer/store/teamRefreshFanoutDiagnostics.ts index e61ab873..515c3347 100644 --- a/src/renderer/store/teamRefreshFanoutDiagnostics.ts +++ b/src/renderer/store/teamRefreshFanoutDiagnostics.ts @@ -60,7 +60,7 @@ export interface TeamRefreshFanoutStructuredCount { phase: TeamRefreshFanoutPhase; } -export interface TeamRefreshFanoutSummaryRow extends TeamRefreshFanoutStructuredCount {} +export type TeamRefreshFanoutSummaryRow = TeamRefreshFanoutStructuredCount; export interface TeamRefreshFanoutSummary { generatedAt: number; @@ -92,7 +92,7 @@ function createEmptyBucket(): TeamRefreshFanoutBucket { function ensureTeamBucket(teamName: string): TeamRefreshFanoutBucket { if (!buckets.has(teamName) && buckets.size >= MAX_TEAM_REFRESH_DIAGNOSTIC_TEAMS) { - const oldestKey = buckets.keys().next().value as string | undefined; + const oldestKey = buckets.keys().next().value; if (oldestKey) { buckets.delete(oldestKey); } diff --git a/src/shared/types/team.ts b/src/shared/types/team.ts index 7a6d7c77..4c2def63 100644 --- a/src/shared/types/team.ts +++ b/src/shared/types/team.ts @@ -1,5 +1,5 @@ -import type { EnhancedChunk } from '@main/types'; import type { NotificationTarget, TeamEventType } from './notifications'; +import type { EnhancedChunk } from '@main/types'; export interface TeamMember { name: string; diff --git a/test/main/services/team/MixedProviderTeamLaunch.live.test.ts b/test/main/services/team/MixedProviderTeamLaunch.live.test.ts index daad294e..794f072e 100644 --- a/test/main/services/team/MixedProviderTeamLaunch.live.test.ts +++ b/test/main/services/team/MixedProviderTeamLaunch.live.test.ts @@ -235,10 +235,7 @@ liveDescribe('Mixed provider team launch live e2e', () => { await waitUntilWithDiagnostics(async () => { const status = await harness!.svc.getMemberSpawnStatuses(teamName!); - if ( - status.teamLaunchState === 'failed' || - status.teamLaunchState === 'partial_failure' - ) { + if (status.teamLaunchState === 'partial_failure') { throw new Error(await formatMixedLaunchDiagnostics(harness!, teamName!, progressEvents)); } for (const memberName of ['alice', 'cody', 'oscar'] as const) { @@ -268,8 +265,8 @@ liveDescribe('Mixed provider team launch live e2e', () => { const laneIndex = await readOpenCodeRuntimeLaneIndex(getTeamsBasePath(), teamName); expect( - Object.values(laneIndex.lanes).some( - (lane) => lane.state === 'active' && lane.memberName === 'oscar' + Object.entries(laneIndex.lanes).some( + ([laneId, lane]) => lane.state === 'active' && laneId === 'secondary:opencode:oscar' ) ).toBe(true); }, diff --git a/test/main/services/team/TeamProvisioningServicePrompts.test.ts b/test/main/services/team/TeamProvisioningServicePrompts.test.ts index 55c35dfb..1b8356a3 100644 --- a/test/main/services/team/TeamProvisioningServicePrompts.test.ts +++ b/test/main/services/team/TeamProvisioningServicePrompts.test.ts @@ -25,7 +25,7 @@ vi.mock('@main/services/team/ClaudeBinaryResolver', () => ({ vi.mock('@main/utils/childProcess', () => ({ execCli: vi.fn(async (_binaryPath: string | null, args: string[]) => { - if (args[0] === 'model') { + if (args.includes('model') && args.includes('list')) { return { stdout: JSON.stringify({ schemaVersion: 1, @@ -54,7 +54,7 @@ vi.mock('@main/utils/childProcess', () => ({ stderr: '', }; } - if (args[0] === 'runtime') { + if (args.includes('runtime') && args.includes('status')) { return { stdout: JSON.stringify({ providers: {