clawvault 2.6.4 → 2.6.5
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/dist/{chunk-ECRZL5XR.js → chunk-2GKPENIR.js} +21 -5
- package/dist/{chunk-ZZA73MFY.js → chunk-33DOSHTA.js} +176 -36
- package/dist/{chunk-4VQTUVH7.js → chunk-7YZWHM36.js} +52 -26
- package/dist/{chunk-YNIPYN4F.js → chunk-A4EAUO7T.js} +1 -1
- package/dist/{chunk-6NYYDNNG.js → chunk-BV5KWZKR.js} +1 -1
- package/dist/chunk-FBITHIZF.js +351 -0
- package/dist/{chunk-OQGYFZ4A.js → chunk-FUSLEY6L.js} +82 -1
- package/dist/{chunk-42MXU7A6.js → chunk-K4GFGKFD.js} +51 -47
- package/dist/{chunk-LUBZXECN.js → chunk-KSZROBFH.js} +1 -1
- package/dist/{chunk-MFL6EEPF.js → chunk-LMKQ7NIF.js} +5 -5
- package/dist/{chunk-MPOSMDMU.js → chunk-M5O6FQ66.js} +4 -4
- package/dist/{chunk-P5EPF6MB.js → chunk-MW5C6ZQA.js} +110 -13
- package/dist/{chunk-IIOU45CK.js → chunk-QSRRMEYM.js} +1 -1
- package/dist/{chunk-P7SY3D4E.js → chunk-SV7T4HRE.js} +3 -3
- package/dist/{chunk-627Q3QWK.js → chunk-T3FKSZSN.js} +1 -1
- package/dist/{chunk-LIGHWOH6.js → chunk-TS6NDVOU.js} +2 -2
- package/dist/{chunk-YDWHS4LJ.js → chunk-YD7SVXTF.js} +20 -3
- package/dist/cli/index.js +11 -11
- package/dist/commands/canvas.js +1 -1
- package/dist/commands/context.js +4 -4
- package/dist/commands/doctor.js +7 -7
- package/dist/commands/embed.js +2 -2
- package/dist/commands/graph.js +2 -2
- package/dist/commands/inject.js +2 -2
- package/dist/commands/link.js +3 -3
- package/dist/commands/observe.js +4 -4
- package/dist/commands/rebuild.js +2 -2
- package/dist/commands/replay.js +2 -2
- package/dist/commands/setup.js +1 -1
- package/dist/commands/sleep.js +5 -5
- package/dist/commands/status.js +6 -6
- package/dist/commands/wake.js +3 -3
- package/dist/index.d.ts +7 -0
- package/dist/index.js +17 -17
- package/dist/lib/auto-linker.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-WIOLLGAD.js +0 -190
- /package/dist/{chunk-NJYJL5AA.js → chunk-5UM4PMMM.js} +0 -0
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
import {
|
|
2
|
+
withQmdIndexArgs
|
|
3
|
+
} from "./chunk-5PJ4STIC.js";
|
|
4
|
+
|
|
5
|
+
// src/lib/qmd-collections.ts
|
|
6
|
+
import { execFileSync } from "child_process";
|
|
7
|
+
var COLLECTION_HEADER_RE = /^(\S+)\s+\(qmd:\/\/([^)]+)\)\s*$/;
|
|
8
|
+
var DETAIL_LINE_RE = /^\s+([A-Za-z][A-Za-z0-9 _-]*):\s*(.+)\s*$/;
|
|
9
|
+
var QMD_INDEX_ENV_VAR = "CLAWVAULT_QMD_INDEX";
|
|
10
|
+
function normalizeDetailKey(value) {
|
|
11
|
+
return value.trim().toLowerCase().replace(/[ -]+/g, "_");
|
|
12
|
+
}
|
|
13
|
+
function parseCount(raw) {
|
|
14
|
+
if (!raw) return void 0;
|
|
15
|
+
const match = raw.match(/-?\d[\d,]*/);
|
|
16
|
+
if (!match) return void 0;
|
|
17
|
+
const parsed = Number.parseInt(match[0].replace(/,/g, ""), 10);
|
|
18
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
19
|
+
}
|
|
20
|
+
function pickDetail(details, keys) {
|
|
21
|
+
for (const key of keys) {
|
|
22
|
+
const value = details[key];
|
|
23
|
+
if (typeof value === "string" && value.trim()) {
|
|
24
|
+
return value.trim();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return void 0;
|
|
28
|
+
}
|
|
29
|
+
function pickCount(details, keys) {
|
|
30
|
+
for (const key of keys) {
|
|
31
|
+
const parsed = parseCount(details[key]);
|
|
32
|
+
if (parsed !== void 0) {
|
|
33
|
+
return parsed;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return void 0;
|
|
37
|
+
}
|
|
38
|
+
function resolveQmdIndexName(indexName) {
|
|
39
|
+
const explicit = indexName?.trim();
|
|
40
|
+
if (explicit) {
|
|
41
|
+
return explicit;
|
|
42
|
+
}
|
|
43
|
+
const fromEnv = process.env[QMD_INDEX_ENV_VAR]?.trim();
|
|
44
|
+
return fromEnv || void 0;
|
|
45
|
+
}
|
|
46
|
+
function withQmdIndexArgs2(args, indexName) {
|
|
47
|
+
if (args.includes("--index")) {
|
|
48
|
+
return [...args];
|
|
49
|
+
}
|
|
50
|
+
const resolvedIndexName = resolveQmdIndexName(indexName);
|
|
51
|
+
if (!resolvedIndexName) {
|
|
52
|
+
return [...args];
|
|
53
|
+
}
|
|
54
|
+
return ["--index", resolvedIndexName, ...args];
|
|
55
|
+
}
|
|
56
|
+
function parseQmdCollectionList(raw) {
|
|
57
|
+
const collections = [];
|
|
58
|
+
let current = null;
|
|
59
|
+
for (const line of raw.split(/\r?\n/)) {
|
|
60
|
+
const headerMatch = line.match(COLLECTION_HEADER_RE);
|
|
61
|
+
if (headerMatch) {
|
|
62
|
+
current = {
|
|
63
|
+
name: headerMatch[1],
|
|
64
|
+
uri: headerMatch[2],
|
|
65
|
+
details: {}
|
|
66
|
+
};
|
|
67
|
+
collections.push(current);
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
if (!current) continue;
|
|
71
|
+
const detailMatch = line.match(DETAIL_LINE_RE);
|
|
72
|
+
if (!detailMatch) continue;
|
|
73
|
+
const key = normalizeDetailKey(detailMatch[1]);
|
|
74
|
+
current.details[key] = detailMatch[2].trim();
|
|
75
|
+
}
|
|
76
|
+
for (const collection of collections) {
|
|
77
|
+
const root = pickDetail(collection.details, ["root", "path", "directory"]);
|
|
78
|
+
if (root) {
|
|
79
|
+
collection.root = root;
|
|
80
|
+
}
|
|
81
|
+
collection.files = pickCount(collection.details, ["files", "documents", "docs"]);
|
|
82
|
+
collection.vectors = pickCount(collection.details, ["vectors", "embeddings", "vector_embeddings"]);
|
|
83
|
+
collection.pendingEmbeddings = pickCount(collection.details, [
|
|
84
|
+
"pending",
|
|
85
|
+
"pending_vectors",
|
|
86
|
+
"pending_embeddings",
|
|
87
|
+
"unembedded",
|
|
88
|
+
"without_embeddings"
|
|
89
|
+
]);
|
|
90
|
+
if (collection.pendingEmbeddings === void 0 && collection.files !== void 0 && collection.vectors !== void 0) {
|
|
91
|
+
collection.pendingEmbeddings = Math.max(collection.files - collection.vectors, 0);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return collections;
|
|
95
|
+
}
|
|
96
|
+
function listQmdCollections(indexName) {
|
|
97
|
+
const output = execFileSync("qmd", withQmdIndexArgs2(["collection", "list"], indexName), {
|
|
98
|
+
encoding: "utf-8"
|
|
99
|
+
});
|
|
100
|
+
return parseQmdCollectionList(output);
|
|
101
|
+
}
|
|
102
|
+
function removeQmdCollection(name, indexName) {
|
|
103
|
+
try {
|
|
104
|
+
execFileSync("qmd", withQmdIndexArgs2(["collection", "remove", name], indexName), { stdio: "ignore" });
|
|
105
|
+
return;
|
|
106
|
+
} catch {
|
|
107
|
+
execFileSync("qmd", withQmdIndexArgs2(["collection", "rm", name], indexName), { stdio: "ignore" });
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
function collectionExists(name, indexName) {
|
|
111
|
+
try {
|
|
112
|
+
const collections = listQmdCollections(indexName);
|
|
113
|
+
return collections.some((c) => c.name === name);
|
|
114
|
+
} catch {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
function getCollectionByName(name, indexName) {
|
|
119
|
+
try {
|
|
120
|
+
const collections = listQmdCollections(indexName);
|
|
121
|
+
return collections.find((collection) => collection.name === name);
|
|
122
|
+
} catch {
|
|
123
|
+
return void 0;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
function findCollectionByRoot(rootPath, indexName) {
|
|
127
|
+
try {
|
|
128
|
+
const collections = listQmdCollections(indexName);
|
|
129
|
+
const normalizedRoot = rootPath.replace(/\/$/, "");
|
|
130
|
+
return collections.find((c) => {
|
|
131
|
+
if (!c.root) return false;
|
|
132
|
+
const normalizedCollectionRoot = c.root.replace(/\/$/, "");
|
|
133
|
+
return normalizedCollectionRoot === normalizedRoot;
|
|
134
|
+
});
|
|
135
|
+
} catch {
|
|
136
|
+
return void 0;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
function getFirstCollection(indexName) {
|
|
140
|
+
try {
|
|
141
|
+
const collections = listQmdCollections(indexName);
|
|
142
|
+
return collections[0];
|
|
143
|
+
} catch {
|
|
144
|
+
return void 0;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// src/lib/vault-qmd-config.ts
|
|
149
|
+
import * as fs from "fs";
|
|
150
|
+
import * as path from "path";
|
|
151
|
+
var CONFIG_FILE = ".clawvault.json";
|
|
152
|
+
function readTrimmedString(value) {
|
|
153
|
+
if (typeof value !== "string") return void 0;
|
|
154
|
+
const trimmed = value.trim();
|
|
155
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
156
|
+
}
|
|
157
|
+
function autoDetectCollection(vaultPath, fallbackName) {
|
|
158
|
+
const byRoot = findCollectionByRoot(vaultPath);
|
|
159
|
+
if (byRoot) {
|
|
160
|
+
return { collection: byRoot.name, autoDetected: true };
|
|
161
|
+
}
|
|
162
|
+
if (collectionExists(fallbackName)) {
|
|
163
|
+
return { collection: fallbackName, autoDetected: false };
|
|
164
|
+
}
|
|
165
|
+
const first = getFirstCollection();
|
|
166
|
+
if (first) {
|
|
167
|
+
return { collection: first.name, autoDetected: true };
|
|
168
|
+
}
|
|
169
|
+
return { collection: fallbackName, autoDetected: false };
|
|
170
|
+
}
|
|
171
|
+
function loadVaultQmdConfig(vaultPath) {
|
|
172
|
+
const resolvedVaultPath = path.resolve(vaultPath);
|
|
173
|
+
const fallbackName = path.basename(resolvedVaultPath);
|
|
174
|
+
const fallbackRoot = resolvedVaultPath;
|
|
175
|
+
const configPath = path.join(resolvedVaultPath, CONFIG_FILE);
|
|
176
|
+
if (!fs.existsSync(configPath)) {
|
|
177
|
+
const { collection, autoDetected } = autoDetectCollection(resolvedVaultPath, fallbackName);
|
|
178
|
+
return {
|
|
179
|
+
vaultPath: resolvedVaultPath,
|
|
180
|
+
qmdCollection: collection,
|
|
181
|
+
qmdRoot: fallbackRoot,
|
|
182
|
+
autoDetected
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
try {
|
|
186
|
+
const raw = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
187
|
+
const configuredName = readTrimmedString(raw.name) ?? fallbackName;
|
|
188
|
+
const configuredCollection = readTrimmedString(raw.qmdCollection);
|
|
189
|
+
const rawRoot = readTrimmedString(raw.qmdRoot) ?? fallbackRoot;
|
|
190
|
+
const qmdRoot = path.isAbsolute(rawRoot) ? path.resolve(rawRoot) : path.resolve(resolvedVaultPath, rawRoot);
|
|
191
|
+
if (configuredCollection && collectionExists(configuredCollection)) {
|
|
192
|
+
return {
|
|
193
|
+
vaultPath: resolvedVaultPath,
|
|
194
|
+
qmdCollection: configuredCollection,
|
|
195
|
+
qmdRoot
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
const { collection, autoDetected } = autoDetectCollection(qmdRoot, configuredCollection ?? configuredName);
|
|
199
|
+
return {
|
|
200
|
+
vaultPath: resolvedVaultPath,
|
|
201
|
+
qmdCollection: collection,
|
|
202
|
+
qmdRoot,
|
|
203
|
+
autoDetected
|
|
204
|
+
};
|
|
205
|
+
} catch {
|
|
206
|
+
const { collection, autoDetected } = autoDetectCollection(resolvedVaultPath, fallbackName);
|
|
207
|
+
return {
|
|
208
|
+
vaultPath: resolvedVaultPath,
|
|
209
|
+
qmdCollection: collection,
|
|
210
|
+
qmdRoot: fallbackRoot,
|
|
211
|
+
autoDetected
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// src/lib/qmd-embedding-recovery.ts
|
|
217
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
218
|
+
import * as fs2 from "fs";
|
|
219
|
+
import * as path2 from "path";
|
|
220
|
+
var CLAWVAULT_DIR = ".clawvault";
|
|
221
|
+
var QMD_EMBED_WAL_FILE = "qmd-embed.wal.json";
|
|
222
|
+
var WALK_SKIP_DIRS = /* @__PURE__ */ new Set([".git", ".obsidian", ".trash", ".clawvault", "node_modules"]);
|
|
223
|
+
function getQmdEmbedWalPath(vaultPath) {
|
|
224
|
+
return path2.join(vaultPath, CLAWVAULT_DIR, QMD_EMBED_WAL_FILE);
|
|
225
|
+
}
|
|
226
|
+
function writeJsonAtomic(filePath, value) {
|
|
227
|
+
const dir = path2.dirname(filePath);
|
|
228
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
229
|
+
const tmpPath = path2.join(
|
|
230
|
+
dir,
|
|
231
|
+
`${path2.basename(filePath)}.${process.pid}.${Date.now()}.tmp`
|
|
232
|
+
);
|
|
233
|
+
try {
|
|
234
|
+
fs2.writeFileSync(tmpPath, `${JSON.stringify(value, null, 2)}
|
|
235
|
+
`, "utf-8");
|
|
236
|
+
fs2.renameSync(tmpPath, filePath);
|
|
237
|
+
} finally {
|
|
238
|
+
if (fs2.existsSync(tmpPath)) {
|
|
239
|
+
fs2.rmSync(tmpPath, { force: true });
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
function runQmdCommand(args, indexName) {
|
|
244
|
+
execFileSync2("qmd", withQmdIndexArgs(args, indexName), { stdio: "inherit" });
|
|
245
|
+
}
|
|
246
|
+
function runQmdEmbedForce(collection, indexName) {
|
|
247
|
+
try {
|
|
248
|
+
runQmdCommand(["embed", "-f", "-c", collection], indexName);
|
|
249
|
+
} catch {
|
|
250
|
+
runQmdCommand(["embed", "-c", collection], indexName);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
function countMarkdownFiles(rootPath) {
|
|
254
|
+
let total = 0;
|
|
255
|
+
const stack = [rootPath];
|
|
256
|
+
while (stack.length > 0) {
|
|
257
|
+
const current = stack.pop();
|
|
258
|
+
if (!current) continue;
|
|
259
|
+
let entries;
|
|
260
|
+
try {
|
|
261
|
+
entries = fs2.readdirSync(current, { withFileTypes: true });
|
|
262
|
+
} catch {
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
for (const entry of entries) {
|
|
266
|
+
const entryPath = path2.join(current, entry.name);
|
|
267
|
+
if (entry.isDirectory()) {
|
|
268
|
+
if (entry.name.startsWith(".") || WALK_SKIP_DIRS.has(entry.name)) {
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
stack.push(entryPath);
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
if (entry.isFile() && entry.name.endsWith(".md") && !entry.name.startsWith(".")) {
|
|
275
|
+
total += 1;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
return total;
|
|
280
|
+
}
|
|
281
|
+
function shouldRecoverEmptyVectors(collectionInfo, markdownCount) {
|
|
282
|
+
if (!collectionInfo) return false;
|
|
283
|
+
if (markdownCount === 0) return false;
|
|
284
|
+
if (collectionInfo.vectors === void 0) return false;
|
|
285
|
+
if (collectionInfo.vectors > 0) return false;
|
|
286
|
+
if (collectionInfo.pendingEmbeddings !== void 0 && collectionInfo.pendingEmbeddings > 0) {
|
|
287
|
+
return false;
|
|
288
|
+
}
|
|
289
|
+
return true;
|
|
290
|
+
}
|
|
291
|
+
function hasWalMarker(vaultPath) {
|
|
292
|
+
return fs2.existsSync(getQmdEmbedWalPath(vaultPath));
|
|
293
|
+
}
|
|
294
|
+
function clearQmdEmbedWalRecord(vaultPath) {
|
|
295
|
+
const walPath = getQmdEmbedWalPath(vaultPath);
|
|
296
|
+
if (fs2.existsSync(walPath)) {
|
|
297
|
+
fs2.rmSync(walPath, { force: true });
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
function writeQmdEmbedWalRecord(options) {
|
|
301
|
+
const wal = {
|
|
302
|
+
version: 1,
|
|
303
|
+
status: "running",
|
|
304
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
305
|
+
collection: options.collection,
|
|
306
|
+
rootPath: options.rootPath,
|
|
307
|
+
indexName: options.indexName
|
|
308
|
+
};
|
|
309
|
+
writeJsonAtomic(getQmdEmbedWalPath(options.vaultPath), wal);
|
|
310
|
+
}
|
|
311
|
+
function runCrashSafeQmdEmbed(options) {
|
|
312
|
+
writeQmdEmbedWalRecord(options);
|
|
313
|
+
try {
|
|
314
|
+
runQmdCommand(["embed", "-c", options.collection], options.indexName);
|
|
315
|
+
clearQmdEmbedWalRecord(options.vaultPath);
|
|
316
|
+
} catch (err) {
|
|
317
|
+
throw err;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
function recoverQmdEmbeddingIfNeeded(options) {
|
|
321
|
+
const mode = options.mode ?? "marker-or-empty";
|
|
322
|
+
let reason;
|
|
323
|
+
if (hasWalMarker(options.vaultPath)) {
|
|
324
|
+
reason = "interrupted_wal";
|
|
325
|
+
} else if (mode === "marker-or-empty") {
|
|
326
|
+
const collectionInfo = getCollectionByName(options.collection, options.indexName);
|
|
327
|
+
const markdownCount = countMarkdownFiles(options.rootPath);
|
|
328
|
+
if (shouldRecoverEmptyVectors(collectionInfo, markdownCount)) {
|
|
329
|
+
reason = "empty_vectors";
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
if (!reason) {
|
|
333
|
+
return { recovered: false };
|
|
334
|
+
}
|
|
335
|
+
options.onLog?.(
|
|
336
|
+
reason === "interrupted_wal" ? `Detected interrupted qmd embedding run for "${options.collection}". Rebuilding...` : `Detected empty qmd vector state for "${options.collection}". Rebuilding...`
|
|
337
|
+
);
|
|
338
|
+
runQmdCommand(["update", "-c", options.collection], options.indexName);
|
|
339
|
+
runQmdEmbedForce(options.collection, options.indexName);
|
|
340
|
+
clearQmdEmbedWalRecord(options.vaultPath);
|
|
341
|
+
return { recovered: true, reason };
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
export {
|
|
345
|
+
parseQmdCollectionList,
|
|
346
|
+
listQmdCollections,
|
|
347
|
+
removeQmdCollection,
|
|
348
|
+
loadVaultQmdConfig,
|
|
349
|
+
runCrashSafeQmdEmbed,
|
|
350
|
+
recoverQmdEmbeddingIfNeeded
|
|
351
|
+
};
|
|
@@ -56,6 +56,11 @@ var TODO_SIGNAL_RE = /(?:\btodo:\s*|\bwe need to\b|\bdon't forget(?: to)?\b|\bre
|
|
|
56
56
|
var COMMITMENT_TASK_SIGNAL_RE = /\b(?:i'?ll|i will|let me|(?:i'?m\s+)?going to|plan to|should)\b/i;
|
|
57
57
|
var UNRESOLVED_COMMITMENT_RE = /\b(?:need to figure out|tbd|to be determined)\b/i;
|
|
58
58
|
var DEADLINE_SIGNAL_RE = /\b(?:by\s+(?:monday|tuesday|wednesday|thursday|friday|saturday|sunday|tomorrow)|before\s+the\s+\w+|deadline is)\b/i;
|
|
59
|
+
var ROLE_PREFIX_RE = /^([a-z][a-z0-9_-]{1,31})\s*:\s*(.+)$/i;
|
|
60
|
+
var BASE64_DATA_URI_RE = /\bdata:[^;\s]+;base64,[A-Za-z0-9+/=]{24,}\b/gi;
|
|
61
|
+
var LONG_BASE64_TOKEN_RE = /\b[A-Za-z0-9+/]{80,}={0,2}\b/g;
|
|
62
|
+
var NOISE_PREFIX_RE = /^(?:metadata|system metadata|session metadata)\s*:/i;
|
|
63
|
+
var STRUCTURED_NOISE_MARKER_RE = /\b(?:tool[_-]?result|tool[_-]?use|toolcallid|tooluseid|function[_-]?(?:call|result)|stdout|stderr|exitcode|recordedat|trace(?:_|-)?id|parent(?:_|-)?id|session(?:_|-)?id|metadata|base64|mime(?:type)?)\b/i;
|
|
59
64
|
var Compressor = class {
|
|
60
65
|
provider;
|
|
61
66
|
model;
|
|
@@ -72,7 +77,7 @@ var Compressor = class {
|
|
|
72
77
|
this.fetchImpl = options.fetchImpl ?? fetch;
|
|
73
78
|
}
|
|
74
79
|
async compress(messages, existingObservations) {
|
|
75
|
-
const cleanedMessages =
|
|
80
|
+
const cleanedMessages = this.sanitizeIncomingMessages(messages);
|
|
76
81
|
if (cleanedMessages.length === 0) {
|
|
77
82
|
return existingObservations.trim();
|
|
78
83
|
}
|
|
@@ -91,6 +96,82 @@ var Compressor = class {
|
|
|
91
96
|
const fallback = this.fallbackCompression(cleanedMessages);
|
|
92
97
|
return this.mergeObservations(existingObservations, fallback);
|
|
93
98
|
}
|
|
99
|
+
sanitizeIncomingMessages(messages) {
|
|
100
|
+
const sanitized = [];
|
|
101
|
+
for (const message of messages) {
|
|
102
|
+
const cleaned = this.sanitizeIncomingMessage(message);
|
|
103
|
+
if (cleaned) {
|
|
104
|
+
sanitized.push(cleaned);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return sanitized;
|
|
108
|
+
}
|
|
109
|
+
sanitizeIncomingMessage(message) {
|
|
110
|
+
const normalized = message.replace(/\s+/g, " ").trim();
|
|
111
|
+
if (!normalized) {
|
|
112
|
+
return "";
|
|
113
|
+
}
|
|
114
|
+
const roleMatch = ROLE_PREFIX_RE.exec(normalized);
|
|
115
|
+
if (roleMatch && this.isConversationRolePrefix(roleMatch[1])) {
|
|
116
|
+
const role = this.normalizeMessageRole(roleMatch[1]);
|
|
117
|
+
if (this.shouldDropMessageRole(role)) {
|
|
118
|
+
return "";
|
|
119
|
+
}
|
|
120
|
+
const content = this.stripNoisyData(roleMatch[2]);
|
|
121
|
+
if (!content || this.isLikelyStructuredNoise(content)) {
|
|
122
|
+
return "";
|
|
123
|
+
}
|
|
124
|
+
return `${role}: ${content}`;
|
|
125
|
+
}
|
|
126
|
+
const cleaned = this.stripNoisyData(normalized);
|
|
127
|
+
if (!cleaned || this.isLikelyStructuredNoise(cleaned)) {
|
|
128
|
+
return "";
|
|
129
|
+
}
|
|
130
|
+
return cleaned;
|
|
131
|
+
}
|
|
132
|
+
normalizeMessageRole(role) {
|
|
133
|
+
return role.trim().toLowerCase();
|
|
134
|
+
}
|
|
135
|
+
isConversationRolePrefix(role) {
|
|
136
|
+
const normalized = role.trim().toLowerCase().replace(/[\s_-]+/g, "");
|
|
137
|
+
if (!normalized) {
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
if (normalized === "user" || normalized === "assistant" || normalized === "system") {
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
if (normalized === "developer" || normalized === "metadata") {
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
146
|
+
return normalized.startsWith("tool");
|
|
147
|
+
}
|
|
148
|
+
shouldDropMessageRole(role) {
|
|
149
|
+
const normalized = role.replace(/[\s_-]+/g, "");
|
|
150
|
+
if (!normalized) {
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
if (normalized === "system" || normalized === "developer" || normalized === "metadata") {
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
return normalized.startsWith("tool");
|
|
157
|
+
}
|
|
158
|
+
stripNoisyData(value) {
|
|
159
|
+
return value.replace(BASE64_DATA_URI_RE, " ").replace(LONG_BASE64_TOKEN_RE, " ").replace(/\s+/g, " ").trim();
|
|
160
|
+
}
|
|
161
|
+
isLikelyStructuredNoise(value) {
|
|
162
|
+
const trimmed = value.trim();
|
|
163
|
+
if (!trimmed) {
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
if (NOISE_PREFIX_RE.test(trimmed)) {
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
const looksStructured = trimmed.startsWith("{") || trimmed.startsWith("[");
|
|
170
|
+
if (looksStructured && STRUCTURED_NOISE_MARKER_RE.test(trimmed) && trimmed.length >= 40) {
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
94
175
|
resolveProvider() {
|
|
95
176
|
if (process.env.CLAWVAULT_NO_LLM) return null;
|
|
96
177
|
if (this.provider) {
|
|
@@ -14,7 +14,7 @@ function findProtectedRanges(content) {
|
|
|
14
14
|
while ((match = codeBlockRegex.exec(content)) !== null) {
|
|
15
15
|
ranges.push({ start: match.index, end: match.index + match[0].length });
|
|
16
16
|
}
|
|
17
|
-
const inlineCodeRegex =
|
|
17
|
+
const inlineCodeRegex = /(?<!`)`[^`\n]+`(?!`)/g;
|
|
18
18
|
while ((match = inlineCodeRegex.exec(content)) !== null) {
|
|
19
19
|
ranges.push({ start: match.index, end: match.index + match[0].length });
|
|
20
20
|
}
|
|
@@ -28,8 +28,42 @@ function findProtectedRanges(content) {
|
|
|
28
28
|
}
|
|
29
29
|
return ranges;
|
|
30
30
|
}
|
|
31
|
-
function
|
|
32
|
-
return ranges.some((
|
|
31
|
+
function isProtectedRange(start, end, ranges) {
|
|
32
|
+
return ranges.some((range) => start < range.end && end > range.start);
|
|
33
|
+
}
|
|
34
|
+
function createAliasRegex(alias) {
|
|
35
|
+
const escapedAlias = alias.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
36
|
+
return new RegExp(`\\b${escapedAlias}\\b`, "gi");
|
|
37
|
+
}
|
|
38
|
+
function formatWikiLink(path, originalText) {
|
|
39
|
+
return originalText.toLowerCase() === path.split("/").pop()?.toLowerCase() ? `[[${path}]]` : `[[${path}|${originalText}]]`;
|
|
40
|
+
}
|
|
41
|
+
function planLinks(content, index, protectedRanges) {
|
|
42
|
+
const sortedAliases = getSortedAliases(index);
|
|
43
|
+
const linkedEntities = /* @__PURE__ */ new Set();
|
|
44
|
+
const claimedRanges = [];
|
|
45
|
+
const plannedLinks = [];
|
|
46
|
+
for (const { alias, path } of sortedAliases) {
|
|
47
|
+
if (linkedEntities.has(path)) continue;
|
|
48
|
+
const regex = createAliasRegex(alias);
|
|
49
|
+
let match;
|
|
50
|
+
while ((match = regex.exec(content)) !== null) {
|
|
51
|
+
const start = match.index;
|
|
52
|
+
const end = start + match[0].length;
|
|
53
|
+
if (isProtectedRange(start, end, protectedRanges)) continue;
|
|
54
|
+
if (isProtectedRange(start, end, claimedRanges)) continue;
|
|
55
|
+
plannedLinks.push({
|
|
56
|
+
start,
|
|
57
|
+
end,
|
|
58
|
+
originalText: match[0],
|
|
59
|
+
path
|
|
60
|
+
});
|
|
61
|
+
claimedRanges.push({ start, end });
|
|
62
|
+
linkedEntities.add(path);
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return plannedLinks;
|
|
33
67
|
}
|
|
34
68
|
function createLineLookup(content) {
|
|
35
69
|
const lines = content.split("\n");
|
|
@@ -48,56 +82,24 @@ function createLineLookup(content) {
|
|
|
48
82
|
}
|
|
49
83
|
function autoLink(content, index) {
|
|
50
84
|
const protectedRanges = findProtectedRanges(content);
|
|
51
|
-
const
|
|
52
|
-
const linkedEntities = /* @__PURE__ */ new Set();
|
|
85
|
+
const plannedLinks = planLinks(content, index, protectedRanges);
|
|
53
86
|
let result = content;
|
|
54
|
-
|
|
55
|
-
for (const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const regex = new RegExp(`\\b${escapedAlias}\\b`, "gi");
|
|
59
|
-
let match;
|
|
60
|
-
while ((match = regex.exec(content)) !== null) {
|
|
61
|
-
const originalPos = match.index;
|
|
62
|
-
const adjustedPos = originalPos + offset;
|
|
63
|
-
if (isProtected(originalPos, protectedRanges)) continue;
|
|
64
|
-
const beforeMatch = result.substring(0, adjustedPos);
|
|
65
|
-
const openBrackets = (beforeMatch.match(/\[\[/g) || []).length;
|
|
66
|
-
const closeBrackets = (beforeMatch.match(/\]\]/g) || []).length;
|
|
67
|
-
if (openBrackets > closeBrackets) continue;
|
|
68
|
-
const originalText = match[0];
|
|
69
|
-
const replacement = originalText.toLowerCase() === path.split("/").pop()?.toLowerCase() ? `[[${path}]]` : `[[${path}|${originalText}]]`;
|
|
70
|
-
result = result.substring(0, adjustedPos) + replacement + result.substring(adjustedPos + originalText.length);
|
|
71
|
-
offset += replacement.length - originalText.length;
|
|
72
|
-
linkedEntities.add(path);
|
|
73
|
-
break;
|
|
74
|
-
}
|
|
87
|
+
const sortedByPosition = plannedLinks.slice().sort((a, b) => b.start - a.start);
|
|
88
|
+
for (const planned of sortedByPosition) {
|
|
89
|
+
const replacement = formatWikiLink(planned.path, planned.originalText);
|
|
90
|
+
result = result.substring(0, planned.start) + replacement + result.substring(planned.end);
|
|
75
91
|
}
|
|
76
92
|
return result;
|
|
77
93
|
}
|
|
78
94
|
function dryRunLink(content, index) {
|
|
79
95
|
const protectedRanges = findProtectedRanges(content);
|
|
80
|
-
const
|
|
81
|
-
const linkedEntities = /* @__PURE__ */ new Set();
|
|
82
|
-
const matches = [];
|
|
96
|
+
const plannedLinks = planLinks(content, index, protectedRanges);
|
|
83
97
|
const getLineNumber = createLineLookup(content);
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
while ((match = regex.exec(content)) !== null) {
|
|
90
|
-
if (isProtected(match.index, protectedRanges)) continue;
|
|
91
|
-
matches.push({
|
|
92
|
-
alias: match[0],
|
|
93
|
-
path,
|
|
94
|
-
line: getLineNumber(match.index)
|
|
95
|
-
});
|
|
96
|
-
linkedEntities.add(path);
|
|
97
|
-
break;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
return matches;
|
|
98
|
+
return plannedLinks.map((planned) => ({
|
|
99
|
+
alias: planned.originalText,
|
|
100
|
+
path: planned.path,
|
|
101
|
+
line: getLineNumber(planned.start)
|
|
102
|
+
}));
|
|
101
103
|
}
|
|
102
104
|
function findUnlinkedMentions(content, index) {
|
|
103
105
|
const protectedRanges = findProtectedRanges(content);
|
|
@@ -111,7 +113,9 @@ function findUnlinkedMentions(content, index) {
|
|
|
111
113
|
const regex = new RegExp(`\\b${escapedAlias}\\b`, "gi");
|
|
112
114
|
let match;
|
|
113
115
|
while ((match = regex.exec(content)) !== null) {
|
|
114
|
-
|
|
116
|
+
const start = match.index;
|
|
117
|
+
const end = start + match[0].length;
|
|
118
|
+
if (isProtectedRange(start, end, protectedRanges)) continue;
|
|
115
119
|
matches.push({
|
|
116
120
|
alias: match[0],
|
|
117
121
|
path,
|
|
@@ -3,18 +3,18 @@ import {
|
|
|
3
3
|
} from "./chunk-7ZRP733D.js";
|
|
4
4
|
import {
|
|
5
5
|
scanVaultLinks
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-7YZWHM36.js";
|
|
7
7
|
import {
|
|
8
8
|
getObserverStaleness
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-T3FKSZSN.js";
|
|
10
10
|
import {
|
|
11
11
|
ClawVault,
|
|
12
12
|
findVault
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-YD7SVXTF.js";
|
|
14
14
|
import {
|
|
15
15
|
listQmdCollections,
|
|
16
16
|
loadVaultQmdConfig
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-FBITHIZF.js";
|
|
18
18
|
import {
|
|
19
19
|
QMD_INSTALL_COMMAND,
|
|
20
20
|
QMD_INSTALL_URL,
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
} from "./chunk-5PJ4STIC.js";
|
|
23
23
|
import {
|
|
24
24
|
loadMemoryGraphIndex
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-33DOSHTA.js";
|
|
26
26
|
import {
|
|
27
27
|
checkOpenClawCompatibility
|
|
28
28
|
} from "./chunk-X3SPPUFG.js";
|
|
@@ -3,19 +3,19 @@ import {
|
|
|
3
3
|
} from "./chunk-Y6VJKXGL.js";
|
|
4
4
|
import {
|
|
5
5
|
registerObserveCommand
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-SV7T4HRE.js";
|
|
7
7
|
import {
|
|
8
8
|
registerReflectCommand
|
|
9
9
|
} from "./chunk-S5OJEGFG.js";
|
|
10
10
|
import {
|
|
11
11
|
registerContextCommand
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-TS6NDVOU.js";
|
|
13
13
|
import {
|
|
14
14
|
registerEmbedCommand
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-2GKPENIR.js";
|
|
16
16
|
import {
|
|
17
17
|
registerInjectCommand
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-A4EAUO7T.js";
|
|
19
19
|
|
|
20
20
|
// src/cli/index.ts
|
|
21
21
|
function registerCliCommands(program) {
|