feat: Self-Evolving Digital Employee OS P0~P6 + 캘린더 충돌 게이트

신뢰성 코어 (P1~P2):
- Requirement Graph: 업무 유형(회의록/시장조사/업무조사/일정) 필수 요소 주입 + 커버리지 hook
- Confidence Engine(0~100 결정론적) / Escalation Engine(검토 요청) / Epistemic Guard(모름·추정·확실 3분류)
- Provenance: citationTrace 에 출처 수정일·오래됨 경고
- Critic Loop: 문제 신호 turn 만 LLM 검수 1회 + 보완 카드

성장 루프 (P3):
- Gap Detector(Requirement-Knowledge) / Need Engine(30/25/20/15/10 공식) / Knowledge Inventory
- Learning Queue(proposed 전용 병합 — 승인은 사람만) / Decision Journal / Reflection 기록
- 반복 누락 요소(3회+)는 다음 turn 체크리스트에 자동 강조 (T5 루프)

지식 운영 (P4) + 기억 (P5) + 학습 실행 (P6):
- Knowledge Validation + Belief Revision(중복 reject·충돌 시 update/add 권고)
- Knowledge Decay(분야별 반감기 감사) / Knowledge Debt(blocked x impact)
- Organizational Memory(.astra/organization.md 상시 주입)
- Research Agent(approved 큐 -> 조사 브리프+추정 라벨 초안+Validation 게이트 -> proposals/)
- Skill Score(전/후반 추세) + Success Pattern DB(전요소충족+확신도90+ 자동 적재)

병렬 트랙:
- 캘린더 충돌 게이트: conflictCheck + 구조화 이벤트 캐시 + create_calendar_event 차단(force 는 사용자 승인 후)
- Task Eval Harness: 회의록 골든셋 자동 채점 명령 + 성장 리포트/학습 큐/노후 점검 명령

신규 모듈 17종(src/intelligence/), VS Code 명령 5종, 설정 11종, 테스트 +89건(전체 508 통과).
설계 문서: docs/SELF_EVOLVING_OS_MASTER_PLAN.md

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-11 13:42:09 +09:00
parent cbc2558550
commit 2afd1ac589
41 changed files with 4364 additions and 2 deletions
+30
View File
@@ -196,6 +196,15 @@ export async function refreshCalendarCache(context: vscode.ExtensionContext): Pr
try {
fs.mkdirSync(path.dirname(cachePath), { recursive: true });
fs.writeFileSync(cachePath, md, 'utf8');
// 구조화 JSON 캐시 — 충돌 감지(conflictCheck)가 사용. md 와 같은 시점·같은 범위.
const structured = upcoming.map((e) => ({
summary: e.summary,
startIso: e.start.toISOString(),
endIso: e.end ? e.end.toISOString() : undefined,
allDay: e.allDay,
location: e.location || undefined,
}));
fs.writeFileSync(_eventsJsonPath(cachePath), JSON.stringify(structured, null, 2), 'utf8');
} catch (e: any) {
return { ok: false, count: 0, error: `캐시 저장 실패: ${e?.message ?? String(e)}`, cachePath };
}
@@ -215,6 +224,27 @@ export function readCalendarCache(context: vscode.ExtensionContext): string {
}
}
function _eventsJsonPath(mdCachePath: string): string {
return mdCachePath.replace(/\.md$/, '.json');
}
/**
* 구조화 이벤트 캐시 읽기 — 충돌 감지용. 캐시 없음/깨짐 → 빈 배열
* (충돌 검사가 일정 생성을 막는 false-positive 를 내지 않도록 보수적).
*/
export function readCalendarEventsCache(context: vscode.ExtensionContext): Array<{
summary: string; startIso: string; endIso?: string; allDay: boolean; location?: string;
}> {
try {
const file = _eventsJsonPath(_cachePath(context));
if (!fs.existsSync(file)) return [];
const arr = JSON.parse(fs.readFileSync(file, 'utf8'));
return Array.isArray(arr) ? arr.filter((e: any) => e && typeof e.startIso === 'string') : [];
} catch {
return [];
}
}
function _renderMarkdown(events: IcsEvent[], daysAhead: number, now: Date): string {
const tsLabel = (d: Date, allDay: boolean) => {
const yy = d.getFullYear(), mm = String(d.getMonth() + 1).padStart(2, '0'), dd = String(d.getDate()).padStart(2, '0');
+81
View File
@@ -0,0 +1,81 @@
/**
* Schedule Conflict Check — 일정 생성 전 기존 일정과의 겹침 감지.
*
* Self-Evolving OS 마스터 플랜 병렬 트랙 6-2 + 6-3. Requirement Graph 의
* 일정 필수 요소 "충돌 확인" 과 Constitution "승인 없는 외부 액션 금지" 의
* 실행 계층:
* - 에이전트가 <create_calendar_event> 로 일정을 만들기 *전에* ICS 캐시와
* 비교해 겹침을 감지
* - 충돌 시 생성을 *차단*하고 사용자 확인을 요청 (force="true" 명시 시에만 강행)
*
* 순수 모듈 — vscode/네트워크 의존 없음. 캐시 공급은 calendarCache, 차단 배선은
* agent/actions/calendar.ts 담당.
*/
export interface CachedCalEvent {
summary: string;
/** ISO 문자열 (toISOString 또는 로컬 'YYYY-MM-DDTHH:MM'). */
startIso: string;
/** 없으면 1시간으로 간주. */
endIso?: string;
allDay: boolean;
location?: string;
}
export interface CandidateEvent {
startIso: string;
endIso?: string;
/** endIso 없을 때 사용. 기본 60분. */
durationMinutes?: number;
allDay?: boolean;
}
const HOUR_MS = 3600000;
const DAY_MS = 86400000;
function parseIso(iso: string): number | null {
const t = Date.parse(iso);
return isNaN(t) ? null : t;
}
/** [start, end) 구간 계산. all-day 는 해당 날짜 00:00~다음날 00:00 (로컬). */
function rangeOf(startIso: string, endIso: string | undefined, durationMinutes: number | undefined, allDay: boolean): [number, number] | null {
const start = parseIso(startIso);
if (start === null) return null;
if (allDay) {
const d = new Date(start);
const dayStart = new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime();
return [dayStart, dayStart + DAY_MS];
}
let end: number | null = endIso ? parseIso(endIso) : null;
if (end === null) end = start + (durationMinutes && durationMinutes > 0 ? durationMinutes * 60000 : HOUR_MS);
if (end <= start) end = start + HOUR_MS; // 역전 입력 방어
return [start, end];
}
/**
* 후보 일정과 겹치는 기존 일정 반환. 파싱 불가능한 입력은 보수적으로 *충돌 없음*
* 처리 (잘못된 날짜로 생성 자체가 실패할 것이므로 여기서 막지 않는다).
*/
export function findScheduleConflicts(existing: CachedCalEvent[], candidate: CandidateEvent): CachedCalEvent[] {
const cand = rangeOf(candidate.startIso, candidate.endIso, candidate.durationMinutes, candidate.allDay === true);
if (!cand) return [];
const [cs, ce] = cand;
const out: CachedCalEvent[] = [];
for (const ev of existing || []) {
const r = rangeOf(ev.startIso, ev.endIso, undefined, ev.allDay);
if (!r) continue;
const [es, ee] = r;
if (cs < ee && es < ce) out.push(ev); // 구간 겹침
}
return out;
}
/** 충돌 보고 텍스트 — 액션 리포트·에이전트 internal 메시지 공용. */
export function formatConflictReport(conflicts: CachedCalEvent[]): string {
const lines = conflicts.slice(0, 5).map((c) => {
const when = c.allDay ? `${c.startIso.slice(0, 10)} (종일)` : c.startIso.replace('T', ' ').slice(0, 16);
return `- ${c.summary} · ${when}${c.location ? ` · ${c.location}` : ''}`;
});
return `기존 일정과 겹칩니다:\n${lines.join('\n')}\n생성을 보류했습니다. 그래도 진행하려면 사용자 확인 후 force="true" 로 다시 시도하세요.`;
}
+8
View File
@@ -11,9 +11,17 @@ export {
writeCalendarConfig,
refreshCalendarCache,
readCalendarCache,
readCalendarEventsCache,
RefreshResult,
} from './calendarCache';
export {
findScheduleConflicts,
formatConflictReport,
CachedCalEvent,
CandidateEvent,
} from './conflictCheck';
export {
runOAuthLoopback,
refreshAccessToken,