teleton 0.8.5 → 0.8.6
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 +24 -5
- package/dist/{bootstrap-SPDT3XBQ.js → bootstrap-PFBH6ALD.js} +10 -7
- package/dist/bridge-guards-HZTNH7IB.js +9 -0
- package/dist/{chunk-FSL2MOYK.js → chunk-2UUGRY5B.js} +144 -163
- package/dist/{chunk-LM6AL6LN.js → chunk-4MFN75ZK.js} +4449 -2776
- package/dist/{chunk-GUX6ZFVF.js → chunk-4MG2AROG.js} +4 -6
- package/dist/{chunk-6U6VA2OT.js → chunk-6IFNQWIM.js} +7276 -7300
- package/dist/chunk-7KI25UJU.js +215 -0
- package/dist/chunk-AX5NBEHX.js +12 -0
- package/dist/{chunk-7ZXUUDQQ.js → chunk-BLUES3FJ.js} +79 -100
- package/dist/{chunk-KYSAHDYE.js → chunk-BT2I3ETV.js} +1 -1
- package/dist/chunk-CXTZPOTA.js +107 -0
- package/dist/{chunk-4KURCUWD.js → chunk-D3GT6YIY.js} +59 -7
- package/dist/chunk-EKCXKL5M.js +53 -0
- package/dist/{chunk-5K4YDCVU.js → chunk-F6S3L3OV.js} +3 -3
- package/dist/{chunk-L3LPVF4Z.js → chunk-J4WDJ7XS.js} +2 -2
- package/dist/{chunk-Z63KUQX4.js → chunk-JYF2MM5I.js} +120 -110
- package/dist/{chunk-NVKBBTI6.js → chunk-K3QSIIMZ.js} +9 -6
- package/dist/chunk-OMQIAWEU.js +273 -0
- package/dist/chunk-PCT7GYBP.js +274 -0
- package/dist/{chunk-35X3V6OW.js → chunk-QYZBWU2D.js} +5 -5
- package/dist/{chunk-WTDAICGT.js → chunk-R6W4DJRK.js} +7 -7
- package/dist/{chunk-5SEMA47R.js → chunk-RILOEIK6.js} +1 -1
- package/dist/{chunk-6OOHHJ4N.js → chunk-TFTNZZDH.js} +20 -20
- package/dist/chunk-TTOZCZWE.js +96 -0
- package/dist/chunk-UJ54YT2T.js +12 -0
- package/dist/{chunk-2MZP75SH.js → chunk-ULVL2W3D.js} +152 -256
- package/dist/{chunk-NQ6FZKCE.js → chunk-V3S3NXBQ.js} +3 -1
- package/dist/{chunk-H7MFXJZK.js → chunk-WSL4KIOI.js} +31 -26
- package/dist/{chunk-PK3TVFBT.js → chunk-Z5WY7BSB.js} +5 -5
- package/dist/{chunk-LD24DWWE.js → chunk-ZGKE3OTA.js} +110 -47
- package/dist/{chunk-M6M4DCDU.js → chunk-ZHRDETCX.js} +3 -3
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +263 -163
- package/dist/{client-G62EZT6U.js → client-S5UIK6OG.js} +9 -7
- package/dist/daily-logs-3WXGYAQF.js +25 -0
- package/dist/{get-my-gifts-Y7EN7RK4.js → get-my-gifts-3YSYM3LI.js} +3 -2
- package/dist/{harden-permissions-6BLHRCQJ.js → harden-permissions-PV5SGV5D.js} +1 -1
- package/dist/index.d.ts +923 -0
- package/dist/index.js +28 -20
- package/dist/knowledge-RRWUIO3G.js +19 -0
- package/dist/{local-HQ3UJ7KR.js → local-MSZAXWUL.js} +2 -2
- package/dist/mcp-loader-OELDFR63.js +15 -0
- package/dist/{memory-BJH724PQ.js → memory-6U6HGRK2.js} +22 -12
- package/dist/{memory-hook-LUAKTXU5.js → memory-hook-T7Y235KY.js} +7 -7
- package/dist/messages-KV5ADNJB.js +17 -0
- package/dist/{migrate-C4LBLOZH.js → migrate-AX3HOKOO.js} +9 -7
- package/dist/{server-4J56HS62.js → server-MFRYOGHR.js} +20 -16
- package/dist/{server-I6TYJ36S.js → server-SFLCAZFR.js} +220 -19
- package/dist/{setup-server-VJ3MGUSM.js → setup-server-YWAPKZVE.js} +14 -13
- package/dist/{store-2IGAMTES.js → store-PGHQASBC.js} +10 -8
- package/dist/{task-dependency-resolver-CQ432Z7J.js → task-dependency-resolver-YQKADDEU.js} +24 -10
- package/dist/{task-executor-JELRREUV.js → task-executor-LWAWD225.js} +4 -4
- package/dist/{tool-adapter-IVX2XQJE.js → tool-adapter-VKLUZSQS.js} +1 -1
- package/dist/{tool-index-XPCMWBYY.js → tool-index-YEWDF5CK.js} +4 -4
- package/dist/{transcript-OEO3HA4Z.js → transcript-4Y3Z2BJ3.js} +2 -2
- package/dist/web/assets/Config-MNxA69ib.js +1 -0
- package/dist/web/assets/Conversations-Dk958paA.js +1 -0
- package/dist/web/assets/Dashboard-dM18fGOm.js +1 -0
- package/dist/web/assets/Hooks-D2griQnI.js +1 -0
- package/dist/web/assets/Mcp-CtWNzwsz.js +1 -0
- package/dist/web/assets/Memory-CfLwH45G.js +1 -0
- package/dist/web/assets/Plugins-3hoJprFo.js +1 -0
- package/dist/web/assets/SearchInput-CpcETdpE.js +1 -0
- package/dist/web/assets/Soul-BSxE73aK.js +1 -0
- package/dist/web/assets/Tasks-DkCkfu3A.js +1 -0
- package/dist/web/assets/TelegramSettingsPanel-BRzc5G6e.js +1 -0
- package/dist/web/assets/Tools-Du8B8Mb4.js +1 -0
- package/dist/web/assets/Wallet-BLILP2Gn.js +1 -0
- package/dist/web/assets/Workspace-qklcXpXV.js +1 -0
- package/dist/web/assets/index-BwEPTTKp.js +90 -0
- package/dist/web/assets/index-noejUsK7.css +1 -0
- package/dist/web/assets/{index.es-eSR4Qv6s.js → index.es-DdpKlnGb.js} +1 -1
- package/dist/web/assets/useToolManager-tdxkKn3H.js +1 -0
- package/dist/web/assets/utils-CnsbSMo4.js +1 -0
- package/dist/web/index.html +2 -2
- package/package.json +7 -8
- package/dist/web/assets/index-DmlyQVhR.css +0 -1
- package/dist/web/assets/index-Dn5ZH1Y6.js +0 -80
- package/dist/{chunk-WFTC3JJW.js → chunk-3NO7QU7W.js} +1 -1
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import {
|
|
2
|
+
hashText,
|
|
3
|
+
serializeEmbedding
|
|
4
|
+
} from "./chunk-Z5WY7BSB.js";
|
|
5
|
+
import {
|
|
6
|
+
KNOWLEDGE_CHUNK_SIZE
|
|
7
|
+
} from "./chunk-J4WDJ7XS.js";
|
|
8
|
+
|
|
9
|
+
// src/memory/agent/knowledge.ts
|
|
10
|
+
import { readFileSync, existsSync, readdirSync, statSync } from "fs";
|
|
11
|
+
import { join } from "path";
|
|
12
|
+
var _instance = null;
|
|
13
|
+
function setKnowledgeIndexer(indexer) {
|
|
14
|
+
_instance = indexer;
|
|
15
|
+
}
|
|
16
|
+
function getKnowledgeIndexer() {
|
|
17
|
+
return _instance;
|
|
18
|
+
}
|
|
19
|
+
var KnowledgeIndexer = class {
|
|
20
|
+
constructor(db, workspaceDir, embedder, vectorEnabled) {
|
|
21
|
+
this.db = db;
|
|
22
|
+
this.workspaceDir = workspaceDir;
|
|
23
|
+
this.embedder = embedder;
|
|
24
|
+
this.vectorEnabled = vectorEnabled;
|
|
25
|
+
}
|
|
26
|
+
getEmbedder() {
|
|
27
|
+
return this.embedder;
|
|
28
|
+
}
|
|
29
|
+
async indexAll(options) {
|
|
30
|
+
const files = this.listMemoryFiles();
|
|
31
|
+
let indexed = 0;
|
|
32
|
+
let skipped = 0;
|
|
33
|
+
for (const file of files) {
|
|
34
|
+
const wasIndexed = await this.indexFile(file, options?.force);
|
|
35
|
+
if (wasIndexed) {
|
|
36
|
+
indexed++;
|
|
37
|
+
} else {
|
|
38
|
+
skipped++;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return { indexed, skipped };
|
|
42
|
+
}
|
|
43
|
+
async indexFile(absPath, force) {
|
|
44
|
+
if (!existsSync(absPath) || !absPath.endsWith(".md")) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
const content = readFileSync(absPath, "utf-8");
|
|
48
|
+
const relPath = absPath.replace(this.workspaceDir + "/", "");
|
|
49
|
+
const fileHash = hashText(content);
|
|
50
|
+
if (!force) {
|
|
51
|
+
const existing = this.db.prepare(`SELECT hash FROM knowledge WHERE path = ? AND source = 'memory' LIMIT 1`).get(relPath);
|
|
52
|
+
if (existing?.hash === fileHash) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const chunks = this.chunkMarkdown(content, relPath);
|
|
57
|
+
const texts = chunks.map((c) => c.text);
|
|
58
|
+
const embeddings = await this.embedder.embedBatch(texts);
|
|
59
|
+
const chunksToInsert = [];
|
|
60
|
+
const idsToSupersede = [];
|
|
61
|
+
if (this.vectorEnabled) {
|
|
62
|
+
const vecQuery = this.db.prepare(`
|
|
63
|
+
SELECT kv.id, kv.distance
|
|
64
|
+
FROM (
|
|
65
|
+
SELECT id, distance
|
|
66
|
+
FROM knowledge_vec
|
|
67
|
+
WHERE embedding MATCH ? AND k = 5
|
|
68
|
+
) kv
|
|
69
|
+
JOIN knowledge k ON k.id = kv.id
|
|
70
|
+
WHERE k.path != ? AND (k.status = 'active' OR k.status IS NULL)
|
|
71
|
+
ORDER BY kv.distance
|
|
72
|
+
`);
|
|
73
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
74
|
+
const chunk = chunks[i];
|
|
75
|
+
const embedding = embeddings[i] ?? [];
|
|
76
|
+
if (embedding.length === 0) {
|
|
77
|
+
chunksToInsert.push({ chunk, embedding });
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
let skip = false;
|
|
81
|
+
try {
|
|
82
|
+
const similar = vecQuery.all(serializeEmbedding(embedding), relPath);
|
|
83
|
+
const exactDup = similar.find((r) => r.distance < 0.1);
|
|
84
|
+
if (exactDup) {
|
|
85
|
+
skip = true;
|
|
86
|
+
} else {
|
|
87
|
+
const nearDup = similar.find((r) => r.distance < 0.15);
|
|
88
|
+
if (nearDup) {
|
|
89
|
+
idsToSupersede.push(nearDup.id);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
} catch {
|
|
93
|
+
}
|
|
94
|
+
if (!skip) {
|
|
95
|
+
chunksToInsert.push({ chunk, embedding });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
} else {
|
|
99
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
100
|
+
chunksToInsert.push({ chunk: chunks[i], embedding: embeddings[i] ?? [] });
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
this.db.transaction(() => {
|
|
104
|
+
if (this.vectorEnabled) {
|
|
105
|
+
this.db.prepare(
|
|
106
|
+
`DELETE FROM knowledge_vec WHERE id IN (
|
|
107
|
+
SELECT id FROM knowledge WHERE path = ? AND source = 'memory'
|
|
108
|
+
)`
|
|
109
|
+
).run(relPath);
|
|
110
|
+
}
|
|
111
|
+
this.db.prepare(`DELETE FROM knowledge WHERE path = ? AND source = 'memory'`).run(relPath);
|
|
112
|
+
if (idsToSupersede.length > 0) {
|
|
113
|
+
const placeholders = idsToSupersede.map(() => "?").join(", ");
|
|
114
|
+
this.db.prepare(
|
|
115
|
+
`UPDATE knowledge SET status = 'superseded', updated_at = unixepoch() WHERE id IN (${placeholders})`
|
|
116
|
+
).run(...idsToSupersede);
|
|
117
|
+
}
|
|
118
|
+
const insert = this.db.prepare(`
|
|
119
|
+
INSERT INTO knowledge (id, source, path, text, embedding, start_line, end_line, hash, status, memory_type)
|
|
120
|
+
VALUES (?, 'memory', ?, ?, ?, ?, ?, ?, 'active', ?)
|
|
121
|
+
`);
|
|
122
|
+
const insertVec = this.vectorEnabled ? this.db.prepare(`INSERT INTO knowledge_vec (id, embedding) VALUES (?, ?)`) : null;
|
|
123
|
+
const memoryType = this.getMemoryType(relPath);
|
|
124
|
+
for (const { chunk, embedding } of chunksToInsert) {
|
|
125
|
+
insert.run(
|
|
126
|
+
chunk.id,
|
|
127
|
+
chunk.path,
|
|
128
|
+
chunk.text,
|
|
129
|
+
serializeEmbedding(embedding),
|
|
130
|
+
chunk.startLine,
|
|
131
|
+
chunk.endLine,
|
|
132
|
+
fileHash,
|
|
133
|
+
memoryType
|
|
134
|
+
);
|
|
135
|
+
if (insertVec && embedding.length > 0) {
|
|
136
|
+
insertVec.run(chunk.id, serializeEmbedding(embedding));
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
})();
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
async pruneOrphans() {
|
|
143
|
+
const paths = this.db.prepare(
|
|
144
|
+
`SELECT DISTINCT path FROM knowledge WHERE path IS NOT NULL AND source = 'memory'
|
|
145
|
+
AND (status = 'active' OR status IS NULL)`
|
|
146
|
+
).all();
|
|
147
|
+
let markedInactive = 0;
|
|
148
|
+
const orphanedPaths = [];
|
|
149
|
+
for (const { path } of paths) {
|
|
150
|
+
const absPath = join(this.workspaceDir, path);
|
|
151
|
+
const archivedPath = join(
|
|
152
|
+
this.workspaceDir,
|
|
153
|
+
"memory",
|
|
154
|
+
"archived",
|
|
155
|
+
path.replace(/^memory\//, "")
|
|
156
|
+
);
|
|
157
|
+
if (!existsSync(absPath) && !existsSync(archivedPath)) {
|
|
158
|
+
orphanedPaths.push(path);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
if (orphanedPaths.length > 0) {
|
|
162
|
+
const ph = orphanedPaths.map(() => "?").join(", ");
|
|
163
|
+
const result = this.db.prepare(
|
|
164
|
+
`UPDATE knowledge SET status = 'inactive', updated_at = unixepoch()
|
|
165
|
+
WHERE path IN (${ph}) AND source = 'memory'`
|
|
166
|
+
).run(...orphanedPaths);
|
|
167
|
+
markedInactive = result.changes;
|
|
168
|
+
}
|
|
169
|
+
const staleIds = this.db.prepare(
|
|
170
|
+
`SELECT id FROM knowledge WHERE status = 'inactive' AND updated_at < unixepoch() - ?`
|
|
171
|
+
).all(30 * 86400);
|
|
172
|
+
let deleted = 0;
|
|
173
|
+
if (staleIds.length > 0) {
|
|
174
|
+
const ids = staleIds.map((r) => r.id);
|
|
175
|
+
const placeholders = ids.map(() => "?").join(", ");
|
|
176
|
+
this.db.transaction(() => {
|
|
177
|
+
if (this.vectorEnabled) {
|
|
178
|
+
this.db.prepare(`DELETE FROM knowledge_vec WHERE id IN (${placeholders})`).run(...ids);
|
|
179
|
+
}
|
|
180
|
+
this.db.prepare(`DELETE FROM knowledge WHERE id IN (${placeholders})`).run(...ids);
|
|
181
|
+
})();
|
|
182
|
+
deleted = ids.length;
|
|
183
|
+
}
|
|
184
|
+
return { markedInactive, deleted };
|
|
185
|
+
}
|
|
186
|
+
listMemoryFiles() {
|
|
187
|
+
const files = [];
|
|
188
|
+
const memoryMd = join(this.workspaceDir, "MEMORY.md");
|
|
189
|
+
if (existsSync(memoryMd)) {
|
|
190
|
+
files.push(memoryMd);
|
|
191
|
+
}
|
|
192
|
+
const memoryDir = join(this.workspaceDir, "memory");
|
|
193
|
+
if (existsSync(memoryDir)) {
|
|
194
|
+
const entries = readdirSync(memoryDir);
|
|
195
|
+
for (const entry of entries) {
|
|
196
|
+
const absPath = join(memoryDir, entry);
|
|
197
|
+
if (statSync(absPath).isFile() && entry.endsWith(".md")) {
|
|
198
|
+
files.push(absPath);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return files;
|
|
203
|
+
}
|
|
204
|
+
getMemoryType(relPath) {
|
|
205
|
+
if (relPath === "MEMORY.md") return "procedural";
|
|
206
|
+
if (/^memory\/(\d{4}-\d{2}-\d{2}|consolidated-)/.test(relPath)) return "episodic";
|
|
207
|
+
return "semantic";
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Chunk markdown content with structure awareness.
|
|
211
|
+
* Respects heading boundaries, code blocks, and list groups.
|
|
212
|
+
* Target: KNOWLEDGE_CHUNK_SIZE chars, hard max: 2x target.
|
|
213
|
+
*/
|
|
214
|
+
chunkMarkdown(content, path) {
|
|
215
|
+
const lines = content.split("\n");
|
|
216
|
+
const chunks = [];
|
|
217
|
+
const targetSize = KNOWLEDGE_CHUNK_SIZE;
|
|
218
|
+
const hardMax = targetSize * 2;
|
|
219
|
+
let currentChunk = "";
|
|
220
|
+
let startLine = 1;
|
|
221
|
+
let currentLine = 1;
|
|
222
|
+
let inCodeBlock = false;
|
|
223
|
+
let overlapPrefix = "";
|
|
224
|
+
const flushChunk = () => {
|
|
225
|
+
const text = currentChunk.trim();
|
|
226
|
+
if (text.length > 0) {
|
|
227
|
+
chunks.push({
|
|
228
|
+
id: hashText(`${path}:${startLine}:${currentLine - 1}`),
|
|
229
|
+
source: "memory",
|
|
230
|
+
path,
|
|
231
|
+
text,
|
|
232
|
+
startLine,
|
|
233
|
+
endLine: currentLine - 1,
|
|
234
|
+
hash: hashText(text)
|
|
235
|
+
});
|
|
236
|
+
const nonEmpty = text.split("\n").filter((l) => l.trim());
|
|
237
|
+
overlapPrefix = nonEmpty.length > 0 ? nonEmpty.slice(-2).join("\n") + "\n" : "";
|
|
238
|
+
}
|
|
239
|
+
currentChunk = overlapPrefix;
|
|
240
|
+
startLine = currentLine;
|
|
241
|
+
};
|
|
242
|
+
for (const line of lines) {
|
|
243
|
+
if (line.trimStart().startsWith("```")) {
|
|
244
|
+
inCodeBlock = !inCodeBlock;
|
|
245
|
+
}
|
|
246
|
+
if (!inCodeBlock && currentChunk.length >= targetSize) {
|
|
247
|
+
const isHeading = /^#{1,6}\s/.test(line);
|
|
248
|
+
const isBlankLine = line.trim() === "";
|
|
249
|
+
const isHorizontalRule = /^(-{3,}|\*{3,}|_{3,})\s*$/.test(line.trim());
|
|
250
|
+
if (isHeading) {
|
|
251
|
+
flushChunk();
|
|
252
|
+
} else if ((isBlankLine || isHorizontalRule) && currentChunk.length >= targetSize) {
|
|
253
|
+
currentChunk += line + "\n";
|
|
254
|
+
currentLine++;
|
|
255
|
+
flushChunk();
|
|
256
|
+
continue;
|
|
257
|
+
} else if (currentChunk.length >= hardMax) {
|
|
258
|
+
flushChunk();
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
currentChunk += line + "\n";
|
|
262
|
+
currentLine++;
|
|
263
|
+
}
|
|
264
|
+
flushChunk();
|
|
265
|
+
return chunks;
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
export {
|
|
270
|
+
setKnowledgeIndexer,
|
|
271
|
+
getKnowledgeIndexer,
|
|
272
|
+
KnowledgeIndexer
|
|
273
|
+
};
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ALLOWED_EXTENSIONS,
|
|
3
|
+
WORKSPACE_PATHS,
|
|
4
|
+
WORKSPACE_ROOT
|
|
5
|
+
} from "./chunk-L653KKCR.js";
|
|
6
|
+
import {
|
|
7
|
+
createLogger
|
|
8
|
+
} from "./chunk-V3S3NXBQ.js";
|
|
9
|
+
|
|
10
|
+
// src/memory/daily-logs.ts
|
|
11
|
+
import { existsSync as existsSync2, mkdirSync, appendFileSync, readFileSync, readdirSync as readdirSync2, unlinkSync } from "fs";
|
|
12
|
+
import { join } from "path";
|
|
13
|
+
|
|
14
|
+
// src/workspace/validator.ts
|
|
15
|
+
import { existsSync, lstatSync, readdirSync } from "fs";
|
|
16
|
+
import { resolve, normalize, relative, extname, basename } from "path";
|
|
17
|
+
import { homedir } from "os";
|
|
18
|
+
var WorkspaceSecurityError = class extends Error {
|
|
19
|
+
constructor(message, attemptedPath) {
|
|
20
|
+
super(message);
|
|
21
|
+
this.attemptedPath = attemptedPath;
|
|
22
|
+
this.name = "WorkspaceSecurityError";
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
function decodeRecursive(str) {
|
|
26
|
+
let decoded = str;
|
|
27
|
+
let prev = "";
|
|
28
|
+
let iterations = 0;
|
|
29
|
+
const maxIterations = 10;
|
|
30
|
+
while (decoded !== prev && iterations < maxIterations) {
|
|
31
|
+
prev = decoded;
|
|
32
|
+
try {
|
|
33
|
+
decoded = decodeURIComponent(decoded);
|
|
34
|
+
} catch {
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
iterations++;
|
|
38
|
+
}
|
|
39
|
+
return decoded;
|
|
40
|
+
}
|
|
41
|
+
function validatePath(inputPath, allowCreate = false) {
|
|
42
|
+
if (!inputPath || inputPath.trim() === "") {
|
|
43
|
+
throw new WorkspaceSecurityError("Path cannot be empty.", inputPath);
|
|
44
|
+
}
|
|
45
|
+
const trimmedPath = inputPath.trim().replace(/\\/g, "/");
|
|
46
|
+
const decodedPath = decodeRecursive(trimmedPath);
|
|
47
|
+
let absolutePath;
|
|
48
|
+
if (decodedPath.startsWith("/")) {
|
|
49
|
+
absolutePath = resolve(normalize(decodedPath));
|
|
50
|
+
} else if (decodedPath.startsWith("~/")) {
|
|
51
|
+
const expanded = decodedPath.replace(/^~(?=$|[\\/])/, homedir());
|
|
52
|
+
absolutePath = resolve(expanded);
|
|
53
|
+
} else {
|
|
54
|
+
absolutePath = resolve(WORKSPACE_ROOT, normalize(decodedPath));
|
|
55
|
+
}
|
|
56
|
+
const relativePath = relative(WORKSPACE_ROOT, absolutePath);
|
|
57
|
+
if (relativePath.startsWith("..") || relativePath.startsWith("/")) {
|
|
58
|
+
throw new WorkspaceSecurityError(
|
|
59
|
+
`Access denied: Path '${inputPath}' is outside the workspace. Only files in ~/.teleton/workspace/ are accessible.`,
|
|
60
|
+
inputPath
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
const exists = existsSync(absolutePath);
|
|
64
|
+
if (!exists && !allowCreate) {
|
|
65
|
+
throw new WorkspaceSecurityError(
|
|
66
|
+
`File not found: '${inputPath}' does not exist in workspace.`,
|
|
67
|
+
inputPath
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
if (exists) {
|
|
71
|
+
const stats = lstatSync(absolutePath);
|
|
72
|
+
if (stats.isSymbolicLink()) {
|
|
73
|
+
throw new WorkspaceSecurityError(
|
|
74
|
+
`Access denied: Symbolic links are not allowed for security reasons.`,
|
|
75
|
+
inputPath
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
absolutePath,
|
|
81
|
+
relativePath,
|
|
82
|
+
exists,
|
|
83
|
+
isDirectory: exists ? lstatSync(absolutePath).isDirectory() : false,
|
|
84
|
+
extension: extname(absolutePath).toLowerCase(),
|
|
85
|
+
filename: basename(absolutePath)
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function validateReadPath(inputPath) {
|
|
89
|
+
const validated = validatePath(inputPath, false);
|
|
90
|
+
if (validated.isDirectory) {
|
|
91
|
+
throw new WorkspaceSecurityError(`Cannot read directory as file: '${inputPath}'`, inputPath);
|
|
92
|
+
}
|
|
93
|
+
return validated;
|
|
94
|
+
}
|
|
95
|
+
var IMMUTABLE_FILES = ["SOUL.md", "STRATEGY.md", "SECURITY.md"];
|
|
96
|
+
function validateWritePath(inputPath, fileType) {
|
|
97
|
+
const validated = validatePath(inputPath, true);
|
|
98
|
+
if (IMMUTABLE_FILES.includes(validated.filename)) {
|
|
99
|
+
throw new WorkspaceSecurityError(
|
|
100
|
+
`Cannot write to ${validated.filename}. This file is configured by the owner. Use memory_write instead.`,
|
|
101
|
+
inputPath
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
if (fileType && ALLOWED_EXTENSIONS[fileType]) {
|
|
105
|
+
const allowedExts = ALLOWED_EXTENSIONS[fileType];
|
|
106
|
+
if (!allowedExts.includes(validated.extension)) {
|
|
107
|
+
throw new WorkspaceSecurityError(
|
|
108
|
+
`Invalid file type: '${validated.extension}' is not allowed for ${fileType}. Allowed: ${allowedExts.join(", ")}`,
|
|
109
|
+
inputPath
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return validated;
|
|
114
|
+
}
|
|
115
|
+
function validateDirectory(inputPath) {
|
|
116
|
+
const validated = validatePath(inputPath, true);
|
|
117
|
+
if (validated.exists && !validated.isDirectory) {
|
|
118
|
+
throw new WorkspaceSecurityError(
|
|
119
|
+
`Path exists but is not a directory: '${inputPath}'`,
|
|
120
|
+
inputPath
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
return validated;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// src/memory/daily-logs.ts
|
|
127
|
+
var log = createLogger("Memory");
|
|
128
|
+
var MEMORY_DIR = WORKSPACE_PATHS.MEMORY_DIR;
|
|
129
|
+
function formatDate(date) {
|
|
130
|
+
const year = date.getFullYear();
|
|
131
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
132
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
133
|
+
return `${year}-${month}-${day}`;
|
|
134
|
+
}
|
|
135
|
+
function getDailyLogPath(date = /* @__PURE__ */ new Date()) {
|
|
136
|
+
return join(MEMORY_DIR, `${formatDate(date)}.md`);
|
|
137
|
+
}
|
|
138
|
+
function ensureMemoryDir() {
|
|
139
|
+
if (!existsSync2(MEMORY_DIR)) {
|
|
140
|
+
mkdirSync(MEMORY_DIR, { recursive: true });
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
function appendToDailyLog(content, date = /* @__PURE__ */ new Date()) {
|
|
144
|
+
try {
|
|
145
|
+
ensureMemoryDir();
|
|
146
|
+
const logPath = getDailyLogPath(date);
|
|
147
|
+
const timestamp = date.toLocaleTimeString("en-US", { hour12: false });
|
|
148
|
+
if (!existsSync2(logPath)) {
|
|
149
|
+
const header = `# Daily Log - ${formatDate(date)}
|
|
150
|
+
|
|
151
|
+
`;
|
|
152
|
+
appendFileSync(logPath, header, { encoding: "utf-8", mode: 384 });
|
|
153
|
+
}
|
|
154
|
+
const entry = `## ${timestamp}
|
|
155
|
+
|
|
156
|
+
${content}
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
`;
|
|
161
|
+
appendFileSync(logPath, entry, "utf-8");
|
|
162
|
+
log.info(`Daily log updated: ${logPath}`);
|
|
163
|
+
} catch (error) {
|
|
164
|
+
log.error({ err: error }, "Failed to write daily log");
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
function readDailyLog(date = /* @__PURE__ */ new Date()) {
|
|
168
|
+
try {
|
|
169
|
+
const logPath = getDailyLogPath(date);
|
|
170
|
+
if (!existsSync2(logPath)) return null;
|
|
171
|
+
return readFileSync(logPath, "utf-8");
|
|
172
|
+
} catch {
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
var DAILY_LOG_LINE_LIMIT = 100;
|
|
177
|
+
function truncateDailyLog(content) {
|
|
178
|
+
const lines = content.split("\n");
|
|
179
|
+
if (lines.length <= DAILY_LOG_LINE_LIMIT) return content;
|
|
180
|
+
const truncated = lines.slice(-DAILY_LOG_LINE_LIMIT).join("\n");
|
|
181
|
+
const dropped = lines.length - DAILY_LOG_LINE_LIMIT;
|
|
182
|
+
return `_[... ${dropped} earlier lines omitted]_
|
|
183
|
+
|
|
184
|
+
${truncated}`;
|
|
185
|
+
}
|
|
186
|
+
function readRecentMemory() {
|
|
187
|
+
const today = /* @__PURE__ */ new Date();
|
|
188
|
+
const yesterday = new Date(today);
|
|
189
|
+
yesterday.setDate(yesterday.getDate() - 1);
|
|
190
|
+
const parts = [];
|
|
191
|
+
const yesterdayLog = readDailyLog(yesterday);
|
|
192
|
+
if (yesterdayLog) {
|
|
193
|
+
parts.push(`## Yesterday (${formatDate(yesterday)})
|
|
194
|
+
|
|
195
|
+
${truncateDailyLog(yesterdayLog)}`);
|
|
196
|
+
}
|
|
197
|
+
const todayLog = readDailyLog(today);
|
|
198
|
+
if (todayLog) {
|
|
199
|
+
parts.push(`## Today (${formatDate(today)})
|
|
200
|
+
|
|
201
|
+
${truncateDailyLog(todayLog)}`);
|
|
202
|
+
}
|
|
203
|
+
if (parts.length === 0) {
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
return `# Recent Memory
|
|
207
|
+
|
|
208
|
+
${parts.join("\n\n---\n\n")}`;
|
|
209
|
+
}
|
|
210
|
+
function writeSessionEndSummary(summary, reason) {
|
|
211
|
+
const content = `### Session End (${reason})
|
|
212
|
+
|
|
213
|
+
${summary}`;
|
|
214
|
+
appendToDailyLog(content);
|
|
215
|
+
}
|
|
216
|
+
function writeSummaryToDailyLog(summary) {
|
|
217
|
+
appendToDailyLog(`### Memory Flush (Pre-Compaction)
|
|
218
|
+
|
|
219
|
+
${summary}`);
|
|
220
|
+
}
|
|
221
|
+
function cleanupOldDailyLogs(maxAgeDays = 60) {
|
|
222
|
+
if (!existsSync2(MEMORY_DIR)) return 0;
|
|
223
|
+
const cutoffMs = Date.now() - maxAgeDays * 864e5;
|
|
224
|
+
const datePattern = /^(\d{4}-\d{2}-\d{2})\.md$/;
|
|
225
|
+
let deleted = 0;
|
|
226
|
+
try {
|
|
227
|
+
const files = readdirSync2(MEMORY_DIR);
|
|
228
|
+
for (const file of files) {
|
|
229
|
+
const match = datePattern.exec(file);
|
|
230
|
+
if (!match) continue;
|
|
231
|
+
const fileDate = new Date(match[1]).getTime();
|
|
232
|
+
if (Number.isNaN(fileDate)) continue;
|
|
233
|
+
if (fileDate < cutoffMs) {
|
|
234
|
+
try {
|
|
235
|
+
unlinkSync(join(MEMORY_DIR, file));
|
|
236
|
+
deleted++;
|
|
237
|
+
} catch (innerError) {
|
|
238
|
+
log.warn({ err: innerError, file }, "Failed to delete old daily log");
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
} catch (error) {
|
|
243
|
+
log.error({ err: error }, "Failed to list memory directory for cleanup");
|
|
244
|
+
}
|
|
245
|
+
if (deleted > 0) {
|
|
246
|
+
log.info(`Cleaned up ${deleted} daily log file(s) older than ${maxAgeDays} days`);
|
|
247
|
+
}
|
|
248
|
+
return deleted;
|
|
249
|
+
}
|
|
250
|
+
function writeConversationMilestone(chatId, topic, details) {
|
|
251
|
+
const content = `### Conversation Milestone
|
|
252
|
+
|
|
253
|
+
**Chat**: ${chatId}
|
|
254
|
+
**Topic**: ${topic}
|
|
255
|
+
|
|
256
|
+
${details}`;
|
|
257
|
+
appendToDailyLog(content);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
export {
|
|
261
|
+
WorkspaceSecurityError,
|
|
262
|
+
validatePath,
|
|
263
|
+
validateReadPath,
|
|
264
|
+
validateWritePath,
|
|
265
|
+
validateDirectory,
|
|
266
|
+
getDailyLogPath,
|
|
267
|
+
appendToDailyLog,
|
|
268
|
+
readDailyLog,
|
|
269
|
+
readRecentMemory,
|
|
270
|
+
writeSessionEndSummary,
|
|
271
|
+
writeSummaryToDailyLog,
|
|
272
|
+
cleanupOldDailyLogs,
|
|
273
|
+
writeConversationMilestone
|
|
274
|
+
};
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
} from "./chunk-L653KKCR.js";
|
|
4
4
|
import {
|
|
5
5
|
createLogger
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-V3S3NXBQ.js";
|
|
7
7
|
|
|
8
8
|
// src/utils/module-db.ts
|
|
9
9
|
import Database from "better-sqlite3";
|
|
@@ -115,13 +115,13 @@ function migrateFromMainDb(moduleDb, tables) {
|
|
|
115
115
|
);
|
|
116
116
|
totalMigrated += src.c;
|
|
117
117
|
log.info(`Migrated ${src.c} rows from memory.db \u2192 ${table}`);
|
|
118
|
-
} catch (
|
|
119
|
-
log.warn({ err:
|
|
118
|
+
} catch (innerError) {
|
|
119
|
+
log.warn({ err: innerError }, `Could not migrate table ${table}`);
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
122
|
moduleDb.exec(`DETACH DATABASE main_db`);
|
|
123
|
-
} catch (
|
|
124
|
-
log.warn({ err:
|
|
123
|
+
} catch (error) {
|
|
124
|
+
log.warn({ err: error }, `Migration from memory.db failed`);
|
|
125
125
|
try {
|
|
126
126
|
moduleDb.exec(`DETACH DATABASE main_db`);
|
|
127
127
|
} catch {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createLogger
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-V3S3NXBQ.js";
|
|
4
4
|
|
|
5
5
|
// src/providers/claude-code-credentials.ts
|
|
6
6
|
import { readFileSync, writeFileSync, existsSync } from "fs";
|
|
@@ -26,8 +26,8 @@ function readCredentialsFile() {
|
|
|
26
26
|
try {
|
|
27
27
|
const raw = readFileSync(filePath, "utf-8");
|
|
28
28
|
return JSON.parse(raw);
|
|
29
|
-
} catch (
|
|
30
|
-
log.warn({ err:
|
|
29
|
+
} catch (error) {
|
|
30
|
+
log.warn({ err: error, path: filePath }, "Failed to parse Claude Code credentials file");
|
|
31
31
|
return null;
|
|
32
32
|
}
|
|
33
33
|
}
|
|
@@ -122,16 +122,16 @@ async function performOAuthRefresh(refreshToken) {
|
|
|
122
122
|
}
|
|
123
123
|
};
|
|
124
124
|
writeFileSync(filePath, JSON.stringify(updated, null, 2), { mode: 384 });
|
|
125
|
-
} catch (
|
|
126
|
-
log.warn({ err:
|
|
125
|
+
} catch (innerError) {
|
|
126
|
+
log.warn({ err: innerError }, "Failed to persist refreshed OAuth credentials to disk");
|
|
127
127
|
}
|
|
128
128
|
cachedToken = data.access_token;
|
|
129
129
|
cachedExpiresAt = newExpiresAt;
|
|
130
130
|
cachedRefreshToken = newRefreshToken;
|
|
131
131
|
log.info("Claude Code OAuth token refreshed successfully");
|
|
132
132
|
return cachedToken;
|
|
133
|
-
} catch (
|
|
134
|
-
log.warn({ err:
|
|
133
|
+
} catch (error) {
|
|
134
|
+
log.warn({ err: error }, "OAuth token refresh request failed");
|
|
135
135
|
return null;
|
|
136
136
|
}
|
|
137
137
|
}
|
|
@@ -1,23 +1,35 @@
|
|
|
1
1
|
// src/config/providers.ts
|
|
2
2
|
var PROVIDER_REGISTRY = {
|
|
3
|
-
|
|
4
|
-
id: "
|
|
5
|
-
displayName: "
|
|
3
|
+
"claude-code": {
|
|
4
|
+
id: "claude-code",
|
|
5
|
+
displayName: "Claude Code (Auto)",
|
|
6
6
|
envVar: "ANTHROPIC_API_KEY",
|
|
7
7
|
keyPrefix: "sk-ant-",
|
|
8
|
-
keyHint: "
|
|
8
|
+
keyHint: "Auto-detected from Claude Code",
|
|
9
9
|
consoleUrl: "https://console.anthropic.com/",
|
|
10
10
|
defaultModel: "claude-opus-4-6",
|
|
11
11
|
utilityModel: "claude-haiku-4-5-20251001",
|
|
12
12
|
toolLimit: null,
|
|
13
13
|
piAiProvider: "anthropic"
|
|
14
14
|
},
|
|
15
|
-
|
|
16
|
-
id: "
|
|
17
|
-
displayName: "
|
|
15
|
+
zai: {
|
|
16
|
+
id: "zai",
|
|
17
|
+
displayName: "ZAI (Zhipu)",
|
|
18
|
+
envVar: "ZAI_API_KEY",
|
|
19
|
+
keyPrefix: null,
|
|
20
|
+
keyHint: "...",
|
|
21
|
+
consoleUrl: "https://z.ai/manage-apikey/apikey-list",
|
|
22
|
+
defaultModel: "glm-4.7-flash",
|
|
23
|
+
utilityModel: "glm-4.5-flash",
|
|
24
|
+
toolLimit: 128,
|
|
25
|
+
piAiProvider: "zai"
|
|
26
|
+
},
|
|
27
|
+
anthropic: {
|
|
28
|
+
id: "anthropic",
|
|
29
|
+
displayName: "Anthropic (Claude)",
|
|
18
30
|
envVar: "ANTHROPIC_API_KEY",
|
|
19
31
|
keyPrefix: "sk-ant-",
|
|
20
|
-
keyHint: "
|
|
32
|
+
keyHint: "sk-ant-api03-...",
|
|
21
33
|
consoleUrl: "https://console.anthropic.com/",
|
|
22
34
|
defaultModel: "claude-opus-4-6",
|
|
23
35
|
utilityModel: "claude-haiku-4-5-20251001",
|
|
@@ -120,18 +132,6 @@ var PROVIDER_REGISTRY = {
|
|
|
120
132
|
toolLimit: 128,
|
|
121
133
|
piAiProvider: "cerebras"
|
|
122
134
|
},
|
|
123
|
-
zai: {
|
|
124
|
-
id: "zai",
|
|
125
|
-
displayName: "ZAI (Zhipu)",
|
|
126
|
-
envVar: "ZAI_API_KEY",
|
|
127
|
-
keyPrefix: null,
|
|
128
|
-
keyHint: "...",
|
|
129
|
-
consoleUrl: "https://z.ai/manage-apikey/apikey-list",
|
|
130
|
-
defaultModel: "glm-4.7",
|
|
131
|
-
utilityModel: "glm-4.7-flash",
|
|
132
|
-
toolLimit: 128,
|
|
133
|
-
piAiProvider: "zai"
|
|
134
|
-
},
|
|
135
135
|
minimax: {
|
|
136
136
|
id: "minimax",
|
|
137
137
|
displayName: "MiniMax",
|