companionbot 0.16.0 → 0.16.2

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.
@@ -3,7 +3,7 @@ import { recordActivity, recordError } from "../../health/index.js";
3
3
  import { getHistory, getModel, getThinkingLevel, runWithChatId, trimHistoryByTokens, smartTrimHistory, detectImportantContext, pinContext, addMessage, } from "../../session/state.js";
4
4
  import * as persistence from "../../session/persistence.js";
5
5
  import { updateLastMessageTime } from "../../heartbeat/index.js";
6
- import { extractUrls, fetchWebContent, formatUrlContent, buildSystemPrompt, } from "../utils/index.js";
6
+ import { extractUrls, fetchWebContent, formatUrlContent, buildSystemPrompt, formatMessageTimestamp, } from "../utils/index.js";
7
7
  import { estimateMessagesTokens } from "../../utils/tokens.js";
8
8
  import { TOKENS, TELEGRAM } from "../../config/constants.js";
9
9
  import { toUserFriendlyError } from "../../utils/retry.js";
@@ -155,8 +155,9 @@ export function registerMessageHandlers(bot) {
155
155
  const response = await fetch(fileUrl);
156
156
  const buffer = await response.arrayBuffer();
157
157
  const base64 = Buffer.from(buffer).toString("base64");
158
- // 캡션이 있으면 사용, 없으면 기본 질문
159
- const caption = ctx.message.caption || "이 사진에 뭐가 있어?";
158
+ // 캡션이 있으면 사용, 없으면 기본 질문 (타임스탬프 포함)
159
+ const rawCaption = ctx.message.caption || "이 사진에 뭐가 있어?";
160
+ const caption = `${formatMessageTimestamp()} ${rawCaption}`;
160
161
  // 이미지와 텍스트를 함께 전송
161
162
  const imageContent = [
162
163
  {
@@ -174,7 +175,7 @@ export function registerMessageHandlers(bot) {
174
175
  ];
175
176
  // API용 메모리 히스토리에는 이미지 데이터 포함
176
177
  history.push({ role: "user", content: imageContent });
177
- // JSONL에는 캡션만 저장 (이미지 base64는 너무 큼)
178
+ // JSONL에는 캡션만 저장 (이미지 base64는 너무 큼) - caption에 이미 타임스탬프 포함
178
179
  persistence.appendMessage(chatId, "user", `[이미지] ${caption}`);
179
180
  try {
180
181
  const systemPrompt = await buildSystemPrompt(modelId, history);
@@ -267,8 +268,9 @@ export function registerMessageHandlers(bot) {
267
268
  messageForHistory = userMessage + "\n\n" + urlRefs.join("\n");
268
269
  }
269
270
  }
270
- // 히스토리에는 간략 버전 저장 + JSONL에 영구 저장
271
- addMessage(chatId, "user", messageForHistory);
271
+ // 히스토리에는 간략 버전 저장 + JSONL에 영구 저장 (타임스탬프 포함)
272
+ const timestampedMessage = formatMessageTimestamp() + " " + messageForHistory;
273
+ addMessage(chatId, "user", timestampedMessage);
272
274
  // Typing indicator 시작 (긴 작업 동안 유지)
273
275
  const typingIndicator = new TypingIndicator(ctx);
274
276
  typingIndicator.start();
@@ -4,3 +4,5 @@ export { extractUrls, fetchWebContent, isSafeUrl, formatUrlContent, clearUrlCach
4
4
  export { buildSystemPrompt, extractName } from "./prompt.js";
5
5
  // Cache utilities
6
6
  export { getWorkspace, invalidateWorkspaceCache, preloadWorkspace, isWorkspaceCached } from "./cache.js";
7
+ // Timestamp utilities
8
+ export { formatMessageTimestamp, addTimestampToMessage } from "./timestamp.js";
@@ -0,0 +1,43 @@
1
+ /**
2
+ * 메시지에 타임스탬프를 추가하는 유틸리티
3
+ * LLM이 시간 순서와 오늘/어제를 구분할 수 있도록 함
4
+ */
5
+ /**
6
+ * 현재 시간을 간결한 형식으로 반환
7
+ * 예: "[10:35]" 또는 "[어제 23:15]"
8
+ */
9
+ export function formatMessageTimestamp(date = new Date()) {
10
+ const now = new Date();
11
+ const isToday = date.toDateString() === now.toDateString();
12
+ const yesterday = new Date(now);
13
+ yesterday.setDate(yesterday.getDate() - 1);
14
+ const isYesterday = date.toDateString() === yesterday.toDateString();
15
+ const time = date.toLocaleTimeString("ko-KR", {
16
+ hour: "2-digit",
17
+ minute: "2-digit",
18
+ hour12: false,
19
+ timeZone: "Asia/Seoul",
20
+ });
21
+ if (isToday) {
22
+ return `[${time}]`;
23
+ }
24
+ else if (isYesterday) {
25
+ return `[어제 ${time}]`;
26
+ }
27
+ else {
28
+ // 더 오래된 경우 날짜도 포함
29
+ const dateStr = date.toLocaleDateString("ko-KR", {
30
+ month: "short",
31
+ day: "numeric",
32
+ timeZone: "Asia/Seoul",
33
+ });
34
+ return `[${dateStr} ${time}]`;
35
+ }
36
+ }
37
+ /**
38
+ * 메시지 내용에 타임스탬프 prefix 추가
39
+ */
40
+ export function addTimestampToMessage(content, date = new Date()) {
41
+ const timestamp = formatMessageTimestamp(date);
42
+ return `${timestamp} ${content}`;
43
+ }
@@ -100,11 +100,11 @@ export const compactTools = [
100
100
  },
101
101
  {
102
102
  name: "save_memory",
103
- description: "Save important info to daily memory (user_info, preference, event, etc.)",
103
+ description: "중요한 정보 저장. 사용자 정보, 선호도, 약속, 결정 나중에 기억해야 할 것들",
104
104
  input_schema: {
105
105
  type: "object",
106
106
  properties: {
107
- content: { type: "string", description: "Information to remember" },
107
+ content: { type: "string", description: "기억할 내용" },
108
108
  category: { type: "string", enum: ["user_info", "preference", "event", "project", "decision", "emotion", "other"] },
109
109
  },
110
110
  required: ["content"],
@@ -112,11 +112,11 @@ export const compactTools = [
112
112
  },
113
113
  {
114
114
  name: "memory_search",
115
- description: "Semantic search through long-term memories",
115
+ description: "과거 기억 검색. 이전에 나눈 대화, 저장한 정보, 사용자 선호도 등을 찾을 때 사용",
116
116
  input_schema: {
117
117
  type: "object",
118
118
  properties: {
119
- query: { type: "string" },
119
+ query: { type: "string", description: "검색할 내용" },
120
120
  limit: { type: "number", description: "Max results (default: 5)" },
121
121
  minScore: { type: "number", description: "Min similarity 0-1 (default: 0.3)" },
122
122
  },
@@ -131,11 +131,11 @@ export const compactTools = [
131
131
  // === 날씨/웹 ===
132
132
  {
133
133
  name: "get_weather",
134
- description: "Get current weather for a city",
134
+ description: "현재 날씨 조회. 외출, 옷차림, 우산 필요 여부 등 물어볼 때 사용",
135
135
  input_schema: {
136
136
  type: "object",
137
137
  properties: {
138
- city: { type: "string" },
138
+ city: { type: "string", description: "도시명 (예: Seoul, Tokyo)" },
139
139
  country: { type: "string", description: "Country code (optional)" },
140
140
  },
141
141
  required: ["city"],
@@ -143,11 +143,11 @@ export const compactTools = [
143
143
  },
144
144
  {
145
145
  name: "web_search",
146
- description: "Search web via Brave API",
146
+ description: " 검색 (Brave API). 최신 정보, 뉴스, 사실 확인이 필요할 때 사용. 검색어로 관련 결과 반환",
147
147
  input_schema: {
148
148
  type: "object",
149
149
  properties: {
150
- query: { type: "string" },
150
+ query: { type: "string", description: "검색어" },
151
151
  count: { type: "number", description: "Results count (default: 5, max: 20)" },
152
152
  },
153
153
  required: ["query"],
@@ -155,11 +155,11 @@ export const compactTools = [
155
155
  },
156
156
  {
157
157
  name: "web_fetch",
158
- description: "Extract readable content from URL",
158
+ description: "URL에서 웹페이지 본문 추출. 링크 내용 확인, 기사 읽기, 문서 요약 등에 사용. HTML → 텍스트 변환",
159
159
  input_schema: {
160
160
  type: "object",
161
161
  properties: {
162
- url: { type: "string" },
162
+ url: { type: "string", description: "가져올 웹페이지 URL" },
163
163
  maxChars: { type: "number", description: "Max chars (default: 5000)" },
164
164
  },
165
165
  required: ["url"],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "companionbot",
3
- "version": "0.16.0",
3
+ "version": "0.16.2",
4
4
  "description": "AI 친구 텔레그램 봇 - Claude API 기반 개인화된 대화 상대",
5
5
  "keywords": [
6
6
  "telegram",