tiger-agent 0.2.0
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/.env.example +22 -0
- package/.env.secrets.example +14 -0
- package/LICENSE +22 -0
- package/README.md +284 -0
- package/bin/tiger.js +96 -0
- package/package.json +58 -0
- package/scripts/audit.sh +54 -0
- package/scripts/backup.sh +42 -0
- package/scripts/cryptoEnv.js +57 -0
- package/scripts/decrypt-env.js +34 -0
- package/scripts/encrypt-env.js +34 -0
- package/scripts/migrate-vector-db.js +44 -0
- package/scripts/onboard.js +319 -0
- package/scripts/scan-secrets.sh +87 -0
- package/scripts/setup.js +302 -0
- package/scripts/sqlite_memory.py +297 -0
- package/scripts/sqlite_vec_setup.py +112 -0
- package/src/agent/contextFiles.js +30 -0
- package/src/agent/db.js +349 -0
- package/src/agent/mainAgent.js +406 -0
- package/src/agent/reflectionAgent.js +193 -0
- package/src/agent/reflectionScheduler.js +21 -0
- package/src/agent/skills.js +169 -0
- package/src/agent/subAgent.js +39 -0
- package/src/agent/toolbox.js +291 -0
- package/src/apiProviders.js +217 -0
- package/src/cli.js +187 -0
- package/src/config.js +141 -0
- package/src/kimiClient.js +88 -0
- package/src/llmClient.js +147 -0
- package/src/telegram/bot.js +182 -0
- package/src/telegram/supervisor.js +84 -0
- package/src/tokenManager.js +223 -0
- package/src/utils.js +30 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
import argparse
|
|
3
|
+
import glob
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
import sqlite3
|
|
7
|
+
import subprocess
|
|
8
|
+
import sys
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def run(cmd):
|
|
13
|
+
return subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def discover_extension_path() -> str:
|
|
17
|
+
try:
|
|
18
|
+
import sqlite_vec # type: ignore
|
|
19
|
+
except Exception:
|
|
20
|
+
return ""
|
|
21
|
+
|
|
22
|
+
# Preferred API if present.
|
|
23
|
+
loadable_fn = getattr(sqlite_vec, "loadable_path", None)
|
|
24
|
+
if callable(loadable_fn):
|
|
25
|
+
try:
|
|
26
|
+
path = str(loadable_fn() or "").strip()
|
|
27
|
+
if path and os.path.exists(path):
|
|
28
|
+
return path
|
|
29
|
+
except Exception:
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
module_file = Path(getattr(sqlite_vec, "__file__", "")).resolve()
|
|
33
|
+
if not module_file.exists():
|
|
34
|
+
return ""
|
|
35
|
+
base = module_file.parent
|
|
36
|
+
patterns = ["*vec*.so", "*vec*.dylib", "*vec*.dll", "*.so", "*.dylib", "*.dll"]
|
|
37
|
+
for pat in patterns:
|
|
38
|
+
for candidate in glob.glob(str(base / pat)):
|
|
39
|
+
if os.path.isfile(candidate):
|
|
40
|
+
return candidate
|
|
41
|
+
return ""
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_load(path: str):
|
|
45
|
+
if not path:
|
|
46
|
+
return False, "extension path is empty"
|
|
47
|
+
conn = sqlite3.connect(":memory:")
|
|
48
|
+
try:
|
|
49
|
+
conn.enable_load_extension(True)
|
|
50
|
+
conn.load_extension(path)
|
|
51
|
+
return True, ""
|
|
52
|
+
except Exception as err:
|
|
53
|
+
return False, str(err)
|
|
54
|
+
finally:
|
|
55
|
+
try:
|
|
56
|
+
conn.enable_load_extension(False)
|
|
57
|
+
except Exception:
|
|
58
|
+
pass
|
|
59
|
+
conn.close()
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def set_env_sqlite_vec(env_path: str, extension_path: str):
|
|
63
|
+
p = Path(env_path)
|
|
64
|
+
text = p.read_text(encoding="utf-8") if p.exists() else ""
|
|
65
|
+
lines = text.splitlines()
|
|
66
|
+
replaced = False
|
|
67
|
+
out = []
|
|
68
|
+
for line in lines:
|
|
69
|
+
if line.startswith("SQLITE_VEC_EXTENSION="):
|
|
70
|
+
out.append(f"SQLITE_VEC_EXTENSION={extension_path}")
|
|
71
|
+
replaced = True
|
|
72
|
+
else:
|
|
73
|
+
out.append(line)
|
|
74
|
+
if not replaced:
|
|
75
|
+
out.append(f"SQLITE_VEC_EXTENSION={extension_path}")
|
|
76
|
+
p.write_text("\n".join(out).rstrip() + "\n", encoding="utf-8")
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def main() -> int:
|
|
80
|
+
parser = argparse.ArgumentParser()
|
|
81
|
+
parser.add_argument("--install", action="store_true", help="Attempt pip install sqlite-vec first")
|
|
82
|
+
parser.add_argument("--write-env", action="store_true", help="Write SQLITE_VEC_EXTENSION to .env")
|
|
83
|
+
parser.add_argument("--env-path", default=".env")
|
|
84
|
+
parser.add_argument("--extension-path", default="")
|
|
85
|
+
args = parser.parse_args()
|
|
86
|
+
|
|
87
|
+
install_error = ""
|
|
88
|
+
if args.install:
|
|
89
|
+
res = run([sys.executable, "-m", "pip", "install", "sqlite-vec"])
|
|
90
|
+
if res.returncode != 0:
|
|
91
|
+
install_error = (res.stderr or res.stdout or "").strip()
|
|
92
|
+
|
|
93
|
+
extension_path = args.extension_path.strip() or discover_extension_path()
|
|
94
|
+
ok, load_error = test_load(extension_path)
|
|
95
|
+
|
|
96
|
+
if ok and args.write_env:
|
|
97
|
+
set_env_sqlite_vec(args.env_path, extension_path)
|
|
98
|
+
|
|
99
|
+
out = {
|
|
100
|
+
"ok": ok,
|
|
101
|
+
"sqlite_version": sqlite3.sqlite_version,
|
|
102
|
+
"extension_path": extension_path,
|
|
103
|
+
"loaded": ok,
|
|
104
|
+
"load_error": load_error,
|
|
105
|
+
"install_error": install_error,
|
|
106
|
+
}
|
|
107
|
+
sys.stdout.write(json.dumps(out))
|
|
108
|
+
return 0 if ok else 1
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
if __name__ == "__main__":
|
|
112
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { dataDir } = require('../config');
|
|
4
|
+
const { ensureDir } = require('../utils');
|
|
5
|
+
|
|
6
|
+
const files = ['soul.md', 'human.md', 'human2.md', 'ownskill.md'];
|
|
7
|
+
|
|
8
|
+
function ensureContextFiles() {
|
|
9
|
+
ensureDir(dataDir);
|
|
10
|
+
for (const name of files) {
|
|
11
|
+
const full = path.join(dataDir, name);
|
|
12
|
+
if (!fs.existsSync(full)) {
|
|
13
|
+
fs.writeFileSync(full, `# ${name.replace('.md', '')}\n\n`, 'utf8');
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function loadContextFiles() {
|
|
19
|
+
ensureContextFiles();
|
|
20
|
+
return files.map((name) => {
|
|
21
|
+
const full = path.join(dataDir, name);
|
|
22
|
+
const content = fs.readFileSync(full, 'utf8');
|
|
23
|
+
return { name, full, content };
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
module.exports = {
|
|
28
|
+
ensureContextFiles,
|
|
29
|
+
loadContextFiles
|
|
30
|
+
};
|
package/src/agent/db.js
ADDED
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { execFileSync } = require('child_process');
|
|
4
|
+
const { ensureDir, cosineSimilarity } = require('../utils');
|
|
5
|
+
const { dbPath, maxMessages, recentMessages, vectorDbPath, sqliteVecExtension } = require('../config');
|
|
6
|
+
|
|
7
|
+
ensureDir(path.dirname(dbPath));
|
|
8
|
+
ensureDir(path.dirname(vectorDbPath));
|
|
9
|
+
|
|
10
|
+
const sqliteMemoryScript = path.resolve(process.cwd(), 'scripts', 'sqlite_memory.py');
|
|
11
|
+
let sqliteMemoryReady = false;
|
|
12
|
+
let sqliteVecLoaded = false;
|
|
13
|
+
let sqliteInitError = '';
|
|
14
|
+
|
|
15
|
+
function runSqliteMemory(args) {
|
|
16
|
+
if (!fs.existsSync(sqliteMemoryScript)) {
|
|
17
|
+
throw new Error('sqlite memory helper script is missing');
|
|
18
|
+
}
|
|
19
|
+
const out = execFileSync('python3', [sqliteMemoryScript, ...args], {
|
|
20
|
+
encoding: 'utf8',
|
|
21
|
+
stdio: ['ignore', 'pipe', 'pipe']
|
|
22
|
+
});
|
|
23
|
+
const parsed = JSON.parse(String(out || '{}'));
|
|
24
|
+
if (parsed && parsed.ok === false) {
|
|
25
|
+
throw new Error(String(parsed.error || 'sqlite memory helper failed'));
|
|
26
|
+
}
|
|
27
|
+
return parsed;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function ensureSqliteMemoryReady() {
|
|
31
|
+
if (sqliteMemoryReady) return;
|
|
32
|
+
try {
|
|
33
|
+
const args = ['init', '--db', vectorDbPath];
|
|
34
|
+
if (sqliteVecExtension) {
|
|
35
|
+
args.push('--vec-ext', sqliteVecExtension);
|
|
36
|
+
}
|
|
37
|
+
const initResult = runSqliteMemory(args);
|
|
38
|
+
sqliteVecLoaded = Boolean(initResult.vec_loaded);
|
|
39
|
+
sqliteInitError = String(initResult.vec_error || '');
|
|
40
|
+
sqliteMemoryReady = true;
|
|
41
|
+
} catch (err) {
|
|
42
|
+
sqliteMemoryReady = false;
|
|
43
|
+
sqliteVecLoaded = false;
|
|
44
|
+
sqliteInitError = String(err.message || err);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function initVectorMemory() {
|
|
49
|
+
ensureSqliteMemoryReady();
|
|
50
|
+
if (sqliteMemoryReady) {
|
|
51
|
+
let counts = null;
|
|
52
|
+
try {
|
|
53
|
+
const statsResult = runSqliteMemory(['stats', '--db', vectorDbPath]);
|
|
54
|
+
counts = statsResult && statsResult.counts ? statsResult.counts : null;
|
|
55
|
+
} catch (err) {
|
|
56
|
+
counts = null;
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
ok: true,
|
|
60
|
+
backend: 'sqlite',
|
|
61
|
+
dbPath: vectorDbPath,
|
|
62
|
+
sqliteVecLoaded,
|
|
63
|
+
sqliteVecExtension,
|
|
64
|
+
sqliteInitError,
|
|
65
|
+
counts
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
ok: false,
|
|
70
|
+
backend: 'json-fallback',
|
|
71
|
+
dbPath: dbPath,
|
|
72
|
+
sqliteVecLoaded: false,
|
|
73
|
+
sqliteVecExtension,
|
|
74
|
+
sqliteInitError,
|
|
75
|
+
counts: null
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function defaultState() {
|
|
80
|
+
return {
|
|
81
|
+
conversations: {},
|
|
82
|
+
messages: [],
|
|
83
|
+
memories: [],
|
|
84
|
+
meta: {}
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function loadState() {
|
|
89
|
+
if (!fs.existsSync(dbPath)) {
|
|
90
|
+
return defaultState();
|
|
91
|
+
}
|
|
92
|
+
try {
|
|
93
|
+
const parsed = JSON.parse(fs.readFileSync(dbPath, 'utf8'));
|
|
94
|
+
if (!parsed || typeof parsed !== 'object') return defaultState();
|
|
95
|
+
return {
|
|
96
|
+
conversations: parsed.conversations || {},
|
|
97
|
+
messages: Array.isArray(parsed.messages) ? parsed.messages : [],
|
|
98
|
+
memories: Array.isArray(parsed.memories) ? parsed.memories : [],
|
|
99
|
+
meta: parsed.meta && typeof parsed.meta === 'object' ? parsed.meta : {}
|
|
100
|
+
};
|
|
101
|
+
} catch (err) {
|
|
102
|
+
return defaultState();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const state = loadState();
|
|
107
|
+
|
|
108
|
+
function saveState() {
|
|
109
|
+
fs.writeFileSync(dbPath, JSON.stringify(state, null, 2), 'utf8');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function now() {
|
|
113
|
+
return Date.now();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function conversationId(platform, userId) {
|
|
117
|
+
return `${platform}:${userId}`;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function nextId(rows) {
|
|
121
|
+
if (!rows.length) return 1;
|
|
122
|
+
return rows[rows.length - 1].id + 1;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function ensureConversation(platform, userId) {
|
|
126
|
+
const id = conversationId(platform, userId);
|
|
127
|
+
const ts = now();
|
|
128
|
+
const existing = state.conversations[id];
|
|
129
|
+
if (existing) {
|
|
130
|
+
existing.updated_at = ts;
|
|
131
|
+
} else {
|
|
132
|
+
state.conversations[id] = {
|
|
133
|
+
id,
|
|
134
|
+
platform,
|
|
135
|
+
user_id: userId,
|
|
136
|
+
created_at: ts,
|
|
137
|
+
updated_at: ts
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
saveState();
|
|
141
|
+
return id;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function addMessage(conversationIdValue, role, content) {
|
|
145
|
+
state.messages.push({
|
|
146
|
+
id: nextId(state.messages),
|
|
147
|
+
conversation_id: conversationIdValue,
|
|
148
|
+
role,
|
|
149
|
+
content,
|
|
150
|
+
created_at: now()
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
if (state.conversations[conversationIdValue]) {
|
|
154
|
+
state.conversations[conversationIdValue].updated_at = now();
|
|
155
|
+
}
|
|
156
|
+
saveState();
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function getRecentMessages(conversationIdValue, limit = recentMessages) {
|
|
160
|
+
return state.messages
|
|
161
|
+
.filter((m) => m.conversation_id === conversationIdValue)
|
|
162
|
+
.slice(-limit)
|
|
163
|
+
.map((m) => ({ role: m.role, content: m.content, created_at: m.created_at }));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function getMessageCount(conversationIdValue) {
|
|
167
|
+
return state.messages.filter((m) => m.conversation_id === conversationIdValue).length;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function getMessagesForCompaction(conversationIdValue) {
|
|
171
|
+
const count = getMessageCount(conversationIdValue);
|
|
172
|
+
if (count <= maxMessages) return [];
|
|
173
|
+
const toCompact = Math.max(0, count - recentMessages);
|
|
174
|
+
if (!toCompact) return [];
|
|
175
|
+
return state.messages
|
|
176
|
+
.filter((m) => m.conversation_id === conversationIdValue)
|
|
177
|
+
.slice(0, toCompact)
|
|
178
|
+
.map((m) => ({ id: m.id, role: m.role, content: m.content }));
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function deleteMessagesUpTo(conversationIdValue, maxId) {
|
|
182
|
+
state.messages = state.messages.filter((m) => {
|
|
183
|
+
if (m.conversation_id !== conversationIdValue) return true;
|
|
184
|
+
return m.id > maxId;
|
|
185
|
+
});
|
|
186
|
+
saveState();
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function addMemory(conversationIdValue, source, content, embedding) {
|
|
190
|
+
const createdAt = now();
|
|
191
|
+
ensureSqliteMemoryReady();
|
|
192
|
+
|
|
193
|
+
if (sqliteMemoryReady) {
|
|
194
|
+
try {
|
|
195
|
+
runSqliteMemory([
|
|
196
|
+
'add',
|
|
197
|
+
'--db',
|
|
198
|
+
vectorDbPath,
|
|
199
|
+
'--conversation-id',
|
|
200
|
+
String(conversationIdValue || ''),
|
|
201
|
+
'--source',
|
|
202
|
+
String(source || ''),
|
|
203
|
+
'--content',
|
|
204
|
+
String(content || ''),
|
|
205
|
+
'--embedding-json',
|
|
206
|
+
JSON.stringify(Array.isArray(embedding) ? embedding : []),
|
|
207
|
+
'--created-at',
|
|
208
|
+
String(createdAt)
|
|
209
|
+
]);
|
|
210
|
+
return;
|
|
211
|
+
} catch (err) {
|
|
212
|
+
// Fall back to legacy JSON memory if sqlite path is unavailable.
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
state.memories.push({
|
|
217
|
+
id: nextId(state.memories),
|
|
218
|
+
conversation_id: conversationIdValue,
|
|
219
|
+
source,
|
|
220
|
+
content,
|
|
221
|
+
embedding,
|
|
222
|
+
created_at: createdAt
|
|
223
|
+
});
|
|
224
|
+
saveState();
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function getMeta(key, fallback = null) {
|
|
228
|
+
if (!key) return fallback;
|
|
229
|
+
if (!Object.prototype.hasOwnProperty.call(state.meta, key)) return fallback;
|
|
230
|
+
return state.meta[key];
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function setMeta(key, value) {
|
|
234
|
+
if (!key) return;
|
|
235
|
+
state.meta[key] = value;
|
|
236
|
+
saveState();
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function getRecentMessagesAll(limit = 200) {
|
|
240
|
+
return state.messages.slice(-limit).map((m) => ({
|
|
241
|
+
conversation_id: m.conversation_id,
|
|
242
|
+
role: m.role,
|
|
243
|
+
content: m.content,
|
|
244
|
+
created_at: m.created_at
|
|
245
|
+
}));
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function getMessagesSince(sinceTs, limit = 500) {
|
|
249
|
+
const threshold = Number(sinceTs || 0);
|
|
250
|
+
return state.messages
|
|
251
|
+
.filter((m) => Number(m.created_at || 0) > threshold)
|
|
252
|
+
.slice(-limit)
|
|
253
|
+
.map((m) => ({
|
|
254
|
+
conversation_id: m.conversation_id,
|
|
255
|
+
role: m.role,
|
|
256
|
+
content: m.content,
|
|
257
|
+
created_at: m.created_at
|
|
258
|
+
}));
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function getRelevantMemories(conversationIdValue, queryEmbedding, limit = 6) {
|
|
262
|
+
ensureSqliteMemoryReady();
|
|
263
|
+
if (sqliteMemoryReady) {
|
|
264
|
+
try {
|
|
265
|
+
const result = runSqliteMemory([
|
|
266
|
+
'search',
|
|
267
|
+
'--db',
|
|
268
|
+
vectorDbPath,
|
|
269
|
+
'--conversation-id',
|
|
270
|
+
String(conversationIdValue || ''),
|
|
271
|
+
'--query-embedding-json',
|
|
272
|
+
JSON.stringify(Array.isArray(queryEmbedding) ? queryEmbedding : []),
|
|
273
|
+
'--limit',
|
|
274
|
+
String(limit),
|
|
275
|
+
'--min-score',
|
|
276
|
+
'0.1',
|
|
277
|
+
'--window',
|
|
278
|
+
'600'
|
|
279
|
+
]);
|
|
280
|
+
const rows = Array.isArray(result.rows) ? result.rows : [];
|
|
281
|
+
if (rows.length) {
|
|
282
|
+
return rows;
|
|
283
|
+
}
|
|
284
|
+
} catch (err) {
|
|
285
|
+
// Fall through to JSON fallback.
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return state.memories
|
|
290
|
+
.filter(
|
|
291
|
+
(m) =>
|
|
292
|
+
m.conversation_id === conversationIdValue ||
|
|
293
|
+
m.conversation_id === 'global' ||
|
|
294
|
+
m.source === 'self_reflection'
|
|
295
|
+
)
|
|
296
|
+
.slice(-300)
|
|
297
|
+
.map((m) => ({
|
|
298
|
+
id: m.id,
|
|
299
|
+
source: m.source,
|
|
300
|
+
content: m.content,
|
|
301
|
+
created_at: m.created_at,
|
|
302
|
+
score: cosineSimilarity(queryEmbedding, m.embedding || [])
|
|
303
|
+
}))
|
|
304
|
+
.filter((m) => m.score > 0.1)
|
|
305
|
+
.sort((a, b) => b.score - a.score)
|
|
306
|
+
.slice(0, limit);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function recordSkillUsage(name, provider = 'tool') {
|
|
310
|
+
const skillName = String(name || '').trim();
|
|
311
|
+
if (!skillName) return;
|
|
312
|
+
ensureSqliteMemoryReady();
|
|
313
|
+
if (!sqliteMemoryReady) return;
|
|
314
|
+
try {
|
|
315
|
+
runSqliteMemory([
|
|
316
|
+
'upsert-skill',
|
|
317
|
+
'--db',
|
|
318
|
+
vectorDbPath,
|
|
319
|
+
'--name',
|
|
320
|
+
skillName,
|
|
321
|
+
'--provider',
|
|
322
|
+
String(provider || 'tool'),
|
|
323
|
+
'--enabled',
|
|
324
|
+
'1',
|
|
325
|
+
'--updated-at',
|
|
326
|
+
String(now())
|
|
327
|
+
]);
|
|
328
|
+
} catch (err) {
|
|
329
|
+
// Non-blocking telemetry.
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
module.exports = {
|
|
334
|
+
db: state,
|
|
335
|
+
ensureConversation,
|
|
336
|
+
addMessage,
|
|
337
|
+
getRecentMessages,
|
|
338
|
+
getMessageCount,
|
|
339
|
+
getMessagesForCompaction,
|
|
340
|
+
deleteMessagesUpTo,
|
|
341
|
+
addMemory,
|
|
342
|
+
getRelevantMemories,
|
|
343
|
+
getMeta,
|
|
344
|
+
setMeta,
|
|
345
|
+
getRecentMessagesAll,
|
|
346
|
+
getMessagesSince,
|
|
347
|
+
initVectorMemory,
|
|
348
|
+
recordSkillUsage
|
|
349
|
+
};
|