@cortexkit/pi-magic-context 0.49.0 → 0.49.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/auto-search-worker.js +3 -3
- package/dist/{embedding-backfill-b71dvfs8.js → embedding-backfill-scywarbn.js} +7 -7
- package/dist/embedding-worker-process.js +29 -0
- package/dist/embedding-worker.js +5 -5
- package/dist/index-0j0zqb4t.js +712 -0
- package/dist/{index-hwqb3s7f.js → index-0rasmwm8.js} +5 -5
- package/dist/{index-bejpsj10.js → index-27jh224p.js} +1 -1
- package/dist/{index-ehybv5nr.js → index-28vnkyy7.js} +1129 -2990
- package/dist/{index-r024r8da.js → index-4gfq5t09.js} +16 -1
- package/dist/{index-vf38qhee.js → index-81nthk8j.js} +44 -1
- package/dist/{index-yzcjxnt2.js → index-b2anqqad.js} +422 -6
- package/dist/{index-1evzceyw.js → index-cyrnv16r.js} +314 -1072
- package/dist/index-fqb1bddn.js +4947 -0
- package/dist/{index-041ex0cj.js → index-g7hndmqm.js} +20 -34
- package/dist/index-gc2749rx.js +1168 -0
- package/dist/{index-t7xfqtg6.js → index-p5zesr4e.js} +22 -718
- package/dist/{index-yarjn32j.js → index-r874cgmv.js} +38 -36
- package/dist/{index-3mgf68pn.js → index-rd53pk0h.js} +1 -1
- package/dist/{index-904reqtg.js → index-rdtgp48t.js} +6 -4
- package/dist/index.js +1159 -1126
- package/dist/{message-index-bounded-qz5dz2se.js → message-index-bounded-110sb4fm.js} +6 -5
- package/dist/migration-worker.js +6 -5
- package/dist/{project-embedding-registry-ky7wrkr1.js → project-embedding-registry-64t0mw8y.js} +6 -6
- package/dist/raw-ordinal-warmup-worker.js +1 -1
- package/dist/{read-session-chunk-24sd89tf.js → read-session-chunk-rvmc25n5.js} +3 -2
- package/dist/{search-jbh28nwy.js → search-7z3w8pmz.js} +15 -14
- package/dist/subagent-entry.js +17 -16
- package/package.json +3 -3
- package/dist/index-3rea6zd9.js +0 -2396
- package/dist/index-bgmv2kaf.js +0 -290
- /package/dist/{index-5prbx026.js → index-2pp04pmg.js} +0 -0
|
@@ -6,6 +6,21 @@ import {
|
|
|
6
6
|
logSlowWriteTransaction
|
|
7
7
|
} from "./index-0vrkp1jq.js";
|
|
8
8
|
|
|
9
|
+
// ../plugin/src/shared/project-directory-key.ts
|
|
10
|
+
import path from "node:path";
|
|
11
|
+
function projectDirectoryKey(directory) {
|
|
12
|
+
const slashed = directory.replaceAll("\\", "/").replace(/^\/\/\?\/UNC\//i, "//").replace(/^\/\/\?\//, "");
|
|
13
|
+
if (/^[a-z]:\//i.test(slashed) || slashed.startsWith("//")) {
|
|
14
|
+
return path.win32.normalize(slashed).replaceAll("\\", "/").replace(/\/$/, "").toLowerCase();
|
|
15
|
+
}
|
|
16
|
+
return path.resolve(directory);
|
|
17
|
+
}
|
|
18
|
+
function sameProjectDirectory(a, b) {
|
|
19
|
+
if (a.length === 0 || b.length === 0)
|
|
20
|
+
return false;
|
|
21
|
+
return projectDirectoryKey(a) === projectDirectoryKey(b);
|
|
22
|
+
}
|
|
23
|
+
|
|
9
24
|
// ../plugin/src/features/magic-context/message-fts-rowid-map.ts
|
|
10
25
|
import { createHash } from "node:crypto";
|
|
11
26
|
var MESSAGE_FTS_ROWID_MAP_BACKFILL_BATCH_SIZE = 100;
|
|
@@ -168,4 +183,4 @@ function deleteUnmappedMessageFtsRows(db, sessionIds) {
|
|
|
168
183
|
WHERE rowid > ? AND session_id IN (${placeholders})`).run(state.watermarkRowid, ...sessionIds);
|
|
169
184
|
}
|
|
170
185
|
|
|
171
|
-
export { recordMessageFtsRowid, recordIndexedMessageTime, startMessageFtsRowidMapBackfill, messageFtsOrdinalRangeIsMapped, deleteUnmappedMessageFtsRows };
|
|
186
|
+
export { projectDirectoryKey, sameProjectDirectory, recordMessageFtsRowid, recordIndexedMessageTime, startMessageFtsRowidMapBackfill, messageFtsOrdinalRangeIsMapped, deleteUnmappedMessageFtsRows };
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
chmodSync,
|
|
5
5
|
closeSync,
|
|
6
6
|
createWriteStream,
|
|
7
|
+
fsyncSync,
|
|
7
8
|
lstatSync,
|
|
8
9
|
mkdirSync,
|
|
9
10
|
openSync,
|
|
@@ -32,6 +33,14 @@ function ensureStorageDirectorySync(directory, forcePrivate = false, recursive =
|
|
|
32
33
|
...privateMode ? { mode: 448 } : {}
|
|
33
34
|
});
|
|
34
35
|
}
|
|
36
|
+
function ensurePrivateStorageDirectorySync(directory) {
|
|
37
|
+
ensureStorageDirectorySync(directory, true);
|
|
38
|
+
const stat = lstatSync(directory);
|
|
39
|
+
if (!stat.isDirectory() || stat.isSymbolicLink())
|
|
40
|
+
throw new Error("Private storage directory is not a real directory");
|
|
41
|
+
if (process.platform !== "win32")
|
|
42
|
+
chmodSync(directory, 448);
|
|
43
|
+
}
|
|
35
44
|
function writeStorageFileSync(filePath, content, options = {}) {
|
|
36
45
|
const { forcePrivate = false, ...writeOptions } = options;
|
|
37
46
|
const privateMode = forcePrivate || shouldEnforcePrivateStoragePermissions();
|
|
@@ -51,6 +60,35 @@ function writeStorageFileAtomicSync(filePath, content, forcePrivate = false) {
|
|
|
51
60
|
throw error;
|
|
52
61
|
}
|
|
53
62
|
}
|
|
63
|
+
function syncStorageDirectorySync(directory) {
|
|
64
|
+
const fd = openSync(directory, "r");
|
|
65
|
+
try {
|
|
66
|
+
fsyncSync(fd);
|
|
67
|
+
} finally {
|
|
68
|
+
closeSync(fd);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function writeStorageFileDurableAtomicSync(filePath, content, afterTempSync) {
|
|
72
|
+
const directory = dirname(filePath);
|
|
73
|
+
ensurePrivateStorageDirectorySync(directory);
|
|
74
|
+
const tempPath = `${filePath}.${process.pid}.${randomBytes(12).toString("hex")}.tmp`;
|
|
75
|
+
let fd;
|
|
76
|
+
try {
|
|
77
|
+
fd = openStorageFileSync(tempPath, "wx", true);
|
|
78
|
+
writeFileSync(fd, content, "utf8");
|
|
79
|
+
fsyncSync(fd);
|
|
80
|
+
closeSync(fd);
|
|
81
|
+
fd = undefined;
|
|
82
|
+
afterTempSync?.();
|
|
83
|
+
renameSync(tempPath, filePath);
|
|
84
|
+
syncStorageDirectorySync(directory);
|
|
85
|
+
syncStorageDirectorySync(dirname(directory));
|
|
86
|
+
} finally {
|
|
87
|
+
if (fd !== undefined)
|
|
88
|
+
closeSync(fd);
|
|
89
|
+
rmSync(tempPath, { force: true });
|
|
90
|
+
}
|
|
91
|
+
}
|
|
54
92
|
function copyStorageFileSync(sourcePath, destinationPath, forcePrivate = false) {
|
|
55
93
|
ensureStorageDirectorySync(dirname(destinationPath), forcePrivate);
|
|
56
94
|
const privateMode = forcePrivate || shouldEnforcePrivateStoragePermissions();
|
|
@@ -94,6 +132,11 @@ function copyStorageTreeSync(sourcePath, destinationPath, forcePrivate = false)
|
|
|
94
132
|
copyStorageTreeSync(join(sourcePath, entry), join(destinationPath, entry), forcePrivate);
|
|
95
133
|
}
|
|
96
134
|
}
|
|
135
|
+
function openStorageFileSync(filePath, flags, forcePrivate = false) {
|
|
136
|
+
ensureStorageDirectorySync(dirname(filePath), forcePrivate);
|
|
137
|
+
const privateMode = forcePrivate || shouldEnforcePrivateStoragePermissions();
|
|
138
|
+
return openSync(filePath, flags, privateMode ? 384 : 438);
|
|
139
|
+
}
|
|
97
140
|
async function writeStorageFileAsync(filePath, content, forcePrivate = false) {
|
|
98
141
|
const privateMode = forcePrivate || shouldEnforcePrivateStoragePermissions();
|
|
99
142
|
ensureStorageDirectorySync(dirname(filePath), privateMode);
|
|
@@ -147,4 +190,4 @@ function tightenStorageTreeSync(root) {
|
|
|
147
190
|
return { tightened, failures };
|
|
148
191
|
}
|
|
149
192
|
|
|
150
|
-
export { setStoragePrivatePermissionEnforcement, shouldEnforcePrivateStoragePermissions, ensureStorageDirectorySync, writeStorageFileSync, writeStorageFileAtomicSync, copyStorageFileSync, copyStorageTreeSync, writeStorageFileAsync, writeStorageFileHandleAsync, createStorageFileHandleAsync, tightenStorageTreeSync };
|
|
193
|
+
export { setStoragePrivatePermissionEnforcement, shouldEnforcePrivateStoragePermissions, ensureStorageDirectorySync, ensurePrivateStorageDirectorySync, writeStorageFileSync, writeStorageFileAtomicSync, syncStorageDirectorySync, writeStorageFileDurableAtomicSync, copyStorageFileSync, copyStorageTreeSync, writeStorageFileAsync, writeStorageFileHandleAsync, createStorageFileHandleAsync, tightenStorageTreeSync };
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getHarness2
|
|
3
3
|
} from "./index-0h2mkab2.js";
|
|
4
|
+
import {
|
|
5
|
+
getMagicContextStorageDir
|
|
6
|
+
} from "./index-x3dcnxh0.js";
|
|
7
|
+
import {
|
|
8
|
+
isPidAlive,
|
|
9
|
+
readProcessStartTime
|
|
10
|
+
} from "./index-0j0zqb4t.js";
|
|
4
11
|
import {
|
|
5
12
|
removeSystemReminders,
|
|
6
13
|
hasMeaningfulUserText2,
|
|
@@ -15,10 +22,10 @@ import {
|
|
|
15
22
|
recordIndexedMessageTime,
|
|
16
23
|
messageFtsOrdinalRangeIsMapped,
|
|
17
24
|
deleteUnmappedMessageFtsRows
|
|
18
|
-
} from "./index-
|
|
25
|
+
} from "./index-4gfq5t09.js";
|
|
19
26
|
import {
|
|
20
27
|
deleteChunkEmbedBackoffForSession
|
|
21
|
-
} from "./index-
|
|
28
|
+
} from "./index-27jh224p.js";
|
|
22
29
|
import {
|
|
23
30
|
closeQuietly,
|
|
24
31
|
TAG_TOMBSTONE_OWNER_PREFIX,
|
|
@@ -30,10 +37,417 @@ import {
|
|
|
30
37
|
} from "./index-mcgehvgw.js";
|
|
31
38
|
import {
|
|
32
39
|
cleanUserText2
|
|
33
|
-
} from "./index-
|
|
40
|
+
} from "./index-fqb1bddn.js";
|
|
34
41
|
import {
|
|
35
42
|
logSlowWriteTransaction
|
|
36
43
|
} from "./index-0vrkp1jq.js";
|
|
44
|
+
import {
|
|
45
|
+
ensurePrivateStorageDirectorySync,
|
|
46
|
+
writeStorageFileSync,
|
|
47
|
+
writeStorageFileAtomicSync,
|
|
48
|
+
syncStorageDirectorySync,
|
|
49
|
+
writeStorageFileDurableAtomicSync
|
|
50
|
+
} from "./index-81nthk8j.js";
|
|
51
|
+
|
|
52
|
+
// ../plugin/src/hooks/magic-context/lkg-recovery-fence.ts
|
|
53
|
+
import { createHash as createHash2, randomUUID as randomUUID2 } from "node:crypto";
|
|
54
|
+
import { lstatSync, readdirSync as readdirSync2, readFileSync as readFileSync2, rmSync as rmSync2 } from "node:fs";
|
|
55
|
+
import { basename, dirname, join as join2 } from "node:path";
|
|
56
|
+
|
|
57
|
+
// ../plugin/src/shared/bounded-session-map.ts
|
|
58
|
+
class BoundedSessionMap {
|
|
59
|
+
maxEntries;
|
|
60
|
+
store = new Map;
|
|
61
|
+
constructor(maxEntries) {
|
|
62
|
+
if (!Number.isFinite(maxEntries) || maxEntries < 1) {
|
|
63
|
+
throw new Error(`BoundedSessionMap: maxEntries must be >= 1, got ${maxEntries}`);
|
|
64
|
+
}
|
|
65
|
+
this.maxEntries = maxEntries;
|
|
66
|
+
}
|
|
67
|
+
get(sessionId) {
|
|
68
|
+
const value = this.store.get(sessionId);
|
|
69
|
+
if (value === undefined)
|
|
70
|
+
return;
|
|
71
|
+
this.store.delete(sessionId);
|
|
72
|
+
this.store.set(sessionId, value);
|
|
73
|
+
return value;
|
|
74
|
+
}
|
|
75
|
+
peek(sessionId) {
|
|
76
|
+
return this.store.get(sessionId);
|
|
77
|
+
}
|
|
78
|
+
has(sessionId) {
|
|
79
|
+
return this.store.has(sessionId);
|
|
80
|
+
}
|
|
81
|
+
set(sessionId, value) {
|
|
82
|
+
if (this.store.has(sessionId)) {
|
|
83
|
+
this.store.delete(sessionId);
|
|
84
|
+
} else if (this.store.size >= this.maxEntries) {
|
|
85
|
+
const oldest = this.store.keys().next().value;
|
|
86
|
+
if (oldest !== undefined)
|
|
87
|
+
this.store.delete(oldest);
|
|
88
|
+
}
|
|
89
|
+
this.store.set(sessionId, value);
|
|
90
|
+
}
|
|
91
|
+
delete(sessionId) {
|
|
92
|
+
return this.store.delete(sessionId);
|
|
93
|
+
}
|
|
94
|
+
clear() {
|
|
95
|
+
this.store.clear();
|
|
96
|
+
}
|
|
97
|
+
get size() {
|
|
98
|
+
return this.store.size;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// ../plugin/src/shared/storage-file-mutex.ts
|
|
103
|
+
import { execFileSync } from "node:child_process";
|
|
104
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
105
|
+
import { readdirSync, readFileSync, rmSync } from "node:fs";
|
|
106
|
+
import { join } from "node:path";
|
|
107
|
+
var ticketChecksum = (name, ticket) => createHash("sha256").update(JSON.stringify([name, ticket.choosing, ticket.number, ticket.birth])).digest("hex");
|
|
108
|
+
function processBirth(pid) {
|
|
109
|
+
if (process.platform === "linux") {
|
|
110
|
+
try {
|
|
111
|
+
const stat = readFileSync(`/proc/${pid}/stat`, "utf8");
|
|
112
|
+
const ticks = stat.slice(stat.lastIndexOf(")") + 2).trim().split(/\s+/)[19];
|
|
113
|
+
if (!ticks || !/^\d+$/.test(ticks))
|
|
114
|
+
return null;
|
|
115
|
+
return `${readFileSync("/proc/sys/kernel/random/boot_id", "utf8").trim()}:${ticks}`;
|
|
116
|
+
} catch {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (process.platform === "darwin") {
|
|
121
|
+
try {
|
|
122
|
+
return execFileSync("ps", ["-p", String(pid), "-o", "lstart="], {
|
|
123
|
+
encoding: "utf8",
|
|
124
|
+
timeout: 1000,
|
|
125
|
+
windowsHide: true,
|
|
126
|
+
env: { ...process.env, TZ: "UTC", LC_ALL: "C" },
|
|
127
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
128
|
+
}).trim() || null;
|
|
129
|
+
} catch {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
const start = readProcessStartTime(pid);
|
|
134
|
+
return start === null ? null : String(start);
|
|
135
|
+
}
|
|
136
|
+
var pause = new Int32Array(new SharedArrayBuffer(4));
|
|
137
|
+
function withStorageFileMutex(directory, work, timeoutMs = 1000) {
|
|
138
|
+
ensurePrivateStorageDirectorySync(directory);
|
|
139
|
+
const name = `${process.pid}-${randomUUID()}.lock`;
|
|
140
|
+
const ownPath = join(directory, name);
|
|
141
|
+
const birth = processBirth(process.pid);
|
|
142
|
+
const ticket = { choosing: true, number: 0, birth, checksum: "" };
|
|
143
|
+
const encodedTicket = () => JSON.stringify({ ...ticket, checksum: ticketChecksum(name, ticket) });
|
|
144
|
+
writeStorageFileSync(ownPath, encodedTicket(), { flag: "wx", forcePrivate: true });
|
|
145
|
+
const deadline = performance.now() + timeoutMs;
|
|
146
|
+
const peers = () => {
|
|
147
|
+
const found = [];
|
|
148
|
+
for (const other of readdirSync(directory)) {
|
|
149
|
+
if (other === name || !/^\d+-[\da-f-]+\.lock$/.test(other))
|
|
150
|
+
continue;
|
|
151
|
+
const pid = Number(other.split("-")[0]);
|
|
152
|
+
const otherPath = join(directory, other);
|
|
153
|
+
let value = null;
|
|
154
|
+
try {
|
|
155
|
+
const parsed = JSON.parse(readFileSync(otherPath, "utf8"));
|
|
156
|
+
if (typeof parsed.choosing === "boolean" && Number.isSafeInteger(parsed.number) && parsed.number >= 0 && (parsed.birth === null || typeof parsed.birth === "string") && parsed.checksum === ticketChecksum(other, parsed))
|
|
157
|
+
value = parsed;
|
|
158
|
+
} catch (error) {
|
|
159
|
+
if (error.code === "ENOENT")
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
const alive = isPidAlive(pid);
|
|
163
|
+
const actualBirth = alive === "alive" && value?.birth ? processBirth(pid) : null;
|
|
164
|
+
if (alive === "dead" || actualBirth !== null && value?.birth && actualBirth !== value.birth) {
|
|
165
|
+
rmSync(otherPath, { force: true });
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
found.push([other, value]);
|
|
169
|
+
}
|
|
170
|
+
return found;
|
|
171
|
+
};
|
|
172
|
+
try {
|
|
173
|
+
ticket.number = 1 + Math.max(0, ...peers().map(([, value]) => value?.number ?? 0));
|
|
174
|
+
ticket.choosing = false;
|
|
175
|
+
writeStorageFileAtomicSync(ownPath, encodedTicket(), true);
|
|
176
|
+
while (peers().some(([other, value]) => !value || value.choosing || value.number < ticket.number || value.number === ticket.number && other < name)) {
|
|
177
|
+
if (performance.now() >= deadline)
|
|
178
|
+
throw new Error("Storage safety publication lock is unavailable");
|
|
179
|
+
Atomics.wait(pause, 0, 0, 10);
|
|
180
|
+
}
|
|
181
|
+
const result = work();
|
|
182
|
+
if (result && typeof result.then === "function")
|
|
183
|
+
throw new TypeError("Storage publication mutex requires synchronous work");
|
|
184
|
+
return result;
|
|
185
|
+
} finally {
|
|
186
|
+
rmSync(ownPath, { force: true });
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// ../plugin/src/hooks/magic-context/lkg-recovery-fence.ts
|
|
191
|
+
class LkgRecoveryFenceError extends Error {
|
|
192
|
+
constructor(message) {
|
|
193
|
+
super(message);
|
|
194
|
+
this.name = "LkgRecoveryFenceError";
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
var platform = process.platform;
|
|
198
|
+
var observer;
|
|
199
|
+
var knownFences = new BoundedSessionMap(1000);
|
|
200
|
+
var ioObserver;
|
|
201
|
+
var cacheKey = (sessionId) => `${getMagicContextStorageDir()}\x00${getHarness2()}\x00${sessionId}`;
|
|
202
|
+
function remember(sessionId, value) {
|
|
203
|
+
knownFences.set(cacheKey(sessionId), value);
|
|
204
|
+
return value;
|
|
205
|
+
}
|
|
206
|
+
function known(sessionId) {
|
|
207
|
+
const prior = knownFences.get(cacheKey(sessionId));
|
|
208
|
+
if (prior)
|
|
209
|
+
return prior;
|
|
210
|
+
const record = read(sessionId);
|
|
211
|
+
if (record)
|
|
212
|
+
return remember(sessionId, { state: record.state, record });
|
|
213
|
+
try {
|
|
214
|
+
lstatSync(lkgRecoveryFencePath(sessionId));
|
|
215
|
+
} catch (error) {
|
|
216
|
+
if (error.code === "ENOENT")
|
|
217
|
+
return remember(sessionId, { state: "clean", record: null });
|
|
218
|
+
}
|
|
219
|
+
return remember(sessionId, { state: "invalid", record: null });
|
|
220
|
+
}
|
|
221
|
+
function clearLkgFenceProcessState(sessionId) {
|
|
222
|
+
if (sessionId)
|
|
223
|
+
knownFences.delete(cacheKey(sessionId));
|
|
224
|
+
else
|
|
225
|
+
knownFences.clear();
|
|
226
|
+
}
|
|
227
|
+
var durableFencesSupported = () => platform !== "win32";
|
|
228
|
+
var hash = (value) => createHash2("sha256").update(value).digest("hex");
|
|
229
|
+
function lkgRecoveryFencePath(sessionId) {
|
|
230
|
+
return join2(getMagicContextStorageDir(), "lkg-recovery-fences", `${hash(sessionId)}.json`);
|
|
231
|
+
}
|
|
232
|
+
function metadata(slot) {
|
|
233
|
+
return [
|
|
234
|
+
slot.inputIdSeq,
|
|
235
|
+
slot.inputContentDigests,
|
|
236
|
+
slot.inputContentSignatures ?? null,
|
|
237
|
+
slot.piOutputEntryIds ?? null,
|
|
238
|
+
slot.lastInputMessageId,
|
|
239
|
+
slot.modelKey,
|
|
240
|
+
slot.providerKey
|
|
241
|
+
];
|
|
242
|
+
}
|
|
243
|
+
function representationKey(slot) {
|
|
244
|
+
return createHash2("sha256").update(slot.jsonPrefix).update(JSON.stringify(metadata(slot))).digest("hex");
|
|
245
|
+
}
|
|
246
|
+
function lkgRecoverySlotKey(slot) {
|
|
247
|
+
return hash(JSON.stringify([
|
|
248
|
+
representationKey(slot),
|
|
249
|
+
slot.capturedAt,
|
|
250
|
+
slot.rowVersion ?? null,
|
|
251
|
+
slot.captureSequence ?? null
|
|
252
|
+
]));
|
|
253
|
+
}
|
|
254
|
+
function payload(record) {
|
|
255
|
+
return [
|
|
256
|
+
record.version,
|
|
257
|
+
record.sessionId,
|
|
258
|
+
record.host,
|
|
259
|
+
record.modelKey,
|
|
260
|
+
record.providerKey,
|
|
261
|
+
record.slotKey,
|
|
262
|
+
record.servedDigest,
|
|
263
|
+
record.revision,
|
|
264
|
+
record.state
|
|
265
|
+
];
|
|
266
|
+
}
|
|
267
|
+
function publishRecord(record) {
|
|
268
|
+
ioObserver?.("write");
|
|
269
|
+
try {
|
|
270
|
+
if (record.state === "clean")
|
|
271
|
+
observer?.("clean");
|
|
272
|
+
writeStorageFileDurableAtomicSync(lkgRecoveryFencePath(record.sessionId), JSON.stringify({ ...record, checksum: hash(JSON.stringify(payload(record))) }), () => observer?.("temp-synced"));
|
|
273
|
+
if (record.state === "dirty")
|
|
274
|
+
observer?.("dirty");
|
|
275
|
+
remember(record.sessionId, {
|
|
276
|
+
state: record.state,
|
|
277
|
+
record: { ...record, checksum: hash(JSON.stringify(payload(record))) }
|
|
278
|
+
});
|
|
279
|
+
} catch (error) {
|
|
280
|
+
const visible = read(record.sessionId);
|
|
281
|
+
if (record.state === "clean" && visible?.state === "clean" && visible.revision === record.revision && visible.slotKey === record.slotKey) {
|
|
282
|
+
const dirty = { ...record, state: "dirty" };
|
|
283
|
+
writeStorageFileDurableAtomicSync(lkgRecoveryFencePath(record.sessionId), JSON.stringify({ ...dirty, checksum: hash(JSON.stringify(payload(dirty))) }));
|
|
284
|
+
}
|
|
285
|
+
remember(record.sessionId, { state: "invalid", record: null });
|
|
286
|
+
throw error;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
function read(sessionId) {
|
|
290
|
+
ioObserver?.("read");
|
|
291
|
+
try {
|
|
292
|
+
const path = lkgRecoveryFencePath(sessionId);
|
|
293
|
+
const stat = lstatSync(path);
|
|
294
|
+
const directory = lstatSync(dirname(path));
|
|
295
|
+
if (!stat.isFile() || stat.size > 4096 || !directory.isDirectory() || directory.isSymbolicLink() || process.platform !== "win32" && ((stat.mode & 63) !== 0 || (directory.mode & 63) !== 0 || process.getuid && stat.uid !== process.getuid()))
|
|
296
|
+
return null;
|
|
297
|
+
const value = JSON.parse(readFileSync2(path, "utf8"));
|
|
298
|
+
if (value.version !== 1 || value.sessionId !== sessionId || value.host !== getHarness2() || value.modelKey !== null && typeof value.modelKey !== "string" || value.providerKey !== null && typeof value.providerKey !== "string" || !/^[a-f0-9]{64}$/.test(value.slotKey) || !/^[a-f0-9]{64}$/.test(value.servedDigest) || typeof value.revision !== "string" || !["clean", "dirty"].includes(value.state) || value.checksum !== hash(JSON.stringify(payload(value))))
|
|
299
|
+
return null;
|
|
300
|
+
return value;
|
|
301
|
+
} catch {
|
|
302
|
+
return null;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
function publish(sessionId, slot, state, revision) {
|
|
306
|
+
const record = {
|
|
307
|
+
version: 1,
|
|
308
|
+
sessionId,
|
|
309
|
+
host: getHarness2(),
|
|
310
|
+
modelKey: slot.modelKey,
|
|
311
|
+
providerKey: slot.providerKey,
|
|
312
|
+
slotKey: lkgRecoverySlotKey(slot),
|
|
313
|
+
servedDigest: hash(slot.jsonPrefix),
|
|
314
|
+
revision,
|
|
315
|
+
state
|
|
316
|
+
};
|
|
317
|
+
publishRecord(record);
|
|
318
|
+
}
|
|
319
|
+
function locked(sessionId, work) {
|
|
320
|
+
ioObserver?.("mutex");
|
|
321
|
+
return withStorageFileMutex(`${lkgRecoveryFencePath(sessionId)}.locks`, () => {
|
|
322
|
+
const path = lkgRecoveryFencePath(sessionId);
|
|
323
|
+
for (const name of readdirSync2(dirname(path))) {
|
|
324
|
+
if (name.startsWith(`${basename(path)}.`) && name.endsWith(".tmp"))
|
|
325
|
+
rmSync2(join2(dirname(path), name), { force: true });
|
|
326
|
+
}
|
|
327
|
+
return work();
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
function hasCleanLkgRecoveryFence(sessionId, _slot) {
|
|
331
|
+
if (!durableFencesSupported())
|
|
332
|
+
return false;
|
|
333
|
+
const state = known(sessionId);
|
|
334
|
+
return state.state === "clean";
|
|
335
|
+
}
|
|
336
|
+
function lkgFenceRequiresRebuild(sessionId, slot, processLocalProof) {
|
|
337
|
+
return !processLocalProof && (durableFencesSupported() ? !hasCleanLkgRecoveryFence(sessionId, slot) : true);
|
|
338
|
+
}
|
|
339
|
+
function lkgRecoveryFenceUnclean(sessionId) {
|
|
340
|
+
return durableFencesSupported() && known(sessionId).state !== "clean";
|
|
341
|
+
}
|
|
342
|
+
function ownsLkgDirtyFence(sessionId, revision) {
|
|
343
|
+
if (!durableFencesSupported())
|
|
344
|
+
return revision === "process-local";
|
|
345
|
+
const record = known(sessionId).record;
|
|
346
|
+
return revision !== undefined && record?.state === "dirty" && record.revision === revision;
|
|
347
|
+
}
|
|
348
|
+
function invalidateCleanLkgRecoveryFence(sessionId, load, ownRevision) {
|
|
349
|
+
if (!durableFencesSupported())
|
|
350
|
+
return "process-local";
|
|
351
|
+
const prior = known(sessionId);
|
|
352
|
+
if (prior.state === "dirty")
|
|
353
|
+
return prior.record?.revision === ownRevision ? ownRevision : undefined;
|
|
354
|
+
return locked(sessionId, () => {
|
|
355
|
+
const slot = load() ?? {
|
|
356
|
+
jsonPrefix: "",
|
|
357
|
+
inputIdSeq: [],
|
|
358
|
+
inputContentDigests: [],
|
|
359
|
+
lastInputMessageId: "",
|
|
360
|
+
modelKey: null,
|
|
361
|
+
providerKey: null,
|
|
362
|
+
capturedAt: 0
|
|
363
|
+
};
|
|
364
|
+
const record = read(sessionId);
|
|
365
|
+
if (record?.state !== "dirty") {
|
|
366
|
+
const revision = randomUUID2();
|
|
367
|
+
publish(sessionId, slot, "dirty", revision);
|
|
368
|
+
return revision;
|
|
369
|
+
}
|
|
370
|
+
return record?.state === "dirty" && record.revision === ownRevision ? ownRevision : undefined;
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
function markLkgRecoveryDirty(args) {
|
|
374
|
+
if (!durableFencesSupported()) {
|
|
375
|
+
if (!args.processLocalProof)
|
|
376
|
+
throw new LkgRecoveryFenceError("Restart replay requires a healthy rebuild on Windows");
|
|
377
|
+
return "process-local";
|
|
378
|
+
}
|
|
379
|
+
const state = known(args.sessionId);
|
|
380
|
+
if (state.state === "dirty" && args.processLocalProof && state.record && state.record.revision === args.ownDirtyRevision)
|
|
381
|
+
return state.record.revision;
|
|
382
|
+
if (state.state === "invalid")
|
|
383
|
+
throw new LkgRecoveryFenceError("No clean durable recovery certificate");
|
|
384
|
+
return locked(args.sessionId, () => {
|
|
385
|
+
const slot = args.slot;
|
|
386
|
+
const before = read(args.sessionId);
|
|
387
|
+
if (before?.state === "dirty") {
|
|
388
|
+
if (!args.processLocalProof || before.revision !== args.ownDirtyRevision)
|
|
389
|
+
throw new LkgRecoveryFenceError(`Recovery superseded this process's last-good request (local=${args.processLocalProof}, owned=${before.revision === args.ownDirtyRevision})`);
|
|
390
|
+
return before.revision;
|
|
391
|
+
}
|
|
392
|
+
if (before && before.state !== "clean")
|
|
393
|
+
throw new LkgRecoveryFenceError("No clean durable recovery certificate");
|
|
394
|
+
const revision = randomUUID2();
|
|
395
|
+
publish(args.sessionId, slot, "dirty", revision);
|
|
396
|
+
return revision;
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
function commitLkgWithRecoveryFence(args) {
|
|
400
|
+
if (!durableFencesSupported())
|
|
401
|
+
return args.commit();
|
|
402
|
+
const state = known(args.sessionId);
|
|
403
|
+
if (state.state === "clean" && !args.requiredRevision && !args.retainDirtyRevision) {
|
|
404
|
+
const committed = args.commit();
|
|
405
|
+
if (committed) {
|
|
406
|
+
observer?.("committed");
|
|
407
|
+
remember(args.sessionId, { state: "clean", record: null });
|
|
408
|
+
}
|
|
409
|
+
return committed;
|
|
410
|
+
}
|
|
411
|
+
if (state.state === "invalid" && !args.rebuild)
|
|
412
|
+
throw new LkgRecoveryFenceError("Unproven prior request requires a prefix rebuild");
|
|
413
|
+
return locked(args.sessionId, () => {
|
|
414
|
+
const before = read(args.sessionId);
|
|
415
|
+
if (args.requiredRevision && before?.revision !== args.requiredRevision)
|
|
416
|
+
throw new LkgRecoveryFenceError("A later publisher superseded the rollback snapshot");
|
|
417
|
+
const ownsDirty = before?.state === "dirty" && (before.revision === args.retainDirtyRevision || before.revision === args.ownDirtyRevision);
|
|
418
|
+
if (before?.state === "dirty" && !ownsDirty && !args.rebuild)
|
|
419
|
+
throw new LkgRecoveryFenceError("A concurrent recovery requires a declared prefix rebuild");
|
|
420
|
+
if (before?.state !== "dirty" && state.state === "invalid")
|
|
421
|
+
publish(args.sessionId, args.load() ?? args.slot, "dirty", randomUUID2());
|
|
422
|
+
if (!args.commit())
|
|
423
|
+
return false;
|
|
424
|
+
observer?.("committed");
|
|
425
|
+
const saved = args.load();
|
|
426
|
+
if (!saved || representationKey(saved) !== representationKey(args.slot))
|
|
427
|
+
throw new LkgRecoveryFenceError("Last-good commit did not match its certificate");
|
|
428
|
+
if (args.retainDirtyRevision || args.cleanDigest !== undefined && hash(saved.jsonPrefix) !== args.cleanDigest)
|
|
429
|
+
return true;
|
|
430
|
+
const dirty = read(args.sessionId);
|
|
431
|
+
if (dirty?.state === "dirty")
|
|
432
|
+
publish(args.sessionId, saved, "clean", dirty.revision);
|
|
433
|
+
else
|
|
434
|
+
remember(args.sessionId, { state: "clean", record: null });
|
|
435
|
+
return true;
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
function clearLkgRecoveryFence(sessionId, remove, shouldClear) {
|
|
439
|
+
if (!durableFencesSupported())
|
|
440
|
+
return remove?.();
|
|
441
|
+
return locked(sessionId, () => {
|
|
442
|
+
const result = remove?.();
|
|
443
|
+
if (shouldClear && !shouldClear(result))
|
|
444
|
+
return result;
|
|
445
|
+
rmSync2(lkgRecoveryFencePath(sessionId), { force: true });
|
|
446
|
+
syncStorageDirectorySync(dirname(lkgRecoveryFencePath(sessionId)));
|
|
447
|
+
clearLkgFenceProcessState(sessionId);
|
|
448
|
+
return result;
|
|
449
|
+
});
|
|
450
|
+
}
|
|
37
451
|
|
|
38
452
|
// ../plugin/src/features/magic-context/session-activity.ts
|
|
39
453
|
var PREFIX = "retrospective_activity:";
|
|
@@ -232,6 +646,8 @@ function deleteSessionScopedRows(db, sessionIds, harness, options = {}) {
|
|
|
232
646
|
retire.run(tagIdentityRevisionKey(id), tagRevisionKey(id), ...harnessTables.map(() => id), ...tagTombstoneOwnerRange(id));
|
|
233
647
|
}
|
|
234
648
|
}
|
|
649
|
+
for (const sessionId of deletableSessionIds)
|
|
650
|
+
clearLkgRecoveryFence(sessionId);
|
|
235
651
|
return deletableSessionIds.length;
|
|
236
652
|
}
|
|
237
653
|
|
|
@@ -259,7 +675,7 @@ function clearCompressionDepthRange(db, sessionId, startOrdinal, endOrdinal) {
|
|
|
259
675
|
}
|
|
260
676
|
|
|
261
677
|
// ../plugin/src/features/magic-context/message-index.ts
|
|
262
|
-
import { createHash } from "node:crypto";
|
|
678
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
263
679
|
var MESSAGE_HISTORY_ORPHAN_SWEEP_BATCH_SIZE = 200;
|
|
264
680
|
var MESSAGE_HISTORY_ORPHAN_SAFETY_AGE_MS = 24 * 60 * 60 * 1000;
|
|
265
681
|
var MESSAGE_HISTORY_ORPHAN_SWEEP_COOLDOWN_MS = 10 * 60 * 1000;
|
|
@@ -445,7 +861,7 @@ function toMessageIndexSource(message) {
|
|
|
445
861
|
role: message.role,
|
|
446
862
|
createdAt: message.createdAt,
|
|
447
863
|
sourceVersion: normalizeSourceVersion(message.version),
|
|
448
|
-
contentHash:
|
|
864
|
+
contentHash: createHash3("sha256").update(content).digest("hex"),
|
|
449
865
|
content
|
|
450
866
|
};
|
|
451
867
|
}
|
|
@@ -784,4 +1200,4 @@ function sweepOrphanedOpenCodeMessageIndexes(db, openReadableOpenCodeDb, options
|
|
|
784
1200
|
return withoutSqliteTransformPass2(() => withSqliteBackgroundWriter2(() => sweepOrphanedOpenCodeMessageIndexesInBackground(db, openReadableOpenCodeDb, options)));
|
|
785
1201
|
}
|
|
786
1202
|
|
|
787
|
-
export { readSessionActivity, observeSessionActivity, backfillSessionActivity, deleteSessionScopedRows, clearCompressionDepth, clearCompressionDepthRange, MESSAGE_INDEX_MAX_FTS_BYTES, messageIndexSkipKey, getMessageIndexSourceIdentity, isMessageIndexSourceCurrent, getLastIndexedOrdinal, getIndexedMessageCorpusSize, markMessageIndexDirty, getMessageIndexReconciliationStartOrdinal, isMessageIndexReconciledThrough, getIndexableContent, indexSingleMessage, indexMessagesAfterOrdinal, sweepOrphanedOpenCodeMessageIndexes };
|
|
1203
|
+
export { BoundedSessionMap, LkgRecoveryFenceError, clearLkgFenceProcessState, lkgFenceRequiresRebuild, lkgRecoveryFenceUnclean, ownsLkgDirtyFence, invalidateCleanLkgRecoveryFence, markLkgRecoveryDirty, commitLkgWithRecoveryFence, clearLkgRecoveryFence, readSessionActivity, observeSessionActivity, backfillSessionActivity, deleteSessionScopedRows, clearCompressionDepth, clearCompressionDepthRange, MESSAGE_INDEX_MAX_FTS_BYTES, messageIndexSkipKey, getMessageIndexSourceIdentity, isMessageIndexSourceCurrent, getLastIndexedOrdinal, getIndexedMessageCorpusSize, markMessageIndexDirty, getMessageIndexReconciliationStartOrdinal, isMessageIndexReconciledThrough, getIndexableContent, indexSingleMessage, indexMessagesAfterOrdinal, sweepOrphanedOpenCodeMessageIndexes };
|