/** * — 폴더(또는 단일 파일) 강제 정독 조사. * * 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) }); } } }