grepmax 0.26.64 → 0.26.65

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.
@@ -0,0 +1,113 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.pruneStatePath = pruneStatePath;
37
+ exports.readPruneState = readPruneState;
38
+ exports.writePruneState = writePruneState;
39
+ const node_crypto_1 = require("node:crypto");
40
+ const fs = __importStar(require("node:fs"));
41
+ const path = __importStar(require("node:path"));
42
+ function pruneStatePath(storeDir) {
43
+ return path.join(storeDir, ".gmax-prune-state.json");
44
+ }
45
+ /** A running receipt survives parent death. Readers must treat it as uncertain,
46
+ * never as successful cleanup or permission to launch automatic maintenance. */
47
+ function readPruneState(storeDir) {
48
+ var _a, _b, _c;
49
+ const identity = fs.realpathSync(storeDir);
50
+ const target = pruneStatePath(identity);
51
+ let stat;
52
+ try {
53
+ stat = fs.lstatSync(target);
54
+ }
55
+ catch (error) {
56
+ if (error.code === "ENOENT")
57
+ return null;
58
+ throw error;
59
+ }
60
+ if (!stat.isFile() || stat.size > 16 * 1024)
61
+ throw new Error("Prune state is unverified or oversized");
62
+ const state = JSON.parse(fs.readFileSync(target, "utf8"));
63
+ if (state.schemaVersion !== 1 ||
64
+ state.storeIdentity !== identity ||
65
+ typeof state.attemptId !== "string" ||
66
+ !state.attemptId ||
67
+ !Number.isSafeInteger(state.version) ||
68
+ state.version < 1 ||
69
+ !Number.isSafeInteger(state.cutoffMs) ||
70
+ !Number.isSafeInteger(state.startedAt) ||
71
+ !["running", "uncertain", "completed"].includes(state.outcome) ||
72
+ (state.outcome === "completed" &&
73
+ (!Number.isSafeInteger(state.finishedAt) ||
74
+ ((_a = state.result) === null || _a === void 0 ? void 0 : _a.version) !== state.version ||
75
+ ((_b = state.result) === null || _b === void 0 ? void 0 : _b.engine) !== "12.0.0" ||
76
+ ((_c = state.result) === null || _c === void 0 ? void 0 : _c.rewritten) !== false)))
77
+ throw new Error("Prune state is invalid; cleanup remains pending");
78
+ return state.outcome === "running"
79
+ ? Object.assign(Object.assign({}, state), { outcome: "uncertain" }) : state;
80
+ }
81
+ /** Persist and sync before native deletion. Failure refuses the operation;
82
+ * failed final writes leave a running/uncertain receipt for the next reader. */
83
+ function writePruneState(state) {
84
+ const target = pruneStatePath(state.storeIdentity);
85
+ if (fs.existsSync(target)) {
86
+ const stat = fs.lstatSync(target);
87
+ if (!stat.isFile() || stat.size > 16 * 1024)
88
+ throw new Error("Prune state is unverified or oversized");
89
+ }
90
+ const temporary = path.join(state.storeIdentity, `.gmax-prune-${(0, node_crypto_1.randomUUID)()}.tmp`);
91
+ try {
92
+ const descriptor = fs.openSync(temporary, "wx", 0o600);
93
+ try {
94
+ fs.writeFileSync(descriptor, `${JSON.stringify(state)}\n`);
95
+ fs.fsyncSync(descriptor);
96
+ }
97
+ finally {
98
+ fs.closeSync(descriptor);
99
+ }
100
+ fs.renameSync(temporary, target);
101
+ const directory = fs.openSync(state.storeIdentity, "r");
102
+ try {
103
+ fs.fsyncSync(directory);
104
+ }
105
+ finally {
106
+ fs.closeSync(directory);
107
+ }
108
+ }
109
+ finally {
110
+ if (fs.existsSync(temporary))
111
+ fs.unlinkSync(temporary);
112
+ }
113
+ }
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.PRUNE_METADATA_HEADROOM_BYTES = void 0;
37
+ exports.createRecoveryBudget = createRecoveryBudget;
38
+ exports.assertRecoveryAdmission = assertRecoveryAdmission;
39
+ exports.admitPrune = admitPrune;
40
+ const fs = __importStar(require("node:fs"));
41
+ const path = __importStar(require("node:path"));
42
+ const resource_budget_1 = require("../utils/resource-budget");
43
+ const safety_latch_1 = require("../utils/safety-latch");
44
+ const maintenance_policy_1 = require("./maintenance-policy");
45
+ exports.PRUNE_METADATA_HEADROOM_BYTES = 1024 * 1024;
46
+ /** Explicit recovery has aggregate admission without changing normal service
47
+ * policy. Offline autostart containment is allowed; a host safety stop is not. */
48
+ function createRecoveryBudget() {
49
+ return new resource_budget_1.ResourceBudget({
50
+ policy: () => "strict",
51
+ quarantine: safety_latch_1.safetyStopReason,
52
+ requireClientRegistration: false,
53
+ samplerOverrides: { sampleTimeoutMs: 8000, kernelProbeTimeoutMs: 5000 },
54
+ sampleMaxAgeMs: 10000,
55
+ });
56
+ }
57
+ function assertRecoveryAdmission(storeDir, budget) {
58
+ const marker = path.join(path.dirname(fs.realpathSync(storeDir)), "autostart-disabled");
59
+ const stat = fs.lstatSync(marker, { throwIfNoEntry: false });
60
+ if (!(stat === null || stat === void 0 ? void 0 : stat.isFile()) || stat.isSymbolicLink())
61
+ throw new Error("Recovery requires a persistent autostart-disabled file in the store home; close store owners separately");
62
+ const disk = (0, maintenance_policy_1.availableStoreDiskBytes)(storeDir);
63
+ if (!Number.isFinite(disk) || disk < exports.PRUNE_METADATA_HEADROOM_BYTES)
64
+ throw new Error("Prune metadata disk headroom is low or unknown");
65
+ const snapshot = budget.check();
66
+ if ((snapshot === null || snapshot === void 0 ? void 0 : snapshot.platform) !== "darwin")
67
+ throw new Error("Production recovery admission currently requires macOS host measurements");
68
+ if (snapshot.physicalFreeMb === null ||
69
+ !Number.isFinite(snapshot.physicalFreeMb) ||
70
+ snapshot.physicalFreeMb < resource_budget_1.HELPER_RESOURCE_RESERVE_MB)
71
+ throw new Error("Insufficient measured physical headroom for prune helper");
72
+ return snapshot;
73
+ }
74
+ function admitPrune(storeDir, budget = createRecoveryBudget()) {
75
+ assertRecoveryAdmission(storeDir, budget);
76
+ const reserve = budget.reserve(resource_budget_1.HELPER_RESOURCE_RESERVE_MB, "prune-helper");
77
+ let snapshot;
78
+ try {
79
+ snapshot = assertRecoveryAdmission(storeDir, budget);
80
+ }
81
+ catch (error) {
82
+ reserve.release();
83
+ throw error;
84
+ }
85
+ return {
86
+ snapshot,
87
+ start: (pid) => reserve.attach(pid),
88
+ approve: () => assertRecoveryAdmission(storeDir, budget),
89
+ check: () => assertRecoveryAdmission(storeDir, budget),
90
+ close: () => reserve.release(),
91
+ };
92
+ }
@@ -0,0 +1,163 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
36
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
37
+ return new (P || (P = Promise))(function (resolve, reject) {
38
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
39
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
40
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
41
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
42
+ });
43
+ };
44
+ Object.defineProperty(exports, "__esModule", { value: true });
45
+ exports.recoverStore = recoverStore;
46
+ const fs = __importStar(require("node:fs"));
47
+ const path = __importStar(require("node:path"));
48
+ const resource_budget_1 = require("../utils/resource-budget");
49
+ const lance_cleanup_1 = require("./lance-cleanup");
50
+ const prune_state_1 = require("./prune-state");
51
+ const recovery_admission_1 = require("./recovery-admission");
52
+ const store_lease_1 = require("./store-lease");
53
+ function recoverStore(request) {
54
+ return __awaiter(this, void 0, void 0, function* () {
55
+ var _a, _b, _c, _d;
56
+ (_a = request.signal) === null || _a === void 0 ? void 0 : _a.throwIfAborted();
57
+ const input = path.resolve(request.table);
58
+ const stat = fs.lstatSync(input);
59
+ if (!stat.isDirectory() || stat.isSymbolicLink() || !input.endsWith(".lance"))
60
+ throw new Error("Recovery requires an existing local Lance table directory, without a table symlink");
61
+ const table = fs.realpathSync(input);
62
+ const store = path.dirname(table);
63
+ const previous = (0, prune_state_1.readPruneState)(store);
64
+ if (!request.prune && !request.check)
65
+ return {
66
+ outcome: "status",
67
+ table,
68
+ state: previous,
69
+ fullTableRewriting: "disabled",
70
+ };
71
+ if (request.prune &&
72
+ (!Number.isSafeInteger(request.version) ||
73
+ request.version < 1 ||
74
+ !request.cutoff ||
75
+ !Number.isSafeInteger(request.cutoff.getTime()) ||
76
+ request.cutoff.getTime() > Date.now()))
77
+ throw new Error("Explicit prune requires a positive --version and a real, nonfuture --cutoff");
78
+ if (request.prune &&
79
+ (previous === null || previous === void 0 ? void 0 : previous.outcome) === "uncertain" &&
80
+ request.acknowledgeUncertain !== previous.attemptId)
81
+ throw new Error(`Previous prune completion is uncertain; inspect retained state, then use --acknowledge-uncertain ${previous.attemptId} for a verified explicit retry`);
82
+ const budget = (0, recovery_admission_1.createRecoveryBudget)();
83
+ (0, recovery_admission_1.assertRecoveryAdmission)(store, budget);
84
+ if (!request.prune) {
85
+ const admission = (0, recovery_admission_1.admitPrune)(store, budget);
86
+ try {
87
+ return {
88
+ outcome: "preflight-passed",
89
+ table,
90
+ state: previous,
91
+ host: admission.snapshot,
92
+ helperReserveMb: resource_budget_1.HELPER_RESOURCE_RESERVE_MB,
93
+ fullTableRewriting: "disabled",
94
+ };
95
+ }
96
+ finally {
97
+ admission.close();
98
+ }
99
+ }
100
+ // Setup is admitted and monitored before acquiring store exclusion. No owner
101
+ // is killed and no marker is created or cleared by recovery itself.
102
+ const controller = new AbortController();
103
+ const abort = () => controller.abort();
104
+ (_b = request.signal) === null || _b === void 0 ? void 0 : _b.addEventListener("abort", abort, { once: true });
105
+ if ((_c = request.signal) === null || _c === void 0 ? void 0 : _c.aborted)
106
+ controller.abort();
107
+ let setup;
108
+ let monitor;
109
+ let lease;
110
+ try {
111
+ setup = budget.reserve(resource_budget_1.HELPER_RESOURCE_RESERVE_MB, "prune-runtime-setup");
112
+ monitor = setInterval(() => {
113
+ try {
114
+ (0, recovery_admission_1.assertRecoveryAdmission)(store, budget);
115
+ }
116
+ catch (_a) {
117
+ controller.abort();
118
+ }
119
+ }, 5000);
120
+ const runtime = yield (0, lance_cleanup_1.prepareCleanupRuntime)(controller.signal);
121
+ clearInterval(monitor);
122
+ setup.release();
123
+ (0, recovery_admission_1.assertRecoveryAdmission)(store, budget);
124
+ lease = yield store_lease_1.StoreLease.acquireExclusive({
125
+ storeDir: store,
126
+ timeoutMs: 1000,
127
+ signal: controller.signal,
128
+ role: "explicit-prune-recovery",
129
+ });
130
+ // Another completed/interrupted attempt may have happened during setup.
131
+ const current = (0, prune_state_1.readPruneState)(store);
132
+ if ((current === null || current === void 0 ? void 0 : current.outcome) === "uncertain" &&
133
+ request.acknowledgeUncertain !== current.attemptId)
134
+ throw new Error(`Prune completion is uncertain; inspect and acknowledge attempt ${current.attemptId} before retry`);
135
+ (0, recovery_admission_1.assertRecoveryAdmission)(store, budget);
136
+ const result = yield (0, lance_cleanup_1.pruneVersions)(runtime, table, request.version, request.cutoff, {
137
+ lease,
138
+ signal: controller.signal,
139
+ acknowledgeUncertain: request.acknowledgeUncertain,
140
+ });
141
+ return {
142
+ outcome: "verified",
143
+ table,
144
+ state: (0, prune_state_1.readPruneState)(store),
145
+ result,
146
+ allocatedBytesRecovered: result.allocatedBytesBefore - result.allocatedBytesAfter,
147
+ filesystemFreeBytesChange: result.freeBytesAfter - result.freeBytesBefore,
148
+ fullTableRewriting: "disabled",
149
+ };
150
+ }
151
+ catch (error) {
152
+ if (error instanceof store_lease_1.StoreLeaseTimeoutError)
153
+ throw new Error(`Recovery refused: store owners are live or unknown (${error.blockers.map((owner) => `${owner.role} pid=${owner.pid}`).join(", ") || "unverified owner"}); close them separately`);
154
+ throw error;
155
+ }
156
+ finally {
157
+ clearInterval(monitor);
158
+ (_d = request.signal) === null || _d === void 0 ? void 0 : _d.removeEventListener("abort", abort);
159
+ setup === null || setup === void 0 ? void 0 : setup.release();
160
+ yield lease === null || lease === void 0 ? void 0 : lease.release();
161
+ }
162
+ });
163
+ }
@@ -108,6 +108,14 @@ const defaultProbeDeps = {
108
108
  }).trim(),
109
109
  };
110
110
  function defaultProbeOwner(owner, deps = defaultProbeDeps) {
111
+ const parent = probeProcessOwner(owner, deps);
112
+ if ((parent === "dead" || parent === "reused") && owner.activeHelper) {
113
+ const child = probeProcessOwner(Object.assign(Object.assign({}, owner), { pid: owner.activeHelper.pid, processStart: owner.activeHelper.processStart }), deps);
114
+ return child === "same" || child === "unknown" ? child : parent;
115
+ }
116
+ return parent;
117
+ }
118
+ function probeProcessOwner(owner, deps) {
111
119
  try {
112
120
  deps.signal(owner.pid);
113
121
  }
@@ -124,6 +132,8 @@ function defaultProbeOwner(owner, deps = defaultProbeDeps) {
124
132
  if (code !== "EPERM")
125
133
  return "unknown";
126
134
  }
135
+ if (owner.processStart === "unverified")
136
+ return "unknown";
127
137
  if (owner.processStart.startsWith(`pid:${owner.pid}:started:`))
128
138
  return "same";
129
139
  try {
@@ -138,6 +148,7 @@ function defaultProbeOwner(owner, deps = defaultProbeDeps) {
138
148
  }
139
149
  }
140
150
  function readOwner(filePath) {
151
+ var _a;
141
152
  try {
142
153
  const parsed = JSON.parse(fs.readFileSync(filePath, "utf8"));
143
154
  if (!parsed || typeof parsed !== "object")
@@ -147,18 +158,40 @@ function readOwner(filePath) {
147
158
  typeof owner.processStart === "string" &&
148
159
  typeof owner.nonce === "string" &&
149
160
  typeof owner.role === "string" &&
150
- typeof owner.acquiredAt === "number"
161
+ typeof owner.acquiredAt === "number" &&
162
+ (owner.activeHelper === undefined ||
163
+ (Number.isSafeInteger((_a = owner.activeHelper) === null || _a === void 0 ? void 0 : _a.pid) &&
164
+ owner.activeHelper.pid > 0 &&
165
+ typeof owner.activeHelper.processStart === "string" &&
166
+ !!owner.activeHelper.processStart))
151
167
  ? owner
152
168
  : null;
153
169
  }
154
- catch (_a) {
170
+ catch (_b) {
155
171
  return null;
156
172
  }
157
173
  }
158
- function writeOwnerAtomic(filePath, owner) {
174
+ function writeOwnerAtomic(filePath, owner, durable = false) {
159
175
  const temp = `${filePath}.${owner.nonce}.tmp`;
160
- fs.writeFileSync(temp, `${JSON.stringify(owner)}\n`, { flag: "wx" });
176
+ const fd = fs.openSync(temp, "wx", 0o600);
177
+ try {
178
+ fs.writeFileSync(fd, `${JSON.stringify(owner)}\n`);
179
+ if (durable)
180
+ fs.fsyncSync(fd);
181
+ }
182
+ finally {
183
+ fs.closeSync(fd);
184
+ }
161
185
  fs.renameSync(temp, filePath);
186
+ if (durable) {
187
+ const dir = fs.openSync(path.dirname(filePath), "r");
188
+ try {
189
+ fs.fsyncSync(dir);
190
+ }
191
+ finally {
192
+ fs.closeSync(dir);
193
+ }
194
+ }
162
195
  }
163
196
  function delay(ms, signal) {
164
197
  return __awaiter(this, void 0, void 0, function* () {
@@ -189,6 +222,7 @@ class StoreLease {
189
222
  this.storeDir = storeDir;
190
223
  this.released = false;
191
224
  this.claimedBy = null;
225
+ this.exclusiveUseRunning = false;
192
226
  }
193
227
  static acquireShared(options) {
194
228
  return __awaiter(this, void 0, void 0, function* () {
@@ -269,6 +303,9 @@ class StoreLease {
269
303
  }
270
304
  release() {
271
305
  return __awaiter(this, arguments, void 0, function* (options = {}) {
306
+ if (this.exclusiveUseRunning) {
307
+ throw new Error("Exclusive store operation must finish before releasing its lease");
308
+ }
272
309
  if (this.released)
273
310
  return;
274
311
  if (this.mode === "shared") {
@@ -306,7 +343,82 @@ class StoreLease {
306
343
  if (this.claimedBy === holder)
307
344
  this.claimedBy = null;
308
345
  }
346
+ /** Hold verified cross-process exclusion through a destructive helper and its
347
+ * verification. A local write gate is not a substitute for this ownership. */
348
+ withExclusiveUse(storeDir, operation) {
349
+ return __awaiter(this, void 0, void 0, function* () {
350
+ if (this.exclusiveUseRunning) {
351
+ throw new Error("An exclusive store operation is already running");
352
+ }
353
+ const verify = () => {
354
+ if (this.mode !== "exclusive" || this.released) {
355
+ throw new Error("Pruning requires an unreleased exclusive store lease");
356
+ }
357
+ if (fs.realpathSync(storeDir) !== fs.realpathSync(this.storeDir)) {
358
+ throw new Error("Exclusive store lease belongs to a different store");
359
+ }
360
+ const ownerStat = fs.lstatSync(this.paths.intentOwnerFile);
361
+ if (!ownerStat.isFile() || ownerStat.size > 16 * 1024) {
362
+ throw new Error("Exclusive store lease owner is unverified or oversized");
363
+ }
364
+ const current = readOwner(this.paths.intentOwnerFile);
365
+ if ((current === null || current === void 0 ? void 0 : current.nonce) !== this.owner.nonce ||
366
+ current.pid !== this.owner.pid ||
367
+ current.processStart !== this.owner.processStart) {
368
+ throw new Error("Exclusive store lease ownership could not be verified");
369
+ }
370
+ // acquireExclusive has already removed verified stale readers. Refuse any
371
+ // remaining marker rather than assuming ignored/unknown readers are safe.
372
+ if (fs
373
+ .readdirSync(this.paths.readersDir)
374
+ .some((name) => name.endsWith(".json"))) {
375
+ throw new Error("Pruning requires all shared store owners to close");
376
+ }
377
+ };
378
+ verify();
379
+ this.exclusiveUseRunning = true;
380
+ try {
381
+ const result = yield operation();
382
+ verify();
383
+ return result;
384
+ }
385
+ finally {
386
+ this.exclusiveUseRunning = false;
387
+ }
388
+ });
389
+ }
309
390
  /** Atomically replace this shared lease with an exclusive lease. */
391
+ pinExclusiveHelper(pid) {
392
+ if (!this.exclusiveUseRunning ||
393
+ this.owner.activeHelper ||
394
+ !Number.isSafeInteger(pid) ||
395
+ pid < 1)
396
+ throw new Error("Helper requires an active exclusive store operation");
397
+ const current = readOwner(this.paths.intentOwnerFile);
398
+ if ((current === null || current === void 0 ? void 0 : current.nonce) !== this.owner.nonce || current.activeHelper)
399
+ throw new Error("Exclusive store ownership changed before helper launch");
400
+ // Publish uncertainty first. A failed identity probe must retain exclusion
401
+ // until this child is confirmed dead, even if the parent is killed.
402
+ this.owner.activeHelper = { pid, processStart: "unverified" };
403
+ // The helper is the primary on-disk owner, so older readers also retain
404
+ // exclusion after parent death. It is blocked on stdin until this identity
405
+ // is durably verified; uncertainty here cannot admit native deletion.
406
+ writeOwnerAtomic(this.paths.intentOwnerFile, Object.assign(Object.assign({}, this.owner), { pid, processStart: "unverified" }), true);
407
+ const start = defaultProbeDeps.processStartOf(pid);
408
+ if (!start)
409
+ throw new Error("Prune helper identity unavailable");
410
+ this.owner.activeHelper.processStart = start;
411
+ writeOwnerAtomic(this.paths.intentOwnerFile, Object.assign(Object.assign({}, this.owner), { pid, processStart: start }), true);
412
+ return () => {
413
+ var _a;
414
+ const latest = readOwner(this.paths.intentOwnerFile);
415
+ if ((latest === null || latest === void 0 ? void 0 : latest.nonce) !== this.owner.nonce ||
416
+ ((_a = latest.activeHelper) === null || _a === void 0 ? void 0 : _a.pid) !== pid)
417
+ throw new Error("Exclusive store ownership changed during helper close");
418
+ delete this.owner.activeHelper;
419
+ writeOwnerAtomic(this.paths.intentOwnerFile, this.owner, true);
420
+ };
421
+ }
310
422
  upgrade() {
311
423
  return __awaiter(this, arguments, void 0, function* (options = {}) {
312
424
  if (this.mode !== "shared") {
@@ -328,6 +440,9 @@ class StoreLease {
328
440
  /** Replace this exclusive lease with a shared lease without an ownership gap. */
329
441
  downgrade() {
330
442
  return __awaiter(this, arguments, void 0, function* (options = {}) {
443
+ if (this.exclusiveUseRunning) {
444
+ throw new Error("Exclusive store operation must finish before downgrading its lease");
445
+ }
331
446
  if (this.mode !== "exclusive") {
332
447
  throw new Error("Only an exclusive store lease can be downgraded");
333
448
  }
@@ -132,7 +132,7 @@ function parseBatchFootprints(output) {
132
132
  function sampleHostResources(extraPids = [], overrides = {}, extraGroups = []) {
133
133
  var _a;
134
134
  var _b, _c;
135
- 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, run: (command, args, timeoutMs) => (0, node_child_process_1.execFileSync)(command, args, {
135
+ 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, sampleTimeoutMs: exports.HOST_SAMPLE_TIMEOUT_MS, kernelProbeTimeoutMs: 500, run: (command, args, timeoutMs) => (0, node_child_process_1.execFileSync)(command, args, {
136
136
  encoding: "utf8",
137
137
  timeout: timeoutMs,
138
138
  maxBuffer: 512 * 1024,
@@ -153,7 +153,7 @@ function sampleHostResources(extraPids = [], overrides = {}, extraGroups = []) {
153
153
  };
154
154
  if (deps.platform !== "darwin")
155
155
  return snapshot;
156
- const deadline = deps.monotonic() + exports.HOST_SAMPLE_TIMEOUT_MS;
156
+ const deadline = deps.monotonic() + deps.sampleTimeoutMs;
157
157
  const run = (command, args, maximumMs = 500) => {
158
158
  const remaining = Math.floor(deadline - deps.monotonic());
159
159
  if (remaining <= 0)
@@ -167,7 +167,7 @@ function sampleHostResources(extraPids = [], overrides = {}, extraGroups = []) {
167
167
  }
168
168
  catch (_d) { }
169
169
  try {
170
- const zone = (0, kernel_zone_1.parseZprintOutput)(run("zprint", ["-L", "data.kalloc.1024"]));
170
+ const zone = (0, kernel_zone_1.parseZprintOutput)(run("zprint", ["-L", "data.kalloc.1024"], deps.kernelProbeTimeoutMs));
171
171
  if (zone) {
172
172
  const bytes = zone.elements * zone.elementSize;
173
173
  if (Number.isSafeInteger(bytes)) {
@@ -75,7 +75,7 @@ function resolveResourceBudgetMb(raw = process.env.GMAX_RESOURCE_BUDGET_MB) {
75
75
  throw new ResourceAdmissionError("resource budget must be 256..6144 MiB");
76
76
  return n;
77
77
  }
78
- function assertResourceSnapshot(snapshot, chargedMb, budgetMb, now = Date.now()) {
78
+ function assertResourceSnapshot(snapshot, chargedMb, budgetMb, now = Date.now(), sampleMaxAgeMs = 5000) {
79
79
  if (snapshot.memoryPressure === "critical" ||
80
80
  snapshot.kernelPressure === "critical")
81
81
  throw new ResourceAdmissionError("critical host pressure; heavy work refused", true);
@@ -84,7 +84,7 @@ function assertResourceSnapshot(snapshot, chargedMb, budgetMb, now = Date.now())
84
84
  if (snapshot.platform !== "darwin")
85
85
  return;
86
86
  if (now < snapshot.at ||
87
- now - snapshot.at > 5000 ||
87
+ now - snapshot.at > sampleMaxAgeMs ||
88
88
  snapshot.completedAt < snapshot.at ||
89
89
  snapshot.completedAt > now)
90
90
  throw new ResourceAdmissionError("resource sample stale or clock changed; heavy work paused");
@@ -107,8 +107,8 @@ class ResourceBudget {
107
107
  constructor(overrides = {}) {
108
108
  this.deps = Object.assign({ root: path.join(config_1.PATHS.sharedRoot, "resource-budget"), platform: process.platform, pid: process.pid, now: Date.now, sample: (extraPids = []) => {
109
109
  const records = this.readRecords();
110
- return (0, host_resource_1.sampleHostResources)([...records.map((r) => r.pid), ...extraPids], {}, records.flatMap((r) => (r.groupPid ? [r.groupPid] : [])));
111
- }, signal: (pid) => process.kill(pid, 0), policy: host_guard_policy_1.hostGuardPolicy, criticalSample: host_guard_policy_1.sampleCriticalPressure, quarantine: autostart_1.daemonStartDeniedReason, latch: safety_latch_1.latchSafetyStop, processStart: () => (0, node_child_process_1.execFileSync)("ps", ["-p", String(process.pid), "-o", "lstart="], {
110
+ return (0, host_resource_1.sampleHostResources)([...records.map((r) => r.pid), ...extraPids], this.deps.samplerOverrides, records.flatMap((r) => (r.groupPid ? [r.groupPid] : [])));
111
+ }, 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: safety_latch_1.latchSafetyStop, processStart: () => (0, node_child_process_1.execFileSync)("ps", ["-p", String(process.pid), "-o", "lstart="], {
112
112
  encoding: "utf8",
113
113
  timeout: 500,
114
114
  maxBuffer: 1024,
@@ -250,10 +250,10 @@ class ResourceBudget {
250
250
  }
251
251
  checked(snapshot, records) {
252
252
  try {
253
- assertResourceSnapshot(snapshot, this.charge(snapshot, records), resolveResourceBudgetMb(), this.deps.now());
253
+ assertResourceSnapshot(snapshot, this.charge(snapshot, records), resolveResourceBudgetMb(), this.deps.now(), this.deps.sampleMaxAgeMs);
254
254
  const legacy = snapshot.processes.filter((p) => p.role === "mcp" &&
255
255
  !records.some((r) => r.pid === p.pid && r.kind === "client"));
256
- if (legacy.length)
256
+ if (legacy.length && this.deps.requireClientRegistration)
257
257
  throw new ResourceAdmissionError(`${legacy.length} MCP client(s) need to reconnect after upgrading; heavy work paused`);
258
258
  }
259
259
  catch (error) {