grepmax 0.26.76 → 0.26.78
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.
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"name": "grepmax",
|
|
11
11
|
"source": "./plugins/grepmax",
|
|
12
12
|
"description": "Semantic code search for Claude Code. Automatically indexes your project and provides intelligent search capabilities.",
|
|
13
|
-
"version": "0.26.
|
|
13
|
+
"version": "0.26.78",
|
|
14
14
|
"author": {
|
|
15
15
|
"name": "Robert Owens",
|
|
16
16
|
"email": "robowens@me.com"
|
package/README.md
CHANGED
|
@@ -528,11 +528,11 @@ The package includes a separate prune-only helper with pinned Lance 12 dependenc
|
|
|
528
528
|
|
|
529
529
|
`gmax recover --table /path/to/lancedb/chunks.lance --json` reads the durable recovery receipt without starting a daemon or preparing Python. `--check` samples admission without pruning. `--prune --version <current-version> --cutoff <absolute-ISO-timestamp>` explicitly requests one offline prune. A prior uncertain attempt requires `--acknowledge-uncertain <attempt-id>` after inspection. Recovery never creates/clears quarantine, stops other owners, retries automatically or enables rewriting.
|
|
530
530
|
|
|
531
|
-
Production recovery currently requires macOS, a persistent `autostart-disabled` file in the selected store home, and closed store owners. Recovery
|
|
531
|
+
Production recovery currently requires macOS, a persistent `autostart-disabled` file in the selected store home, and closed store owners. Recovery uses the shared aggregate resource budget and requires measured OS pressure and healthy kernel pressure. The configured `critical-only` policy permits a known OS memory warning while retaining aggregate accounting; the default `strict` policy pauses on warnings. Immediately free RAM is measured for diagnostics but has no fixed minimum: macOS can reclaim cached memory, so a low free-page count alone must not prevent cleanup. The helper reserves 512 MiB of admission capacity; this is not an OS-enforced footprint cap. Recovery samples have an eight-second total ceiling, including the established five-second kernel probe allowance, with fresh sampling at deletion admission and five-second heartbeat scheduling. Unknown, stale, critical, kernel-warning or over-budget measurements refuse/interrupt recovery. Pruning needs at least 1 MiB of measured metadata disk headroom, not full-table rewrite space. Initial wheel setup requires 512 MiB; an existing environment is checked against the lock offline without that download-space requirement.
|
|
532
532
|
|
|
533
533
|
The child waits for launch and deletion admission tokens, with verified helper PID/start identity recorded as the primary exclusive owner. Existing readers retain exclusion if the parent dies while the helper is alive. Current/tagged/post-cutoff metadata is verified after pruning; recovery refuses more than 256 protected versions before deletion to bound verification. Interrupted attempts require verification and explicit acknowledgement before retry. Linux/APFS fixtures exercise native retention and interruption; supported production host admission is tested separately and still runs freshly for each operation.
|
|
534
534
|
|
|
535
|
-
If a short-lived client child exits
|
|
535
|
+
If a short-lived client child exits during memory sampling, confirmed dead processes can leave the sample while all surviving processes retain measured footprints. An uncertain exit, new process or changed identity requires a complete fresh measurement; a normal-pressure sample may do that once within the original deadline. It never accepts an unmeasured live process. Continued changes, unavailable measurements and critical pressure still refuse admission; warning admission follows the recovery policy above.
|
|
536
536
|
|
|
537
537
|
For an operator-coordinated recovery window:
|
|
538
538
|
|
|
@@ -39,6 +39,7 @@ exports.assertRecoveryAdmission = assertRecoveryAdmission;
|
|
|
39
39
|
exports.admitPrune = admitPrune;
|
|
40
40
|
const fs = __importStar(require("node:fs"));
|
|
41
41
|
const path = __importStar(require("node:path"));
|
|
42
|
+
const host_guard_policy_1 = require("../utils/host-guard-policy");
|
|
42
43
|
const resource_budget_1 = require("../utils/resource-budget");
|
|
43
44
|
const safety_latch_1 = require("../utils/safety-latch");
|
|
44
45
|
const maintenance_policy_1 = require("./maintenance-policy");
|
|
@@ -48,6 +49,10 @@ exports.PRUNE_METADATA_HEADROOM_BYTES = 1024 * 1024;
|
|
|
48
49
|
function createRecoveryBudget() {
|
|
49
50
|
return new resource_budget_1.ResourceBudget({
|
|
50
51
|
policy: () => "strict",
|
|
52
|
+
// Keep measured aggregate accounting in both policies. The user's
|
|
53
|
+
// critical-only selection permits known OS warning, not missing samples,
|
|
54
|
+
// unknown/kernel pressure or an overcommitted aggregate reservation.
|
|
55
|
+
allowMemoryWarning: () => (0, host_guard_policy_1.hostGuardPolicy)() === "critical-only",
|
|
51
56
|
quarantine: safety_latch_1.safetyStopReason,
|
|
52
57
|
requireClientRegistration: false,
|
|
53
58
|
samplerOverrides: { sampleTimeoutMs: 8000, kernelProbeTimeoutMs: 5000 },
|
|
@@ -153,7 +153,17 @@ function sampleHostResources(extraPids = [], overrides = {}, extraGroups = []) {
|
|
|
153
153
|
function sampleHostResourcesOnce(extraPids, overrides, extraGroups) {
|
|
154
154
|
var _a;
|
|
155
155
|
var _b, _c;
|
|
156
|
-
const deps = Object.assign({ platform: process.platform, pid: process.pid, uid: (_b = (_a = process.getuid) === null || _a === void 0 ? void 0 : _a.call(process)) !== null && _b !== void 0 ? _b : -1, now: Date.now, monotonic: () => performance.now(), freeBytes: os.freemem,
|
|
156
|
+
const deps = Object.assign({ platform: process.platform, pid: process.pid, uid: (_b = (_a = process.getuid) === null || _a === void 0 ? void 0 : _a.call(process)) !== null && _b !== void 0 ? _b : -1, now: Date.now, monotonic: () => performance.now(), freeBytes: os.freemem, processAlive: (pid) => {
|
|
157
|
+
try {
|
|
158
|
+
process.kill(pid, 0);
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
if (error.code === "ESRCH")
|
|
163
|
+
return false;
|
|
164
|
+
throw new Error("process liveness unavailable");
|
|
165
|
+
}
|
|
166
|
+
}, sampleTimeoutMs: exports.HOST_SAMPLE_TIMEOUT_MS, kernelProbeTimeoutMs: 500, run: (command, args, timeoutMs) => (0, node_child_process_1.execFileSync)(command, args, {
|
|
157
167
|
encoding: "utf8",
|
|
158
168
|
timeout: timeoutMs,
|
|
159
169
|
maxBuffer: 512 * 1024,
|
|
@@ -224,8 +234,19 @@ function sampleHostResourcesOnce(extraPids, overrides, extraGroups) {
|
|
|
224
234
|
p.role,
|
|
225
235
|
];
|
|
226
236
|
if (JSON.stringify(after.map(identity)) !==
|
|
227
|
-
JSON.stringify(snapshot.processes.map(identity)))
|
|
228
|
-
|
|
237
|
+
JSON.stringify(snapshot.processes.map(identity))) {
|
|
238
|
+
// A verified exit releases memory. It need not invalidate measured
|
|
239
|
+
// survivors, even when the exiting child's footprint is unavailable.
|
|
240
|
+
// Additions, reuse, reparenting and uncertain liveness still require a
|
|
241
|
+
// complete fresh measurement; never treat them as zero-memory exits.
|
|
242
|
+
const previous = new Map(snapshot.processes.map((p) => [p.pid, p]));
|
|
243
|
+
if (after.some((p) => !previous.has(p.pid) ||
|
|
244
|
+
JSON.stringify(identity(p)) !==
|
|
245
|
+
JSON.stringify(identity(previous.get(p.pid)))) ||
|
|
246
|
+
snapshot.processes.some((p) => !after.some((q) => q.pid === p.pid) && deps.processAlive(p.pid)))
|
|
247
|
+
throw new Error("process inventory changed during sample");
|
|
248
|
+
snapshot.processes = after.map((p) => { var _a; return (Object.assign(Object.assign({}, p), { footprintMb: (_a = footprints.get(p.pid)) !== null && _a !== void 0 ? _a : null })); });
|
|
249
|
+
}
|
|
229
250
|
if (snapshot.processes.some((p) => p.footprintMb === null))
|
|
230
251
|
throw new Error("process footprint unavailable");
|
|
231
252
|
snapshot.aggregateFootprintMb = snapshot.processes.reduce((sum, p) => sum + p.footprintMb, 0);
|
|
@@ -77,11 +77,12 @@ function resolveResourceBudgetMb(raw = process.env.GMAX_RESOURCE_BUDGET_MB) {
|
|
|
77
77
|
throw new ResourceAdmissionError("resource budget must be 256..6144 MiB");
|
|
78
78
|
return n;
|
|
79
79
|
}
|
|
80
|
-
function assertResourceSnapshot(snapshot, chargedMb, budgetMb, now = Date.now(), sampleMaxAgeMs = 5000) {
|
|
80
|
+
function assertResourceSnapshot(snapshot, chargedMb, budgetMb, now = Date.now(), sampleMaxAgeMs = 5000, allowMemoryWarning = false) {
|
|
81
81
|
if (snapshot.memoryPressure === "critical" ||
|
|
82
82
|
snapshot.kernelPressure === "critical")
|
|
83
83
|
throw new ResourceAdmissionError("critical host pressure; heavy work refused", true);
|
|
84
|
-
if (snapshot.memoryPressure === "warn"
|
|
84
|
+
if ((snapshot.memoryPressure === "warn" && !allowMemoryWarning) ||
|
|
85
|
+
snapshot.kernelPressure === "warn")
|
|
85
86
|
throw new ResourceAdmissionError("warning host pressure; heavy work paused");
|
|
86
87
|
if (snapshot.platform !== "darwin")
|
|
87
88
|
return;
|
|
@@ -92,7 +93,8 @@ function assertResourceSnapshot(snapshot, chargedMb, budgetMb, now = Date.now(),
|
|
|
92
93
|
throw new ResourceAdmissionError("resource sample stale or clock changed; heavy work paused");
|
|
93
94
|
if (snapshot.incompleteReasons.length ||
|
|
94
95
|
snapshot.aggregateFootprintMb === null ||
|
|
95
|
-
snapshot.memoryPressure !== "normal"
|
|
96
|
+
(snapshot.memoryPressure !== "normal" &&
|
|
97
|
+
!(allowMemoryWarning && snapshot.memoryPressure === "warn")) ||
|
|
96
98
|
snapshot.kernelPressure !== "ok")
|
|
97
99
|
throw new ResourceAdmissionError(`unknown aggregate resources; heavy work paused: ${snapshot.incompleteReasons.join(", ") || "incomplete sample"}`);
|
|
98
100
|
if (!Number.isFinite(chargedMb) ||
|
|
@@ -110,7 +112,7 @@ class ResourceBudget {
|
|
|
110
112
|
this.deps = Object.assign({ root: path.join(config_1.PATHS.sharedRoot, "resource-budget"), platform: process.platform, pid: process.pid, now: Date.now, sample: (extraPids = []) => {
|
|
111
113
|
const records = this.readExistingRecords();
|
|
112
114
|
return (0, host_resource_1.sampleHostResources)([...records.map((r) => r.pid), ...extraPids], this.deps.samplerOverrides, records.flatMap((r) => (r.groupPid ? [r.groupPid] : [])));
|
|
113
|
-
}, signal: (pid) => process.kill(pid, 0), policy: host_guard_policy_1.hostGuardPolicy, requireClientRegistration: true, samplerOverrides: {}, sampleMaxAgeMs: 5000, criticalSample: host_guard_policy_1.sampleCriticalPressure, quarantine: autostart_1.daemonStartDeniedReason, latch: (reason, diagnostics) => (0, safety_latch_1.latchSafetyStop)(reason, undefined, diagnostics), processStart: () => (0, node_child_process_1.execFileSync)("ps", ["-p", String(process.pid), "-o", "lstart="], {
|
|
115
|
+
}, signal: (pid) => process.kill(pid, 0), policy: host_guard_policy_1.hostGuardPolicy, requireClientRegistration: true, allowMemoryWarning: () => false, samplerOverrides: {}, sampleMaxAgeMs: 5000, criticalSample: host_guard_policy_1.sampleCriticalPressure, quarantine: autostart_1.daemonStartDeniedReason, latch: (reason, diagnostics) => (0, safety_latch_1.latchSafetyStop)(reason, undefined, diagnostics), processStart: () => (0, node_child_process_1.execFileSync)("ps", ["-p", String(process.pid), "-o", "lstart="], {
|
|
114
116
|
encoding: "utf8",
|
|
115
117
|
timeout: 500,
|
|
116
118
|
maxBuffer: 1024,
|
|
@@ -267,7 +269,7 @@ class ResourceBudget {
|
|
|
267
269
|
}
|
|
268
270
|
checked(snapshot, records, kind, mb) {
|
|
269
271
|
try {
|
|
270
|
-
assertResourceSnapshot(snapshot, this.charge(snapshot, records), resolveResourceBudgetMb(), this.deps.now(), this.deps.sampleMaxAgeMs);
|
|
272
|
+
assertResourceSnapshot(snapshot, this.charge(snapshot, records), resolveResourceBudgetMb(), this.deps.now(), this.deps.sampleMaxAgeMs, this.deps.allowMemoryWarning());
|
|
271
273
|
const legacy = snapshot.processes.filter((p) => p.role === "mcp" &&
|
|
272
274
|
!records.some((r) => r.pid === p.pid && r.kind === "client"));
|
|
273
275
|
if (legacy.length && this.deps.requireClientRegistration)
|
|
@@ -344,7 +346,7 @@ class ResourceBudget {
|
|
|
344
346
|
}
|
|
345
347
|
const records = this.readExistingRecords();
|
|
346
348
|
const snapshot = this.deps.sample();
|
|
347
|
-
assertResourceSnapshot(snapshot, this.charge(snapshot, records), resolveResourceBudgetMb(), this.deps.now(), this.deps.sampleMaxAgeMs);
|
|
349
|
+
assertResourceSnapshot(snapshot, this.charge(snapshot, records), resolveResourceBudgetMb(), this.deps.now(), this.deps.sampleMaxAgeMs, this.deps.allowMemoryWarning());
|
|
348
350
|
if (this.deps.quarantine())
|
|
349
351
|
throw new ResourceAdmissionError("host containment");
|
|
350
352
|
return snapshot;
|
package/package.json
CHANGED