grepmax 0.17.0 → 0.17.1
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/commands/mcp.js +3 -3
- package/dist/commands/search.js +2 -2
- package/dist/lib/daemon/daemon.js +36 -1
- package/dist/lib/daemon/ipc-handler.js +1 -1
- package/dist/lib/search/searcher.js +5 -1
- package/dist/lib/workers/pool.js +26 -0
- package/package.json +1 -1
- package/plugins/grepmax/.claude-plugin/plugin.json +1 -1
package/dist/commands/mcp.js
CHANGED
|
@@ -521,7 +521,7 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
521
521
|
}
|
|
522
522
|
}
|
|
523
523
|
}
|
|
524
|
-
const result = yield searcher.search(query, limit, { rerank:
|
|
524
|
+
const result = yield searcher.search(query, limit, { rerank: process.env.GMAX_RERANK === "1" }, Object.keys(filters).length > 0 ? filters : undefined, pathPrefix);
|
|
525
525
|
if (!result.data || result.data.length === 0) {
|
|
526
526
|
return ok("No matches found. Try broadening your query, using fewer keywords, or check `gmax status` to verify the project is indexed.");
|
|
527
527
|
}
|
|
@@ -1578,7 +1578,7 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
1578
1578
|
const rel = (p) => p.startsWith(`${projectRoot}/`) ? p.slice(projectRoot.length + 1) : p;
|
|
1579
1579
|
if (query) {
|
|
1580
1580
|
const searcher = getSearcher();
|
|
1581
|
-
const response = yield searcher.search(query, limit, { rerank:
|
|
1581
|
+
const response = yield searcher.search(query, limit, { rerank: process.env.GMAX_RERANK === "1" }, {}, projectRoot);
|
|
1582
1582
|
const changedSet = new Set(changedFiles);
|
|
1583
1583
|
let filtered = response.data.filter((r) => changedSet.has(String(r.path || "")));
|
|
1584
1584
|
if (role)
|
|
@@ -1753,7 +1753,7 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
1753
1753
|
const limit = Math.min(Math.max(Number(args.limit) || 10, 1), 25);
|
|
1754
1754
|
try {
|
|
1755
1755
|
const searcher = getSearcher();
|
|
1756
|
-
const response = yield searcher.search(topic, limit, { rerank:
|
|
1756
|
+
const response = yield searcher.search(topic, limit, { rerank: process.env.GMAX_RERANK === "1" }, {}, projectRoot);
|
|
1757
1757
|
if (response.data.length === 0)
|
|
1758
1758
|
return ok(`No results found for "${topic}".`);
|
|
1759
1759
|
const rel = (p) => p.startsWith(`${projectRoot}/`) ? p.slice(projectRoot.length + 1) : p;
|
package/dist/commands/search.js
CHANGED
|
@@ -571,7 +571,7 @@ Examples:
|
|
|
571
571
|
? searchFilters
|
|
572
572
|
: undefined,
|
|
573
573
|
pathPrefix: pathFilter,
|
|
574
|
-
rerank:
|
|
574
|
+
rerank: process.env.GMAX_RERANK === "1",
|
|
575
575
|
explain: options.explain,
|
|
576
576
|
includeSkeletons: options.skeleton,
|
|
577
577
|
includeGraph: options.symbol,
|
|
@@ -668,7 +668,7 @@ Examples:
|
|
|
668
668
|
}
|
|
669
669
|
}
|
|
670
670
|
const searcher = new searcher_1.Searcher(vectorDb);
|
|
671
|
-
searchResult = yield searcher.search(pattern, parseInt(options.m, 10), { rerank:
|
|
671
|
+
searchResult = yield searcher.search(pattern, parseInt(options.m, 10), { rerank: process.env.GMAX_RERANK === "1", explain: options.explain }, Object.keys(searchFilters).length > 0
|
|
672
672
|
? searchFilters
|
|
673
673
|
: undefined, pathFilter);
|
|
674
674
|
} // end if (!searchResult) — in-process fallback
|
|
@@ -109,6 +109,8 @@ class Daemon {
|
|
|
109
109
|
this.startTime = Date.now();
|
|
110
110
|
this.heartbeatInterval = null;
|
|
111
111
|
this.idleInterval = null;
|
|
112
|
+
this.heartbeatTick = 0;
|
|
113
|
+
this.mlxRecoveryInFlight = false;
|
|
112
114
|
this.shuttingDown = false;
|
|
113
115
|
this.pendingOps = new Set();
|
|
114
116
|
this.watcherFailCount = new Map();
|
|
@@ -289,6 +291,14 @@ class Daemon {
|
|
|
289
291
|
}
|
|
290
292
|
catch (_a) { }
|
|
291
293
|
(0, log_rotate_1.rotateLogFds)(path.join(config_1.PATHS.logsDir, "daemon.log"));
|
|
294
|
+
// Every 5 ticks (5 min), probe the MLX embed server and respawn if
|
|
295
|
+
// it's gone zombie (port held but /health unresponsive). Closes the
|
|
296
|
+
// 42h-degradation window where workers silently fell back to ONNX CPU
|
|
297
|
+
// after a frozen MLX process kept the port bound (v0.17.0 bug #1).
|
|
298
|
+
this.heartbeatTick++;
|
|
299
|
+
if (this.heartbeatTick % 5 === 0) {
|
|
300
|
+
void this.checkMlxHealth();
|
|
301
|
+
}
|
|
292
302
|
}, HEARTBEAT_INTERVAL_MS);
|
|
293
303
|
// 10. Idle timeout (skip when disabled via env)
|
|
294
304
|
if (IDLE_TIMEOUT_MS > 0) {
|
|
@@ -796,7 +806,7 @@ class Daemon {
|
|
|
796
806
|
this.lastActivity = Date.now();
|
|
797
807
|
let result;
|
|
798
808
|
try {
|
|
799
|
-
result = yield searcher.search(payload.query, payload.limit, { rerank: payload.rerank
|
|
809
|
+
result = yield searcher.search(payload.query, payload.limit, { rerank: payload.rerank === true, explain: payload.explain === true }, payload.filters, payload.pathPrefix, undefined, signal);
|
|
800
810
|
}
|
|
801
811
|
catch (err) {
|
|
802
812
|
if ((err === null || err === void 0 ? void 0 : err.name) === "AbortError") {
|
|
@@ -1185,6 +1195,31 @@ class Daemon {
|
|
|
1185
1195
|
return null;
|
|
1186
1196
|
}
|
|
1187
1197
|
}
|
|
1198
|
+
checkMlxHealth() {
|
|
1199
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1200
|
+
if (this.shuttingDown || this.mlxRecoveryInFlight)
|
|
1201
|
+
return;
|
|
1202
|
+
if (yield this.isMlxServerUp())
|
|
1203
|
+
return;
|
|
1204
|
+
const port = parseInt(process.env.MLX_EMBED_PORT || "8100", 10);
|
|
1205
|
+
const stalePid = this.getPortPid(port);
|
|
1206
|
+
if (!stalePid)
|
|
1207
|
+
return; // No process — let the next user-facing path spawn it.
|
|
1208
|
+
this.mlxRecoveryInFlight = true;
|
|
1209
|
+
try {
|
|
1210
|
+
console.log(`[daemon] MLX zombie detected on port ${port} (PID ${stalePid}) — killing and respawning`);
|
|
1211
|
+
yield (0, process_1.killProcess)(stalePid);
|
|
1212
|
+
yield new Promise((r) => setTimeout(r, 500));
|
|
1213
|
+
yield this.ensureMlxServer();
|
|
1214
|
+
}
|
|
1215
|
+
catch (err) {
|
|
1216
|
+
console.error(`[daemon] MLX recovery failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1217
|
+
}
|
|
1218
|
+
finally {
|
|
1219
|
+
this.mlxRecoveryInFlight = false;
|
|
1220
|
+
}
|
|
1221
|
+
});
|
|
1222
|
+
}
|
|
1188
1223
|
ensureMlxServer(mlxModel) {
|
|
1189
1224
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1190
1225
|
if (yield this.isMlxServerUp()) {
|
|
@@ -169,7 +169,7 @@ function handleCommand(daemon, cmd, conn) {
|
|
|
169
169
|
limit: limitRaw,
|
|
170
170
|
filters,
|
|
171
171
|
pathPrefix: typeof cmd.pathPrefix === "string" ? cmd.pathPrefix : undefined,
|
|
172
|
-
rerank: cmd.rerank
|
|
172
|
+
rerank: cmd.rerank === true,
|
|
173
173
|
explain: cmd.explain === true,
|
|
174
174
|
includeSkeletons: cmd.includeSkeletons === true,
|
|
175
175
|
skeletonLimit: skeletonLimitRaw,
|
|
@@ -340,7 +340,11 @@ class Searcher {
|
|
|
340
340
|
return __awaiter(this, void 0, void 0, function* () {
|
|
341
341
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
342
342
|
const finalLimit = top_k !== null && top_k !== void 0 ? top_k : 10;
|
|
343
|
-
|
|
343
|
+
// ColBERT rerank is opt-in as of v0.17.1. On the 97-case eval it
|
|
344
|
+
// regresses MRR@10 by ~3% and doubles query latency; sweep across
|
|
345
|
+
// FUSED_WEIGHT ∈ {0,0.1,0.5,1,2} showed rerank scores dominate
|
|
346
|
+
// fused scores ~30:1 so blend tuning can't recover the loss.
|
|
347
|
+
const doRerank = (_a = _search_options === null || _search_options === void 0 ? void 0 : _search_options.rerank) !== null && _a !== void 0 ? _a : false;
|
|
344
348
|
const explain = (_b = _search_options === null || _search_options === void 0 ? void 0 : _search_options.explain) !== null && _b !== void 0 ? _b : false;
|
|
345
349
|
const searchIntent = intent || (0, intent_1.detectIntent)(query);
|
|
346
350
|
const pool = (0, pool_1.getWorkerPool)();
|
package/dist/lib/workers/pool.js
CHANGED
|
@@ -83,6 +83,13 @@ const TASK_TIMEOUT_MS = (() => {
|
|
|
83
83
|
return 120000;
|
|
84
84
|
})();
|
|
85
85
|
const FORCE_KILL_GRACE_MS = 200;
|
|
86
|
+
// Longer grace for idle reaps: the worker isn't urgently in the way, and a
|
|
87
|
+
// graceful SIGTERM lets ONNX free ~1GB of model memory. But if SIGTERM is
|
|
88
|
+
// ignored (a worker burning 100% CPU inside a native ONNX matmul tight loop
|
|
89
|
+
// won't service signals — the 42h zombie we saw in v0.17.0 validation),
|
|
90
|
+
// escalate to SIGKILL. ~5s is well above ONNX teardown time but short
|
|
91
|
+
// enough that the reap loop self-heals within a minute.
|
|
92
|
+
const REAP_FORCE_KILL_GRACE_MS = 5000;
|
|
86
93
|
class ProcessWorker {
|
|
87
94
|
constructor(modulePath, execArgv, maxMemoryMb) {
|
|
88
95
|
this.modulePath = modulePath;
|
|
@@ -444,11 +451,30 @@ class WorkerPool {
|
|
|
444
451
|
w.child.removeAllListeners("message");
|
|
445
452
|
w.child.removeAllListeners("exit");
|
|
446
453
|
w.child.removeAllListeners("error");
|
|
454
|
+
const pid = w.child.pid;
|
|
447
455
|
try {
|
|
448
456
|
w.child.kill("SIGTERM");
|
|
449
457
|
}
|
|
450
458
|
catch (_a) { }
|
|
451
459
|
this.workers = this.workers.filter((x) => x !== w);
|
|
460
|
+
// SIGTERM is ignored by a worker stuck inside a native ONNX matmul
|
|
461
|
+
// tight loop. Escalate to SIGKILL if the process is still alive after
|
|
462
|
+
// the grace period. Rare; warn-level so it's visible if it fires.
|
|
463
|
+
if (pid !== undefined) {
|
|
464
|
+
setTimeout(() => {
|
|
465
|
+
try {
|
|
466
|
+
process.kill(pid, 0);
|
|
467
|
+
(0, logger_1.log)("pool", `reap escalation: SIGTERM ignored by PID:${pid}, sending SIGKILL`);
|
|
468
|
+
try {
|
|
469
|
+
process.kill(pid, "SIGKILL");
|
|
470
|
+
}
|
|
471
|
+
catch (_a) { }
|
|
472
|
+
}
|
|
473
|
+
catch (_b) {
|
|
474
|
+
// ESRCH — process already gone, nothing to do.
|
|
475
|
+
}
|
|
476
|
+
}, REAP_FORCE_KILL_GRACE_MS);
|
|
477
|
+
}
|
|
452
478
|
});
|
|
453
479
|
}
|
|
454
480
|
destroy() {
|
package/package.json
CHANGED