fix(ci): restore dev validation gates
This commit is contained in:
parent
b92c8c2f54
commit
92a1c2067b
33 changed files with 106 additions and 69 deletions
|
|
@ -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<T>(
|
|||
if (isShutdownStarted()) {
|
||||
return;
|
||||
}
|
||||
await run(items[index]!);
|
||||
await run(items[index]);
|
||||
}
|
||||
});
|
||||
await Promise.allSettled(workers);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<TeamRuntimeSettingsJson | null | undefined>;
|
||||
baseSettings?: (TeamRuntimeSettingsJson | null | undefined)[];
|
||||
anthropicHelper?: AnthropicTeamApiKeyHelperMaterial | null;
|
||||
}): Promise<TeamRuntimeSettingsBundle | null> {
|
||||
const fragments = [...(input.baseSettings ?? [])].filter(
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -148,8 +148,8 @@ interface TeamSummaryCacheEntry {
|
|||
}
|
||||
|
||||
type CachedTaskReadResult =
|
||||
| { task: Record<string, unknown>; skipReason?: undefined }
|
||||
| { task?: undefined; skipReason: string };
|
||||
| { task: Record<string, unknown>; skipReason?: never }
|
||||
| { task?: never; skipReason: string };
|
||||
|
||||
interface TaskFileCacheEntry {
|
||||
fingerprint: string;
|
||||
|
|
|
|||
|
|
@ -216,3 +216,5 @@ export const BaseItem = memo(
|
|||
);
|
||||
}
|
||||
);
|
||||
|
||||
BaseItem.displayName = 'BaseItem';
|
||||
|
|
|
|||
|
|
@ -282,3 +282,5 @@ export const ExecutionTrace: React.FC<ExecutionTraceProps> = React.memo(
|
|||
);
|
||||
}
|
||||
);
|
||||
|
||||
ExecutionTrace.displayName = 'ExecutionTrace';
|
||||
|
|
|
|||
|
|
@ -236,3 +236,5 @@ export const LinkedToolItem = memo(
|
|||
);
|
||||
}
|
||||
);
|
||||
|
||||
LinkedToolItem.displayName = 'LinkedToolItem';
|
||||
|
|
|
|||
|
|
@ -215,3 +215,5 @@ export const MetricsPill = memo(
|
|||
);
|
||||
}
|
||||
);
|
||||
|
||||
MetricsPill.displayName = 'MetricsPill';
|
||||
|
|
|
|||
|
|
@ -81,3 +81,5 @@ export const SlashItem = memo(
|
|||
);
|
||||
}
|
||||
);
|
||||
|
||||
SlashItem.displayName = 'SlashItem';
|
||||
|
|
|
|||
|
|
@ -598,3 +598,5 @@ export const SubagentItem: React.FC<SubagentItemProps> = React.memo(
|
|||
);
|
||||
}
|
||||
);
|
||||
|
||||
SubagentItem.displayName = 'SubagentItem';
|
||||
|
|
|
|||
|
|
@ -264,3 +264,5 @@ export const TeammateMessageItem = memo(
|
|||
);
|
||||
}
|
||||
);
|
||||
|
||||
TeammateMessageItem.displayName = 'TeammateMessageItem';
|
||||
|
|
|
|||
|
|
@ -82,3 +82,5 @@ export const TextItem: React.FC<TextItemProps> = React.memo(
|
|||
);
|
||||
}
|
||||
);
|
||||
|
||||
TextItem.displayName = 'TextItem';
|
||||
|
|
|
|||
|
|
@ -82,3 +82,5 @@ export const ThinkingItem: React.FC<ThinkingItemProps> = React.memo(
|
|||
);
|
||||
}
|
||||
);
|
||||
|
||||
ThinkingItem.displayName = 'ThinkingItem';
|
||||
|
|
|
|||
|
|
@ -59,3 +59,5 @@ export const CollapsibleOutputSection = memo(
|
|||
);
|
||||
}
|
||||
);
|
||||
|
||||
CollapsibleOutputSection.displayName = 'CollapsibleOutputSection';
|
||||
|
|
|
|||
|
|
@ -1112,3 +1112,5 @@ export const DateGroupedSessions = memo((): React.JSX.Element => {
|
|||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
DateGroupedSessions.displayName = 'DateGroupedSessions';
|
||||
|
|
|
|||
|
|
@ -117,3 +117,5 @@ export const MemberBadge = memo(
|
|||
);
|
||||
}
|
||||
);
|
||||
|
||||
MemberBadge.displayName = 'MemberBadge';
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -79,3 +79,5 @@ export const ReplyQuoteBlock = memo(
|
|||
);
|
||||
}
|
||||
);
|
||||
|
||||
ReplyQuoteBlock.displayName = 'ReplyQuoteBlock';
|
||||
|
|
|
|||
|
|
@ -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. */
|
||||
|
|
|
|||
|
|
@ -58,3 +58,5 @@ export const CurrentTaskIndicator = memo(
|
|||
);
|
||||
}
|
||||
);
|
||||
|
||||
CurrentTaskIndicator.displayName = 'CurrentTaskIndicator';
|
||||
|
|
|
|||
|
|
@ -27,3 +27,5 @@ export const MemberPresenceDot = memo(
|
|||
);
|
||||
}
|
||||
);
|
||||
|
||||
MemberPresenceDot.displayName = 'MemberPresenceDot';
|
||||
|
|
|
|||
|
|
@ -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']);
|
||||
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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: {
|
||||
|
|
|
|||
Loading…
Reference in a new issue