@deeplake/hivemind 0.7.31 → 0.7.33
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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/bundle/cli.js +594 -267
- package/codex/bundle/capture.js +585 -124
- package/codex/bundle/commands/auth-login.js +163 -30
- package/codex/bundle/embeddings/embed-daemon.js +55 -4
- package/codex/bundle/pre-tool-use.js +475 -85
- package/codex/bundle/session-start-setup.js +193 -60
- package/codex/bundle/session-start.js +221 -88
- package/codex/bundle/shell/deeplake-shell.js +458 -68
- package/codex/bundle/stop.js +565 -175
- package/codex/bundle/wiki-worker.js +258 -19
- package/cursor/bundle/capture.js +660 -199
- package/cursor/bundle/commands/auth-login.js +163 -30
- package/cursor/bundle/embeddings/embed-daemon.js +55 -4
- package/cursor/bundle/pre-tool-use.js +460 -70
- package/cursor/bundle/session-start.js +271 -131
- package/cursor/bundle/shell/deeplake-shell.js +458 -68
- package/cursor/bundle/wiki-worker.js +258 -19
- package/hermes/bundle/capture.js +661 -200
- package/hermes/bundle/commands/auth-login.js +163 -30
- package/hermes/bundle/embeddings/embed-daemon.js +55 -4
- package/hermes/bundle/pre-tool-use.js +459 -69
- package/hermes/bundle/session-start.js +268 -128
- package/hermes/bundle/shell/deeplake-shell.js +458 -68
- package/hermes/bundle/wiki-worker.js +258 -19
- package/mcp/bundle/server.js +190 -57
- package/openclaw/dist/index.js +160 -32
- package/openclaw/openclaw.plugin.json +1 -1
- package/openclaw/package.json +1 -1
- package/package.json +1 -1
package/codex/bundle/capture.js
CHANGED
|
@@ -17,21 +17,21 @@ __export(index_marker_store_exports, {
|
|
|
17
17
|
hasFreshIndexMarker: () => hasFreshIndexMarker,
|
|
18
18
|
writeIndexMarker: () => writeIndexMarker
|
|
19
19
|
});
|
|
20
|
-
import { existsSync as existsSync2, mkdirSync, readFileSync as
|
|
21
|
-
import { join as
|
|
20
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "node:fs";
|
|
21
|
+
import { join as join5 } from "node:path";
|
|
22
22
|
import { tmpdir } from "node:os";
|
|
23
23
|
function getIndexMarkerDir() {
|
|
24
|
-
return process.env.HIVEMIND_INDEX_MARKER_DIR ??
|
|
24
|
+
return process.env.HIVEMIND_INDEX_MARKER_DIR ?? join5(tmpdir(), "hivemind-deeplake-indexes");
|
|
25
25
|
}
|
|
26
26
|
function buildIndexMarkerPath(workspaceId, orgId, table, suffix) {
|
|
27
27
|
const markerKey = [workspaceId, orgId, table, suffix].join("__").replace(/[^a-zA-Z0-9_.-]/g, "_");
|
|
28
|
-
return
|
|
28
|
+
return join5(getIndexMarkerDir(), `${markerKey}.json`);
|
|
29
29
|
}
|
|
30
30
|
function hasFreshIndexMarker(markerPath) {
|
|
31
31
|
if (!existsSync2(markerPath))
|
|
32
32
|
return false;
|
|
33
33
|
try {
|
|
34
|
-
const raw = JSON.parse(
|
|
34
|
+
const raw = JSON.parse(readFileSync4(markerPath, "utf-8"));
|
|
35
35
|
const updatedAt = raw.updatedAt ? new Date(raw.updatedAt).getTime() : NaN;
|
|
36
36
|
if (!Number.isFinite(updatedAt) || Date.now() - updatedAt > INDEX_MARKER_TTL_MS)
|
|
37
37
|
return false;
|
|
@@ -41,8 +41,8 @@ function hasFreshIndexMarker(markerPath) {
|
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
function writeIndexMarker(markerPath) {
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
mkdirSync3(getIndexMarkerDir(), { recursive: true });
|
|
45
|
+
writeFileSync3(markerPath, JSON.stringify({ updatedAt: (/* @__PURE__ */ new Date()).toISOString() }), "utf-8");
|
|
46
46
|
}
|
|
47
47
|
var INDEX_MARKER_TTL_MS;
|
|
48
48
|
var init_index_marker_store = __esm({
|
|
@@ -54,13 +54,13 @@ var init_index_marker_store = __esm({
|
|
|
54
54
|
|
|
55
55
|
// dist/src/utils/stdin.js
|
|
56
56
|
function readStdin() {
|
|
57
|
-
return new Promise((
|
|
57
|
+
return new Promise((resolve2, reject) => {
|
|
58
58
|
let data = "";
|
|
59
59
|
process.stdin.setEncoding("utf-8");
|
|
60
60
|
process.stdin.on("data", (chunk) => data += chunk);
|
|
61
61
|
process.stdin.on("end", () => {
|
|
62
62
|
try {
|
|
63
|
-
|
|
63
|
+
resolve2(JSON.parse(data));
|
|
64
64
|
} catch (err) {
|
|
65
65
|
reject(new Error(`Failed to parse hook input: ${err}`));
|
|
66
66
|
}
|
|
@@ -147,6 +147,125 @@ function deeplakeClientHeader() {
|
|
|
147
147
|
return { [DEEPLAKE_CLIENT_HEADER]: deeplakeClientValue() };
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
+
// dist/src/notifications/queue.js
|
|
151
|
+
import { readFileSync as readFileSync2, writeFileSync, renameSync, mkdirSync, openSync, closeSync, unlinkSync, statSync } from "node:fs";
|
|
152
|
+
import { join as join3, resolve } from "node:path";
|
|
153
|
+
import { homedir as homedir3 } from "node:os";
|
|
154
|
+
import { setTimeout as sleep } from "node:timers/promises";
|
|
155
|
+
var log2 = (msg) => log("notifications-queue", msg);
|
|
156
|
+
var LOCK_RETRY_MAX = 50;
|
|
157
|
+
var LOCK_RETRY_BASE_MS = 5;
|
|
158
|
+
var LOCK_STALE_MS = 5e3;
|
|
159
|
+
function queuePath() {
|
|
160
|
+
return join3(homedir3(), ".deeplake", "notifications-queue.json");
|
|
161
|
+
}
|
|
162
|
+
function lockPath() {
|
|
163
|
+
return `${queuePath()}.lock`;
|
|
164
|
+
}
|
|
165
|
+
function readQueue() {
|
|
166
|
+
try {
|
|
167
|
+
const raw = readFileSync2(queuePath(), "utf-8");
|
|
168
|
+
const parsed = JSON.parse(raw);
|
|
169
|
+
if (!parsed || !Array.isArray(parsed.queue)) {
|
|
170
|
+
log2(`queue malformed \u2192 treating as empty`);
|
|
171
|
+
return { queue: [] };
|
|
172
|
+
}
|
|
173
|
+
return { queue: parsed.queue };
|
|
174
|
+
} catch {
|
|
175
|
+
return { queue: [] };
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
function _isQueuePathInsideHome(path, home) {
|
|
179
|
+
const r = resolve(path);
|
|
180
|
+
const h = resolve(home);
|
|
181
|
+
return r.startsWith(h + "/") || r === h;
|
|
182
|
+
}
|
|
183
|
+
function writeQueue(q) {
|
|
184
|
+
const path = queuePath();
|
|
185
|
+
const home = resolve(homedir3());
|
|
186
|
+
if (!_isQueuePathInsideHome(path, home)) {
|
|
187
|
+
throw new Error(`notifications-queue write blocked: ${path} is outside ${home}`);
|
|
188
|
+
}
|
|
189
|
+
mkdirSync(join3(home, ".deeplake"), { recursive: true, mode: 448 });
|
|
190
|
+
const tmp = `${path}.${process.pid}.tmp`;
|
|
191
|
+
writeFileSync(tmp, JSON.stringify(q, null, 2), { mode: 384 });
|
|
192
|
+
renameSync(tmp, path);
|
|
193
|
+
}
|
|
194
|
+
async function withQueueLock(fn) {
|
|
195
|
+
const path = lockPath();
|
|
196
|
+
mkdirSync(join3(homedir3(), ".deeplake"), { recursive: true, mode: 448 });
|
|
197
|
+
let fd = null;
|
|
198
|
+
for (let attempt = 0; attempt < LOCK_RETRY_MAX; attempt++) {
|
|
199
|
+
try {
|
|
200
|
+
fd = openSync(path, "wx", 384);
|
|
201
|
+
break;
|
|
202
|
+
} catch (e) {
|
|
203
|
+
const code = e.code;
|
|
204
|
+
if (code !== "EEXIST")
|
|
205
|
+
throw e;
|
|
206
|
+
try {
|
|
207
|
+
const age = Date.now() - statSync(path).mtimeMs;
|
|
208
|
+
if (age > LOCK_STALE_MS) {
|
|
209
|
+
unlinkSync(path);
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
} catch {
|
|
213
|
+
}
|
|
214
|
+
const delay = LOCK_RETRY_BASE_MS * (attempt + 1);
|
|
215
|
+
await sleep(delay);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
if (fd === null) {
|
|
219
|
+
log2(`lock acquisition gave up after ${LOCK_RETRY_MAX} attempts \u2014 proceeding unlocked (last-writer-wins)`);
|
|
220
|
+
return fn();
|
|
221
|
+
}
|
|
222
|
+
try {
|
|
223
|
+
return fn();
|
|
224
|
+
} finally {
|
|
225
|
+
try {
|
|
226
|
+
closeSync(fd);
|
|
227
|
+
} catch {
|
|
228
|
+
}
|
|
229
|
+
try {
|
|
230
|
+
unlinkSync(path);
|
|
231
|
+
} catch {
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
function sameDedupKey(a, b) {
|
|
236
|
+
if (a.id !== b.id)
|
|
237
|
+
return false;
|
|
238
|
+
return JSON.stringify(a.dedupKey) === JSON.stringify(b.dedupKey);
|
|
239
|
+
}
|
|
240
|
+
async function enqueueNotification(n) {
|
|
241
|
+
await withQueueLock(() => {
|
|
242
|
+
const q = readQueue();
|
|
243
|
+
if (q.queue.some((existing) => sameDedupKey(existing, n))) {
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
q.queue.push(n);
|
|
247
|
+
writeQueue(q);
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// dist/src/commands/auth-creds.js
|
|
252
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2 } from "node:fs";
|
|
253
|
+
import { join as join4 } from "node:path";
|
|
254
|
+
import { homedir as homedir4 } from "node:os";
|
|
255
|
+
function configDir() {
|
|
256
|
+
return join4(homedir4(), ".deeplake");
|
|
257
|
+
}
|
|
258
|
+
function credsPath() {
|
|
259
|
+
return join4(configDir(), "credentials.json");
|
|
260
|
+
}
|
|
261
|
+
function loadCredentials() {
|
|
262
|
+
try {
|
|
263
|
+
return JSON.parse(readFileSync3(credsPath(), "utf-8"));
|
|
264
|
+
} catch {
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
150
269
|
// dist/src/deeplake-api.js
|
|
151
270
|
var indexMarkerStorePromise = null;
|
|
152
271
|
function getIndexMarkerStore() {
|
|
@@ -154,7 +273,7 @@ function getIndexMarkerStore() {
|
|
|
154
273
|
indexMarkerStorePromise = Promise.resolve().then(() => (init_index_marker_store(), index_marker_store_exports));
|
|
155
274
|
return indexMarkerStorePromise;
|
|
156
275
|
}
|
|
157
|
-
var
|
|
276
|
+
var log3 = (msg) => log("sdk", msg);
|
|
158
277
|
function summarizeSql(sql, maxLen = 220) {
|
|
159
278
|
const compact = sql.replace(/\s+/g, " ").trim();
|
|
160
279
|
return compact.length > maxLen ? `${compact.slice(0, maxLen)}...` : compact;
|
|
@@ -166,7 +285,38 @@ function traceSql(msg) {
|
|
|
166
285
|
process.stderr.write(`[deeplake-sql] ${msg}
|
|
167
286
|
`);
|
|
168
287
|
if (process.env.HIVEMIND_DEBUG === "1")
|
|
169
|
-
|
|
288
|
+
log3(msg);
|
|
289
|
+
}
|
|
290
|
+
var _signalledBalanceExhausted = false;
|
|
291
|
+
function maybeSignalBalanceExhausted(status, bodyText) {
|
|
292
|
+
if (status !== 402)
|
|
293
|
+
return;
|
|
294
|
+
if (!bodyText.includes("balance_cents"))
|
|
295
|
+
return;
|
|
296
|
+
if (_signalledBalanceExhausted)
|
|
297
|
+
return;
|
|
298
|
+
_signalledBalanceExhausted = true;
|
|
299
|
+
log3(`balance exhausted \u2014 enqueuing session-start banner (body=${bodyText.slice(0, 120)})`);
|
|
300
|
+
enqueueNotification({
|
|
301
|
+
id: "balance-exhausted",
|
|
302
|
+
severity: "warn",
|
|
303
|
+
transient: true,
|
|
304
|
+
title: "Hivemind credits exhausted \u2014 top up to keep capturing",
|
|
305
|
+
body: `Sessions are not being saved and memory recall is returning empty. Top up at ${billingUrl()} to restore capture and recall.`,
|
|
306
|
+
dedupKey: { reason: "balance-zero" }
|
|
307
|
+
}).catch((e) => {
|
|
308
|
+
log3(`enqueue balance-exhausted failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
function billingUrl() {
|
|
312
|
+
try {
|
|
313
|
+
const c = loadCredentials();
|
|
314
|
+
if (c?.orgName && c?.workspaceId) {
|
|
315
|
+
return `https://deeplake.ai/${encodeURIComponent(c.orgName)}/workspace/${encodeURIComponent(c.workspaceId)}/billing`;
|
|
316
|
+
}
|
|
317
|
+
} catch {
|
|
318
|
+
}
|
|
319
|
+
return "https://deeplake.ai";
|
|
170
320
|
}
|
|
171
321
|
var RETRYABLE_CODES = /* @__PURE__ */ new Set([429, 500, 502, 503, 504]);
|
|
172
322
|
var MAX_RETRIES = 3;
|
|
@@ -175,8 +325,8 @@ var MAX_CONCURRENCY = 5;
|
|
|
175
325
|
function getQueryTimeoutMs() {
|
|
176
326
|
return Number(process.env.HIVEMIND_QUERY_TIMEOUT_MS ?? 1e4);
|
|
177
327
|
}
|
|
178
|
-
function
|
|
179
|
-
return new Promise((
|
|
328
|
+
function sleep2(ms) {
|
|
329
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
180
330
|
}
|
|
181
331
|
function isTimeoutError(error) {
|
|
182
332
|
const name = error instanceof Error ? error.name.toLowerCase() : "";
|
|
@@ -206,7 +356,7 @@ var Semaphore = class {
|
|
|
206
356
|
this.active++;
|
|
207
357
|
return;
|
|
208
358
|
}
|
|
209
|
-
await new Promise((
|
|
359
|
+
await new Promise((resolve2) => this.waiting.push(resolve2));
|
|
210
360
|
}
|
|
211
361
|
release() {
|
|
212
362
|
this.active--;
|
|
@@ -277,8 +427,8 @@ var DeeplakeApi = class {
|
|
|
277
427
|
lastError = e instanceof Error ? e : new Error(String(e));
|
|
278
428
|
if (attempt < MAX_RETRIES) {
|
|
279
429
|
const delay = BASE_DELAY_MS * Math.pow(2, attempt) + Math.random() * 200;
|
|
280
|
-
|
|
281
|
-
await
|
|
430
|
+
log3(`query retry ${attempt + 1}/${MAX_RETRIES} (fetch error: ${lastError.message}) in ${delay.toFixed(0)}ms`);
|
|
431
|
+
await sleep2(delay);
|
|
282
432
|
continue;
|
|
283
433
|
}
|
|
284
434
|
throw lastError;
|
|
@@ -294,10 +444,11 @@ var DeeplakeApi = class {
|
|
|
294
444
|
const alreadyExists = resp.status === 500 && isDuplicateIndexError(text);
|
|
295
445
|
if (!alreadyExists && attempt < MAX_RETRIES && (RETRYABLE_CODES.has(resp.status) || retryable403)) {
|
|
296
446
|
const delay = BASE_DELAY_MS * Math.pow(2, attempt) + Math.random() * 200;
|
|
297
|
-
|
|
298
|
-
await
|
|
447
|
+
log3(`query retry ${attempt + 1}/${MAX_RETRIES} (${resp.status}) in ${delay.toFixed(0)}ms`);
|
|
448
|
+
await sleep2(delay);
|
|
299
449
|
continue;
|
|
300
450
|
}
|
|
451
|
+
maybeSignalBalanceExhausted(resp.status, text);
|
|
301
452
|
throw new Error(`Query failed: ${resp.status}: ${text.slice(0, 200)}`);
|
|
302
453
|
}
|
|
303
454
|
throw lastError ?? new Error("Query failed: max retries exceeded");
|
|
@@ -318,7 +469,7 @@ var DeeplakeApi = class {
|
|
|
318
469
|
const chunk = rows.slice(i, i + CONCURRENCY);
|
|
319
470
|
await Promise.allSettled(chunk.map((r) => this.upsertRowSql(r)));
|
|
320
471
|
}
|
|
321
|
-
|
|
472
|
+
log3(`commit: ${rows.length} rows`);
|
|
322
473
|
}
|
|
323
474
|
async upsertRowSql(row) {
|
|
324
475
|
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -374,7 +525,7 @@ var DeeplakeApi = class {
|
|
|
374
525
|
markers.writeIndexMarker(markerPath);
|
|
375
526
|
return;
|
|
376
527
|
}
|
|
377
|
-
|
|
528
|
+
log3(`index "${indexName}" skipped: ${e.message}`);
|
|
378
529
|
}
|
|
379
530
|
}
|
|
380
531
|
/**
|
|
@@ -464,13 +615,13 @@ var DeeplakeApi = class {
|
|
|
464
615
|
};
|
|
465
616
|
}
|
|
466
617
|
if (attempt < MAX_RETRIES && RETRYABLE_CODES.has(resp.status)) {
|
|
467
|
-
await
|
|
618
|
+
await sleep2(BASE_DELAY_MS * Math.pow(2, attempt) + Math.random() * 200);
|
|
468
619
|
continue;
|
|
469
620
|
}
|
|
470
621
|
return { tables: [], cacheable: false };
|
|
471
622
|
} catch {
|
|
472
623
|
if (attempt < MAX_RETRIES) {
|
|
473
|
-
await
|
|
624
|
+
await sleep2(BASE_DELAY_MS * Math.pow(2, attempt));
|
|
474
625
|
continue;
|
|
475
626
|
}
|
|
476
627
|
return { tables: [], cacheable: false };
|
|
@@ -498,9 +649,9 @@ var DeeplakeApi = class {
|
|
|
498
649
|
} catch (err) {
|
|
499
650
|
lastErr = err;
|
|
500
651
|
const msg = err instanceof Error ? err.message : String(err);
|
|
501
|
-
|
|
652
|
+
log3(`CREATE TABLE "${label}" attempt ${attempt + 1}/${OUTER_BACKOFFS_MS.length + 1} failed: ${msg}`);
|
|
502
653
|
if (attempt < OUTER_BACKOFFS_MS.length) {
|
|
503
|
-
await
|
|
654
|
+
await sleep2(OUTER_BACKOFFS_MS[attempt]);
|
|
504
655
|
}
|
|
505
656
|
}
|
|
506
657
|
}
|
|
@@ -511,9 +662,9 @@ var DeeplakeApi = class {
|
|
|
511
662
|
const tbl = sqlIdent(name ?? this.tableName);
|
|
512
663
|
const tables = await this.listTables();
|
|
513
664
|
if (!tables.includes(tbl)) {
|
|
514
|
-
|
|
665
|
+
log3(`table "${tbl}" not found, creating`);
|
|
515
666
|
await this.createTableWithRetry(`CREATE TABLE IF NOT EXISTS "${tbl}" (id TEXT NOT NULL DEFAULT '', path TEXT NOT NULL DEFAULT '', filename TEXT NOT NULL DEFAULT '', summary TEXT NOT NULL DEFAULT '', summary_embedding FLOAT4[], author TEXT NOT NULL DEFAULT '', mime_type TEXT NOT NULL DEFAULT 'text/plain', size_bytes BIGINT NOT NULL DEFAULT 0, project TEXT NOT NULL DEFAULT '', description TEXT NOT NULL DEFAULT '', agent TEXT NOT NULL DEFAULT '', plugin_version TEXT NOT NULL DEFAULT '', creation_date TEXT NOT NULL DEFAULT '', last_update_date TEXT NOT NULL DEFAULT '') USING deeplake`, tbl);
|
|
516
|
-
|
|
667
|
+
log3(`table "${tbl}" created`);
|
|
517
668
|
if (!tables.includes(tbl))
|
|
518
669
|
this._tablesCache = [...tables, tbl];
|
|
519
670
|
}
|
|
@@ -526,9 +677,9 @@ var DeeplakeApi = class {
|
|
|
526
677
|
const safe = sqlIdent(name);
|
|
527
678
|
const tables = await this.listTables();
|
|
528
679
|
if (!tables.includes(safe)) {
|
|
529
|
-
|
|
680
|
+
log3(`table "${safe}" not found, creating`);
|
|
530
681
|
await this.createTableWithRetry(`CREATE TABLE IF NOT EXISTS "${safe}" (id TEXT NOT NULL DEFAULT '', path TEXT NOT NULL DEFAULT '', filename TEXT NOT NULL DEFAULT '', message JSONB, message_embedding FLOAT4[], author TEXT NOT NULL DEFAULT '', mime_type TEXT NOT NULL DEFAULT 'application/json', size_bytes BIGINT NOT NULL DEFAULT 0, project TEXT NOT NULL DEFAULT '', description TEXT NOT NULL DEFAULT '', agent TEXT NOT NULL DEFAULT '', plugin_version TEXT NOT NULL DEFAULT '', creation_date TEXT NOT NULL DEFAULT '', last_update_date TEXT NOT NULL DEFAULT '') USING deeplake`, safe);
|
|
531
|
-
|
|
682
|
+
log3(`table "${safe}" created`);
|
|
532
683
|
if (!tables.includes(safe))
|
|
533
684
|
this._tablesCache = [...tables, safe];
|
|
534
685
|
}
|
|
@@ -551,9 +702,9 @@ var DeeplakeApi = class {
|
|
|
551
702
|
const safe = sqlIdent(name);
|
|
552
703
|
const tables = await this.listTables();
|
|
553
704
|
if (!tables.includes(safe)) {
|
|
554
|
-
|
|
705
|
+
log3(`table "${safe}" not found, creating`);
|
|
555
706
|
await this.createTableWithRetry(`CREATE TABLE IF NOT EXISTS "${safe}" (id TEXT NOT NULL DEFAULT '', name TEXT NOT NULL DEFAULT '', project TEXT NOT NULL DEFAULT '', project_key TEXT NOT NULL DEFAULT '', local_path TEXT NOT NULL DEFAULT '', install TEXT NOT NULL DEFAULT 'project', source_sessions TEXT NOT NULL DEFAULT '[]', source_agent TEXT NOT NULL DEFAULT '', scope TEXT NOT NULL DEFAULT 'me', author TEXT NOT NULL DEFAULT '', description TEXT NOT NULL DEFAULT '', trigger_text TEXT NOT NULL DEFAULT '', body TEXT NOT NULL DEFAULT '', version BIGINT NOT NULL DEFAULT 1, created_at TEXT NOT NULL DEFAULT '', updated_at TEXT NOT NULL DEFAULT '') USING deeplake`, safe);
|
|
556
|
-
|
|
707
|
+
log3(`table "${safe}" created`);
|
|
557
708
|
if (!tables.includes(safe))
|
|
558
709
|
this._tablesCache = [...tables, safe];
|
|
559
710
|
}
|
|
@@ -570,9 +721,9 @@ function buildSessionPath(config, sessionId) {
|
|
|
570
721
|
// dist/src/embeddings/client.js
|
|
571
722
|
import { connect } from "node:net";
|
|
572
723
|
import { spawn } from "node:child_process";
|
|
573
|
-
import { openSync, closeSync, writeSync, unlinkSync, existsSync as existsSync3, readFileSync as
|
|
574
|
-
import { homedir as
|
|
575
|
-
import { join as
|
|
724
|
+
import { openSync as openSync2, closeSync as closeSync2, writeSync, unlinkSync as unlinkSync3, existsSync as existsSync3, readFileSync as readFileSync5 } from "node:fs";
|
|
725
|
+
import { homedir as homedir5 } from "node:os";
|
|
726
|
+
import { join as join6 } from "node:path";
|
|
576
727
|
|
|
577
728
|
// dist/src/embeddings/protocol.js
|
|
578
729
|
var DEFAULT_SOCKET_DIR = "/tmp";
|
|
@@ -586,12 +737,13 @@ function pidPathFor(uid, dir = DEFAULT_SOCKET_DIR) {
|
|
|
586
737
|
}
|
|
587
738
|
|
|
588
739
|
// dist/src/embeddings/client.js
|
|
589
|
-
var SHARED_DAEMON_PATH =
|
|
590
|
-
var
|
|
740
|
+
var SHARED_DAEMON_PATH = join6(homedir5(), ".hivemind", "embed-deps", "embed-daemon.js");
|
|
741
|
+
var log4 = (m) => log("embed-client", m);
|
|
591
742
|
function getUid() {
|
|
592
743
|
const uid = typeof process.getuid === "function" ? process.getuid() : void 0;
|
|
593
744
|
return uid !== void 0 ? String(uid) : process.env.USER ?? "default";
|
|
594
745
|
}
|
|
746
|
+
var _recycledStuckDaemon = false;
|
|
595
747
|
var EmbedClient = class {
|
|
596
748
|
socketPath;
|
|
597
749
|
pidPath;
|
|
@@ -600,6 +752,7 @@ var EmbedClient = class {
|
|
|
600
752
|
autoSpawn;
|
|
601
753
|
spawnWaitMs;
|
|
602
754
|
nextId = 0;
|
|
755
|
+
helloVerified = false;
|
|
603
756
|
constructor(opts = {}) {
|
|
604
757
|
const uid = getUid();
|
|
605
758
|
const dir = opts.socketDir ?? "/tmp";
|
|
@@ -616,8 +769,33 @@ var EmbedClient = class {
|
|
|
616
769
|
*
|
|
617
770
|
* Fire-and-forget spawn on miss: if the daemon isn't up, this call returns
|
|
618
771
|
* null AND kicks off a background spawn. The next call finds a ready daemon.
|
|
772
|
+
*
|
|
773
|
+
* Stuck-daemon recycle: if the daemon returns a transformers-missing
|
|
774
|
+
* error (typical after a marketplace upgrade left an older daemon process
|
|
775
|
+
* alive but with no node_modules accessible from its bundle path), we
|
|
776
|
+
* SIGTERM it and clear its sock/pid so the very next call spawns a fresh
|
|
777
|
+
* daemon from the current bundle. Without this, the stuck daemon would
|
|
778
|
+
* keep poisoning every session until its 10-minute idle-out fires.
|
|
619
779
|
*/
|
|
620
780
|
async embed(text, kind = "document") {
|
|
781
|
+
const v = await this.embedAttempt(text, kind);
|
|
782
|
+
if (v !== "recycled")
|
|
783
|
+
return v;
|
|
784
|
+
if (!this.autoSpawn)
|
|
785
|
+
return null;
|
|
786
|
+
this.trySpawnDaemon();
|
|
787
|
+
await this.waitForDaemonReady();
|
|
788
|
+
const retry = await this.embedAttempt(text, kind);
|
|
789
|
+
return retry === "recycled" ? null : retry;
|
|
790
|
+
}
|
|
791
|
+
/**
|
|
792
|
+
* One round-trip: connect → verify → embed. Returns:
|
|
793
|
+
* - number[] : embedding vector (happy path)
|
|
794
|
+
* - null : timeout / daemon error / transformers-missing
|
|
795
|
+
* - "recycled": verifyDaemonOnce killed the daemon mid-call;
|
|
796
|
+
* caller should respawn and retry once.
|
|
797
|
+
*/
|
|
798
|
+
async embedAttempt(text, kind) {
|
|
621
799
|
let sock;
|
|
622
800
|
try {
|
|
623
801
|
sock = await this.connectOnce();
|
|
@@ -627,17 +805,25 @@ var EmbedClient = class {
|
|
|
627
805
|
return null;
|
|
628
806
|
}
|
|
629
807
|
try {
|
|
808
|
+
const recycled = await this.verifyDaemonOnce(sock);
|
|
809
|
+
if (recycled) {
|
|
810
|
+
return "recycled";
|
|
811
|
+
}
|
|
630
812
|
const id = String(++this.nextId);
|
|
631
813
|
const req = { op: "embed", id, kind, text };
|
|
632
814
|
const resp = await this.sendAndWait(sock, req);
|
|
633
815
|
if (resp.error || !("embedding" in resp) || !resp.embedding) {
|
|
634
|
-
|
|
816
|
+
const err = resp.error ?? "no embedding";
|
|
817
|
+
log4(`embed err: ${err}`);
|
|
818
|
+
if (isTransformersMissingError(err)) {
|
|
819
|
+
this.handleTransformersMissing(err);
|
|
820
|
+
}
|
|
635
821
|
return null;
|
|
636
822
|
}
|
|
637
823
|
return resp.embedding;
|
|
638
824
|
} catch (e) {
|
|
639
825
|
const err = e instanceof Error ? e.message : String(e);
|
|
640
|
-
|
|
826
|
+
log4(`embed failed: ${err}`);
|
|
641
827
|
return null;
|
|
642
828
|
} finally {
|
|
643
829
|
try {
|
|
@@ -646,6 +832,123 @@ var EmbedClient = class {
|
|
|
646
832
|
}
|
|
647
833
|
}
|
|
648
834
|
}
|
|
835
|
+
/**
|
|
836
|
+
* Poll for the sock file to come back after `trySpawnDaemon` — used by
|
|
837
|
+
* the recycle retry path. Best-effort: caps at `spawnWaitMs` and
|
|
838
|
+
* returns regardless so the retry attempt can run.
|
|
839
|
+
*/
|
|
840
|
+
async waitForDaemonReady() {
|
|
841
|
+
const deadline = Date.now() + this.spawnWaitMs;
|
|
842
|
+
while (Date.now() < deadline) {
|
|
843
|
+
if (existsSync3(this.socketPath))
|
|
844
|
+
return;
|
|
845
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
/**
|
|
849
|
+
* Send a `hello` on first successful connect per EmbedClient instance.
|
|
850
|
+
* If the daemon answers with a path that doesn't match our configured
|
|
851
|
+
* daemonEntry — typical after a marketplace upgrade replaced the bundle
|
|
852
|
+
* — SIGTERM the daemon + clear sock/pid so the next call spawns from the
|
|
853
|
+
* current bundle.
|
|
854
|
+
*
|
|
855
|
+
* `helloVerified` is set ONLY after we've seen a compatible response,
|
|
856
|
+
* so a transient probe failure or a recycle-triggering mismatch leaves
|
|
857
|
+
* the flag false; the next reconnect re-runs verification against
|
|
858
|
+
* whatever daemon is then live (typically the fresh spawn).
|
|
859
|
+
*/
|
|
860
|
+
async verifyDaemonOnce(sock) {
|
|
861
|
+
if (this.helloVerified)
|
|
862
|
+
return false;
|
|
863
|
+
if (!this.daemonEntry) {
|
|
864
|
+
this.helloVerified = true;
|
|
865
|
+
return false;
|
|
866
|
+
}
|
|
867
|
+
const id = String(++this.nextId);
|
|
868
|
+
const req = { op: "hello", id };
|
|
869
|
+
let resp;
|
|
870
|
+
try {
|
|
871
|
+
resp = await this.sendAndWait(sock, req);
|
|
872
|
+
} catch (e) {
|
|
873
|
+
log4(`hello probe failed (inconclusive, will retry next connect): ${e instanceof Error ? e.message : String(e)}`);
|
|
874
|
+
return false;
|
|
875
|
+
}
|
|
876
|
+
const hello = resp;
|
|
877
|
+
if (_recycledStuckDaemon) {
|
|
878
|
+
return false;
|
|
879
|
+
}
|
|
880
|
+
if (!hello.daemonPath) {
|
|
881
|
+
_recycledStuckDaemon = true;
|
|
882
|
+
log4(`daemon does not implement hello (older protocol); recycling`);
|
|
883
|
+
this.recycleDaemon(hello.pid);
|
|
884
|
+
return true;
|
|
885
|
+
}
|
|
886
|
+
if (hello.daemonPath !== this.daemonEntry && !existsSync3(hello.daemonPath)) {
|
|
887
|
+
_recycledStuckDaemon = true;
|
|
888
|
+
log4(`daemon path no longer on disk \u2014 running=${hello.daemonPath} (gone) expected=${this.daemonEntry}; recycling`);
|
|
889
|
+
this.recycleDaemon(hello.pid);
|
|
890
|
+
return true;
|
|
891
|
+
}
|
|
892
|
+
this.helloVerified = true;
|
|
893
|
+
return false;
|
|
894
|
+
}
|
|
895
|
+
/**
|
|
896
|
+
* On a transformers-missing error from the daemon, SIGTERM the stuck
|
|
897
|
+
* daemon (the bundle daemon that can't find its deps) and clear
|
|
898
|
+
* sock/pid so the next call spawns fresh.
|
|
899
|
+
*
|
|
900
|
+
* Previously this also enqueued a user-visible "Hivemind embeddings
|
|
901
|
+
* disabled — deps missing" notification telling the user to run
|
|
902
|
+
* `hivemind embeddings install`. The notification was removed because
|
|
903
|
+
* (a) the recycle alone often fixes the issue silently, and (b) the
|
|
904
|
+
* warning kept stacking on top of the primary session-start banner
|
|
905
|
+
* which clashed with the single-slot priority model. The `detail`
|
|
906
|
+
* argument is retained for future telemetry / debug logging.
|
|
907
|
+
*/
|
|
908
|
+
handleTransformersMissing(_detail) {
|
|
909
|
+
if (!_recycledStuckDaemon) {
|
|
910
|
+
_recycledStuckDaemon = true;
|
|
911
|
+
this.recycleDaemon(null);
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
/**
|
|
915
|
+
* Best-effort SIGTERM + sock/pid cleanup. Tolerant of every missing-file
|
|
916
|
+
* combination and dead-PID cases.
|
|
917
|
+
*
|
|
918
|
+
* Identity check: gate the SIGTERM on the daemon's socket file still
|
|
919
|
+
* existing. We know the daemon was alive moments ago (we either just
|
|
920
|
+
* got a hello response or the caller saw a transformers-missing error
|
|
921
|
+
* the daemon emitted), but if the socket file is gone by the time we
|
|
922
|
+
* try to kill, the daemon process is also gone and the PID we
|
|
923
|
+
* captured may already have been recycled by the OS to an unrelated
|
|
924
|
+
* user process. Mirrors the gate added to `killEmbedDaemon` in the
|
|
925
|
+
* CLI — same failure mode, rarer trigger.
|
|
926
|
+
*/
|
|
927
|
+
recycleDaemon(reportedPid) {
|
|
928
|
+
let pid = reportedPid;
|
|
929
|
+
if (pid === null) {
|
|
930
|
+
try {
|
|
931
|
+
pid = Number.parseInt(readFileSync5(this.pidPath, "utf-8").trim(), 10);
|
|
932
|
+
} catch {
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
if (Number.isFinite(pid) && pid !== null && pid > 0 && existsSync3(this.socketPath)) {
|
|
936
|
+
try {
|
|
937
|
+
process.kill(pid, "SIGTERM");
|
|
938
|
+
} catch {
|
|
939
|
+
}
|
|
940
|
+
} else if (pid !== null) {
|
|
941
|
+
log4(`recycle: socket gone, skipping SIGTERM on possibly-stale pid ${pid}`);
|
|
942
|
+
}
|
|
943
|
+
try {
|
|
944
|
+
unlinkSync3(this.socketPath);
|
|
945
|
+
} catch {
|
|
946
|
+
}
|
|
947
|
+
try {
|
|
948
|
+
unlinkSync3(this.pidPath);
|
|
949
|
+
} catch {
|
|
950
|
+
}
|
|
951
|
+
}
|
|
649
952
|
/**
|
|
650
953
|
* Wait up to spawnWaitMs for the daemon to accept connections, spawning if
|
|
651
954
|
* necessary. Meant for SessionStart / long-running batches — not the hot path.
|
|
@@ -669,7 +972,7 @@ var EmbedClient = class {
|
|
|
669
972
|
}
|
|
670
973
|
}
|
|
671
974
|
connectOnce() {
|
|
672
|
-
return new Promise((
|
|
975
|
+
return new Promise((resolve2, reject) => {
|
|
673
976
|
const sock = connect(this.socketPath);
|
|
674
977
|
const to = setTimeout(() => {
|
|
675
978
|
sock.destroy();
|
|
@@ -677,7 +980,7 @@ var EmbedClient = class {
|
|
|
677
980
|
}, this.timeoutMs);
|
|
678
981
|
sock.once("connect", () => {
|
|
679
982
|
clearTimeout(to);
|
|
680
|
-
|
|
983
|
+
resolve2(sock);
|
|
681
984
|
});
|
|
682
985
|
sock.once("error", (e) => {
|
|
683
986
|
clearTimeout(to);
|
|
@@ -688,16 +991,16 @@ var EmbedClient = class {
|
|
|
688
991
|
trySpawnDaemon() {
|
|
689
992
|
let fd;
|
|
690
993
|
try {
|
|
691
|
-
fd =
|
|
994
|
+
fd = openSync2(this.pidPath, "wx", 384);
|
|
692
995
|
writeSync(fd, String(process.pid));
|
|
693
996
|
} catch (e) {
|
|
694
997
|
if (this.isPidFileStale()) {
|
|
695
998
|
try {
|
|
696
|
-
|
|
999
|
+
unlinkSync3(this.pidPath);
|
|
697
1000
|
} catch {
|
|
698
1001
|
}
|
|
699
1002
|
try {
|
|
700
|
-
fd =
|
|
1003
|
+
fd = openSync2(this.pidPath, "wx", 384);
|
|
701
1004
|
writeSync(fd, String(process.pid));
|
|
702
1005
|
} catch {
|
|
703
1006
|
return;
|
|
@@ -707,10 +1010,10 @@ var EmbedClient = class {
|
|
|
707
1010
|
}
|
|
708
1011
|
}
|
|
709
1012
|
if (!this.daemonEntry || !existsSync3(this.daemonEntry)) {
|
|
710
|
-
|
|
1013
|
+
log4(`daemonEntry not configured or missing: ${this.daemonEntry}`);
|
|
711
1014
|
try {
|
|
712
|
-
|
|
713
|
-
|
|
1015
|
+
closeSync2(fd);
|
|
1016
|
+
unlinkSync3(this.pidPath);
|
|
714
1017
|
} catch {
|
|
715
1018
|
}
|
|
716
1019
|
return;
|
|
@@ -722,14 +1025,14 @@ var EmbedClient = class {
|
|
|
722
1025
|
env: process.env
|
|
723
1026
|
});
|
|
724
1027
|
child.unref();
|
|
725
|
-
|
|
1028
|
+
log4(`spawned daemon pid=${child.pid}`);
|
|
726
1029
|
} finally {
|
|
727
|
-
|
|
1030
|
+
closeSync2(fd);
|
|
728
1031
|
}
|
|
729
1032
|
}
|
|
730
1033
|
isPidFileStale() {
|
|
731
1034
|
try {
|
|
732
|
-
const raw =
|
|
1035
|
+
const raw = readFileSync5(this.pidPath, "utf-8").trim();
|
|
733
1036
|
const pid = Number(raw);
|
|
734
1037
|
if (!pid || Number.isNaN(pid))
|
|
735
1038
|
return true;
|
|
@@ -747,7 +1050,7 @@ var EmbedClient = class {
|
|
|
747
1050
|
const deadline = Date.now() + this.spawnWaitMs;
|
|
748
1051
|
let delay = 30;
|
|
749
1052
|
while (Date.now() < deadline) {
|
|
750
|
-
await
|
|
1053
|
+
await sleep3(delay);
|
|
751
1054
|
delay = Math.min(delay * 1.5, 300);
|
|
752
1055
|
if (!existsSync3(this.socketPath))
|
|
753
1056
|
continue;
|
|
@@ -759,7 +1062,7 @@ var EmbedClient = class {
|
|
|
759
1062
|
throw new Error("daemon did not become ready within spawnWaitMs");
|
|
760
1063
|
}
|
|
761
1064
|
sendAndWait(sock, req) {
|
|
762
|
-
return new Promise((
|
|
1065
|
+
return new Promise((resolve2, reject) => {
|
|
763
1066
|
let buf = "";
|
|
764
1067
|
const to = setTimeout(() => {
|
|
765
1068
|
sock.destroy();
|
|
@@ -774,7 +1077,7 @@ var EmbedClient = class {
|
|
|
774
1077
|
const line = buf.slice(0, nl);
|
|
775
1078
|
clearTimeout(to);
|
|
776
1079
|
try {
|
|
777
|
-
|
|
1080
|
+
resolve2(JSON.parse(line));
|
|
778
1081
|
} catch (e) {
|
|
779
1082
|
reject(e);
|
|
780
1083
|
}
|
|
@@ -791,9 +1094,14 @@ var EmbedClient = class {
|
|
|
791
1094
|
});
|
|
792
1095
|
}
|
|
793
1096
|
};
|
|
794
|
-
function
|
|
1097
|
+
function sleep3(ms) {
|
|
795
1098
|
return new Promise((r) => setTimeout(r, ms));
|
|
796
1099
|
}
|
|
1100
|
+
function isTransformersMissingError(err) {
|
|
1101
|
+
if (/hivemind embeddings install/i.test(err))
|
|
1102
|
+
return true;
|
|
1103
|
+
return /@huggingface\/transformers/i.test(err);
|
|
1104
|
+
}
|
|
797
1105
|
|
|
798
1106
|
// dist/src/embeddings/sql.js
|
|
799
1107
|
function embeddingSqlLiteral(vec) {
|
|
@@ -810,23 +1118,105 @@ function embeddingSqlLiteral(vec) {
|
|
|
810
1118
|
|
|
811
1119
|
// dist/src/embeddings/disable.js
|
|
812
1120
|
import { createRequire } from "node:module";
|
|
813
|
-
import { homedir as
|
|
814
|
-
import { join as
|
|
1121
|
+
import { homedir as homedir7 } from "node:os";
|
|
1122
|
+
import { join as join8 } from "node:path";
|
|
815
1123
|
import { pathToFileURL } from "node:url";
|
|
1124
|
+
|
|
1125
|
+
// dist/src/user-config.js
|
|
1126
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync6, renameSync as renameSync2, writeFileSync as writeFileSync4 } from "node:fs";
|
|
1127
|
+
import { homedir as homedir6 } from "node:os";
|
|
1128
|
+
import { dirname, join as join7 } from "node:path";
|
|
1129
|
+
var _configPath = () => process.env.HIVEMIND_CONFIG_PATH ?? join7(homedir6(), ".deeplake", "config.json");
|
|
1130
|
+
var _cache = null;
|
|
1131
|
+
var _migrated = false;
|
|
1132
|
+
function readUserConfig() {
|
|
1133
|
+
if (_cache !== null)
|
|
1134
|
+
return _cache;
|
|
1135
|
+
const path = _configPath();
|
|
1136
|
+
if (!existsSync4(path)) {
|
|
1137
|
+
_cache = {};
|
|
1138
|
+
return _cache;
|
|
1139
|
+
}
|
|
1140
|
+
try {
|
|
1141
|
+
const raw = readFileSync6(path, "utf-8");
|
|
1142
|
+
const parsed = JSON.parse(raw);
|
|
1143
|
+
_cache = isPlainObject(parsed) ? parsed : {};
|
|
1144
|
+
} catch {
|
|
1145
|
+
_cache = {};
|
|
1146
|
+
}
|
|
1147
|
+
return _cache;
|
|
1148
|
+
}
|
|
1149
|
+
function writeUserConfig(patch) {
|
|
1150
|
+
const current = readUserConfig();
|
|
1151
|
+
const merged = deepMerge(current, patch);
|
|
1152
|
+
const path = _configPath();
|
|
1153
|
+
const dir = dirname(path);
|
|
1154
|
+
if (!existsSync4(dir))
|
|
1155
|
+
mkdirSync4(dir, { recursive: true });
|
|
1156
|
+
const tmp = `${path}.tmp.${process.pid}`;
|
|
1157
|
+
writeFileSync4(tmp, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
1158
|
+
renameSync2(tmp, path);
|
|
1159
|
+
_cache = merged;
|
|
1160
|
+
return merged;
|
|
1161
|
+
}
|
|
1162
|
+
function getEmbeddingsEnabled() {
|
|
1163
|
+
const cfg = readUserConfig();
|
|
1164
|
+
if (cfg.embeddings && typeof cfg.embeddings.enabled === "boolean") {
|
|
1165
|
+
return cfg.embeddings.enabled;
|
|
1166
|
+
}
|
|
1167
|
+
if (_migrated) {
|
|
1168
|
+
return migrationValueFromEnv();
|
|
1169
|
+
}
|
|
1170
|
+
_migrated = true;
|
|
1171
|
+
const enabled = migrationValueFromEnv();
|
|
1172
|
+
try {
|
|
1173
|
+
writeUserConfig({ embeddings: { enabled } });
|
|
1174
|
+
} catch {
|
|
1175
|
+
_cache = { ...cfg ?? {}, embeddings: { ...cfg?.embeddings ?? {}, enabled } };
|
|
1176
|
+
}
|
|
1177
|
+
return enabled;
|
|
1178
|
+
}
|
|
1179
|
+
function migrationValueFromEnv() {
|
|
1180
|
+
const raw = process.env.HIVEMIND_EMBEDDINGS;
|
|
1181
|
+
if (raw === void 0)
|
|
1182
|
+
return false;
|
|
1183
|
+
if (raw === "false")
|
|
1184
|
+
return false;
|
|
1185
|
+
return true;
|
|
1186
|
+
}
|
|
1187
|
+
function isPlainObject(value) {
|
|
1188
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1189
|
+
}
|
|
1190
|
+
function deepMerge(base, patch) {
|
|
1191
|
+
const out = { ...base };
|
|
1192
|
+
for (const key of Object.keys(patch)) {
|
|
1193
|
+
const patchVal = patch[key];
|
|
1194
|
+
const baseVal = base[key];
|
|
1195
|
+
if (isPlainObject(patchVal) && isPlainObject(baseVal)) {
|
|
1196
|
+
out[key] = { ...baseVal, ...patchVal };
|
|
1197
|
+
} else if (patchVal !== void 0) {
|
|
1198
|
+
out[key] = patchVal;
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
return out;
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
// dist/src/embeddings/disable.js
|
|
816
1205
|
var cachedStatus = null;
|
|
817
1206
|
function defaultResolveTransformers() {
|
|
1207
|
+
const sharedDir = join8(homedir7(), ".hivemind", "embed-deps");
|
|
818
1208
|
try {
|
|
819
|
-
createRequire(
|
|
1209
|
+
createRequire(pathToFileURL(`${sharedDir}/`).href).resolve("@huggingface/transformers");
|
|
820
1210
|
return;
|
|
821
1211
|
} catch {
|
|
822
1212
|
}
|
|
823
|
-
|
|
824
|
-
createRequire(pathToFileURL(`${sharedDir}/`).href).resolve("@huggingface/transformers");
|
|
1213
|
+
createRequire(import.meta.url).resolve("@huggingface/transformers");
|
|
825
1214
|
}
|
|
826
1215
|
var _resolve = defaultResolveTransformers;
|
|
1216
|
+
var _readEnabled = getEmbeddingsEnabled;
|
|
827
1217
|
function detectStatus() {
|
|
828
|
-
if (
|
|
829
|
-
return "
|
|
1218
|
+
if (!_readEnabled())
|
|
1219
|
+
return "user-disabled";
|
|
830
1220
|
try {
|
|
831
1221
|
_resolve();
|
|
832
1222
|
return "enabled";
|
|
@@ -844,55 +1234,120 @@ function embeddingsDisabled() {
|
|
|
844
1234
|
return embeddingsStatus() !== "enabled";
|
|
845
1235
|
}
|
|
846
1236
|
|
|
1237
|
+
// dist/src/embeddings/self-heal.js
|
|
1238
|
+
import { existsSync as existsSync5, lstatSync, mkdirSync as mkdirSync5, readlinkSync, renameSync as renameSync3, rmSync, symlinkSync, statSync as statSync2 } from "node:fs";
|
|
1239
|
+
import { homedir as homedir8 } from "node:os";
|
|
1240
|
+
import { basename, dirname as dirname2, join as join9 } from "node:path";
|
|
1241
|
+
function ensurePluginNodeModulesLink(opts) {
|
|
1242
|
+
if (basename(opts.bundleDir) !== "bundle") {
|
|
1243
|
+
return { kind: "not-bundle-layout", bundleDir: opts.bundleDir };
|
|
1244
|
+
}
|
|
1245
|
+
const target = opts.sharedNodeModules ?? join9(homedir8(), ".hivemind", "embed-deps", "node_modules");
|
|
1246
|
+
const pluginDir = dirname2(opts.bundleDir);
|
|
1247
|
+
const link = join9(pluginDir, "node_modules");
|
|
1248
|
+
if (!existsSync5(target)) {
|
|
1249
|
+
return { kind: "shared-deps-missing", target };
|
|
1250
|
+
}
|
|
1251
|
+
let linkStat;
|
|
1252
|
+
try {
|
|
1253
|
+
linkStat = lstatSync(link);
|
|
1254
|
+
} catch {
|
|
1255
|
+
return createSymlinkAtomic(target, link);
|
|
1256
|
+
}
|
|
1257
|
+
if (linkStat.isSymbolicLink()) {
|
|
1258
|
+
let existingTarget;
|
|
1259
|
+
try {
|
|
1260
|
+
existingTarget = readlinkSync(link);
|
|
1261
|
+
} catch (e) {
|
|
1262
|
+
return { kind: "error", detail: `readlink failed: ${e instanceof Error ? e.message : String(e)}` };
|
|
1263
|
+
}
|
|
1264
|
+
if (existingTarget === target) {
|
|
1265
|
+
return { kind: "already-linked", target, link };
|
|
1266
|
+
}
|
|
1267
|
+
try {
|
|
1268
|
+
statSync2(link);
|
|
1269
|
+
return { kind: "linked-elsewhere", link, existingTarget };
|
|
1270
|
+
} catch {
|
|
1271
|
+
try {
|
|
1272
|
+
rmSync(link);
|
|
1273
|
+
} catch {
|
|
1274
|
+
}
|
|
1275
|
+
const recreated = createSymlinkAtomic(target, link);
|
|
1276
|
+
if (recreated.kind === "linked") {
|
|
1277
|
+
return { kind: "stale-link-removed", link, danglingTarget: existingTarget };
|
|
1278
|
+
}
|
|
1279
|
+
return recreated;
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
return { kind: "plugin-owns-node-modules", link };
|
|
1283
|
+
}
|
|
1284
|
+
function createSymlinkAtomic(target, link) {
|
|
1285
|
+
try {
|
|
1286
|
+
const parent = dirname2(link);
|
|
1287
|
+
if (!existsSync5(parent))
|
|
1288
|
+
mkdirSync5(parent, { recursive: true });
|
|
1289
|
+
const tmp = `${link}.tmp.${process.pid}`;
|
|
1290
|
+
try {
|
|
1291
|
+
rmSync(tmp, { force: true });
|
|
1292
|
+
} catch {
|
|
1293
|
+
}
|
|
1294
|
+
symlinkSync(target, tmp);
|
|
1295
|
+
renameSync3(tmp, link);
|
|
1296
|
+
return { kind: "linked", target, link };
|
|
1297
|
+
} catch (e) {
|
|
1298
|
+
return { kind: "error", detail: e instanceof Error ? e.message : String(e) };
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
|
|
847
1302
|
// dist/src/hooks/codex/capture.js
|
|
848
1303
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
849
|
-
import { dirname as
|
|
1304
|
+
import { dirname as dirname5, join as join14 } from "node:path";
|
|
850
1305
|
|
|
851
1306
|
// dist/src/hooks/summary-state.js
|
|
852
|
-
import { readFileSync as
|
|
853
|
-
import { homedir as
|
|
854
|
-
import { join as
|
|
1307
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, writeSync as writeSync2, mkdirSync as mkdirSync6, renameSync as renameSync4, existsSync as existsSync6, unlinkSync as unlinkSync4, openSync as openSync3, closeSync as closeSync3 } from "node:fs";
|
|
1308
|
+
import { homedir as homedir9 } from "node:os";
|
|
1309
|
+
import { join as join10 } from "node:path";
|
|
855
1310
|
var dlog = (msg) => log("summary-state", msg);
|
|
856
|
-
var STATE_DIR =
|
|
1311
|
+
var STATE_DIR = join10(homedir9(), ".claude", "hooks", "summary-state");
|
|
857
1312
|
var YIELD_BUF = new Int32Array(new SharedArrayBuffer(4));
|
|
858
1313
|
function statePath(sessionId) {
|
|
859
|
-
return
|
|
1314
|
+
return join10(STATE_DIR, `${sessionId}.json`);
|
|
860
1315
|
}
|
|
861
|
-
function
|
|
862
|
-
return
|
|
1316
|
+
function lockPath2(sessionId) {
|
|
1317
|
+
return join10(STATE_DIR, `${sessionId}.lock`);
|
|
863
1318
|
}
|
|
864
1319
|
function readState(sessionId) {
|
|
865
1320
|
const p = statePath(sessionId);
|
|
866
|
-
if (!
|
|
1321
|
+
if (!existsSync6(p))
|
|
867
1322
|
return null;
|
|
868
1323
|
try {
|
|
869
|
-
return JSON.parse(
|
|
1324
|
+
return JSON.parse(readFileSync7(p, "utf-8"));
|
|
870
1325
|
} catch {
|
|
871
1326
|
return null;
|
|
872
1327
|
}
|
|
873
1328
|
}
|
|
874
1329
|
function writeState(sessionId, state) {
|
|
875
|
-
|
|
1330
|
+
mkdirSync6(STATE_DIR, { recursive: true });
|
|
876
1331
|
const p = statePath(sessionId);
|
|
877
1332
|
const tmp = `${p}.${process.pid}.${Date.now()}.tmp`;
|
|
878
|
-
|
|
879
|
-
|
|
1333
|
+
writeFileSync5(tmp, JSON.stringify(state));
|
|
1334
|
+
renameSync4(tmp, p);
|
|
880
1335
|
}
|
|
881
1336
|
function withRmwLock(sessionId, fn) {
|
|
882
|
-
|
|
1337
|
+
mkdirSync6(STATE_DIR, { recursive: true });
|
|
883
1338
|
const rmwLock = statePath(sessionId) + ".rmw";
|
|
884
1339
|
const deadline = Date.now() + 2e3;
|
|
885
1340
|
let fd = null;
|
|
886
1341
|
while (fd === null) {
|
|
887
1342
|
try {
|
|
888
|
-
fd =
|
|
1343
|
+
fd = openSync3(rmwLock, "wx");
|
|
889
1344
|
} catch (e) {
|
|
890
1345
|
if (e.code !== "EEXIST")
|
|
891
1346
|
throw e;
|
|
892
1347
|
if (Date.now() > deadline) {
|
|
893
1348
|
dlog(`rmw lock deadline exceeded for ${sessionId}, reclaiming stale lock`);
|
|
894
1349
|
try {
|
|
895
|
-
|
|
1350
|
+
unlinkSync4(rmwLock);
|
|
896
1351
|
} catch (unlinkErr) {
|
|
897
1352
|
dlog(`stale rmw lock unlink failed for ${sessionId}: ${unlinkErr.message}`);
|
|
898
1353
|
}
|
|
@@ -904,9 +1359,9 @@ function withRmwLock(sessionId, fn) {
|
|
|
904
1359
|
try {
|
|
905
1360
|
return fn();
|
|
906
1361
|
} finally {
|
|
907
|
-
|
|
1362
|
+
closeSync3(fd);
|
|
908
1363
|
try {
|
|
909
|
-
|
|
1364
|
+
unlinkSync4(rmwLock);
|
|
910
1365
|
} catch (unlinkErr) {
|
|
911
1366
|
dlog(`rmw lock cleanup failed for ${sessionId}: ${unlinkErr.message}`);
|
|
912
1367
|
}
|
|
@@ -941,29 +1396,29 @@ function shouldTrigger(state, cfg, now = Date.now()) {
|
|
|
941
1396
|
return false;
|
|
942
1397
|
}
|
|
943
1398
|
function tryAcquireLock(sessionId, maxAgeMs = 10 * 60 * 1e3) {
|
|
944
|
-
|
|
945
|
-
const p =
|
|
946
|
-
if (
|
|
1399
|
+
mkdirSync6(STATE_DIR, { recursive: true });
|
|
1400
|
+
const p = lockPath2(sessionId);
|
|
1401
|
+
if (existsSync6(p)) {
|
|
947
1402
|
try {
|
|
948
|
-
const ageMs = Date.now() - parseInt(
|
|
1403
|
+
const ageMs = Date.now() - parseInt(readFileSync7(p, "utf-8"), 10);
|
|
949
1404
|
if (Number.isFinite(ageMs) && ageMs < maxAgeMs)
|
|
950
1405
|
return false;
|
|
951
1406
|
} catch (readErr) {
|
|
952
1407
|
dlog(`lock file unreadable for ${sessionId}, treating as stale: ${readErr.message}`);
|
|
953
1408
|
}
|
|
954
1409
|
try {
|
|
955
|
-
|
|
1410
|
+
unlinkSync4(p);
|
|
956
1411
|
} catch (unlinkErr) {
|
|
957
1412
|
dlog(`could not unlink stale lock for ${sessionId}: ${unlinkErr.message}`);
|
|
958
1413
|
return false;
|
|
959
1414
|
}
|
|
960
1415
|
}
|
|
961
1416
|
try {
|
|
962
|
-
const fd =
|
|
1417
|
+
const fd = openSync3(p, "wx");
|
|
963
1418
|
try {
|
|
964
1419
|
writeSync2(fd, String(Date.now()));
|
|
965
1420
|
} finally {
|
|
966
|
-
|
|
1421
|
+
closeSync3(fd);
|
|
967
1422
|
}
|
|
968
1423
|
return true;
|
|
969
1424
|
} catch (e) {
|
|
@@ -974,7 +1429,7 @@ function tryAcquireLock(sessionId, maxAgeMs = 10 * 60 * 1e3) {
|
|
|
974
1429
|
}
|
|
975
1430
|
function releaseLock(sessionId) {
|
|
976
1431
|
try {
|
|
977
|
-
|
|
1432
|
+
unlinkSync4(lockPath2(sessionId));
|
|
978
1433
|
} catch (e) {
|
|
979
1434
|
if (e?.code !== "ENOENT") {
|
|
980
1435
|
dlog(`releaseLock unlink failed for ${sessionId}: ${e.message}`);
|
|
@@ -985,20 +1440,20 @@ function releaseLock(sessionId) {
|
|
|
985
1440
|
// dist/src/hooks/codex/spawn-wiki-worker.js
|
|
986
1441
|
import { spawn as spawn2, execSync } from "node:child_process";
|
|
987
1442
|
import { fileURLToPath } from "node:url";
|
|
988
|
-
import { dirname as
|
|
989
|
-
import { writeFileSync as
|
|
990
|
-
import { homedir as
|
|
1443
|
+
import { dirname as dirname4, join as join13 } from "node:path";
|
|
1444
|
+
import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync8 } from "node:fs";
|
|
1445
|
+
import { homedir as homedir10, tmpdir as tmpdir2 } from "node:os";
|
|
991
1446
|
|
|
992
1447
|
// dist/src/utils/wiki-log.js
|
|
993
|
-
import { mkdirSync as
|
|
994
|
-
import { join as
|
|
1448
|
+
import { mkdirSync as mkdirSync7, appendFileSync as appendFileSync2 } from "node:fs";
|
|
1449
|
+
import { join as join11 } from "node:path";
|
|
995
1450
|
function makeWikiLogger(hooksDir, filename = "deeplake-wiki.log") {
|
|
996
|
-
const path =
|
|
1451
|
+
const path = join11(hooksDir, filename);
|
|
997
1452
|
return {
|
|
998
1453
|
path,
|
|
999
1454
|
log(msg) {
|
|
1000
1455
|
try {
|
|
1001
|
-
|
|
1456
|
+
mkdirSync7(hooksDir, { recursive: true });
|
|
1002
1457
|
appendFileSync2(path, `[${utcTimestamp()}] ${msg}
|
|
1003
1458
|
`);
|
|
1004
1459
|
} catch {
|
|
@@ -1008,18 +1463,18 @@ function makeWikiLogger(hooksDir, filename = "deeplake-wiki.log") {
|
|
|
1008
1463
|
}
|
|
1009
1464
|
|
|
1010
1465
|
// dist/src/utils/version-check.js
|
|
1011
|
-
import { readFileSync as
|
|
1012
|
-
import { dirname, join as
|
|
1466
|
+
import { readFileSync as readFileSync8 } from "node:fs";
|
|
1467
|
+
import { dirname as dirname3, join as join12 } from "node:path";
|
|
1013
1468
|
function getInstalledVersion(bundleDir, pluginManifestDir) {
|
|
1014
1469
|
try {
|
|
1015
|
-
const pluginJson =
|
|
1016
|
-
const plugin = JSON.parse(
|
|
1470
|
+
const pluginJson = join12(bundleDir, "..", pluginManifestDir, "plugin.json");
|
|
1471
|
+
const plugin = JSON.parse(readFileSync8(pluginJson, "utf-8"));
|
|
1017
1472
|
if (plugin.version)
|
|
1018
1473
|
return plugin.version;
|
|
1019
1474
|
} catch {
|
|
1020
1475
|
}
|
|
1021
1476
|
try {
|
|
1022
|
-
const stamp =
|
|
1477
|
+
const stamp = readFileSync8(join12(bundleDir, "..", ".hivemind_version"), "utf-8").trim();
|
|
1023
1478
|
if (stamp)
|
|
1024
1479
|
return stamp;
|
|
1025
1480
|
} catch {
|
|
@@ -1034,14 +1489,14 @@ function getInstalledVersion(bundleDir, pluginManifestDir) {
|
|
|
1034
1489
|
]);
|
|
1035
1490
|
let dir = bundleDir;
|
|
1036
1491
|
for (let i = 0; i < 5; i++) {
|
|
1037
|
-
const candidate =
|
|
1492
|
+
const candidate = join12(dir, "package.json");
|
|
1038
1493
|
try {
|
|
1039
|
-
const pkg = JSON.parse(
|
|
1494
|
+
const pkg = JSON.parse(readFileSync8(candidate, "utf-8"));
|
|
1040
1495
|
if (HIVEMIND_PKG_NAMES.has(pkg.name) && pkg.version)
|
|
1041
1496
|
return pkg.version;
|
|
1042
1497
|
} catch {
|
|
1043
1498
|
}
|
|
1044
|
-
const parent =
|
|
1499
|
+
const parent = dirname3(dir);
|
|
1045
1500
|
if (parent === dir)
|
|
1046
1501
|
break;
|
|
1047
1502
|
dir = parent;
|
|
@@ -1050,8 +1505,8 @@ function getInstalledVersion(bundleDir, pluginManifestDir) {
|
|
|
1050
1505
|
}
|
|
1051
1506
|
|
|
1052
1507
|
// dist/src/hooks/codex/spawn-wiki-worker.js
|
|
1053
|
-
var HOME =
|
|
1054
|
-
var wikiLogger = makeWikiLogger(
|
|
1508
|
+
var HOME = homedir10();
|
|
1509
|
+
var wikiLogger = makeWikiLogger(join13(HOME, ".codex", "hooks"));
|
|
1055
1510
|
var WIKI_LOG = wikiLogger.path;
|
|
1056
1511
|
var WIKI_PROMPT_TEMPLATE = `You are building a personal wiki from a coding session. Your goal is to extract every piece of knowledge \u2014 entities, decisions, relationships, and facts \u2014 into a structured, searchable wiki entry.
|
|
1057
1512
|
|
|
@@ -1113,11 +1568,11 @@ function findCodexBin() {
|
|
|
1113
1568
|
function spawnCodexWikiWorker(opts) {
|
|
1114
1569
|
const { config, sessionId, cwd, bundleDir, reason } = opts;
|
|
1115
1570
|
const projectName = cwd.split("/").pop() || "unknown";
|
|
1116
|
-
const tmpDir =
|
|
1117
|
-
|
|
1571
|
+
const tmpDir = join13(tmpdir2(), `deeplake-wiki-${sessionId}-${Date.now()}`);
|
|
1572
|
+
mkdirSync8(tmpDir, { recursive: true });
|
|
1118
1573
|
const pluginVersion = getInstalledVersion(bundleDir, ".codex-plugin") ?? "";
|
|
1119
|
-
const configFile =
|
|
1120
|
-
|
|
1574
|
+
const configFile = join13(tmpDir, "config.json");
|
|
1575
|
+
writeFileSync6(configFile, JSON.stringify({
|
|
1121
1576
|
apiUrl: config.apiUrl,
|
|
1122
1577
|
token: config.token,
|
|
1123
1578
|
orgId: config.orgId,
|
|
@@ -1131,11 +1586,11 @@ function spawnCodexWikiWorker(opts) {
|
|
|
1131
1586
|
tmpDir,
|
|
1132
1587
|
codexBin: findCodexBin(),
|
|
1133
1588
|
wikiLog: WIKI_LOG,
|
|
1134
|
-
hooksDir:
|
|
1589
|
+
hooksDir: join13(HOME, ".codex", "hooks"),
|
|
1135
1590
|
promptTemplate: WIKI_PROMPT_TEMPLATE
|
|
1136
1591
|
}));
|
|
1137
1592
|
wikiLog(`${reason}: spawning summary worker for ${sessionId}`);
|
|
1138
|
-
const workerPath =
|
|
1593
|
+
const workerPath = join13(bundleDir, "wiki-worker.js");
|
|
1139
1594
|
spawn2("nohup", ["node", workerPath, configFile], {
|
|
1140
1595
|
detached: true,
|
|
1141
1596
|
stdio: ["ignore", "ignore", "ignore"]
|
|
@@ -1143,16 +1598,22 @@ function spawnCodexWikiWorker(opts) {
|
|
|
1143
1598
|
wikiLog(`${reason}: spawned summary worker for ${sessionId}`);
|
|
1144
1599
|
}
|
|
1145
1600
|
function bundleDirFromImportMeta(importMetaUrl) {
|
|
1146
|
-
return
|
|
1601
|
+
return dirname4(fileURLToPath(importMetaUrl));
|
|
1147
1602
|
}
|
|
1148
1603
|
|
|
1149
1604
|
// dist/src/hooks/codex/capture.js
|
|
1150
|
-
var
|
|
1605
|
+
var log5 = (msg) => log("codex-capture", msg);
|
|
1151
1606
|
function resolveEmbedDaemonPath() {
|
|
1152
|
-
return
|
|
1607
|
+
return join14(dirname5(fileURLToPath2(import.meta.url)), "embeddings", "embed-daemon.js");
|
|
1153
1608
|
}
|
|
1154
|
-
var __bundleDir =
|
|
1609
|
+
var __bundleDir = dirname5(fileURLToPath2(import.meta.url));
|
|
1155
1610
|
var PLUGIN_VERSION = getInstalledVersion(__bundleDir, ".codex-plugin") ?? "";
|
|
1611
|
+
if (!embeddingsDisabled()) {
|
|
1612
|
+
try {
|
|
1613
|
+
ensurePluginNodeModulesLink({ bundleDir: __bundleDir });
|
|
1614
|
+
} catch {
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1156
1617
|
var CAPTURE = process.env.HIVEMIND_CAPTURE !== "false";
|
|
1157
1618
|
async function main() {
|
|
1158
1619
|
if (!CAPTURE)
|
|
@@ -1160,7 +1621,7 @@ async function main() {
|
|
|
1160
1621
|
const input = await readStdin();
|
|
1161
1622
|
const config = loadConfig();
|
|
1162
1623
|
if (!config) {
|
|
1163
|
-
|
|
1624
|
+
log5("no config");
|
|
1164
1625
|
return;
|
|
1165
1626
|
}
|
|
1166
1627
|
const sessionsTable = config.sessionsTableName;
|
|
@@ -1177,7 +1638,7 @@ async function main() {
|
|
|
1177
1638
|
};
|
|
1178
1639
|
let entry;
|
|
1179
1640
|
if (input.hook_event_name === "UserPromptSubmit" && input.prompt !== void 0) {
|
|
1180
|
-
|
|
1641
|
+
log5(`user session=${input.session_id}`);
|
|
1181
1642
|
entry = {
|
|
1182
1643
|
id: crypto.randomUUID(),
|
|
1183
1644
|
...meta,
|
|
@@ -1185,7 +1646,7 @@ async function main() {
|
|
|
1185
1646
|
content: input.prompt
|
|
1186
1647
|
};
|
|
1187
1648
|
} else if (input.hook_event_name === "PostToolUse" && input.tool_name !== void 0) {
|
|
1188
|
-
|
|
1649
|
+
log5(`tool=${input.tool_name} session=${input.session_id}`);
|
|
1189
1650
|
entry = {
|
|
1190
1651
|
id: crypto.randomUUID(),
|
|
1191
1652
|
...meta,
|
|
@@ -1196,12 +1657,12 @@ async function main() {
|
|
|
1196
1657
|
tool_response: JSON.stringify(input.tool_response)
|
|
1197
1658
|
};
|
|
1198
1659
|
} else {
|
|
1199
|
-
|
|
1660
|
+
log5(`unknown event: ${input.hook_event_name}, skipping`);
|
|
1200
1661
|
return;
|
|
1201
1662
|
}
|
|
1202
1663
|
const sessionPath = buildSessionPath(config, input.session_id);
|
|
1203
1664
|
const line = JSON.stringify(entry);
|
|
1204
|
-
|
|
1665
|
+
log5(`writing to ${sessionPath}`);
|
|
1205
1666
|
const projectName = (input.cwd ?? "").split("/").pop() || "unknown";
|
|
1206
1667
|
const filename = sessionPath.split("/").pop() ?? "";
|
|
1207
1668
|
const jsonForSql = line.replace(/'/g, "''");
|
|
@@ -1212,14 +1673,14 @@ async function main() {
|
|
|
1212
1673
|
await api.query(insertSql);
|
|
1213
1674
|
} catch (e) {
|
|
1214
1675
|
if (e.message?.includes("permission denied") || e.message?.includes("does not exist")) {
|
|
1215
|
-
|
|
1676
|
+
log5("table missing, creating and retrying");
|
|
1216
1677
|
await api.ensureSessionsTable(sessionsTable);
|
|
1217
1678
|
await api.query(insertSql);
|
|
1218
1679
|
} else {
|
|
1219
1680
|
throw e;
|
|
1220
1681
|
}
|
|
1221
1682
|
}
|
|
1222
|
-
|
|
1683
|
+
log5("capture ok");
|
|
1223
1684
|
maybeTriggerPeriodicSummary(input.session_id, input.cwd ?? "", config);
|
|
1224
1685
|
}
|
|
1225
1686
|
function maybeTriggerPeriodicSummary(sessionId, cwd, config) {
|
|
@@ -1231,7 +1692,7 @@ function maybeTriggerPeriodicSummary(sessionId, cwd, config) {
|
|
|
1231
1692
|
if (!shouldTrigger(state, cfg))
|
|
1232
1693
|
return;
|
|
1233
1694
|
if (!tryAcquireLock(sessionId)) {
|
|
1234
|
-
|
|
1695
|
+
log5(`periodic trigger suppressed (lock held) session=${sessionId}`);
|
|
1235
1696
|
return;
|
|
1236
1697
|
}
|
|
1237
1698
|
wikiLog(`Periodic: threshold hit (total=${state.totalCount}, since=${state.totalCount - state.lastSummaryCount}, N=${cfg.everyNMessages}, hours=${cfg.everyHours})`);
|
|
@@ -1244,19 +1705,19 @@ function maybeTriggerPeriodicSummary(sessionId, cwd, config) {
|
|
|
1244
1705
|
reason: "Periodic"
|
|
1245
1706
|
});
|
|
1246
1707
|
} catch (e) {
|
|
1247
|
-
|
|
1708
|
+
log5(`periodic spawn failed: ${e.message}`);
|
|
1248
1709
|
try {
|
|
1249
1710
|
releaseLock(sessionId);
|
|
1250
1711
|
} catch (releaseErr) {
|
|
1251
|
-
|
|
1712
|
+
log5(`releaseLock after periodic spawn failure also failed: ${releaseErr.message}`);
|
|
1252
1713
|
}
|
|
1253
1714
|
throw e;
|
|
1254
1715
|
}
|
|
1255
1716
|
} catch (e) {
|
|
1256
|
-
|
|
1717
|
+
log5(`periodic trigger error: ${e.message}`);
|
|
1257
1718
|
}
|
|
1258
1719
|
}
|
|
1259
1720
|
main().catch((e) => {
|
|
1260
|
-
|
|
1721
|
+
log5(`fatal: ${e.message}`);
|
|
1261
1722
|
process.exit(0);
|
|
1262
1723
|
});
|