feat(meet): 확신 게이트 등록 + /meet confirm + 데일리 브리핑 (v2.2.216)

캘린더 등록 정책을 "확신 없으면 등록 대신 질문"으로 전환:
- 액션 표에 상태 컬럼(확정/진행미정/기한미정/조건부:선행/반복:주기) — LLM 분류.
- 확정+기한만 자동 등록. 진행미정·기한미정·조건부는 보류 목록으로 질문,
  `/meet confirm 1=6/20 2=ok 3=skip` 답변으로 등록 완결 (/meet pending 재확인).
- 조건부 규칙: ok=날짜 없는 Tasks 로 [조건부] 등록(선행조건 노트 명시),
  날짜=그날을 '조건 확인일'로 등록 — 의존 대상이 제목/노트에서 즉시 인지됨.
- 반복 업무: 반복 등록 없이 첫 1회만(다음 해당 요일) — 까먹음 방지.
- 기한 해석 불가 확정건: 구버전의 +5일 추측 등록 제거 → 보류 질문.
- 과거 날짜(옛 녹취): 과거 날짜 그대로 등록 + "과거자료·완료확인 필요" 표기.
- 중복 방지: 녹취 sha256 해시 레지스트리(.astra/meet_registered.json)로
  같은 녹취 재실행 시 이중 등록 차단.
- tasksApi: due 옵션화(날짜 없는 task 지원).

데일리 브리핑 (신규):
- 평일 KST 09:30(설정 가능) 오늘의 캘린더 일정 + Tasks(오늘 마감/기한 경과/
  조건부 대기)를 텔레그램 발송. 텔레그램·캘린더 미연결 시 조용히 skip.
- g1nation.dailyBriefing.enabled(기본 true) / .time("09:30").

테스트: meetRegistration 15건 (분류 게이트·confirm 파싱·날짜 정규화·중복 키).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-11 16:22:19 +09:00
parent 4eb8bf03f7
commit 70ea421827
10 changed files with 777 additions and 60 deletions
+9 -6
View File
@@ -16,8 +16,11 @@ const API_BASE = 'https://tasks.googleapis.com/tasks/v1';
export interface TaskInput {
/** 작업 제목 (필수). */
title: string;
/** 마감일 'YYYY-MM-DD' — Google Tasks 는 시간 무시, 날짜만 사용. */
due: string;
/**
* 마감일 'YYYY-MM-DD' — Google Tasks 는 시간 무시, 날짜만 사용.
* 생략 시 *날짜 없는* task 로 등록 (조건부 task: 선행 작업 완료 시 진행, D-Day 없음).
*/
due?: string;
/** 메모 (옵션) — Tasks UI 에서 작업 본문 아래 노트로 표시. */
notes?: string;
/**
@@ -31,7 +34,7 @@ export interface CreatedTask {
/** Google 이 발급한 task id. */
id: string;
title: string;
due: string;
due?: string;
/** Google Tasks API 의 self link (API 용). 사용자용 deep link 는 별도로 없음. */
selfLink?: string;
}
@@ -51,7 +54,7 @@ export async function createTask(
input: TaskInput,
): Promise<{ ok: true; task: CreatedTask } | { ok: false; error: string }> {
if (!input.title?.trim()) return { ok: false, error: 'title 비어 있음' };
if (!/^\d{4}-\d{2}-\d{2}$/.test(input.due)) {
if (input.due !== undefined && !/^\d{4}-\d{2}-\d{2}$/.test(input.due)) {
return { ok: false, error: `due 는 'YYYY-MM-DD' 형식이어야 함 (받은 값: ${input.due})` };
}
@@ -63,8 +66,8 @@ export async function createTask(
const body = {
title: input.title.trim(),
// Tasks API 의 `due` 는 RFC3339 timestamp 인데 시간 부분은 서버에서 무시되고
// 날짜만 사용. UTC midnight 으로 보내는 게 표준 패턴.
due: `${input.due}T00:00:00.000Z`,
// 날짜만 사용. UTC midnight 으로 보내는 게 표준 패턴. due 생략 = 날짜 없는 task.
...(input.due ? { due: `${input.due}T00:00:00.000Z` } : {}),
...(input.notes ? { notes: input.notes } : {}),
};