import { createHash } from 'node:crypto'; import * as fs from 'node:fs'; import * as path from 'node:path'; import { parentPort } from 'node:worker_threads'; import { readBootstrapLaunchSnapshot } from '@main/services/team/TeamBootstrapStateReader'; import { normalizePersistedLaunchSnapshot } from '@main/services/team/TeamLaunchStateEvaluator'; import { choosePreferredLaunchStateSummary, normalizePersistedLaunchSummaryProjection, shouldSuppressLegacyLaunchArtifactHeuristic, TEAM_LAUNCH_SUMMARY_FILE, } from '@main/services/team/TeamLaunchSummaryProjection'; import { isLeadMember } from '@shared/utils/leadDetection'; import { buildTeamMemberColorMap } from '@shared/utils/teamMemberColors'; interface ListTeamsPayload { teamsDir: string; largeConfigBytes: number; configHeadBytes: number; maxConfigBytes: number; maxConfigReadMs: number; maxMembersMetaBytes: number; maxSessionHistoryInSummary: number; maxProjectPathHistoryInSummary: number; concurrency: number; } interface GetAllTasksPayload { tasksBase: string; projectionCacheBase?: string; maxTaskBytes: number; maxTaskReadMs: number; concurrency: number; } type WorkerRequest = | { id: string; op: 'warmup'; payload?: Record } | { id: string; op: 'listTeams'; payload: ListTeamsPayload } | { id: string; op: 'getAllTasks'; payload: GetAllTasksPayload }; type WorkerResponse = | { id: string; ok: true; result: unknown; diag?: unknown } | { id: string; ok: false; error: string }; const UUID_TASK_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i; function deriveTaskDisplayId(taskId: string): string { const normalized = taskId.trim(); if (!normalized) return normalized; return UUID_TASK_ID_PATTERN.test(normalized) ? normalized.slice(0, 8).toLowerCase() : normalized; } /** * Normalise escaped newline sequences (`\\n`) that some MCP/CLI sources * write as literal two-character strings instead of real line-breaks. */ function unescapeLiteralNewlines(text: string): string { return text.replace(/\\n/g, '\n').replace(/\\t/g, '\t'); } // --------------------------------------------------------------------------- // Diagnostic types // --------------------------------------------------------------------------- interface SlowEntry { teamName: string; ms: number; } interface ListTeamsDiag { op: string; startedAt: number; teamsDir: string; totalDirs: number; returned: number; skipped: number; skipReasons: Record; slowest: SlowEntry[]; cacheHits: number; cacheMisses: number; cacheWriteSkips: number; cacheEvictions: number; totalMs: number; } interface GetAllTasksDiag { op: string; startedAt: number; tasksBase: string; teamDirs: number; returned: number; skipped: number; skipReasons: Record; slowestTeams: SlowEntry[]; cacheHits: number; cacheMisses: number; cacheWriteSkips: number; cacheEvictions: number; persistentCacheHits: number; persistentCacheMisses: number; persistentCacheLoads: number; persistentCacheWrites: number; persistentCacheReadFailures: number; persistentCacheWriteFailures: number; totalMs: number; } interface TaskReadDiag { skipped: number; skipReasons: Record; cacheHits: number; cacheMisses: number; cacheWriteSkips: number; persistentCacheHits: number; persistentCacheMisses: number; persistentCacheLoads: number; persistentCacheWrites: number; persistentCacheReadFailures: number; persistentCacheWriteFailures: number; } const MAX_LAUNCH_STATE_BYTES = 32 * 1024; const TEAM_LAUNCH_STATE_FILE = 'launch-state.json'; const REVIEW_LIFECYCLE_EVENTS = new Set([ 'review_requested', 'review_changes_requested', 'review_approved', 'review_started', ]); const REVIEW_RESET_STATUSES = new Set(['in_progress', 'deleted']); const TEAM_SUMMARY_CACHE_MAX_ENTRIES = 1000; const TASK_FILE_CACHE_MAX_ENTRIES = 10000; const PERSISTENT_TASK_PROJECTION_CACHE_VERSION = 1; const PERSISTENT_TASK_PROJECTION_CACHE_DIR = 'v1'; const MAX_PERSISTENT_TASK_PROJECTION_CACHE_BYTES = 16 * 1024 * 1024; const CACHEABLE_TASK_SKIP_REASONS = new Set(['task_internal', 'task_deleted']); const BOOTSTRAP_STATE_FILE = 'bootstrap-state.json'; const BOOTSTRAP_JOURNAL_FILE = 'bootstrap-journal.jsonl'; interface PathFingerprint { exists: boolean; isFile?: boolean; isDirectory?: boolean; highResolution?: boolean; size?: string; mode?: string; dev?: string; ino?: string; mtimeNs?: string; ctimeNs?: string; birthtimeNs?: string; mtimeMs?: number; ctimeMs?: number; birthtimeMs?: number; errorCode?: string; } interface TeamSummaryCacheEntry { fingerprint: string; summary: Record; teamsDir: string; optionKey: string; lastUsedAt: number; } type CachedTaskReadResult = | { task: Record; skipReason?: never } | { task?: never; skipReason: string }; interface TaskFileCacheEntry { fingerprint: string; result: CachedTaskReadResult; tasksBase: string; lastUsedAt: number; } interface PersistentTaskProjectionCacheEntry { fingerprint: string; result: CachedTaskReadResult; } const teamSummaryCache = new Map(); const taskFileCache = new Map(); interface TeamSummaryDependencyFingerprint { value: string; cacheSafe: boolean; } interface LaunchStateSummaryRead { summary: ReturnType | null; cacheable: boolean; } // --------------------------------------------------------------------------- // Parsed JSON types (loose shapes from disk) // --------------------------------------------------------------------------- interface ParsedConfig { name?: unknown; description?: unknown; color?: unknown; projectPath?: unknown; leadSessionId?: unknown; deletedAt?: unknown; projectPathHistory?: unknown; sessionHistory?: unknown; members?: unknown; } interface RawMember { name?: unknown; agentType?: unknown; role?: unknown; cwd?: unknown; color?: unknown; providerId?: unknown; provider?: unknown; removedAt?: unknown; } interface ParsedTask { id?: unknown; displayId?: unknown; subject?: unknown; title?: unknown; description?: unknown; descriptionTaskRefs?: unknown; activeForm?: unknown; prompt?: unknown; promptTaskRefs?: unknown; owner?: unknown; createdBy?: unknown; status?: unknown; blocks?: unknown; blockedBy?: unknown; related?: unknown; createdAt?: unknown; projectPath?: unknown; comments?: unknown; needsClarification?: unknown; reviewState?: unknown; metadata?: { _internal?: unknown }; workIntervals?: unknown; reviewIntervals?: unknown; historyEvents?: unknown; attachments?: unknown; sourceMessageId?: unknown; sourceMessage?: unknown; } interface RawWorkInterval { startedAt?: unknown; completedAt?: unknown; } interface RawReviewInterval { reviewer?: unknown; startedAt?: unknown; completedAt?: unknown; } interface RawHistoryEvent { id?: unknown; type?: unknown; timestamp?: unknown; actor?: unknown; [key: string]: unknown; } interface RawComment { id?: unknown; author?: unknown; text?: unknown; createdAt?: unknown; type?: unknown; taskRefs?: unknown; } // --------------------------------------------------------------------------- // Helpers // --------------------------------------------------------------------------- function isAbortError(error: unknown): boolean { return ( !!error && typeof error === 'object' && 'name' in error && (error as { name?: unknown }).name === 'AbortError' ); } async function readFileUtf8WithTimeout(filePath: string, timeoutMs: number): Promise { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), timeoutMs); try { return await fs.promises.readFile(filePath, { encoding: 'utf8', signal: controller.signal }); } catch (error) { if (isAbortError(error)) { const err = new Error('READ_TIMEOUT'); (err as NodeJS.ErrnoException).code = 'READ_TIMEOUT'; throw err; } throw error; } finally { clearTimeout(timeoutId); } } async function readFileHeadUtf8(filePath: string, maxBytes: number): Promise { const handle = await fs.promises.open(filePath, 'r'); try { const stat = await handle.stat(); const bytesToRead = Math.max(0, Math.min(stat.size, maxBytes)); if (bytesToRead === 0) return ''; const buffer = Buffer.alloc(bytesToRead); await handle.read(buffer, 0, bytesToRead, 0); return buffer.toString('utf8'); } finally { await handle.close(); } } function extractQuotedString(head: string, key: string): string | null { const re = new RegExp(`"${key}"\\s*:\\s*("(?:\\\\.|[^"\\\\])*")`); const match = re.exec(head); if (!match?.[1]) return null; try { const value = JSON.parse(match[1]) as unknown; return typeof value === 'string' ? value : null; } catch { return null; } } async function mapLimit( items: readonly T[], limit: number, fn: (item: T) => Promise ): Promise { const results = new Array(items.length); let index = 0; const workerCount = Math.max(1, Math.min(limit, items.length)); const workers = new Array(workerCount).fill(0).map(async () => { while (true) { const i = index++; if (i >= items.length) return; results[i] = await fn(items[i]); } }); await Promise.all(workers); return results; } function nowMs(): number { return Date.now(); } function bumpSkipReason(reasons: Record, reason: string): void { reasons[reason] = (reasons[reason] || 0) + 1; } function pushSlowest(list: SlowEntry[], entry: SlowEntry, maxLen: number): void { list.push(entry); list.sort((a, b) => b.ms - a.ms); if (list.length > maxLen) list.length = maxLen; } function cloneCached(value: T): T { return typeof structuredClone === 'function' ? structuredClone(value) : (JSON.parse(JSON.stringify(value)) as T); } function dateFromFingerprintMs(ms: unknown): Date | null { if (typeof ms !== 'number' || !Number.isFinite(ms) || ms <= 0) { return null; } const date = new Date(ms); return Number.isFinite(date.getTime()) ? date : null; } async function statPathFingerprint(filePath: string): Promise { try { const stat = await fs.promises.stat(filePath, { bigint: true }); const mtimeNs = typeof (stat as fs.BigIntStats & { mtimeNs?: bigint }).mtimeNs === 'bigint' ? (stat as fs.BigIntStats & { mtimeNs: bigint }).mtimeNs : undefined; const ctimeNs = typeof (stat as fs.BigIntStats & { ctimeNs?: bigint }).ctimeNs === 'bigint' ? (stat as fs.BigIntStats & { ctimeNs: bigint }).ctimeNs : undefined; const birthtimeNs = typeof (stat as fs.BigIntStats & { birthtimeNs?: bigint }).birthtimeNs === 'bigint' ? (stat as fs.BigIntStats & { birthtimeNs: bigint }).birthtimeNs : undefined; return { exists: true, isFile: stat.isFile(), isDirectory: stat.isDirectory(), highResolution: typeof mtimeNs === 'bigint' && typeof ctimeNs === 'bigint', size: stat.size.toString(), mode: stat.mode.toString(), dev: stat.dev.toString(), ino: stat.ino.toString(), mtimeNs: mtimeNs?.toString(), ctimeNs: ctimeNs?.toString(), birthtimeNs: birthtimeNs?.toString(), mtimeMs: Number(stat.mtimeMs), ctimeMs: Number(stat.ctimeMs), birthtimeMs: Number(stat.birthtimeMs), }; } catch (error) { return { exists: false, errorCode: typeof (error as NodeJS.ErrnoException | undefined)?.code === 'string' ? (error as NodeJS.ErrnoException).code : undefined, }; } } function fingerprintToString(value: unknown): string { return JSON.stringify(value); } function isCacheSafeFingerprint(fingerprint: PathFingerprint): boolean { if (fingerprint.exists) { return fingerprint.highResolution === true; } return fingerprint.errorCode === 'ENOENT' || fingerprint.errorCode === 'ENOTDIR'; } function makeTeamSummaryOptionKey(payload: ListTeamsPayload): string { return fingerprintToString({ largeConfigBytes: payload.largeConfigBytes, configHeadBytes: payload.configHeadBytes, maxConfigBytes: payload.maxConfigBytes, maxConfigReadMs: payload.maxConfigReadMs, maxMembersMetaBytes: payload.maxMembersMetaBytes, maxSessionHistoryInSummary: payload.maxSessionHistoryInSummary, maxProjectPathHistoryInSummary: payload.maxProjectPathHistoryInSummary, }); } function makeTeamSummaryCacheKey(teamsDir: string, teamName: string, optionKey: string): string { return `${teamsDir}\0${teamName}\0${optionKey}`; } function canCacheTeamSummary(summary: Record): boolean { if (summary.teamLaunchState === 'partial_pending') { return false; } const pendingKeys = [ 'pendingCount', 'runtimeAlivePendingCount', 'shellOnlyPendingCount', 'runtimeProcessPendingCount', 'runtimeCandidatePendingCount', 'noRuntimePendingCount', 'permissionPendingCount', ]; return pendingKeys.every((key) => { const value = summary[key]; return typeof value !== 'number' || value <= 0; }); } async function readInboxNamesFingerprint(inboxDir: string): Promise<{ dir: PathFingerprint; names: string[]; cacheSafe: boolean; }> { const dir = await statPathFingerprint(inboxDir); if (!dir.exists || !dir.isDirectory) { return { dir, names: [], cacheSafe: isCacheSafeFingerprint(dir) }; } try { const entries = await fs.promises.readdir(inboxDir, { withFileTypes: true }); const names = entries .filter((entry) => entry.isFile() && entry.name.endsWith('.json')) .map((entry) => entry.name) .sort(); return { dir, names, cacheSafe: isCacheSafeFingerprint(dir) }; } catch (error) { return { dir: { ...dir, errorCode: typeof (error as NodeJS.ErrnoException | undefined)?.code === 'string' ? (error as NodeJS.ErrnoException).code : 'READDIR_FAILED', }, names: [], cacheSafe: false, }; } } async function buildTeamSummaryFingerprint( teamsDir: string, teamName: string, optionKey: string ): Promise { const teamDir = path.join(teamsDir, teamName); const [ config, teamMeta, membersMeta, launchState, launchSummary, bootstrapState, bootstrapJournal, ] = await Promise.all([ statPathFingerprint(path.join(teamDir, 'config.json')), statPathFingerprint(path.join(teamDir, 'team.meta.json')), statPathFingerprint(path.join(teamDir, 'members.meta.json')), statPathFingerprint(path.join(teamDir, TEAM_LAUNCH_STATE_FILE)), statPathFingerprint(path.join(teamDir, TEAM_LAUNCH_SUMMARY_FILE)), statPathFingerprint(path.join(teamDir, BOOTSTRAP_STATE_FILE)), statPathFingerprint(path.join(teamDir, BOOTSTRAP_JOURNAL_FILE)), ]); const inbox = await readInboxNamesFingerprint(path.join(teamDir, 'inboxes')); const dependencyFingerprint = { version: 1, optionKey, config, teamMeta, membersMeta, launchState, launchSummary, bootstrapState, bootstrapJournal, inbox, }; return { value: fingerprintToString(dependencyFingerprint), cacheSafe: [ config, teamMeta, membersMeta, launchState, launchSummary, bootstrapState, bootstrapJournal, ].every(isCacheSafeFingerprint) && inbox.cacheSafe, }; } async function cacheTeamSummaryIfStable( cacheKey: string, teamsDir: string, teamName: string, optionKey: string, fingerprintBefore: TeamSummaryDependencyFingerprint, summary: Record, cacheAllowed: boolean, diag: ListTeamsDiag ): Promise { if (!cacheAllowed) { teamSummaryCache.delete(cacheKey); diag.cacheWriteSkips++; return; } if (!canCacheTeamSummary(summary)) { teamSummaryCache.delete(cacheKey); diag.cacheWriteSkips++; return; } if (!fingerprintBefore.cacheSafe) { diag.cacheWriteSkips++; return; } const fingerprintAfter = await buildTeamSummaryFingerprint(teamsDir, teamName, optionKey); if (!fingerprintAfter.cacheSafe || fingerprintAfter.value !== fingerprintBefore.value) { diag.cacheWriteSkips++; return; } teamSummaryCache.set(cacheKey, { fingerprint: fingerprintAfter.value, summary: cloneCached(summary), teamsDir, optionKey, lastUsedAt: nowMs(), }); } function pruneTeamSummaryCache( teamsDir: string, optionKey: string, liveTeamNames: ReadonlySet, diag: ListTeamsDiag ): void { for (const [key, entry] of teamSummaryCache) { if (entry.teamsDir === teamsDir && entry.optionKey === optionKey) { const teamName = key.split('\0')[1] ?? ''; if (!liveTeamNames.has(teamName)) { teamSummaryCache.delete(key); diag.cacheEvictions++; } } } while (teamSummaryCache.size > TEAM_SUMMARY_CACHE_MAX_ENTRIES) { const oldest = teamSummaryCache.keys().next().value; if (typeof oldest !== 'string') break; teamSummaryCache.delete(oldest); diag.cacheEvictions++; } } function makeTaskOptionKey(payload: GetAllTasksPayload): string { return fingerprintToString({ maxTaskBytes: payload.maxTaskBytes, maxTaskReadMs: payload.maxTaskReadMs, }); } function makeTaskCacheKey( tasksBase: string, teamName: string, fileName: string, optionKey: string ): string { return `${tasksBase}\0${teamName}\0${fileName}\0${optionKey}`; } async function cacheTaskReadResultIfStable( cacheKey: string, taskPath: string, tasksBase: string, fingerprintBefore: string, fingerprintBeforeCacheSafe: boolean, result: CachedTaskReadResult, taskDiag: TaskReadDiag ): Promise { if (!fingerprintBeforeCacheSafe) { taskDiag.cacheWriteSkips++; return false; } const after = await statPathFingerprint(taskPath); if (!isCacheSafeFingerprint(after) || fingerprintToString(after) !== fingerprintBefore) { taskDiag.cacheWriteSkips++; return false; } taskFileCache.set(cacheKey, { fingerprint: fingerprintBefore, result: cloneCached(result), tasksBase, lastUsedAt: nowMs(), }); return true; } function applyCachedTaskReadResult( cached: CachedTaskReadResult, tasks: unknown[], taskDiag: TaskReadDiag ): void { if (cached.skipReason) { taskDiag.skipped++; bumpSkipReason(taskDiag.skipReasons, cached.skipReason); return; } tasks.push(cached.task); } function pruneTaskFileCache( tasksBase: string, liveCacheKeys: ReadonlySet, diag: GetAllTasksDiag ): void { for (const [key, entry] of taskFileCache) { if (entry.tasksBase === tasksBase && !liveCacheKeys.has(key)) { taskFileCache.delete(key); diag.cacheEvictions++; } } while (taskFileCache.size > TASK_FILE_CACHE_MAX_ENTRIES) { const oldest = taskFileCache.keys().next().value; if (typeof oldest !== 'string') break; taskFileCache.delete(oldest); diag.cacheEvictions++; } } function getPersistentTaskProjectionCachePath( payload: GetAllTasksPayload, teamName: string ): string | null { const base = typeof payload.projectionCacheBase === 'string' ? payload.projectionCacheBase : ''; if (!base.trim()) return null; const digest = createHash('sha256') .update(payload.tasksBase) .update('\0') .update(teamName) .digest('hex'); return path.join(base, PERSISTENT_TASK_PROJECTION_CACHE_DIR, `${digest}.json`); } function isRecord(value: unknown): value is Record { return Boolean(value) && typeof value === 'object' && !Array.isArray(value); } function isSafeTaskProjectionFileName(file: string): boolean { return ( file.endsWith('.json') && !file.startsWith('.') && !file.includes('/') && !file.includes('\\') && file !== '.lock' && file !== '.highwatermark' ); } function normalizePersistentTaskReadResult( value: unknown, teamName: string ): CachedTaskReadResult | null { if (!isRecord(value)) return null; const skipReason = value.skipReason; if (typeof skipReason === 'string') { return CACHEABLE_TASK_SKIP_REASONS.has(skipReason) ? { skipReason } : null; } const task = value.task; if (!isRecord(task)) return null; if (task.teamName !== teamName) return null; if (typeof task.id !== 'string') return null; if (typeof task.subject !== 'string') return null; const status = task.status === 'pending' || task.status === 'in_progress' || task.status === 'completed' || task.status === 'deleted' ? task.status : null; if (!status) return null; if (status === 'deleted') return null; return { task: restorePersistentTaskProjectionShape(task, teamName, status) }; } function restorePersistentTaskProjectionShape( task: Record, teamName: string, status: string ): Record { const id = typeof task.id === 'string' ? task.id : ''; const subject = typeof task.subject === 'string' ? task.subject : ''; const displayId = task.displayId; const reviewState = normalizeFallbackReviewState(task.reviewState, status); return { id, displayId: typeof displayId === 'string' && displayId.trim().length > 0 ? displayId.trim() : deriveTaskDisplayId(id), subject, description: typeof task.description === 'string' ? task.description : undefined, descriptionTaskRefs: Array.isArray(task.descriptionTaskRefs) ? task.descriptionTaskRefs : undefined, activeForm: typeof task.activeForm === 'string' ? task.activeForm : undefined, prompt: typeof task.prompt === 'string' ? task.prompt : undefined, promptTaskRefs: Array.isArray(task.promptTaskRefs) ? task.promptTaskRefs : undefined, owner: typeof task.owner === 'string' ? task.owner : undefined, createdBy: typeof task.createdBy === 'string' ? task.createdBy : undefined, status, workIntervals: normalizeWorkIntervals(task), reviewIntervals: normalizeReviewIntervals(task), historyEvents: normalizeHistoryEvents(task), blocks: Array.isArray(task.blocks) ? task.blocks : undefined, blockedBy: Array.isArray(task.blockedBy) ? task.blockedBy : undefined, related: Array.isArray(task.related) ? (task.related as unknown[]).filter((id): id is string => typeof id === 'string') : undefined, createdAt: typeof task.createdAt === 'string' ? task.createdAt : undefined, updatedAt: typeof task.updatedAt === 'string' ? task.updatedAt : undefined, projectPath: typeof task.projectPath === 'string' ? task.projectPath : undefined, comments: normalizeComments(task), needsClarification: task.needsClarification === 'lead' || task.needsClarification === 'user' ? task.needsClarification : undefined, reviewState, deletedAt: undefined, attachments: Array.isArray(task.attachments) ? task.attachments : undefined, sourceMessageId: typeof task.sourceMessageId === 'string' && task.sourceMessageId.trim() ? task.sourceMessageId.trim() : undefined, sourceMessage: isRecord(task.sourceMessage) && typeof task.sourceMessage.text === 'string' && typeof task.sourceMessage.from === 'string' && typeof task.sourceMessage.timestamp === 'string' ? task.sourceMessage : undefined, teamName, }; } function normalizePersistentTaskProjectionEntry( value: unknown, teamName: string ): PersistentTaskProjectionCacheEntry | null { if (!isRecord(value) || typeof value.fingerprint !== 'string') return null; const result = normalizePersistentTaskReadResult(value.result, teamName); return result ? { fingerprint: value.fingerprint, result } : null; } async function readPersistentTaskProjectionCache( payload: GetAllTasksPayload, teamName: string, optionKey: string, taskDiag: TaskReadDiag ): Promise | null> { const cachePath = getPersistentTaskProjectionCachePath(payload, teamName); if (!cachePath) return null; try { const stat = await fs.promises.stat(cachePath); if (!stat.isFile() || stat.size > MAX_PERSISTENT_TASK_PROJECTION_CACHE_BYTES) { taskDiag.persistentCacheReadFailures++; return null; } const raw = await fs.promises.readFile(cachePath, 'utf8'); const parsed = JSON.parse(raw) as unknown; if (!isRecord(parsed)) { taskDiag.persistentCacheReadFailures++; return null; } if ( parsed.version !== PERSISTENT_TASK_PROJECTION_CACHE_VERSION || parsed.tasksBase !== payload.tasksBase || parsed.teamName !== teamName || parsed.optionKey !== optionKey || !isRecord(parsed.entries) ) { taskDiag.persistentCacheMisses++; return null; } const entries = new Map(); for (const [file, entry] of Object.entries(parsed.entries)) { if (!isSafeTaskProjectionFileName(file)) continue; const normalized = normalizePersistentTaskProjectionEntry(entry, teamName); if (normalized) { entries.set(file, normalized); } } taskDiag.persistentCacheLoads++; return entries; } catch (error) { if ((error as NodeJS.ErrnoException | undefined)?.code === 'ENOENT') { return null; } taskDiag.persistentCacheReadFailures++; return null; } } function shouldWritePersistentTaskProjectionCache( previous: ReadonlyMap | null, next: ReadonlyMap, taskDiag: TaskReadDiag ): boolean { if (next.size === 0) return false; if (!previous) return true; if (previous.size !== next.size) return true; return taskDiag.persistentCacheMisses > 0 || taskDiag.cacheMisses > 0; } async function writePersistentTaskProjectionCache( payload: GetAllTasksPayload, teamName: string, optionKey: string, entries: ReadonlyMap, taskDiag: TaskReadDiag ): Promise { const cachePath = getPersistentTaskProjectionCachePath(payload, teamName); if (!cachePath || entries.size === 0) return; const body = { version: PERSISTENT_TASK_PROJECTION_CACHE_VERSION, tasksBase: payload.tasksBase, teamName, optionKey, writtenAt: nowMs(), entries: Object.fromEntries(entries), }; const tmpPath = `${cachePath}.${process.pid}.${Date.now()}.${Math.random() .toString(36) .slice(2)}.tmp`; try { await fs.promises.mkdir(path.dirname(cachePath), { recursive: true }); await fs.promises.writeFile(tmpPath, JSON.stringify(body), 'utf8'); await fs.promises.rename(tmpPath, cachePath); taskDiag.persistentCacheWrites++; } catch { taskDiag.persistentCacheWriteFailures++; await fs.promises.rm(tmpPath, { force: true }).catch(() => undefined); } } // --------------------------------------------------------------------------- // listTeams // --------------------------------------------------------------------------- function isRawMember(v: unknown): v is RawMember { return !!v && typeof v === 'object'; } function normalizeProjectPathCandidate(value: unknown): string | undefined { if (typeof value !== 'string') { return undefined; } const trimmed = value.trim(); return trimmed.length > 0 ? trimmed : undefined; } function getRawConfigMembers(config: Pick): RawMember[] { if (!Array.isArray(config.members)) { return []; } return config.members.filter(isRawMember); } function resolveProjectPathFromConfig( config: Pick ): string | undefined { const direct = normalizeProjectPathCandidate(config.projectPath); if (direct) { return direct; } const members = getRawConfigMembers(config); const leadMemberCwd = members.find((member) => isLeadMember(member))?.cwd; const leadResolved = normalizeProjectPathCandidate(leadMemberCwd); if (leadResolved) { return leadResolved; } const distinctMemberCwds = Array.from( new Set( members .map((member) => normalizeProjectPathCandidate(member.cwd)) .filter((cwd): cwd is string => Boolean(cwd)) ) ); if (distinctMemberCwds.length === 1) { return distinctMemberCwds[0]; } if (Array.isArray(config.projectPathHistory)) { for (let i = config.projectPathHistory.length - 1; i >= 0; i -= 1) { const historyValue = normalizeProjectPathCandidate(config.projectPathHistory[i]); if (historyValue) { return historyValue; } } } return undefined; } function mergeMember( m: RawMember, memberMap: Map, removedKeys: ReadonlySet ): void { const name = typeof m.name === 'string' ? m.name.trim() : ''; if (!name) return; if (name === 'user' || isLeadMember(m)) return; const key = name.toLowerCase(); if (removedKeys.has(key)) return; const existing = memberMap.get(key); memberMap.set(key, { name: existing?.name ?? name, role: (typeof m.role === 'string' && m.role.trim()) || existing?.role, color: (typeof m.color === 'string' && m.color.trim()) || existing?.color, }); } function dropCliAutoSuffixedMembers( memberMap: Map ): void { const keys = Array.from(memberMap.keys()); const allLower = new Set(keys); // keys are already lowercased for (const key of keys) { const member = memberMap.get(key); const name = member?.name ?? ''; const match = /^(.+)-(\d+)$/.exec(name.trim()); if (!match?.[1] || !match[2]) continue; const suffix = Number(match[2]); if (!Number.isFinite(suffix) || suffix < 2) continue; const baseLower = match[1].toLowerCase(); if (allLower.has(baseLower)) { memberMap.delete(key); } } } const PROVISIONER_SUFFIX = '-provisioner'; /** * Drop CLI provisioner artifacts ("{name}-provisioner") unconditionally. * These are temporary internal agents created during team provisioning * and should never be shown to the user. */ function dropCliProvisionerMembers( memberMap: Map ): void { for (const [key, member] of Array.from(memberMap.entries())) { const lower = member.name.trim().toLowerCase(); if (!lower.endsWith(PROVISIONER_SUFFIX)) continue; const base = lower.slice(0, -PROVISIONER_SUFFIX.length); if (base) { memberMap.delete(key); } } } async function readLaunchState( teamsDir: string, teamName: string ): Promise { const bootstrapSnapshot = await readBootstrapLaunchSnapshot(teamName); const launchStatePath = path.join(teamsDir, teamName, TEAM_LAUNCH_STATE_FILE); const launchSummaryPath = path.join(teamsDir, teamName, TEAM_LAUNCH_SUMMARY_FILE); const [launchSnapshot, launchSummaryProjection] = await Promise.all([ (async () => { try { const stat = await fs.promises.stat(launchStatePath); if (!stat.isFile() || stat.size > MAX_LAUNCH_STATE_BYTES) { return null; } const raw = await fs.promises.readFile(launchStatePath, 'utf8'); return normalizePersistedLaunchSnapshot(teamName, JSON.parse(raw)); } catch { return null; } })(), (async () => { try { const stat = await fs.promises.stat(launchSummaryPath); if (!stat.isFile() || stat.size > MAX_LAUNCH_STATE_BYTES) { return null; } const raw = await fs.promises.readFile(launchSummaryPath, 'utf8'); return normalizePersistedLaunchSummaryProjection(teamName, JSON.parse(raw)); } catch { return null; } })(), ]); const summary = choosePreferredLaunchStateSummary({ bootstrapSnapshot, launchSnapshot, launchSummaryProjection, }); if (launchSnapshot) { return { summary, cacheable: true }; } if (!bootstrapSnapshot) { return { summary, cacheable: true }; } if ( bootstrapSnapshot.launchPhase === 'finished' && bootstrapSnapshot.teamLaunchState !== 'partial_pending' ) { return { summary, cacheable: true }; } return { summary, cacheable: false }; } /** * Reads a draft team summary from team.meta.json when config.json is missing. * Returns null if team.meta.json doesn't exist or is invalid. */ async function readDraftTeamMeta( teamsDir: string, teamName: string, options: { maxConfigReadMs: number; maxMembersMetaBytes: number } ): Promise | null> { const metaPath = path.join(teamsDir, teamName, 'team.meta.json'); try { const stat = await fs.promises.stat(metaPath); if (!stat.isFile() || stat.size > 256 * 1024) return null; const raw = await readFileUtf8WithTimeout(metaPath, options.maxConfigReadMs); const meta = JSON.parse(raw) as Record; if (meta?.version !== 1 || typeof meta?.cwd !== 'string') return null; const displayName = typeof meta.displayName === 'string' && meta.displayName.trim() ? meta.displayName.trim() : teamName; // Read members.meta.json for member count let memberCount = 0; let leadName: string | undefined; let leadColor: string | undefined; try { const membersPath = path.join(teamsDir, teamName, 'members.meta.json'); const membersStat = await fs.promises.stat(membersPath); if (!membersStat.isFile() || membersStat.size > options.maxMembersMetaBytes) { throw new Error('members_meta_too_large'); } const membersRaw = await readFileUtf8WithTimeout(membersPath, options.maxConfigReadMs); const membersData = JSON.parse(membersRaw) as { members?: unknown[] }; if (Array.isArray(membersData?.members)) { memberCount = membersData.members.filter((member) => { if (!isRawMember(member)) return false; const name = typeof member.name === 'string' ? member.name.trim() : ''; if (!member.removedAt && isLeadMember(member)) { if (name) { leadName = name; } const color = typeof member.color === 'string' ? member.color.trim() : ''; if (color) { leadColor = color; } } if (!name || name === 'user' || isLeadMember(member)) return false; return !member.removedAt; }).length; } } catch { // best-effort } return { teamName, displayName, description: typeof meta.description === 'string' ? meta.description : '', memberCount, taskCount: 0, lastActivity: typeof meta.createdAt === 'number' ? new Date(meta.createdAt).toISOString() : null, color: typeof meta.color === 'string' ? meta.color : undefined, ...(leadName ? { leadName } : {}), ...(leadColor ? { leadColor } : {}), projectPath: typeof meta.cwd === 'string' ? meta.cwd : undefined, pendingCreate: true, }; } catch { return null; } } async function listTeams( payload: ListTeamsPayload ): Promise<{ teams: unknown[]; diag: ListTeamsDiag }> { const startedAt = nowMs(); const diag: ListTeamsDiag = { op: 'listTeams', startedAt, teamsDir: payload.teamsDir, totalDirs: 0, returned: 0, skipped: 0, skipReasons: {}, slowest: [], cacheHits: 0, cacheMisses: 0, cacheWriteSkips: 0, cacheEvictions: 0, totalMs: 0, }; let entries: fs.Dirent[]; try { entries = await fs.promises.readdir(payload.teamsDir, { withFileTypes: true }); } catch { diag.totalMs = nowMs() - startedAt; return { teams: [], diag }; } const teamDirs = entries.filter((e) => e.isDirectory()); diag.totalDirs = teamDirs.length; const optionKey = makeTeamSummaryOptionKey(payload); const liveTeamNames = new Set(teamDirs.map((entry) => entry.name)); const perTeam = await mapLimit(teamDirs, payload.concurrency, async (entry) => { const teamName = entry.name; const t0 = nowMs(); const configPath = path.join(payload.teamsDir, teamName, 'config.json'); const cacheKey = makeTeamSummaryCacheKey(payload.teamsDir, teamName, optionKey); const dependencyFingerprint = await buildTeamSummaryFingerprint( payload.teamsDir, teamName, optionKey ); const cached = teamSummaryCache.get(cacheKey); if (dependencyFingerprint.cacheSafe && cached?.fingerprint === dependencyFingerprint.value) { cached.lastUsedAt = nowMs(); diag.cacheHits++; return cached.summary; } diag.cacheMisses++; const skip = (reason: string): null => { diag.skipped++; bumpSkipReason(diag.skipReasons, reason); return null; }; let stat: fs.Stats; try { stat = await fs.promises.stat(configPath); } catch { // Fallback: check for draft team (team.meta.json without config.json) const draft = await readDraftTeamMeta(payload.teamsDir, teamName, payload); if (draft) { await cacheTeamSummaryIfStable( cacheKey, payload.teamsDir, teamName, optionKey, dependencyFingerprint, draft, true, diag ); return draft; } return skip('config_stat_failed'); } if (!stat.isFile()) { const draft = await readDraftTeamMeta(payload.teamsDir, teamName, payload); if (draft) { await cacheTeamSummaryIfStable( cacheKey, payload.teamsDir, teamName, optionKey, dependencyFingerprint, draft, true, diag ); return draft; } return skip('config_not_file'); } if (stat.size > payload.maxConfigBytes) return skip('config_too_large'); let config: ParsedConfig | null = null; let displayName: string | null = null; let description = ''; let color: string | undefined; let projectPath: string | undefined; let leadSessionId: string | undefined; let deletedAt: string | undefined; let projectPathHistory: string[] | undefined; let sessionHistory: string[] | undefined; try { if (stat.size > payload.largeConfigBytes) { const head = await readFileHeadUtf8(configPath, payload.configHeadBytes); displayName = extractQuotedString(head, 'name'); const desc = extractQuotedString(head, 'description'); description = typeof desc === 'string' ? desc : ''; const c = extractQuotedString(head, 'color'); color = typeof c === 'string' && c.trim().length > 0 ? c : undefined; const pp = extractQuotedString(head, 'projectPath'); projectPath = typeof pp === 'string' && pp.trim().length > 0 ? pp : undefined; const lead = extractQuotedString(head, 'leadSessionId'); leadSessionId = typeof lead === 'string' && lead.trim().length > 0 ? lead : undefined; const del = extractQuotedString(head, 'deletedAt'); deletedAt = typeof del === 'string' ? del : undefined; } else { const raw = await readFileUtf8WithTimeout(configPath, payload.maxConfigReadMs); config = JSON.parse(raw) as ParsedConfig; displayName = typeof config.name === 'string' ? config.name : null; description = typeof config.description === 'string' ? config.description : ''; color = typeof config.color === 'string' && config.color.trim().length > 0 ? config.color : undefined; projectPath = resolveProjectPathFromConfig(config); leadSessionId = typeof config.leadSessionId === 'string' && config.leadSessionId.trim().length > 0 ? config.leadSessionId : undefined; projectPathHistory = Array.isArray(config.projectPathHistory) ? (config.projectPathHistory as string[]).slice(-payload.maxProjectPathHistoryInSummary) : undefined; sessionHistory = Array.isArray(config.sessionHistory) ? (config.sessionHistory as string[]).slice(-payload.maxSessionHistoryInSummary) : undefined; deletedAt = typeof config.deletedAt === 'string' ? config.deletedAt : undefined; } } catch (error) { const code = (error as NodeJS.ErrnoException).code; if (code === 'READ_TIMEOUT') return skip('config_read_timeout'); return skip('config_parse_failed'); } if (typeof displayName !== 'string' || displayName.trim() === '') { return skip('invalid_display_name'); } const memberMap = new Map(); const removedKeys = new Set(); const expectedTeammateNames = new Set(); const confirmedArtifactNames = new Set(); const metaRuntimeMembers: { name: string; providerId?: 'anthropic' | 'codex' | 'gemini' | 'opencode'; removedAt?: unknown; }[] = []; let leadProviderId: 'anthropic' | 'codex' | 'gemini' | 'opencode' | undefined; let leadName: string | undefined; let leadColor: string | undefined; const captureLeadMember = (member: RawMember, overwrite = false): void => { if (member.removedAt) return; if (!isLeadMember(member)) return; const name = typeof member.name === 'string' ? member.name.trim() : ''; if (name && (overwrite || !leadName)) { leadName = name; } const colorValue = typeof member.color === 'string' ? member.color.trim() : ''; if (colorValue && (overwrite || !leadColor)) { leadColor = colorValue; } }; try { const teamMetaPath = path.join(payload.teamsDir, teamName, 'team.meta.json'); const teamMetaStat = await fs.promises.stat(teamMetaPath); if (teamMetaStat.isFile() && teamMetaStat.size <= 256 * 1024) { const raw = await readFileUtf8WithTimeout(teamMetaPath, payload.maxConfigReadMs); const parsed = JSON.parse(raw) as { providerId?: unknown }; leadProviderId = parsed?.providerId === 'anthropic' || parsed?.providerId === 'codex' || parsed?.providerId === 'gemini' || parsed?.providerId === 'opencode' ? parsed.providerId : undefined; } } catch { leadProviderId = undefined; } try { const metaPath = path.join(payload.teamsDir, teamName, 'members.meta.json'); const metaStat = await fs.promises.stat(metaPath); if (metaStat.isFile() && metaStat.size <= payload.maxMembersMetaBytes) { const raw = await readFileUtf8WithTimeout(metaPath, payload.maxConfigReadMs); const parsed = JSON.parse(raw) as { members?: unknown }; const members: unknown[] = Array.isArray(parsed?.members) ? parsed.members : []; for (const member of members) { if (!isRawMember(member)) continue; const rawProviderId = member.providerId ?? member.provider; const providerId = rawProviderId === 'anthropic' || rawProviderId === 'codex' || rawProviderId === 'gemini' || rawProviderId === 'opencode' ? rawProviderId : undefined; const name = typeof member.name === 'string' ? member.name.trim() : ''; if (!name) continue; captureLeadMember(member); if (isLeadMember(member)) continue; const key = name.toLowerCase(); if (member.removedAt) { removedKeys.add(key); metaRuntimeMembers.push({ name, providerId, removedAt: member.removedAt, }); continue; } expectedTeammateNames.add(name); metaRuntimeMembers.push({ name, providerId, }); mergeMember(member, memberMap, removedKeys); } } } catch { // ignore } // Merge config members AFTER meta so removedAt can suppress stale config entries. if (config && Array.isArray(config.members)) { for (const member of config.members as unknown[]) { if (isRawMember(member)) { const name = typeof member.name === 'string' ? member.name.trim() : ''; captureLeadMember(member, true); if (name && name !== 'user' && !isLeadMember(member)) { confirmedArtifactNames.add(name); } mergeMember(member, memberMap, removedKeys); } } } try { const inboxDir = path.join(payload.teamsDir, teamName, 'inboxes'); const inboxEntries = await fs.promises.readdir(inboxDir, { withFileTypes: true }); for (const entry of inboxEntries) { if (!entry.isFile() || !entry.name.endsWith('.json')) continue; const inboxName = entry.name.slice(0, -'.json'.length).trim(); if (!inboxName || inboxName === 'user' || isLeadMember({ name: inboxName })) continue; confirmedArtifactNames.add(inboxName); } } catch { // best-effort } dropCliAutoSuffixedMembers(memberMap); dropCliProvisionerMembers(memberMap); const members = Array.from(memberMap.values()); const memberColors = buildTeamMemberColorMap(members, { preferProvidedColors: false }); const coloredMembers = members.map((member) => ({ ...member, color: memberColors.get(member.name) ?? member.color, })); const suppressLegacyLaunchArtifactHeuristic = shouldSuppressLegacyLaunchArtifactHeuristic({ leadProviderId, members: metaRuntimeMembers, }); const launchStateRead = await readLaunchState(payload.teamsDir, teamName); const fallbackLaunchStateSummary = (): ReturnType => { if (suppressLegacyLaunchArtifactHeuristic) { return null; } if (!leadSessionId || expectedTeammateNames.size === 0 || confirmedArtifactNames.size === 0) { return null; } const missingMembers = Array.from(expectedTeammateNames).filter( (name) => !confirmedArtifactNames.has(name) ); if (missingMembers.length === 0) { return null; } return { partialLaunchFailure: true as const, expectedMemberCount: expectedTeammateNames.size, confirmedMemberCount: confirmedArtifactNames.size, missingMembers, }; }; const launchStateSummary = launchStateRead.summary ?? fallbackLaunchStateSummary(); const summary = { teamName, displayName, description, memberCount: memberMap.size, taskCount: 0, lastActivity: null, ...(coloredMembers.length > 0 ? { members: coloredMembers } : {}), ...(leadName ? { leadName } : {}), ...(leadColor ? { leadColor } : {}), ...(color ? { color } : {}), ...(projectPath ? { projectPath } : {}), ...(leadSessionId ? { leadSessionId } : {}), ...(projectPathHistory ? { projectPathHistory } : {}), ...(sessionHistory ? { sessionHistory } : {}), ...(deletedAt ? { deletedAt } : {}), ...(launchStateSummary ?? {}), }; const ms = nowMs() - t0; if (ms >= 250) { pushSlowest(diag.slowest, { teamName, ms }, 10); } await cacheTeamSummaryIfStable( cacheKey, payload.teamsDir, teamName, optionKey, dependencyFingerprint, summary, launchStateRead.cacheable, diag ); return summary; }); const teams = perTeam.filter((t): t is NonNullable => t !== null); pruneTeamSummaryCache(payload.teamsDir, optionKey, liveTeamNames, diag); diag.returned = teams.length; diag.totalMs = nowMs() - startedAt; return { teams, diag }; } // --------------------------------------------------------------------------- // Task normalization helpers // --------------------------------------------------------------------------- function normalizeWorkIntervals( parsed: ParsedTask ): { startedAt: string; completedAt?: string }[] | undefined { if (!Array.isArray(parsed.workIntervals)) return undefined; return (parsed.workIntervals as unknown[]) .filter( (i): i is RawWorkInterval => Boolean(i) && typeof i === 'object' && typeof (i as RawWorkInterval).startedAt === 'string' && ((i as RawWorkInterval).completedAt === undefined || typeof (i as RawWorkInterval).completedAt === 'string') ) .map((i) => ({ startedAt: i.startedAt as string, completedAt: i.completedAt as string | undefined, })); } function normalizeReviewIntervals( parsed: ParsedTask ): { reviewer: string; startedAt: string; completedAt?: string }[] | undefined { if (!Array.isArray(parsed.reviewIntervals)) return undefined; return (parsed.reviewIntervals as unknown[]) .filter( (i): i is RawReviewInterval => Boolean(i) && typeof i === 'object' && typeof (i as RawReviewInterval).reviewer === 'string' && typeof (i as RawReviewInterval).startedAt === 'string' && ((i as RawReviewInterval).completedAt === undefined || typeof (i as RawReviewInterval).completedAt === 'string') ) .map((i) => ({ reviewer: i.reviewer as string, startedAt: i.startedAt as string, completedAt: i.completedAt as string | undefined, })); } function normalizeHistoryEvents(parsed: ParsedTask): RawHistoryEvent[] | undefined { if (!Array.isArray(parsed.historyEvents)) return undefined; return (parsed.historyEvents as unknown[]) .filter( (i): i is RawHistoryEvent => Boolean(i) && typeof i === 'object' && typeof (i as RawHistoryEvent).id === 'string' && typeof (i as RawHistoryEvent).timestamp === 'string' && typeof (i as RawHistoryEvent).type === 'string' ) .map((i) => ({ ...i })); } function normalizeReviewState(value: unknown): string { return value === 'review' || value === 'needsFix' || value === 'approved' ? value : 'none'; } function normalizeFallbackReviewState(value: unknown, status: string): string { const reviewState = normalizeReviewState(value); if (reviewState === 'none') return 'none'; if (status === 'in_progress' || status === 'deleted') return 'none'; if (status === 'pending') return reviewState === 'needsFix' ? 'needsFix' : 'none'; if (status === 'completed') { return reviewState === 'review' || reviewState === 'approved' || reviewState === 'needsFix' ? reviewState : 'none'; } return reviewState; } function eventReviewState(event: RawHistoryEvent): string | null { const type = typeof event.type === 'string' ? event.type : ''; if (!REVIEW_LIFECYCLE_EVENTS.has(type)) { return null; } return normalizeReviewState(event.to); } function derivePendingReviewState(events: RawHistoryEvent[], startIndex: number): string { for (let i = startIndex - 1; i >= 0; i--) { const previous = events[i]; const reviewState = eventReviewState(previous); if (reviewState) { return reviewState === 'needsFix' ? 'needsFix' : 'none'; } if ( previous.type === 'task_created' || (previous.type === 'status_changed' && (REVIEW_RESET_STATUSES.has(String(previous.to || '')) || previous.to === 'pending')) ) { return 'none'; } } return 'none'; } /** Derive review state from historyEvents (inline reducer for worker isolation). */ function deriveReviewStateFromEvents(events: RawHistoryEvent[] | undefined): string | null { if (!Array.isArray(events) || events.length === 0) return null; for (let i = events.length - 1; i >= 0; i--) { const e = events[i]; const reviewState = eventReviewState(e); if (reviewState) { return reviewState; } if (e.type === 'status_changed' && REVIEW_RESET_STATUSES.has(String(e.to || ''))) { return 'none'; } if (e.type === 'status_changed' && e.to === 'pending') { return derivePendingReviewState(events, i); } } return null; } function normalizeComments(parsed: ParsedTask): unknown[] | undefined { if (!Array.isArray(parsed.comments)) return undefined; return (parsed.comments as unknown[]) .filter( (c): c is RawComment => !!c && typeof c === 'object' && typeof (c as RawComment).id === 'string' && typeof (c as RawComment).author === 'string' && typeof (c as RawComment).text === 'string' && typeof (c as RawComment).createdAt === 'string' ) .map((c) => ({ id: c.id as string, author: c.author as string, text: unescapeLiteralNewlines(c.text as string), createdAt: c.createdAt as string, taskRefs: Array.isArray(c.taskRefs) ? c.taskRefs : undefined, type: c.type === 'regular' || c.type === 'review_request' || c.type === 'review_approved' ? (c.type as string) : 'regular', })); } // --------------------------------------------------------------------------- // getAllTasks // --------------------------------------------------------------------------- async function readTasksDirForTeam( tasksDir: string, teamName: string, payload: GetAllTasksPayload ): Promise<{ tasks: unknown[]; taskDiag: TaskReadDiag; liveCacheKeys: Set }> { const taskDiag: TaskReadDiag = { skipped: 0, skipReasons: {}, cacheHits: 0, cacheMisses: 0, cacheWriteSkips: 0, persistentCacheHits: 0, persistentCacheMisses: 0, persistentCacheLoads: 0, persistentCacheWrites: 0, persistentCacheReadFailures: 0, persistentCacheWriteFailures: 0, }; let entries: string[]; try { entries = await fs.promises.readdir(tasksDir); } catch (error) { if ((error as NodeJS.ErrnoException).code === 'ENOENT') { return { tasks: [], taskDiag, liveCacheKeys: new Set() }; } throw error; } const tasks: unknown[] = []; const liveCacheKeys = new Set(); const optionKey = makeTaskOptionKey(payload); const persistentCache = await readPersistentTaskProjectionCache( payload, teamName, optionKey, taskDiag ); const nextPersistentEntries = new Map(); for (const file of entries) { if ( !file.endsWith('.json') || file.startsWith('.') || file === '.lock' || file === '.highwatermark' ) { continue; } const taskPath = path.join(tasksDir, file); const cacheKey = makeTaskCacheKey(payload.tasksBase, teamName, file, optionKey); liveCacheKeys.add(cacheKey); try { const pathFingerprint = await statPathFingerprint(taskPath); const taskSize = Number(pathFingerprint.size ?? Number.NaN); if ( !pathFingerprint.isFile || !Number.isFinite(taskSize) || taskSize > payload.maxTaskBytes ) { taskDiag.skipped++; bumpSkipReason(taskDiag.skipReasons, 'task_not_file_or_large'); continue; } const fingerprint = fingerprintToString(pathFingerprint); const fingerprintCacheSafe = isCacheSafeFingerprint(pathFingerprint); const cached = taskFileCache.get(cacheKey); if (fingerprintCacheSafe && cached?.fingerprint === fingerprint) { cached.lastUsedAt = nowMs(); taskDiag.cacheHits++; applyCachedTaskReadResult(cached.result, tasks, taskDiag); nextPersistentEntries.set(file, { fingerprint, result: cloneCached(cached.result), }); continue; } const persistentEntry = persistentCache?.get(file); if (fingerprintCacheSafe && persistentEntry?.fingerprint === fingerprint) { const result = cloneCached(persistentEntry.result); taskFileCache.set(cacheKey, { fingerprint, result: cloneCached(result), tasksBase: payload.tasksBase, lastUsedAt: nowMs(), }); taskDiag.persistentCacheHits++; applyCachedTaskReadResult(result, tasks, taskDiag); nextPersistentEntries.set(file, { fingerprint, result: cloneCached(result), }); continue; } if (persistentCache) { taskDiag.persistentCacheMisses++; } taskDiag.cacheMisses++; const raw = await readFileUtf8WithTimeout(taskPath, payload.maxTaskReadMs); const parsed = JSON.parse(raw) as ParsedTask; const metadata = parsed.metadata; if (metadata?._internal === true) { taskDiag.skipped++; bumpSkipReason(taskDiag.skipReasons, 'task_internal'); const result: CachedTaskReadResult = { skipReason: 'task_internal' }; const cachedStable = await cacheTaskReadResultIfStable( cacheKey, taskPath, payload.tasksBase, fingerprint, fingerprintCacheSafe, result, taskDiag ); if (cachedStable) { nextPersistentEntries.set(file, { fingerprint, result: cloneCached(result), }); } continue; } if (parsed.status === 'deleted') { taskDiag.skipped++; bumpSkipReason(taskDiag.skipReasons, 'task_deleted'); const result: CachedTaskReadResult = { skipReason: 'task_deleted' }; const cachedStable = await cacheTaskReadResultIfStable( cacheKey, taskPath, payload.tasksBase, fingerprint, fingerprintCacheSafe, result, taskDiag ); if (cachedStable) { nextPersistentEntries.set(file, { fingerprint, result: cloneCached(result), }); } continue; } const subject = typeof parsed.subject === 'string' ? parsed.subject : typeof parsed.title === 'string' ? parsed.title : ''; let createdAt: string | undefined = typeof parsed.createdAt === 'string' ? parsed.createdAt : undefined; let updatedAt: string | undefined; try { const birthtime = dateFromFingerprintMs(pathFingerprint.birthtimeMs); const mtime = dateFromFingerprintMs(pathFingerprint.mtimeMs); if (!createdAt) { createdAt = (birthtime ?? mtime)?.toISOString(); } updatedAt = mtime?.toISOString(); } catch { /* ignore */ } const needsClarification = parsed.needsClarification === 'lead' || parsed.needsClarification === 'user' ? (parsed.needsClarification as string) : undefined; const historyEvents = normalizeHistoryEvents(parsed); const status = parsed.status === 'pending' || parsed.status === 'in_progress' || parsed.status === 'completed' || parsed.status === 'deleted' ? (parsed.status as string) : 'pending'; const derivedReviewState = deriveReviewStateFromEvents(historyEvents); const reviewState = derivedReviewState !== null ? normalizeFallbackReviewState(derivedReviewState, status) : normalizeFallbackReviewState(parsed.reviewState, status); const task = { id: typeof parsed.id === 'string' || typeof parsed.id === 'number' ? String(parsed.id) : '', displayId: typeof parsed.displayId === 'string' && parsed.displayId.trim().length > 0 ? parsed.displayId.trim() : deriveTaskDisplayId( typeof parsed.id === 'string' || typeof parsed.id === 'number' ? String(parsed.id) : '' ), subject, description: typeof parsed.description === 'string' ? unescapeLiteralNewlines(parsed.description) : undefined, descriptionTaskRefs: Array.isArray(parsed.descriptionTaskRefs) ? (parsed.descriptionTaskRefs as unknown[]) : undefined, activeForm: typeof parsed.activeForm === 'string' ? parsed.activeForm : undefined, prompt: typeof parsed.prompt === 'string' ? parsed.prompt : undefined, promptTaskRefs: Array.isArray(parsed.promptTaskRefs) ? (parsed.promptTaskRefs as unknown[]) : undefined, owner: typeof parsed.owner === 'string' ? parsed.owner : undefined, createdBy: typeof parsed.createdBy === 'string' ? parsed.createdBy : undefined, status, workIntervals: normalizeWorkIntervals(parsed), reviewIntervals: normalizeReviewIntervals(parsed), historyEvents, blocks: Array.isArray(parsed.blocks) ? (parsed.blocks as unknown[]) : undefined, blockedBy: Array.isArray(parsed.blockedBy) ? (parsed.blockedBy as unknown[]) : undefined, related: Array.isArray(parsed.related) ? (parsed.related as unknown[]).filter((id): id is string => typeof id === 'string') : undefined, createdAt, updatedAt, projectPath: typeof parsed.projectPath === 'string' ? parsed.projectPath : undefined, comments: normalizeComments(parsed), needsClarification, reviewState, deletedAt: undefined, attachments: Array.isArray(parsed.attachments) ? (parsed.attachments as unknown[]) : undefined, sourceMessageId: typeof parsed.sourceMessageId === 'string' && parsed.sourceMessageId.trim() ? parsed.sourceMessageId.trim() : undefined, sourceMessage: parsed.sourceMessage && typeof parsed.sourceMessage === 'object' && typeof (parsed.sourceMessage as Record).text === 'string' && typeof (parsed.sourceMessage as Record).from === 'string' && typeof (parsed.sourceMessage as Record).timestamp === 'string' ? (parsed.sourceMessage as Record) : undefined, teamName, }; tasks.push(task); const result: CachedTaskReadResult = { task }; const cachedStable = await cacheTaskReadResultIfStable( cacheKey, taskPath, payload.tasksBase, fingerprint, fingerprintCacheSafe, result, taskDiag ); if (cachedStable) { nextPersistentEntries.set(file, { fingerprint, result: cloneCached(result), }); } } catch (error) { taskDiag.skipped++; const code = (error as NodeJS.ErrnoException).code; if (code === 'READ_TIMEOUT') { bumpSkipReason(taskDiag.skipReasons, 'task_read_timeout'); } else { bumpSkipReason(taskDiag.skipReasons, 'task_parse_failed'); } } } if (shouldWritePersistentTaskProjectionCache(persistentCache, nextPersistentEntries, taskDiag)) { await writePersistentTaskProjectionCache( payload, teamName, optionKey, nextPersistentEntries, taskDiag ); } return { tasks, taskDiag, liveCacheKeys }; } function mergeTaskDiag(target: GetAllTasksDiag, source: TaskReadDiag): void { target.skipped += source.skipped; target.cacheHits += source.cacheHits; target.cacheMisses += source.cacheMisses; target.cacheWriteSkips += source.cacheWriteSkips; target.persistentCacheHits += source.persistentCacheHits; target.persistentCacheMisses += source.persistentCacheMisses; target.persistentCacheLoads += source.persistentCacheLoads; target.persistentCacheWrites += source.persistentCacheWrites; target.persistentCacheReadFailures += source.persistentCacheReadFailures; target.persistentCacheWriteFailures += source.persistentCacheWriteFailures; for (const [reason, count] of Object.entries(source.skipReasons)) { target.skipReasons[reason] = (target.skipReasons[reason] || 0) + count; } } async function getAllTasks( payload: GetAllTasksPayload ): Promise<{ tasks: unknown[]; diag: GetAllTasksDiag }> { const startedAt = nowMs(); const diag: GetAllTasksDiag = { op: 'getAllTasks', startedAt, tasksBase: payload.tasksBase, teamDirs: 0, returned: 0, skipped: 0, skipReasons: {}, slowestTeams: [], cacheHits: 0, cacheMisses: 0, cacheWriteSkips: 0, cacheEvictions: 0, persistentCacheHits: 0, persistentCacheMisses: 0, persistentCacheLoads: 0, persistentCacheWrites: 0, persistentCacheReadFailures: 0, persistentCacheWriteFailures: 0, totalMs: 0, }; let entries: fs.Dirent[]; try { entries = await fs.promises.readdir(payload.tasksBase, { withFileTypes: true }); } catch (error) { if ((error as NodeJS.ErrnoException).code === 'ENOENT') { diag.totalMs = nowMs() - startedAt; return { tasks: [], diag }; } throw error; } const dirs = entries.filter((e) => e.isDirectory()); diag.teamDirs = dirs.length; const liveCacheKeys = new Set(); const chunks = await mapLimit(dirs, payload.concurrency, async (entry) => { const teamName = entry.name; const t0 = nowMs(); try { const tasksDir = path.join(payload.tasksBase, teamName); const { tasks, taskDiag, liveCacheKeys: teamLiveCacheKeys, } = await readTasksDirForTeam(tasksDir, teamName, payload); for (const key of teamLiveCacheKeys) { liveCacheKeys.add(key); } mergeTaskDiag(diag, taskDiag); const ms = nowMs() - t0; if (ms >= 250) { pushSlowest(diag.slowestTeams, { teamName, ms }, 10); } return tasks; } catch { diag.skipped++; bumpSkipReason(diag.skipReasons, 'team_dir_failed'); return []; } }); const tasks = chunks.flat(); pruneTaskFileCache(payload.tasksBase, liveCacheKeys, diag); diag.returned = tasks.length; diag.totalMs = nowMs() - startedAt; return { tasks, diag }; } // --------------------------------------------------------------------------- // Worker message handler // --------------------------------------------------------------------------- function post(msg: WorkerResponse): void { parentPort?.postMessage(msg); } parentPort?.on('message', async (msg: WorkerRequest) => { const { id, op } = msg; try { if (op === 'warmup') { post({ id, ok: true, result: { ready: true, teamSummaryCacheEntries: teamSummaryCache.size, taskFileCacheEntries: taskFileCache.size, }, diag: { op, totalMs: 0 }, }); return; } if (op === 'listTeams') { const { teams, diag } = await listTeams(msg.payload); post({ id, ok: true, result: teams, diag }); return; } if (op === 'getAllTasks') { const { tasks, diag } = await getAllTasks(msg.payload); post({ id, ok: true, result: tasks, diag }); return; } post({ id, ok: false, error: `Unknown op: ${String(op)}` }); } catch (error) { post({ id, ok: false, error: error instanceof Error ? error.message : String(error) }); } });