companionbot 0.14.0 → 0.15.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,327 +1,156 @@
1
1
  # CompanionBot
2
2
 
3
- > Claude 기반의 개인화된 페르소나를 가진 AI Companion Bot
3
+ > Claude 기반 개인 AI 친구 - 텔레그램
4
4
 
5
5
  [![npm version](https://badge.fury.io/js/companionbot.svg)](https://www.npmjs.com/package/companionbot)
6
6
  [![Node.js](https://img.shields.io/badge/node-%3E%3D18.0.0-brightgreen)](https://nodejs.org)
7
7
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
8
8
 
9
- ## 주요 기능
9
+ ## 특징
10
10
 
11
- ### 💬 대화
12
- - **자연스러운 대화** - Claude Sonnet/Opus/Haiku 모델 선택 가능
13
- - **페르소나 커스터마이징** - 실행 온보딩으로 봇의 성격, 말투, 이름 설정
14
- - **이미지 분석** - 사진을 보내면 분석
15
- - **링크 요약** - URL을 보내면 내용을 읽고 요약
11
+ - 🧠 **Extended Thinking** - Claude의 사고 과정 활용
12
+ - 🔍 **시맨틱 메모리** - 로컬 임베딩으로 관련 기억 검색
13
+ - **빠른 응답** - 사전 로딩, 병렬 처리, 스마트 캐싱
14
+ - 🎭 **페르소나** - 이름, 성격, 말투 커스터마이징
15
+ - 🔧 **20+ 도구** - 파일, 웹, 일정, 메모리
16
16
 
17
- ### 🔍 정보 검색 (v0.3.0)
18
- - **웹 검색** - "최신 React 뉴스 검색해줘" (Brave Search API)
19
- - **웹 페이지 읽기** - URL 내용을 가져와서 분석/요약
20
-
21
- ### ⏰ 일정 관리
22
- - **리마인더** - "10분 뒤에 알려줘"
23
- - **Google Calendar 연동** - 일정 조회/추가/삭제
24
- - **일일 브리핑** - 매일 아침 날씨와 일정 알림
25
- - **Heartbeat** - 주기적으로 체크리스트 확인 후 알림
26
-
27
- ### 🕐 스케줄링 (v0.3.0)
28
- - **Cron 작업** - "매일 아침 9시에 뉴스 알려줘", "평일 오후 6시에 퇴근 알림"
29
- - **일회성 예약** - "내일 오전 9시에 알려줘"
30
- - **반복 작업** - "30분마다 주식 가격 확인해줘"
31
-
32
- ### 🤖 고급 기능 (v0.3.0+)
33
- - **서브 에이전트** - 복잡한 작업을 백그라운드에서 처리
34
- - **백그라운드 실행** - 긴 명령어를 백그라운드에서 실행하고 결과 확인
35
- - **파일 시스템** - 워크스페이스 내 파일 읽기/쓰기/편집
36
- - **일일 메모리** - 대화 내용 자동 저장
37
-
38
- ### 🧠 시맨틱 메모리 (v0.6.0)
39
- - **벡터 검색** - 임베딩 기반 관련 메모리 검색
40
- - **자동 인덱싱** - 메모리 파일 자동 청크 분할 및 임베딩
41
- - **유사도 매칭** - 의미 기반으로 관련 대화 기록 찾기
42
-
43
- ### 🔧 시스템 (v0.6.0)
44
- - **헬스 체크** - 봇 상태 모니터링 (uptime, 메시지 수, 오류 수)
45
- - **업데이트 알림** - 새 버전 출시 시 자동 알림
46
-
47
- ## 설치
48
-
49
- ### 사전 준비
50
-
51
- - **Node.js 18+** ([다운로드](https://nodejs.org))
52
- - **Telegram Bot Token** - [@BotFather](https://t.me/BotFather)에서 봇 생성 후 발급
53
- - **Anthropic API Key** - [console.anthropic.com](https://console.anthropic.com)에서 발급
54
-
55
- #### Linux 사용자 (keytar 의존성)
56
-
57
- ```bash
58
- # Debian/Ubuntu
59
- sudo apt-get install libsecret-1-dev
60
-
61
- # Fedora
62
- sudo dnf install libsecret-devel
63
-
64
- # Arch
65
- sudo pacman -S libsecret
66
- ```
67
-
68
- ### 간편 설치
17
+ ## 🚀 빠른 시작
69
18
 
70
19
  ```bash
71
20
  npm install -g companionbot
72
21
  companionbot
73
22
  ```
74
23
 
75
- ### 개발자 설치
76
-
77
- ```bash
78
- git clone https://github.com/DinN0000/CompanionBot.git
79
- cd companionbot
80
- npm install
81
- npm run build
82
- npm start
83
- ```
84
-
85
- ## 첫 실행
24
+ 실행 시 안내에 따라:
25
+ 1. Telegram Bot Token 입력 ([@BotFather](https://t.me/BotFather))
26
+ 2. Anthropic API Key 입력 ([console.anthropic.com](https://console.anthropic.com))
27
+ 3. Telegram에서 봇에게 `/start` 보내기
86
28
 
87
- 처음 실행하면 대화형 설정이 시작됩니다:
88
-
89
- ```
90
- 🤖 CompanionBot 첫 실행입니다!
91
-
92
- [1/2] Telegram Bot Token
93
- @BotFather에서 봇 생성 후 토큰을 붙여넣으세요.
94
- Token: _
95
-
96
- [2/2] Anthropic API Key
97
- console.anthropic.com에서 발급받으세요.
98
- API Key: _
99
-
100
- 📁 워크스페이스 생성 중...
101
- → ~/.companionbot/ 생성 완료
102
-
103
- 🚀 봇을 시작합니다!
104
- ```
105
-
106
- 설정 완료 후 **Telegram에서 봇에게 `/start`를 보내면** 온보딩이 시작됩니다:
107
- - 봇 이름 짓기
108
- - 성격과 말투 설정
109
- - 사용자 정보 입력
110
-
111
- ## 명령어
112
-
113
- ### 기본 명령어
29
+ ## 📱 명령어
114
30
 
115
31
  | 명령어 | 설명 |
116
32
  |--------|------|
117
- | `/start` | 시작 (첫 실행 시 온보딩) |
33
+ | `/help` | 도움말 보기 |
34
+ | `/model` | AI 모델 변경 (haiku/sonnet/opus) |
118
35
  | `/compact` | 대화 정리 (토큰 절약) |
119
- | `/memory` | 최근 일주일 기억 보기 |
120
- | `/model [id]` | AI 모델 변경 (sonnet/opus/haiku) |
121
- | `/reset` | 페르소나 초기화 (온보딩 다시) |
122
-
123
- ### 기능 설정
124
-
125
- | 명령어 | 설명 |
126
- |--------|------|
127
- | `/setup` | 전체 기능 설정 메뉴 |
128
- | `/setup weather` | 날씨 API 설정 |
129
- | `/setup calendar` | Google Calendar 설정 |
130
- | `/setup briefing` | 일일 브리핑 설정 |
131
- | `/setup heartbeat` | Heartbeat 설정 |
132
-
133
- ### 빠른 명령어
134
-
135
- | 명령어 | 설명 |
136
- |--------|------|
137
- | `/briefing` | 일일 브리핑 토글 |
138
- | `/heartbeat` | Heartbeat 토글 |
139
- | `/reminders` | 알림 목록 보기 |
140
- | `/calendar` | 오늘 일정 보기 |
141
-
142
- ### 자연어 명령
36
+ | `/memory` | 최근 기억 보기 |
37
+ | `/health` | 상태 확인 |
38
+ | `/setup` | 전체 기능 설정 |
143
39
 
144
- 명령어 대신 자연스럽게 말해도 됩니다:
40
+ ### 자연어로도 가능
145
41
 
146
42
  ```
147
- 모델 변경 "하이쿠로 바꿔줘" / "opus로 변경해줘"
148
- 리마인더 "10분 뒤에 알려줘" / "내일 9시에 회의 알림"
149
- 브리핑 "브리핑 켜줘" / "지금 브리핑 해줘" / "아침 9시에 브리핑"
150
- Heartbeat "하트비트 켜줘" / "10분마다 체크해줘"
151
- 날씨 "서울 날씨 어때?" / "도쿄 날씨 알려줘"
152
- 메모리 "이거 기억해둬"
153
- 웹 검색 "React 19 검색해줘" / "최신 뉴스 찾아줘"
154
- Cron "매일 아침 9시에 뉴스 알려줘" / "평일 오후 6시에 퇴근 알림"
155
- 서브에이전트 "이 코드 분석해줘" (복잡한 작업은 자동으로 서브에이전트 사용)
43
+ "하이쿠로 바꿔줘"
44
+ "10분 뒤에 알려줘"
45
+ "서울 날씨 어때?"
46
+ "React 19 검색해줘"
47
+ "매일 아침 9시에 뉴스 알려줘"
156
48
  ```
157
49
 
158
- ## 선택적 기능 설정
50
+ ## 🔧 주요 기능
159
51
 
160
- ### 🌤️ 날씨 (OpenWeatherMap)
52
+ ### AI 엔진
53
+ - **Claude 모델** - Sonnet 4 / Opus 4 / Haiku 3.5
54
+ - **Extended Thinking** - 내부 스트리밍으로 thinking 지원
55
+ - **도구 사용** - 20+ 도구, 병렬 실행
161
56
 
162
- 1. [openweathermap.org](https://openweathermap.org) 가입
163
- 2. API Keys에서 무료 키 발급
164
- 3. 봇에게 DM으로: `/weather_setup YOUR_API_KEY`
57
+ ### 메모리 시스템
58
+ - **로컬 임베딩** - @xenova/transformers
59
+ - **하이브리드 검색** - 벡터 + 키워드 (FTS5)
60
+ - **한국어 최적화** - trigram tokenizer
165
61
 
166
- ### 📅 Google Calendar
62
+ ### 일정/알림
63
+ - **리마인더** - 자연어로 알림 설정
64
+ - **Google Calendar** - 일정 조회/추가
65
+ - **Cron** - 반복 작업 스케줄링
66
+ - **브리핑/하트비트** - 주기적 알림
167
67
 
168
- 1. [Google Cloud Console](https://console.cloud.google.com) 접속
169
- 2. 프로젝트 생성 Calendar API 활성화
170
- 3. OAuth 동의 화면 설정 (앱 이름, 범위 추가)
171
- 4. 사용자 인증 정보 OAuth 클라이언트 ID (데스크톱 앱)
172
- 5. 봇에게 DM으로: `/calendar_setup CLIENT_ID CLIENT_SECRET`
173
- 6. 인증 링크 클릭하여 Google 로그인
68
+ ### 성능
69
+ - **Warmup** - 시작 사전 로딩
70
+ - **병렬 처리** - 워크스페이스, 도구 실행
71
+ - **LRU 캐시** - 임베딩, 워크스페이스
174
72
 
175
- ### 🔍 웹 검색 (Brave Search)
176
-
177
- 1. [Brave Search API](https://api.search.brave.com) 가입
178
- 2. API 키 발급
179
- 3. 터미널에서: `npm run setup brave YOUR_API_KEY`
180
-
181
- ## PM2로 상시 실행
182
-
183
- ```bash
184
- # PM2 설치
185
- npm install -g pm2
186
-
187
- # 봇 시작
188
- pm2 start npm --name companionbot -- start
189
-
190
- # 부팅 시 자동 시작
191
- pm2 startup && pm2 save
192
-
193
- # 로그 확인
194
- pm2 logs companionbot
195
-
196
- # 재시작
197
- pm2 restart companionbot
198
- ```
199
-
200
- ## 워크스페이스
73
+ ## 📁 워크스페이스
201
74
 
202
75
  `~/.companionbot/` 구조:
203
76
 
204
77
  ```
205
- ├── AGENTS.md # 운영 지침 (행동 규칙)
206
- ├── BOOTSTRAP.md # 온보딩 프롬프트 (완료 후 삭제됨)
207
- ├── HEARTBEAT.md # 주기적 체크 항목
208
- ├── IDENTITY.md # 봇 정체성 (이름, 이모지, 소개)
209
- ├── MEMORY.md # 장기 기억
210
- ├── SOUL.md # 봇 성격과 말투
211
- ├── TOOLS.md # 도구 설정 노트
78
+ ├── SOUL.md # 봇 성격/말투
79
+ ├── IDENTITY.md # 이름, 이모지
212
80
  ├── USER.md # 사용자 정보
213
- ├── canvas/ # 작업 디렉토리
214
- ├── cron.json # 크론 작업 저장
81
+ ├── MEMORY.md # 장기 기억
82
+ ├── AGENTS.md # 행동 지침
83
+ ├── HEARTBEAT.md # 주기적 체크 항목
84
+ ├── TOOLS.md # 도구 설정
85
+ ├── canvas/ # 작업 디렉토리
215
86
  └── memory/ # 일일 로그
216
87
  └── YYYY-MM-DD.md
217
88
  ```
218
89
 
219
- ### 파일 커스터마이징
220
-
221
- - **SOUL.md** - 봇의 성격, 말투, 관심사 수정
222
- - **HEARTBEAT.md** - 주기적으로 체크할 항목 설정
223
- - **AGENTS.md** - 봇의 행동 지침 수정
90
+ ## ⚙️ 선택 기능
224
91
 
225
- ## 시크릿 저장
226
-
227
- API 키는 OS 키체인에 안전하게 저장됩니다:
92
+ ### 날씨 (OpenWeatherMap)
93
+ ```bash
94
+ companionbot setup weather
95
+ ```
228
96
 
229
- | OS | 저장 위치 |
230
- |----|-----------|
231
- | macOS | Keychain Access |
232
- | Windows | Credential Manager |
233
- | Linux | libsecret (GNOME Keyring 등) |
97
+ ### Google Calendar
98
+ ```bash
99
+ companionbot setup calendar
100
+ ```
234
101
 
235
- 완전 초기화: `~/.companionbot/` 폴더 삭제 후 다시 실행
102
+ ### 검색 (Brave Search)
103
+ ```bash
104
+ companionbot setup brave
105
+ ```
236
106
 
237
- ## 트러블슈팅
107
+ ## 🖥️ PM2로 상시 실행
238
108
 
239
- ### 봇이 응답하지 않아요
109
+ ```bash
110
+ npm install -g pm2
111
+ pm2 start companionbot --name bot
112
+ pm2 startup && pm2 save
113
+ ```
240
114
 
241
- 1. **API 키 확인**: Anthropic API 키가 유효한지 확인
242
- 2. **토큰 확인**: Telegram Bot Token이 정확한지 확인
243
- 3. **로그 확인**: `pm2 logs companionbot` 또는 터미널 출력 확인
115
+ ## 🔒 보안
244
116
 
245
- ### "rate limit" 오류가 나요
117
+ - API 키는 OS 키체인에 저장 (macOS Keychain, Windows Credential Manager, Linux libsecret)
118
+ - 파일 접근 경로 검증 (TOCTOU 방지)
119
+ - 명령어 화이트리스트
120
+ - SSRF 방지
246
121
 
247
- - Anthropic API 사용량 한도 초과
248
- - 잠시 후 다시 시도하거나 API 플랜 업그레이드
122
+ ## 🐛 트러블슈팅
249
123
 
250
- ### Linux에서 설치 오류
124
+ ### 봇이 응답 안 해요
125
+ 1. API 키 확인
126
+ 2. `tail -f /tmp/companionbot.log` 로그 확인
251
127
 
128
+ ### Linux 설치 오류
252
129
  ```bash
253
- # keytar 의존성 설치 필요
254
130
  sudo apt-get install libsecret-1-dev # Debian/Ubuntu
255
131
  ```
256
132
 
257
- ### Google Calendar 인증 실패
258
-
259
- 1. OAuth 클라이언트 ID가 "데스크톱 앱" 유형인지 확인
260
- 2. 리디렉션 URI에 `http://localhost:3847/oauth2callback` 추가
261
- 3. `/setup calendar off` 후 `/calendar_setup`으로 다시 시도
262
-
263
- ### 온보딩이 다시 안 나와요
264
-
133
+ ### 초기화하고 싶어요
265
134
  ```bash
266
- # 페르소나 리셋
267
- # Telegram에서: /reset
268
-
269
- # 또는 완전 초기화
270
- rm -rf ~/.companionbot
271
- companionbot
135
+ rm -rf ~/.companionbot && companionbot
272
136
  ```
273
137
 
274
- ## 개발
138
+ ## 📜 버전
275
139
 
276
- ```bash
277
- npm run dev # 개발 모드 (tsx)
278
- npm run build # TypeScript 빌드
279
- npm start # 빌드된 코드 실행
280
- npm test # 테스트 실행
281
- ```
140
+ 현재: **v0.15.0**
141
+
142
+ 주요 변경:
143
+ - 메모리 검색 도구 방식 (성능 개선)
144
+ - tools 모듈 분할 (15개 파일)
145
+ - Agent 메모리 누수 방지
146
+ - /help 명령어, 한국어 메시지 통일
147
+
148
+ 전체 변경 이력: [CHANGELOG.md](CHANGELOG.md)
282
149
 
283
- ## 버전 히스토리
284
-
285
- ### v0.6.0 (현재)
286
- - 🧠 시맨틱 메모리 검색 (임베딩 기반)
287
- - 💚 헬스 체크 모니터링
288
- - 🔄 자동 업데이트 알림
289
-
290
- ### v0.5.0
291
- - 🔒 보안 강화 (SSRF 방지, 경로 검증)
292
- - ✅ 테스트 추가 (vitest)
293
- - 📖 문서 개선
294
-
295
- ### v0.4.0
296
- - 🛡️ 보안 강화 (TOCTOU, 심볼릭 링크 검증)
297
- - 🧹 세션 자동 정리
298
- - 🔧 환경변수 기반 경로 설정
299
-
300
- ### v0.3.0
301
- - 🔍 웹 검색 (Brave Search API)
302
- - 🕐 Cron 스케줄링 (한국어 지원)
303
- - 🤖 서브 에이전트 (백그라운드 작업)
304
- - 🖥️ 백그라운드 명령어 실행
305
- - 📝 파일 편집 도구 추가
306
-
307
- ### v0.2.0
308
- - 📅 Google Calendar 연동
309
- - ☀️ 일일 브리핑
310
- - 💓 Heartbeat 시스템
311
- - 🌤️ 날씨 조회
312
-
313
- ### v0.1.0
314
- - 🚀 초기 릴리스
315
- - 💬 Claude 기반 대화
316
- - 🎭 페르소나 온보딩
317
- - ⏰ 리마인더
318
- - 🖼️ 이미지 분석
319
- - 🔗 링크 요약
320
-
321
- ## 라이선스
150
+ ## 📄 라이선스
322
151
 
323
152
  [MIT](LICENSE)
324
153
 
325
154
  ---
326
155
 
327
- **문제가 있거나 기능 요청이 있으면** [Issues](https://github.com/DinN0000/CompanionBot/issues)에 등록해주세요!
156
+ **Issues**: [github.com/DinN0000/CompanionBot/issues](https://github.com/DinN0000/CompanionBot/issues)
@@ -8,6 +8,11 @@
8
8
  */
9
9
  import Anthropic from "@anthropic-ai/sdk";
10
10
  import { randomUUID } from "crypto";
11
+ // ===== 제한 상수 =====
12
+ const MAX_CONCURRENT_AGENTS = 10; // 전체 동시 Agent 최대 개수
13
+ const MAX_AGENTS_PER_CHAT = 3; // chatId당 최대 동시 Agent 개수
14
+ const CLEANUP_INTERVAL_MS = 30 * 60 * 1000; // 30분마다 cleanup
15
+ const AGENT_TTL_MS = 30 * 60 * 1000; // Agent 보관 시간 (30분)
11
16
  // Agent 저장소
12
17
  const agents = new Map();
13
18
  // AbortController 저장소 (실행 중인 API 호출 취소용)
@@ -28,10 +33,50 @@ function getClient() {
28
33
  export function setAgentBot(bot) {
29
34
  botInstance = bot;
30
35
  }
36
+ /**
37
+ * 가장 오래된 Agent 정리 (한도 초과 시)
38
+ */
39
+ function evictOldestAgent() {
40
+ let oldest = null;
41
+ for (const agent of agents.values()) {
42
+ if (!oldest || agent.createdAt < oldest.createdAt) {
43
+ oldest = agent;
44
+ }
45
+ }
46
+ if (oldest) {
47
+ console.log(`[AgentManager] Evicting oldest agent: ${oldest.id}`);
48
+ // running이면 취소
49
+ if (oldest.status === "running") {
50
+ cancelAgent(oldest.id);
51
+ }
52
+ agents.delete(oldest.id);
53
+ }
54
+ }
55
+ /**
56
+ * chatId당 Agent 개수 확인
57
+ */
58
+ function countAgentsForChat(chatId) {
59
+ let count = 0;
60
+ for (const agent of agents.values()) {
61
+ if (agent.chatId === chatId && agent.status === "running") {
62
+ count++;
63
+ }
64
+ }
65
+ return count;
66
+ }
31
67
  /**
32
68
  * Sub-agent 생성 및 실행
33
69
  */
34
70
  export async function spawnAgent(task, chatId) {
71
+ // chatId당 제한 확인
72
+ const chatAgentCount = countAgentsForChat(chatId);
73
+ if (chatAgentCount >= MAX_AGENTS_PER_CHAT) {
74
+ throw new Error(`이 채팅에서 동시에 실행 가능한 Agent 수(${MAX_AGENTS_PER_CHAT}개)를 초과했습니다. 기존 Agent 완료를 기다려주세요.`);
75
+ }
76
+ // 전체 한도 확인 및 정리
77
+ while (agents.size >= MAX_CONCURRENT_AGENTS) {
78
+ evictOldestAgent();
79
+ }
35
80
  const id = randomUUID().slice(0, 8);
36
81
  const agent = {
37
82
  id,
@@ -41,6 +86,7 @@ export async function spawnAgent(task, chatId) {
41
86
  createdAt: new Date(),
42
87
  };
43
88
  agents.set(id, agent);
89
+ console.log(`[AgentManager] Agent created: ${id} (total: ${agents.size}/${MAX_CONCURRENT_AGENTS})`);
44
90
  // 비동기로 agent 실행 (await 하지 않음)
45
91
  runAgent(agent).catch((err) => {
46
92
  console.error(`[Agent ${id}] Error:`, err);
@@ -183,35 +229,47 @@ export function getAgent(agentId) {
183
229
  return agents.get(agentId);
184
230
  }
185
231
  /**
186
- * 오래된 agent 정리 (1시간 이상)
187
- * - 완료된 agent: completedAt 기준 1시간
188
- * - running 상태도 createdAt 기준 1시간 지나면 정리 (stuck 방지)
232
+ * 오래된 agent 정리 (30분 이상)
233
+ * - 완료된 agent: completedAt 기준 30분
234
+ * - running 상태도 createdAt 기준 30분 지나면 정리 (stuck 방지)
189
235
  */
190
236
  export function cleanupOldAgents() {
191
- const oneHourAgo = Date.now() - 60 * 60 * 1000;
237
+ const cutoff = Date.now() - AGENT_TTL_MS;
238
+ let cleaned = 0;
192
239
  for (const [id, agent] of agents.entries()) {
193
240
  // 완료된 agent: completedAt 기준
194
- if (agent.completedAt && agent.completedAt.getTime() < oneHourAgo) {
241
+ if (agent.completedAt && agent.completedAt.getTime() < cutoff) {
195
242
  agents.delete(id);
243
+ cleaned++;
196
244
  continue;
197
245
  }
198
- // running 상태도 1시간 지나면 정리 (stuck agent 방지)
199
- if (agent.status === "running" && agent.createdAt.getTime() < oneHourAgo) {
200
- console.log(`[Agent ${id}] Cleaning up stuck agent (running > 1h)`);
246
+ // running 상태도 TTL 지나면 정리 (stuck agent 방지)
247
+ if (agent.status === "running" && agent.createdAt.getTime() < cutoff) {
248
+ console.log(`[Agent ${id}] Cleaning up stuck agent (running > 30min)`);
249
+ // 실행 중인 API 호출 취소
250
+ const controller = abortControllers.get(id);
251
+ if (controller) {
252
+ controller.abort();
253
+ abortControllers.delete(id);
254
+ }
201
255
  agents.delete(id);
256
+ cleaned++;
202
257
  }
203
258
  }
259
+ if (cleaned > 0) {
260
+ console.log(`[AgentManager] Cleanup: removed ${cleaned} agents (remaining: ${agents.size})`);
261
+ }
204
262
  }
205
263
  // Cleanup interval 참조 저장
206
264
  let cleanupIntervalId = null;
207
265
  /**
208
- * 정기 cleanup 시작
266
+ * 정기 cleanup 시작 (30분 주기)
209
267
  */
210
268
  export function startCleanup() {
211
269
  if (cleanupIntervalId)
212
270
  return; // 이미 실행 중
213
- cleanupIntervalId = setInterval(cleanupOldAgents, 10 * 60 * 1000);
214
- console.log("[AgentManager] Cleanup interval started");
271
+ cleanupIntervalId = setInterval(cleanupOldAgents, CLEANUP_INTERVAL_MS);
272
+ console.log(`[AgentManager] Cleanup interval started (every ${CLEANUP_INTERVAL_MS / 60000}min)`);
215
273
  }
216
274
  /**
217
275
  * 정기 cleanup 중지
@@ -72,9 +72,11 @@ export function createBot(token) {
72
72
  // 명령어 목록 등록
73
73
  bot.api
74
74
  .setMyCommands([
75
+ { command: "help", description: "도움말 보기" },
76
+ { command: "model", description: "AI 모델 변경" },
75
77
  { command: "compact", description: "대화 정리하기" },
76
78
  { command: "memory", description: "최근 기억 보기" },
77
- { command: "reminders", description: "알림 목록 보기" },
79
+ { command: "health", description: " 상태 확인" },
78
80
  ])
79
81
  .catch((err) => console.error("Failed to set commands:", err));
80
82
  // 핸들러 등록
@@ -60,6 +60,32 @@ import { setHeartbeatConfig, getHeartbeatConfig, disableHeartbeat, } from "../..
60
60
  import { getWorkspace, invalidateWorkspaceCache, buildSystemPrompt, extractName, } from "../utils/index.js";
61
61
  import { ensureDefaultCronJobs } from "../../cron/scheduler.js";
62
62
  export function registerCommands(bot) {
63
+ // /help 명령어 - 전체 기능 안내
64
+ bot.command("help", async (ctx) => {
65
+ await ctx.reply(`📖 도움말\n\n` +
66
+ `🎯 기본 기능\n` +
67
+ `/model - AI 모델 변경 (sonnet/opus/haiku)\n` +
68
+ `/compact - 대화 압축해서 토큰 절약\n` +
69
+ `/clear - 대화 초기화\n\n` +
70
+ `📌 기억/핀\n` +
71
+ `/memory - 최근 기억 보기\n` +
72
+ `/pin [내용] - 중요한 정보 핀하기\n` +
73
+ `/pins - 핀 목록 보기\n` +
74
+ `/context - 현재 맥락 상태\n\n` +
75
+ `⏰ 알림/일정\n` +
76
+ `/reminders - 알림 목록\n` +
77
+ `/briefing - 일일 브리핑 켜기/상태\n` +
78
+ `/calendar - 오늘 일정 보기\n\n` +
79
+ `⚙️ 설정\n` +
80
+ `/setup - 기능별 설정 관리\n` +
81
+ `/health - 봇 상태 확인\n` +
82
+ `/reset - 페르소나 초기화\n\n` +
83
+ `💡 자연어로도 말할 수 있어요:\n` +
84
+ `• "opus로 바꿔줘"\n` +
85
+ `• "10분 뒤에 알려줘"\n` +
86
+ `• "기억해: 나는 채식주의자야"\n` +
87
+ `• "내일 일정 뭐야?"`);
88
+ });
63
89
  // /start 명령어
64
90
  bot.command("start", async (ctx) => {
65
91
  const chatId = ctx.chat.id;
@@ -210,19 +236,19 @@ export function registerCommands(bot) {
210
236
  const modelList = Object.entries(MODELS)
211
237
  .map(([id, m]) => `${id === currentModel ? "→" : " "} /model ${id} - ${m.name}`)
212
238
  .join("\n");
213
- await ctx.reply(`Current model: ${MODELS[currentModel].name}\n\n` +
214
- `Available models:\n${modelList}\n\n` +
215
- `Tip: You can also ask me to change models in natural language!`);
239
+ await ctx.reply(`현재 모델: ${MODELS[currentModel].name}\n\n` +
240
+ `사용 가능한 모델:\n${modelList}\n\n` +
241
+ `팁: "모델 바꿔줘"처럼 자연어로도 바꿀 있어!`);
216
242
  return;
217
243
  }
218
244
  if (arg in MODELS) {
219
245
  const modelId = arg;
220
246
  setModel(chatId, modelId);
221
- await ctx.reply(`Model changed to: ${MODELS[modelId].name}`);
247
+ await ctx.reply(`모델 변경됨: ${MODELS[modelId].name}`);
222
248
  }
223
249
  else {
224
- await ctx.reply(`Unknown model: ${arg}\n\n` +
225
- `Available: sonnet, opus, haiku`);
250
+ await ctx.reply(`모르는 모델이야: ${arg}\n\n` +
251
+ `사용 가능: sonnet, opus, haiku`);
226
252
  }
227
253
  });
228
254
  // /setup 명령어 - 추가 기능 설정 및 관리