/**
* — 폴더(또는 단일 파일) 강제 정독 조사.
*
* list_files 가 "이름 목록"을 주는 것과 달리, 이 액션은 대상 파일들을 코드가 직접
* 읽어 파일별 추출 노트(Map)를 만들고 병합 노트를 컨텍스트에 주입한다 — 모델이
* 파일명만 보고 내용을 상상하는 헛조사(할루시네이션)를 구조적으로 차단. (v2.2.309)
* 노트는 내용 해시 캐시로 영속화되어 재조사 시 재사용된다 (온디맨드 지식화).
*/
import { HandlerContext } from './types';
import { validatePath } from '../../security';
import { getConfig } from '../../config';
import { simpleChatCompletion } from '../../intelligence/llmCall';
import { runInvestigation, formatInvestigationNotes } from '../../intelligence/investigationPipeline';
import { logInfo, logError } from '../../utils';
export async function applyInvestigateFilesActions(ctx: HandlerContext): Promise {
const { aiMessage, rootPath, report } = ctx;
const regex = /]+)['"]?(?:\s+focus=['"]?([^'">]*)['"]?)?\s*\/?>(?:<\/investigate_files>)?/gi;
let match: RegExpExecArray | null;
while ((match = regex.exec(aiMessage)) !== null) {
const relPath = match[1].trim() || '.';
const focus = (match[2] || '').trim();
try {
validatePath(rootPath, relPath); // 경로 탈출 방지 (결과는 pipeline 이 재해석)
const cfg = getConfig();
const llm = { baseUrl: cfg.ollamaUrl, model: cfg.defaultModel };
const result = await runInvestigation({
rootPath,
relDir: relPath,
llmCall: (system, user) => simpleChatCompletion(system, user, {
...llm, temperature: 0.1, maxTokens: 600, timeoutMs: 120000,
}),
});
if (result.notes.length === 0) {
report.push(`🔎 Investigate: ${relPath} — 읽을 텍스트 파일 없음 (제외 ${result.skipped.length}건)`);
ctx.chatHistory.push({
role: 'system', internal: true,
content: `[Result of investigate_files "${relPath}"] 읽을 수 있는 텍스트 파일이 없다. 제외: ${result.skipped.slice(0, 10).join(', ') || '—'}`,
});
continue;
}
ctx.chatHistory.push({ role: 'system', internal: true, content: formatInvestigationNotes(result, focus) });
report.push(`🔎 Investigated: ${relPath} — 파일 ${result.notes.length}개 정독 (캐시 ${result.cacheHits} · LLM ${result.llmCalls} · 제외 ${result.skipped.length})`);
logInfo('Investigation pipeline 완료.', { relPath, files: result.notes.length, cacheHits: result.cacheHits, llmCalls: result.llmCalls });
} catch (err: any) {
report.push(`❌ Investigate failed: ${relPath} — ${err?.message ?? err}`);
logError('Investigation pipeline 실패.', { relPath, error: err?.message ?? String(err) });
}
}
}