clawvault 3.2.0 → 3.3.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.
Files changed (112) hide show
  1. package/README.md +54 -14
  2. package/bin/clawvault.js +0 -2
  3. package/bin/command-registration.test.js +13 -1
  4. package/bin/help-contract.test.js +14 -0
  5. package/bin/register-core-commands.js +88 -0
  6. package/bin/register-core-commands.test.js +80 -0
  7. package/bin/register-maintenance-commands.js +57 -6
  8. package/bin/register-query-commands.js +10 -28
  9. package/bin/test-helpers/cli-command-fixtures.js +1 -0
  10. package/dist/chunk-2PKBIKDH.js +130 -0
  11. package/dist/{chunk-2JQ3O2YL.js → chunk-5EFSWZO6.js} +3 -3
  12. package/dist/{chunk-77Q5CSPJ.js → chunk-7SWP5FKU.js} +33 -701
  13. package/dist/{chunk-URXDAUVH.js → chunk-AXSJIFOJ.js} +174 -1
  14. package/dist/{chunk-23YDQ3QU.js → chunk-BLQXXX7Q.js} +6 -6
  15. package/dist/chunk-CSHO3PJB.js +684 -0
  16. package/dist/{chunk-SLXOR3CC.js → chunk-DOIUYIXV.js} +2 -2
  17. package/dist/{chunk-NCKFNBHJ.js → chunk-DVOUSOR3.js} +79 -5
  18. package/dist/{chunk-CLJTREDS.js → chunk-ECGJYWNA.js} +193 -41
  19. package/dist/{chunk-BUEW6IIK.js → chunk-EL6UBSX5.js} +5 -5
  20. package/dist/{chunk-6FH3IULF.js → chunk-FZ5I2NF7.js} +1 -1
  21. package/dist/{chunk-ZN54U2OZ.js → chunk-GFCHWMGD.js} +3 -3
  22. package/dist/{chunk-GNJL4YGR.js → chunk-GJO3CFUN.js} +30 -6
  23. package/dist/chunk-H3JZIB5O.js +322 -0
  24. package/dist/chunk-HEHO7SMV.js +51 -0
  25. package/dist/{chunk-STCQGCEQ.js → chunk-HGDDW24U.js} +3 -3
  26. package/dist/chunk-J3YUXVID.js +907 -0
  27. package/dist/{chunk-Y6VJKXGL.js → chunk-KCYWJDDW.js} +1 -1
  28. package/dist/{chunk-W4SPAEE7.js → chunk-OFOCU2V4.js} +5 -4
  29. package/dist/chunk-PTWPPVC7.js +972 -0
  30. package/dist/{chunk-QSHD36LH.js → chunk-QFWERBDP.js} +2 -2
  31. package/dist/{chunk-QSRRMEYM.js → chunk-S7N7HI5E.js} +1 -1
  32. package/dist/{chunk-PBACDKKP.js → chunk-T7E764W3.js} +3 -3
  33. package/dist/chunk-TDWFBDAQ.js +1016 -0
  34. package/dist/{chunk-ESVS6K2B.js → chunk-TWMI3SNN.js} +6 -5
  35. package/dist/{chunk-2RAZ4ZFE.js → chunk-VBILES4B.js} +1 -1
  36. package/dist/{chunk-ESFLMDRB.js → chunk-VXAGOLDP.js} +3 -3
  37. package/dist/chunk-YCUVAOFC.js +158 -0
  38. package/dist/{chunk-SS4B7P7V.js → chunk-YIDV4VV2.js} +1 -1
  39. package/dist/chunk-ZKWPCBYT.js +600 -0
  40. package/dist/cli/index.js +24 -24
  41. package/dist/commands/archive.js +2 -2
  42. package/dist/commands/benchmark.d.ts +12 -0
  43. package/dist/commands/benchmark.js +12 -0
  44. package/dist/commands/context.js +6 -5
  45. package/dist/commands/doctor.d.ts +8 -3
  46. package/dist/commands/doctor.js +6 -20
  47. package/dist/commands/embed.js +5 -4
  48. package/dist/commands/entities.js +1 -1
  49. package/dist/commands/graph.js +2 -2
  50. package/dist/commands/inbox.d.ts +23 -0
  51. package/dist/commands/inbox.js +11 -0
  52. package/dist/commands/inject.d.ts +1 -1
  53. package/dist/commands/inject.js +3 -3
  54. package/dist/commands/link.js +6 -6
  55. package/dist/commands/maintain.d.ts +32 -0
  56. package/dist/commands/maintain.js +12 -0
  57. package/dist/commands/migrate-observations.js +2 -2
  58. package/dist/commands/observe.js +9 -8
  59. package/dist/commands/rebuild-embeddings.js +47 -16
  60. package/dist/commands/rebuild.js +7 -6
  61. package/dist/commands/reflect.js +5 -5
  62. package/dist/commands/replay.js +8 -7
  63. package/dist/commands/setup.js +3 -2
  64. package/dist/commands/sleep.d.ts +1 -1
  65. package/dist/commands/sleep.js +17 -15
  66. package/dist/commands/status.js +26 -24
  67. package/dist/commands/sync-bd.js +2 -2
  68. package/dist/commands/tailscale.js +2 -2
  69. package/dist/commands/wake.d.ts +1 -1
  70. package/dist/commands/wake.js +8 -7
  71. package/dist/index.d.ts +168 -16
  72. package/dist/index.js +271 -108
  73. package/dist/{inject-DYUrDqQO.d.ts → inject-DEb_jpLi.d.ts} +3 -1
  74. package/dist/lib/config.js +1 -1
  75. package/dist/{types-BbWJoC1c.d.ts → types-DslKvCaj.d.ts} +51 -1
  76. package/hooks/clawvault/HOOK.md +22 -5
  77. package/hooks/clawvault/handler.js +213 -78
  78. package/hooks/clawvault/handler.test.js +109 -43
  79. package/hooks/clawvault/integrity.js +112 -0
  80. package/hooks/clawvault/integrity.test.js +32 -0
  81. package/hooks/clawvault/openclaw.plugin.json +133 -15
  82. package/openclaw.plugin.json +126 -20
  83. package/package.json +2 -2
  84. package/bin/register-workgraph-commands.js +0 -1368
  85. package/dist/chunk-33VSQP4J.js +0 -37
  86. package/dist/chunk-4BQTQMJP.js +0 -93
  87. package/dist/chunk-EK6S23ZB.js +0 -469
  88. package/dist/chunk-GAOWA7GR.js +0 -501
  89. package/dist/chunk-GGA32J2R.js +0 -784
  90. package/dist/chunk-MM6QGW3P.js +0 -207
  91. package/dist/chunk-QVEERJSP.js +0 -152
  92. package/dist/chunk-U4O6C46S.js +0 -154
  93. package/dist/chunk-VSL7KY3M.js +0 -189
  94. package/dist/chunk-WMGIIABP.js +0 -15
  95. package/dist/commands/workgraph.d.ts +0 -124
  96. package/dist/commands/workgraph.js +0 -38
  97. package/dist/ledger-B7g7jhqG.d.ts +0 -44
  98. package/dist/registry-BR4326o0.d.ts +0 -30
  99. package/dist/store-CA-6sKCJ.d.ts +0 -34
  100. package/dist/thread-B9LhXNU0.d.ts +0 -41
  101. package/dist/workgraph/index.d.ts +0 -5
  102. package/dist/workgraph/index.js +0 -23
  103. package/dist/workgraph/ledger.d.ts +0 -2
  104. package/dist/workgraph/ledger.js +0 -25
  105. package/dist/workgraph/registry.d.ts +0 -2
  106. package/dist/workgraph/registry.js +0 -19
  107. package/dist/workgraph/store.d.ts +0 -2
  108. package/dist/workgraph/store.js +0 -25
  109. package/dist/workgraph/thread.d.ts +0 -2
  110. package/dist/workgraph/thread.js +0 -25
  111. package/dist/workgraph/types.d.ts +0 -54
  112. package/dist/workgraph/types.js +0 -7
@@ -0,0 +1,322 @@
1
+ // src/lib/embedding-store.ts
2
+ import * as crypto from "crypto";
3
+ import * as fs from "fs";
4
+ import * as path from "path";
5
+ var CACHE_DIR = ".clawvault";
6
+ var CACHE_FILE = "embeddings.bin.json";
7
+ var LEGACY_BIN_FILE = "embeddings.bin";
8
+ var EmbeddingStore = class {
9
+ vaultPath;
10
+ provider = "none";
11
+ model = "";
12
+ vectors = /* @__PURE__ */ new Map();
13
+ dirty = false;
14
+ constructor(vaultPath) {
15
+ this.vaultPath = path.resolve(vaultPath);
16
+ }
17
+ setVaultPath(vaultPath) {
18
+ this.vaultPath = path.resolve(vaultPath);
19
+ }
20
+ setSignature(provider, model) {
21
+ if (this.provider !== provider || this.model !== model) {
22
+ this.provider = provider;
23
+ this.model = model;
24
+ this.dirty = true;
25
+ }
26
+ }
27
+ getSignature() {
28
+ return { provider: this.provider, model: this.model };
29
+ }
30
+ isCompatible(provider, model) {
31
+ if (this.provider === "none" || !this.model) {
32
+ return this.vectors.size === 0;
33
+ }
34
+ return this.provider === provider && this.model === model;
35
+ }
36
+ load() {
37
+ this.vectors.clear();
38
+ const cachePath = this.getCachePath();
39
+ if (!fs.existsSync(cachePath)) {
40
+ return;
41
+ }
42
+ try {
43
+ const raw = JSON.parse(fs.readFileSync(cachePath, "utf-8"));
44
+ if ("version" in raw && "vectors" in raw) {
45
+ this.provider = typeof raw.provider === "string" ? raw.provider : "none";
46
+ this.model = typeof raw.model === "string" ? raw.model : "";
47
+ for (const [docId, value] of Object.entries(raw.vectors)) {
48
+ if (Array.isArray(value)) {
49
+ this.vectors.set(docId, {
50
+ hash: "",
51
+ embedding: new Float32Array(value)
52
+ });
53
+ continue;
54
+ }
55
+ if (!value || !Array.isArray(value.embedding)) {
56
+ continue;
57
+ }
58
+ this.vectors.set(docId, {
59
+ hash: typeof value.hash === "string" ? value.hash : "",
60
+ embedding: new Float32Array(value.embedding)
61
+ });
62
+ }
63
+ } else {
64
+ this.provider = "none";
65
+ this.model = "";
66
+ for (const [docId, embedding] of Object.entries(raw)) {
67
+ if (Array.isArray(embedding)) {
68
+ this.vectors.set(docId, {
69
+ hash: "",
70
+ embedding: new Float32Array(embedding)
71
+ });
72
+ }
73
+ }
74
+ }
75
+ this.dirty = false;
76
+ } catch {
77
+ this.provider = "none";
78
+ this.model = "";
79
+ this.vectors.clear();
80
+ this.dirty = false;
81
+ }
82
+ }
83
+ save() {
84
+ if (!this.dirty) return;
85
+ const cacheDir = path.dirname(this.getCachePath());
86
+ fs.mkdirSync(cacheDir, { recursive: true });
87
+ const vectors = {};
88
+ for (const [docId, entry] of this.vectors.entries()) {
89
+ vectors[docId] = {
90
+ hash: entry.hash,
91
+ embedding: Array.from(entry.embedding)
92
+ };
93
+ }
94
+ const payload = {
95
+ version: 2,
96
+ provider: this.provider,
97
+ model: this.model,
98
+ vectors
99
+ };
100
+ fs.writeFileSync(this.getCachePath(), JSON.stringify(payload));
101
+ const legacyBinPath = path.join(cacheDir, LEGACY_BIN_FILE);
102
+ if (!fs.existsSync(legacyBinPath)) {
103
+ fs.writeFileSync(legacyBinPath, "");
104
+ }
105
+ this.dirty = false;
106
+ }
107
+ get(docId) {
108
+ return this.vectors.get(docId)?.embedding;
109
+ }
110
+ getHash(docId) {
111
+ return this.vectors.get(docId)?.hash;
112
+ }
113
+ set(docId, hash, embedding) {
114
+ this.vectors.set(docId, { hash, embedding });
115
+ this.dirty = true;
116
+ }
117
+ has(docId) {
118
+ return this.vectors.has(docId);
119
+ }
120
+ delete(docId) {
121
+ if (this.vectors.delete(docId)) {
122
+ this.dirty = true;
123
+ }
124
+ }
125
+ prune(validDocIds) {
126
+ let removedAny = false;
127
+ for (const key of this.vectors.keys()) {
128
+ if (!validDocIds.has(key)) {
129
+ this.vectors.delete(key);
130
+ removedAny = true;
131
+ }
132
+ }
133
+ if (removedAny) {
134
+ this.dirty = true;
135
+ }
136
+ }
137
+ entries() {
138
+ const pairs = Array.from(this.vectors.entries()).map(([id, entry]) => [id, entry.embedding]);
139
+ return pairs[Symbol.iterator]();
140
+ }
141
+ get size() {
142
+ return this.vectors.size;
143
+ }
144
+ clear() {
145
+ if (this.vectors.size === 0) return;
146
+ this.vectors.clear();
147
+ this.dirty = true;
148
+ }
149
+ getCachePath() {
150
+ return path.join(this.vaultPath, CACHE_DIR, CACHE_FILE);
151
+ }
152
+ };
153
+ function computeEmbeddingHash(text) {
154
+ return crypto.createHash("sha1").update(text).digest("hex");
155
+ }
156
+
157
+ // src/lib/hosted-embeddings.ts
158
+ var DEFAULT_OPENAI_MODEL = "text-embedding-3-small";
159
+ var DEFAULT_GEMINI_MODEL = "text-embedding-004";
160
+ var DEFAULT_OLLAMA_MODEL = "nomic-embed-text";
161
+ function normalizeBaseUrl(url) {
162
+ return url.replace(/\/+$/, "");
163
+ }
164
+ function toFloat32(values) {
165
+ if (!Array.isArray(values)) {
166
+ throw new Error("Embedding response did not contain a numeric vector.");
167
+ }
168
+ return new Float32Array(values.map((value) => Number(value)));
169
+ }
170
+ function resolveApiKey(provider, configuredApiKey) {
171
+ if (configuredApiKey?.trim()) return configuredApiKey.trim();
172
+ if (provider === "openai") return process.env.OPENAI_API_KEY?.trim();
173
+ if (provider === "gemini") return process.env.GEMINI_API_KEY?.trim() || process.env.GOOGLE_API_KEY?.trim();
174
+ return void 0;
175
+ }
176
+ function resolveEmbeddingConfig(searchConfig) {
177
+ const provider = searchConfig?.embeddings?.provider ?? "none";
178
+ if (provider === "none") {
179
+ return null;
180
+ }
181
+ if (provider === "openai") {
182
+ return {
183
+ provider,
184
+ model: searchConfig?.embeddings?.model?.trim() || DEFAULT_OPENAI_MODEL,
185
+ baseUrl: normalizeBaseUrl(searchConfig?.embeddings?.baseUrl?.trim() || "https://api.openai.com/v1"),
186
+ apiKey: resolveApiKey(provider, searchConfig?.embeddings?.apiKey)
187
+ };
188
+ }
189
+ if (provider === "gemini") {
190
+ return {
191
+ provider,
192
+ model: searchConfig?.embeddings?.model?.trim() || DEFAULT_GEMINI_MODEL,
193
+ baseUrl: normalizeBaseUrl(searchConfig?.embeddings?.baseUrl?.trim() || "https://generativelanguage.googleapis.com/v1beta"),
194
+ apiKey: resolveApiKey(provider, searchConfig?.embeddings?.apiKey)
195
+ };
196
+ }
197
+ return {
198
+ provider,
199
+ model: searchConfig?.embeddings?.model?.trim() || DEFAULT_OLLAMA_MODEL,
200
+ baseUrl: normalizeBaseUrl(searchConfig?.embeddings?.baseUrl?.trim() || "http://127.0.0.1:11434")
201
+ };
202
+ }
203
+ async function openAiEmbed(text, config) {
204
+ if (!config.apiKey) {
205
+ throw new Error("OpenAI embeddings require an API key (search.embeddings.apiKey or OPENAI_API_KEY).");
206
+ }
207
+ const response = await fetch(`${config.baseUrl}/embeddings`, {
208
+ method: "POST",
209
+ headers: {
210
+ "Content-Type": "application/json",
211
+ Authorization: `Bearer ${config.apiKey}`
212
+ },
213
+ body: JSON.stringify({
214
+ model: config.model,
215
+ input: text
216
+ }),
217
+ signal: AbortSignal.timeout(2e4)
218
+ });
219
+ if (!response.ok) {
220
+ throw new Error(`OpenAI embedding request failed with status ${response.status}.`);
221
+ }
222
+ const payload = await response.json();
223
+ return toFloat32(payload.data?.[0]?.embedding);
224
+ }
225
+ function geminiModelPath(model) {
226
+ return model.startsWith("models/") ? model : `models/${model}`;
227
+ }
228
+ async function geminiEmbed(text, config, isQuery) {
229
+ if (!config.apiKey) {
230
+ throw new Error("Gemini embeddings require an API key (search.embeddings.apiKey, GEMINI_API_KEY, or GOOGLE_API_KEY).");
231
+ }
232
+ const response = await fetch(`${config.baseUrl}/${geminiModelPath(config.model)}:embedContent`, {
233
+ method: "POST",
234
+ headers: {
235
+ "Content-Type": "application/json",
236
+ "x-goog-api-key": config.apiKey
237
+ },
238
+ body: JSON.stringify({
239
+ content: {
240
+ parts: [{ text }]
241
+ },
242
+ taskType: isQuery ? "RETRIEVAL_QUERY" : "RETRIEVAL_DOCUMENT"
243
+ }),
244
+ signal: AbortSignal.timeout(2e4)
245
+ });
246
+ if (!response.ok) {
247
+ throw new Error(`Gemini embedding request failed with status ${response.status}.`);
248
+ }
249
+ const payload = await response.json();
250
+ const values = payload.embedding?.values ?? payload.embeddings?.[0]?.values;
251
+ return toFloat32(values);
252
+ }
253
+ async function ollamaEmbed(text, config) {
254
+ const primary = await fetch(`${config.baseUrl}/api/embed`, {
255
+ method: "POST",
256
+ headers: { "Content-Type": "application/json" },
257
+ body: JSON.stringify({
258
+ model: config.model,
259
+ input: text
260
+ }),
261
+ signal: AbortSignal.timeout(2e4)
262
+ });
263
+ if (primary.ok) {
264
+ const payload2 = await primary.json();
265
+ return toFloat32(payload2.embeddings?.[0]);
266
+ }
267
+ const fallback = await fetch(`${config.baseUrl}/api/embeddings`, {
268
+ method: "POST",
269
+ headers: { "Content-Type": "application/json" },
270
+ body: JSON.stringify({
271
+ model: config.model,
272
+ prompt: text
273
+ }),
274
+ signal: AbortSignal.timeout(2e4)
275
+ });
276
+ if (!fallback.ok) {
277
+ throw new Error(`Ollama embedding request failed with status ${fallback.status}.`);
278
+ }
279
+ const payload = await fallback.json();
280
+ return toFloat32(payload.embedding);
281
+ }
282
+ async function embedText(text, config, options = {}) {
283
+ const normalized = text.trim();
284
+ if (!normalized) {
285
+ throw new Error("Cannot embed empty text.");
286
+ }
287
+ const truncated = normalized.slice(0, 12e3);
288
+ if (config.provider === "openai") {
289
+ return openAiEmbed(truncated, config);
290
+ }
291
+ if (config.provider === "gemini") {
292
+ return geminiEmbed(truncated, config, options.isQuery ?? false);
293
+ }
294
+ return ollamaEmbed(truncated, config);
295
+ }
296
+ function cosineSimilarity(a, b) {
297
+ if (a.length !== b.length || a.length === 0) {
298
+ return 0;
299
+ }
300
+ let dot = 0;
301
+ let normA = 0;
302
+ let normB = 0;
303
+ for (let index = 0; index < a.length; index += 1) {
304
+ const av = a[index];
305
+ const bv = b[index];
306
+ dot += av * bv;
307
+ normA += av * av;
308
+ normB += bv * bv;
309
+ }
310
+ if (normA === 0 || normB === 0) {
311
+ return 0;
312
+ }
313
+ return dot / (Math.sqrt(normA) * Math.sqrt(normB));
314
+ }
315
+
316
+ export {
317
+ EmbeddingStore,
318
+ computeEmbeddingHash,
319
+ resolveEmbeddingConfig,
320
+ embedText,
321
+ cosineSimilarity
322
+ };
@@ -0,0 +1,51 @@
1
+ import {
2
+ addInboxItem
3
+ } from "./chunk-2PKBIKDH.js";
4
+ import {
5
+ resolveVaultPath
6
+ } from "./chunk-GJO3CFUN.js";
7
+
8
+ // src/commands/inbox.ts
9
+ import * as fs from "fs";
10
+ function resolveInboxContent(options) {
11
+ const inline = options.content?.trim();
12
+ if (inline) {
13
+ return inline;
14
+ }
15
+ if (options.stdin) {
16
+ const reader = options.readStdin ?? (() => fs.readFileSync(0, "utf-8"));
17
+ const stdinContent = reader().trim();
18
+ if (stdinContent) {
19
+ return stdinContent;
20
+ }
21
+ }
22
+ throw new Error("Inbox content is required. Provide <content> or pipe stdin.");
23
+ }
24
+ async function inboxAddCommand(options) {
25
+ const vaultPath = resolveVaultPath({ explicitPath: options.vaultPath });
26
+ const content = resolveInboxContent(options);
27
+ const result = addInboxItem(vaultPath, content, {
28
+ title: options.title,
29
+ source: options.source
30
+ });
31
+ console.log(`\u2713 Added inbox capture: ${result.id}`);
32
+ console.log(` Path: ${result.path}`);
33
+ return result;
34
+ }
35
+ function registerInboxCommand(program) {
36
+ const inbox = program.command("inbox").description("Manage raw captures in the vault inbox");
37
+ inbox.command("add [content]").description("Add content to inbox (or pipe stdin)").option("-t, --title <title>", "Optional capture title").option("--source <source>", "Source label (email, transcript, note, export, etc.)").option("--stdin", "Read content from stdin").option("-v, --vault <path>", "Vault path").action(async (content, rawOptions) => {
38
+ await inboxAddCommand({
39
+ vaultPath: rawOptions.vault,
40
+ content,
41
+ title: rawOptions.title,
42
+ source: rawOptions.source,
43
+ stdin: rawOptions.stdin || !process.stdin.isTTY
44
+ });
45
+ });
46
+ }
47
+
48
+ export {
49
+ inboxAddCommand,
50
+ registerInboxCommand
51
+ };
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  runReflection
3
- } from "./chunk-ESVS6K2B.js";
3
+ } from "./chunk-TWMI3SNN.js";
4
4
  import {
5
5
  Observer
6
- } from "./chunk-77Q5CSPJ.js";
6
+ } from "./chunk-7SWP5FKU.js";
7
7
  import {
8
8
  resolveVaultPath
9
- } from "./chunk-GNJL4YGR.js";
9
+ } from "./chunk-GJO3CFUN.js";
10
10
 
11
11
  // src/commands/replay.ts
12
12
  import * as fs from "fs";