agent-ecosystem/src/main/services/team/taskLogs/exact/BoardTaskExactLogDetailService.ts
2026-04-13 17:00:49 +03:00

77 lines
2.7 KiB
TypeScript

import { BoardTaskActivityRecordSource } from '../activity/BoardTaskActivityRecordSource';
import { BoardTaskExactLogChunkBuilder } from './BoardTaskExactLogChunkBuilder';
import { BoardTaskExactLogDetailSelector } from './BoardTaskExactLogDetailSelector';
import { BoardTaskExactLogStrictParser } from './BoardTaskExactLogStrictParser';
import { BoardTaskExactLogSummarySelector } from './BoardTaskExactLogSummarySelector';
import { isBoardTaskExactLogsReadEnabled } from './featureGates';
import { getBoardTaskExactLogFileVersions } from './fileVersions';
import type { BoardTaskExactLogDetailResult } from '@shared/types';
export class BoardTaskExactLogDetailService {
constructor(
private readonly recordSource: BoardTaskActivityRecordSource = new BoardTaskActivityRecordSource(),
private readonly summarySelector: BoardTaskExactLogSummarySelector = new BoardTaskExactLogSummarySelector(),
private readonly strictParser: BoardTaskExactLogStrictParser = new BoardTaskExactLogStrictParser(),
private readonly detailSelector: BoardTaskExactLogDetailSelector = new BoardTaskExactLogDetailSelector(),
private readonly chunkBuilder: BoardTaskExactLogChunkBuilder = new BoardTaskExactLogChunkBuilder()
) {}
async getTaskExactLogDetail(
teamName: string,
taskId: string,
exactLogId: string,
expectedSourceGeneration: string
): Promise<BoardTaskExactLogDetailResult> {
if (!isBoardTaskExactLogsReadEnabled()) {
return { status: 'missing' };
}
const records = await this.recordSource.getTaskRecords(teamName, taskId);
if (records.length === 0) {
return { status: 'missing' };
}
const fileVersionsByPath = await getBoardTaskExactLogFileVersions(
records.map((record) => record.source.filePath)
);
const candidate = this.summarySelector
.selectSummaries({
records,
fileVersionsByPath,
})
.find((item) => item.id === exactLogId);
if (!candidate) {
return { status: 'missing' };
}
if (!candidate.canLoadDetail) {
return { status: 'missing' };
}
if (candidate.sourceGeneration !== expectedSourceGeneration) {
return { status: 'stale' };
}
const parsedMessagesByFile = await this.strictParser.parseFiles([candidate.source.filePath]);
const detailCandidate = this.detailSelector.selectDetail({
candidate,
records,
parsedMessagesByFile,
});
if (!detailCandidate) {
return { status: 'missing' };
}
const chunks = this.chunkBuilder.buildBundleChunks(detailCandidate.filteredMessages);
return {
status: 'ok',
detail: {
id: detailCandidate.id,
chunks,
},
};
}
}