@cortexkit/pi-magic-context 0.32.0 → 0.32.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/index.js +984 -479
- package/dist/subagent-entry.js +430 -229
- package/package.json +1 -1
package/dist/subagent-entry.js
CHANGED
|
@@ -8128,11 +8128,11 @@ function buildNodeSqliteDatabaseClass(DatabaseSync) {
|
|
|
8128
8128
|
}
|
|
8129
8129
|
transaction(fn) {
|
|
8130
8130
|
const self = this;
|
|
8131
|
-
const
|
|
8131
|
+
const execute = (mode, receiver, args) => {
|
|
8132
8132
|
const nested = self.isTransaction === true;
|
|
8133
|
-
self.exec(nested ? `SAVEPOINT ${SAVEPOINT}` :
|
|
8133
|
+
self.exec(nested ? `SAVEPOINT ${SAVEPOINT}` : `BEGIN${mode ? ` ${mode}` : ""}`);
|
|
8134
8134
|
try {
|
|
8135
|
-
const result = fn.apply(
|
|
8135
|
+
const result = fn.apply(receiver, args);
|
|
8136
8136
|
self.exec(nested ? `RELEASE ${SAVEPOINT}` : "COMMIT");
|
|
8137
8137
|
return result;
|
|
8138
8138
|
} catch (error) {
|
|
@@ -8145,6 +8145,21 @@ function buildNodeSqliteDatabaseClass(DatabaseSync) {
|
|
|
8145
8145
|
throw error;
|
|
8146
8146
|
}
|
|
8147
8147
|
};
|
|
8148
|
+
const wrapped = function(...args) {
|
|
8149
|
+
return execute("", this, args);
|
|
8150
|
+
};
|
|
8151
|
+
wrapped.default = function(...args) {
|
|
8152
|
+
return execute("", this, args);
|
|
8153
|
+
};
|
|
8154
|
+
wrapped.deferred = function(...args) {
|
|
8155
|
+
return execute("DEFERRED", this, args);
|
|
8156
|
+
};
|
|
8157
|
+
wrapped.immediate = function(...args) {
|
|
8158
|
+
return execute("IMMEDIATE", this, args);
|
|
8159
|
+
};
|
|
8160
|
+
wrapped.exclusive = function(...args) {
|
|
8161
|
+
return execute("EXCLUSIVE", this, args);
|
|
8162
|
+
};
|
|
8148
8163
|
return wrapped;
|
|
8149
8164
|
}
|
|
8150
8165
|
}
|
|
@@ -8188,10 +8203,16 @@ function healAllNullColumns(db) {
|
|
|
8188
8203
|
healNullIntegerColumns(db);
|
|
8189
8204
|
healMissingMemoryBlockIds(db);
|
|
8190
8205
|
}
|
|
8206
|
+
function getSessionMetaColumns(db) {
|
|
8207
|
+
const rows = db.prepare("PRAGMA table_info(session_meta)").all();
|
|
8208
|
+
return new Set(rows.flatMap((row) => typeof row.name === "string" ? [row.name] : []));
|
|
8209
|
+
}
|
|
8191
8210
|
function healMissingMemoryBlockIds(db) {
|
|
8192
|
-
|
|
8193
|
-
|
|
8194
|
-
|
|
8211
|
+
const columns = getSessionMetaColumns(db);
|
|
8212
|
+
if (!columns.has("memory_block_cache") || !columns.has("memory_block_ids") || !columns.has("memory_block_count")) {
|
|
8213
|
+
return;
|
|
8214
|
+
}
|
|
8215
|
+
db.prepare("UPDATE session_meta SET memory_block_cache = '' WHERE memory_block_cache != '' AND (memory_block_ids IS NULL OR memory_block_ids = '') AND memory_block_count > 0").run();
|
|
8195
8216
|
}
|
|
8196
8217
|
function healNullTextColumns(db) {
|
|
8197
8218
|
const columns = [
|
|
@@ -8220,10 +8241,11 @@ function healNullTextColumns(db) {
|
|
|
8220
8241
|
["compaction_marker_state", ""],
|
|
8221
8242
|
["key_files", ""]
|
|
8222
8243
|
];
|
|
8244
|
+
const existingColumns = getSessionMetaColumns(db);
|
|
8223
8245
|
for (const [column, fallback] of columns) {
|
|
8224
|
-
|
|
8225
|
-
|
|
8226
|
-
}
|
|
8246
|
+
if (!existingColumns.has(column))
|
|
8247
|
+
continue;
|
|
8248
|
+
db.prepare(`UPDATE session_meta SET ${column} = ? WHERE ${column} IS NULL`).run(fallback);
|
|
8227
8249
|
}
|
|
8228
8250
|
}
|
|
8229
8251
|
function healNullIntegerColumns(db) {
|
|
@@ -8254,10 +8276,11 @@ function healNullIntegerColumns(db) {
|
|
|
8254
8276
|
["emergency_drain_active", 0],
|
|
8255
8277
|
["historian_drain_failure_at", 0]
|
|
8256
8278
|
];
|
|
8279
|
+
const existingColumns = getSessionMetaColumns(db);
|
|
8257
8280
|
for (const [column, fallback] of columns) {
|
|
8258
|
-
|
|
8259
|
-
|
|
8260
|
-
}
|
|
8281
|
+
if (!existingColumns.has(column))
|
|
8282
|
+
continue;
|
|
8283
|
+
db.prepare(`UPDATE session_meta SET ${column} = ? WHERE ${column} IS NULL`).run(fallback);
|
|
8261
8284
|
}
|
|
8262
8285
|
}
|
|
8263
8286
|
|
|
@@ -10209,6 +10232,34 @@ var MIGRATIONS = [
|
|
|
10209
10232
|
ensureColumn(db, "session_meta", "wrapup_in_progress_state", "TEXT");
|
|
10210
10233
|
}
|
|
10211
10234
|
}
|
|
10235
|
+
},
|
|
10236
|
+
{
|
|
10237
|
+
version: 51,
|
|
10238
|
+
description: "version tool-owner backfill state and repair legacy NULL session metadata",
|
|
10239
|
+
up(db) {
|
|
10240
|
+
db.exec(`
|
|
10241
|
+
CREATE TABLE IF NOT EXISTS tool_owner_backfill_state (
|
|
10242
|
+
session_id TEXT PRIMARY KEY,
|
|
10243
|
+
status TEXT NOT NULL CHECK (status IN ('pending', 'running', 'completed', 'skipped')),
|
|
10244
|
+
started_at INTEGER,
|
|
10245
|
+
lease_expires_at INTEGER,
|
|
10246
|
+
completed_at INTEGER,
|
|
10247
|
+
last_error TEXT
|
|
10248
|
+
);
|
|
10249
|
+
CREATE INDEX IF NOT EXISTS idx_tool_owner_backfill_state_status
|
|
10250
|
+
ON tool_owner_backfill_state(status);
|
|
10251
|
+
`);
|
|
10252
|
+
healAllNullColumns(db);
|
|
10253
|
+
}
|
|
10254
|
+
},
|
|
10255
|
+
{
|
|
10256
|
+
version: 52,
|
|
10257
|
+
description: "persist emergency recovery origin",
|
|
10258
|
+
up(db) {
|
|
10259
|
+
if (tableExists2(db, "session_meta")) {
|
|
10260
|
+
ensureColumn(db, "session_meta", "emergency_recovery_origin", "TEXT DEFAULT ''");
|
|
10261
|
+
}
|
|
10262
|
+
}
|
|
10212
10263
|
}
|
|
10213
10264
|
];
|
|
10214
10265
|
var LATEST_MIGRATION_VERSION = MIGRATIONS.reduce((max, m) => Math.max(max, m.version), 0);
|
|
@@ -10238,40 +10289,45 @@ function isSiblingMigrationConflict(db, error, version) {
|
|
|
10238
10289
|
}
|
|
10239
10290
|
function runMigrations(db) {
|
|
10240
10291
|
ensureMigrationsTable(db);
|
|
10241
|
-
let
|
|
10242
|
-
|
|
10243
|
-
|
|
10244
|
-
|
|
10245
|
-
}
|
|
10246
|
-
log(`[migrations] current schema version: ${currentVersion}, applying ${pendingMigrations.length} migration(s)`);
|
|
10247
|
-
let migrationIndex = 0;
|
|
10248
|
-
while (migrationIndex < pendingMigrations.length) {
|
|
10249
|
-
const migration = pendingMigrations[migrationIndex];
|
|
10292
|
+
let loggedPlan = false;
|
|
10293
|
+
while (true) {
|
|
10294
|
+
let migration;
|
|
10295
|
+
let currentVersion = 0;
|
|
10250
10296
|
try {
|
|
10251
|
-
db.transaction(() => {
|
|
10297
|
+
const applied = db.transaction(() => {
|
|
10298
|
+
currentVersion = getCurrentVersion(db);
|
|
10299
|
+
migration = MIGRATIONS.find((candidate) => candidate.version > currentVersion);
|
|
10300
|
+
if (!migration)
|
|
10301
|
+
return false;
|
|
10302
|
+
if (!loggedPlan) {
|
|
10303
|
+
const pendingCount = MIGRATIONS.filter((candidate) => candidate.version > currentVersion).length;
|
|
10304
|
+
log(`[migrations] current schema version: ${currentVersion}, applying ${pendingCount} migration(s)`);
|
|
10305
|
+
loggedPlan = true;
|
|
10306
|
+
}
|
|
10252
10307
|
migration.up(db);
|
|
10253
10308
|
db.prepare("INSERT INTO schema_migrations (version, description, applied_at) VALUES (?, ?, ?)").run(migration.version, migration.description, Date.now());
|
|
10254
|
-
|
|
10309
|
+
return true;
|
|
10310
|
+
}).immediate();
|
|
10311
|
+
if (!applied || !migration)
|
|
10312
|
+
break;
|
|
10255
10313
|
log(`[migrations] applied v${migration.version}: ${migration.description}`);
|
|
10256
|
-
migrationIndex += 1;
|
|
10257
10314
|
} catch (error) {
|
|
10258
|
-
if (isSiblingMigrationConflict(db, error, migration.version)) {
|
|
10315
|
+
if (migration && isSiblingMigrationConflict(db, error, migration.version)) {
|
|
10259
10316
|
log(`[migrations] v${migration.version} already applied by sibling instance — resuming with re-read version`);
|
|
10260
10317
|
const reReadVersion = getCurrentVersion(db);
|
|
10261
|
-
if (reReadVersion
|
|
10262
|
-
|
|
10263
|
-
|
|
10264
|
-
}
|
|
10265
|
-
currentVersion = reReadVersion;
|
|
10266
|
-
pendingMigrations = MIGRATIONS.filter((m) => m.version > currentVersion);
|
|
10267
|
-
migrationIndex = 0;
|
|
10268
|
-
continue;
|
|
10318
|
+
if (reReadVersion > currentVersion)
|
|
10319
|
+
continue;
|
|
10320
|
+
throw new Error(`Migration v${migration.version} failed: sibling conflict reported but version did not advance. Database may need manual repair.`);
|
|
10269
10321
|
}
|
|
10270
|
-
|
|
10271
|
-
|
|
10322
|
+
const version = migration?.version ?? currentVersion + 1;
|
|
10323
|
+
const description = migration?.description ?? "acquire migration write lock";
|
|
10324
|
+
log(`[migrations] FAILED v${version}: ${description} — ${error instanceof Error ? error.message : String(error)}`);
|
|
10325
|
+
throw new Error(`Migration v${version} failed: ${error instanceof Error ? error.message : String(error)}. Database may need manual repair.`);
|
|
10272
10326
|
}
|
|
10273
10327
|
}
|
|
10274
|
-
|
|
10328
|
+
if (loggedPlan) {
|
|
10329
|
+
log(`[migrations] schema version now: ${MIGRATIONS[MIGRATIONS.length - 1].version}`);
|
|
10330
|
+
}
|
|
10275
10331
|
}
|
|
10276
10332
|
|
|
10277
10333
|
// ../plugin/src/shared/commit-detection.ts
|
|
@@ -143025,7 +143081,8 @@ function countSessionsByStatus(db, status) {
|
|
|
143025
143081
|
function acquireSessionLease(db, sessionId, now) {
|
|
143026
143082
|
const expiresAt = now + LEASE_DURATION_MS;
|
|
143027
143083
|
const result = db.prepare(`INSERT INTO tool_owner_backfill_state(session_id, status, started_at, lease_expires_at)
|
|
143028
|
-
|
|
143084
|
+
SELECT ?, 'running', ?, ?
|
|
143085
|
+
WHERE EXISTS (SELECT 1 FROM tags WHERE session_id = ?)
|
|
143029
143086
|
ON CONFLICT(session_id) DO UPDATE SET
|
|
143030
143087
|
status = 'running',
|
|
143031
143088
|
started_at = excluded.started_at,
|
|
@@ -143033,7 +143090,7 @@ function acquireSessionLease(db, sessionId, now) {
|
|
|
143033
143090
|
last_error = NULL
|
|
143034
143091
|
WHERE tool_owner_backfill_state.status IN ('pending', 'skipped')
|
|
143035
143092
|
OR (tool_owner_backfill_state.status = 'running'
|
|
143036
|
-
AND tool_owner_backfill_state.lease_expires_at < ?)`).run(sessionId, now, expiresAt, now);
|
|
143093
|
+
AND tool_owner_backfill_state.lease_expires_at < ?)`).run(sessionId, now, expiresAt, sessionId, now);
|
|
143037
143094
|
return (result.changes ?? 0) === 1;
|
|
143038
143095
|
}
|
|
143039
143096
|
function renewSessionLease(db, sessionId, now) {
|
|
@@ -143053,13 +143110,9 @@ function markSessionPendingRetry(db, sessionId) {
|
|
|
143053
143110
|
WHERE session_id = ?`).run(sessionId);
|
|
143054
143111
|
}
|
|
143055
143112
|
function markSessionSkipped(db, sessionId, now, reason) {
|
|
143056
|
-
db.prepare(`
|
|
143057
|
-
|
|
143058
|
-
|
|
143059
|
-
status = 'skipped',
|
|
143060
|
-
completed_at = excluded.completed_at,
|
|
143061
|
-
last_error = excluded.last_error,
|
|
143062
|
-
lease_expires_at = NULL`).run(sessionId, now, reason);
|
|
143113
|
+
db.prepare(`UPDATE tool_owner_backfill_state
|
|
143114
|
+
SET status = 'skipped', completed_at = ?, last_error = ?, lease_expires_at = NULL
|
|
143115
|
+
WHERE session_id = ? AND status = 'running'`).run(now, reason, sessionId);
|
|
143063
143116
|
}
|
|
143064
143117
|
function markSessionErrored(db, sessionId, error) {
|
|
143065
143118
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -143143,7 +143196,7 @@ function applyOwnersForSession(db, sessionId, ownersByCallId) {
|
|
|
143143
143196
|
const result = updateRowStmt.run(ownerId, orphan.id);
|
|
143144
143197
|
rowsUpdated += result.changes ?? 0;
|
|
143145
143198
|
}
|
|
143146
|
-
})();
|
|
143199
|
+
}).immediate();
|
|
143147
143200
|
const rowsLeftNull = db.prepare(`SELECT COUNT(*) AS c FROM tags
|
|
143148
143201
|
WHERE session_id = ? AND type = 'tool'
|
|
143149
143202
|
AND tool_owner_message_id IS NULL`).get(sessionId).c;
|
|
@@ -143193,7 +143246,7 @@ var persistenceByDatabase = new WeakMap;
|
|
|
143193
143246
|
var persistenceErrorByDatabase = new WeakMap;
|
|
143194
143247
|
var pathByDatabase = new WeakMap;
|
|
143195
143248
|
var lastSchemaFenceRejection = null;
|
|
143196
|
-
var LATEST_SUPPORTED_VERSION =
|
|
143249
|
+
var LATEST_SUPPORTED_VERSION = 52;
|
|
143197
143250
|
var PERMISSIONS_ENFORCEABLE = process.platform !== "win32";
|
|
143198
143251
|
function ensureSecureStorageDir(dir) {
|
|
143199
143252
|
mkdirSync3(dir, { recursive: true, mode: 448 });
|
|
@@ -143886,6 +143939,17 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
143886
143939
|
pi_stable_id_scheme INTEGER
|
|
143887
143940
|
);
|
|
143888
143941
|
|
|
143942
|
+
CREATE TABLE IF NOT EXISTS tool_owner_backfill_state (
|
|
143943
|
+
session_id TEXT PRIMARY KEY,
|
|
143944
|
+
status TEXT NOT NULL CHECK (status IN ('pending', 'running', 'completed', 'skipped')),
|
|
143945
|
+
started_at INTEGER,
|
|
143946
|
+
lease_expires_at INTEGER,
|
|
143947
|
+
completed_at INTEGER,
|
|
143948
|
+
last_error TEXT
|
|
143949
|
+
);
|
|
143950
|
+
CREATE INDEX IF NOT EXISTS idx_tool_owner_backfill_state_status
|
|
143951
|
+
ON tool_owner_backfill_state(status);
|
|
143952
|
+
|
|
143889
143953
|
CREATE TABLE IF NOT EXISTS subagent_invocations (
|
|
143890
143954
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
143891
143955
|
session_id TEXT NOT NULL,
|
|
@@ -144119,6 +144183,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
144119
144183
|
ensureColumn(db, "session_meta", "detected_context_limit", "INTEGER DEFAULT 0");
|
|
144120
144184
|
ensureColumn(db, "session_meta", "detected_context_limit_model_key", "TEXT");
|
|
144121
144185
|
ensureColumn(db, "session_meta", "needs_emergency_recovery", "INTEGER DEFAULT 0");
|
|
144186
|
+
ensureColumn(db, "session_meta", "emergency_recovery_origin", "TEXT DEFAULT ''");
|
|
144122
144187
|
ensureColumn(db, "session_meta", "pending_compaction_marker_state", "TEXT");
|
|
144123
144188
|
ensureColumn(db, "session_meta", "pending_pi_compaction_marker_state", "TEXT");
|
|
144124
144189
|
ensureColumn(db, "session_meta", "new_work_tokens", "INTEGER NOT NULL DEFAULT 0");
|
|
@@ -159576,10 +159641,10 @@ function civilMinuteKey(date5) {
|
|
|
159576
159641
|
const p = (n, w = 2) => String(n).padStart(w, "0");
|
|
159577
159642
|
return `${p(date5.getFullYear(), 4)}-${p(date5.getMonth() + 1)}-${p(date5.getDate())} ${p(date5.getHours())}:${p(date5.getMinutes())}`;
|
|
159578
159643
|
}
|
|
159579
|
-
function nextOccurrence(cron, after, excludeCivilMinute) {
|
|
159644
|
+
function nextOccurrence(cron, after, excludeCivilMinute, maxSearchMs = MAX_SEARCH_MS) {
|
|
159580
159645
|
const afterMs = after.getTime();
|
|
159581
159646
|
let cursorMs = Math.floor(afterMs / MINUTE_MS) * MINUTE_MS + MINUTE_MS;
|
|
159582
|
-
const capMs = afterMs + MAX_SEARCH_MS;
|
|
159647
|
+
const capMs = afterMs + Math.max(0, Math.min(MAX_SEARCH_MS, maxSearchMs));
|
|
159583
159648
|
while (cursorMs <= capMs) {
|
|
159584
159649
|
const candidate = new Date(cursorMs);
|
|
159585
159650
|
if (matchesCron(cron, candidate)) {
|
|
@@ -159591,14 +159656,14 @@ function nextOccurrence(cron, after, excludeCivilMinute) {
|
|
|
159591
159656
|
}
|
|
159592
159657
|
return null;
|
|
159593
159658
|
}
|
|
159594
|
-
function nextDueAtMs(expression, afterMs, consumedScheduledAtMs) {
|
|
159659
|
+
function nextDueAtMs(expression, afterMs, consumedScheduledAtMs, maxSearchMs = MAX_SEARCH_MS) {
|
|
159595
159660
|
if (expression.trim().length === 0)
|
|
159596
159661
|
return null;
|
|
159597
159662
|
const parsed = parseCron(expression);
|
|
159598
159663
|
if (!parsed.ok)
|
|
159599
159664
|
return null;
|
|
159600
159665
|
const exclude = consumedScheduledAtMs !== undefined ? civilMinuteKey(new Date(consumedScheduledAtMs)) : undefined;
|
|
159601
|
-
const next = nextOccurrence(parsed.cron, new Date(afterMs), exclude);
|
|
159666
|
+
const next = nextOccurrence(parsed.cron, new Date(afterMs), exclude, maxSearchMs);
|
|
159602
159667
|
return next ? next.getTime() : null;
|
|
159603
159668
|
}
|
|
159604
159669
|
|
|
@@ -160842,22 +160907,6 @@ function getInsertEmbeddingStatement(db) {
|
|
|
160842
160907
|
}
|
|
160843
160908
|
return stmt;
|
|
160844
160909
|
}
|
|
160845
|
-
function getClearProjectStatement(db) {
|
|
160846
|
-
let stmt = clearProjectStatements.get(db);
|
|
160847
|
-
if (!stmt) {
|
|
160848
|
-
stmt = db.prepare("DELETE FROM compartment_chunk_embeddings WHERE project_path = ?");
|
|
160849
|
-
clearProjectStatements.set(db, stmt);
|
|
160850
|
-
}
|
|
160851
|
-
return stmt;
|
|
160852
|
-
}
|
|
160853
|
-
function getClearProjectModelStatement(db) {
|
|
160854
|
-
let stmt = clearProjectModelStatements.get(db);
|
|
160855
|
-
if (!stmt) {
|
|
160856
|
-
stmt = db.prepare("DELETE FROM compartment_chunk_embeddings WHERE project_path = ? AND model_id = ?");
|
|
160857
|
-
clearProjectModelStatements.set(db, stmt);
|
|
160858
|
-
}
|
|
160859
|
-
return stmt;
|
|
160860
|
-
}
|
|
160861
160910
|
function getSearchRowsStatement(db, withModel) {
|
|
160862
160911
|
const map2 = withModel ? searchRowsByModelStatements : searchRowsStatements;
|
|
160863
160912
|
let stmt = map2.get(db);
|
|
@@ -161161,12 +161210,6 @@ function replaceCompartmentChunkEmbeddings(db, rows) {
|
|
|
161161
161210
|
}
|
|
161162
161211
|
})();
|
|
161163
161212
|
}
|
|
161164
|
-
function clearChunkEmbeddingsForProject(db, projectPath, modelId) {
|
|
161165
|
-
if (modelId) {
|
|
161166
|
-
return getClearProjectModelStatement(db).run(projectPath, modelId).changes;
|
|
161167
|
-
}
|
|
161168
|
-
return getClearProjectStatement(db).run(projectPath).changes;
|
|
161169
|
-
}
|
|
161170
161213
|
function loadCompartmentChunkEmbeddingsForSearch(db, sessionId, projectPath, modelId) {
|
|
161171
161214
|
if (!modelId) {
|
|
161172
161215
|
throw new Error("loadCompartmentChunkEmbeddingsForSearch requires a current model id");
|
|
@@ -162091,22 +162134,6 @@ function getLoadAllEmbeddingsStatement(db) {
|
|
|
162091
162134
|
}
|
|
162092
162135
|
return stmt;
|
|
162093
162136
|
}
|
|
162094
|
-
function getClearAllEmbeddingsStatement(db) {
|
|
162095
|
-
let stmt = clearAllEmbeddingsStatements.get(db);
|
|
162096
|
-
if (!stmt) {
|
|
162097
|
-
stmt = db.prepare("DELETE FROM memory_embeddings WHERE memory_id IN (SELECT id FROM memories WHERE project_path = ?)");
|
|
162098
|
-
clearAllEmbeddingsStatements.set(db, stmt);
|
|
162099
|
-
}
|
|
162100
|
-
return stmt;
|
|
162101
|
-
}
|
|
162102
|
-
function getClearModelEmbeddingsStatement(db) {
|
|
162103
|
-
let stmt = clearModelEmbeddingsStatements.get(db);
|
|
162104
|
-
if (!stmt) {
|
|
162105
|
-
stmt = db.prepare("DELETE FROM memory_embeddings WHERE model_id = ? AND memory_id IN (SELECT id FROM memories WHERE project_path = ?)");
|
|
162106
|
-
clearModelEmbeddingsStatements.set(db, stmt);
|
|
162107
|
-
}
|
|
162108
|
-
return stmt;
|
|
162109
|
-
}
|
|
162110
162137
|
function saveEmbedding(db, memoryId, embedding, modelId) {
|
|
162111
162138
|
const blob = Buffer.from(embedding.buffer, embedding.byteOffset, embedding.byteLength);
|
|
162112
162139
|
getSaveEmbeddingStatement(db).run(memoryId, blob, modelId);
|
|
@@ -162126,12 +162153,6 @@ function loadAllEmbeddings(db, projectPath, modelId) {
|
|
|
162126
162153
|
}
|
|
162127
162154
|
return embeddings;
|
|
162128
162155
|
}
|
|
162129
|
-
function clearEmbeddingsForProject(db, projectPath, modelId) {
|
|
162130
|
-
if (modelId) {
|
|
162131
|
-
return getClearModelEmbeddingsStatement(db).run(modelId, projectPath).changes;
|
|
162132
|
-
}
|
|
162133
|
-
return getClearAllEmbeddingsStatement(db).run(projectPath).changes;
|
|
162134
|
-
}
|
|
162135
162156
|
function getMemoryEmbedCoverage(db, projectPath, modelId) {
|
|
162136
162157
|
const row = db.prepare(`SELECT
|
|
162137
162158
|
COUNT(*) AS total,
|
|
@@ -162204,24 +162225,6 @@ function getCountEmbeddedStatement(db) {
|
|
|
162204
162225
|
}
|
|
162205
162226
|
return stmt;
|
|
162206
162227
|
}
|
|
162207
|
-
function getClearProjectStatement2(db) {
|
|
162208
|
-
let stmt = clearProjectStatements2.get(db);
|
|
162209
|
-
if (!stmt) {
|
|
162210
|
-
stmt = db.prepare(`DELETE FROM git_commit_embeddings
|
|
162211
|
-
WHERE sha IN (SELECT sha FROM git_commits WHERE project_path = ?)`);
|
|
162212
|
-
clearProjectStatements2.set(db, stmt);
|
|
162213
|
-
}
|
|
162214
|
-
return stmt;
|
|
162215
|
-
}
|
|
162216
|
-
function getClearProjectModelStatement2(db) {
|
|
162217
|
-
let stmt = clearProjectModelStatements2.get(db);
|
|
162218
|
-
if (!stmt) {
|
|
162219
|
-
stmt = db.prepare(`DELETE FROM git_commit_embeddings
|
|
162220
|
-
WHERE model_id = ? AND sha IN (SELECT sha FROM git_commits WHERE project_path = ?)`);
|
|
162221
|
-
clearProjectModelStatements2.set(db, stmt);
|
|
162222
|
-
}
|
|
162223
|
-
return stmt;
|
|
162224
|
-
}
|
|
162225
162228
|
function saveCommitEmbedding(db, sha, embedding, modelId) {
|
|
162226
162229
|
const bytes = new Uint8Array(embedding.buffer, embedding.byteOffset, embedding.byteLength);
|
|
162227
162230
|
getSaveStatement(db).run(sha, bytes, modelId, Date.now());
|
|
@@ -162242,12 +162245,6 @@ function countEmbeddedCommits(db, projectPath, modelId) {
|
|
|
162242
162245
|
const row = getCountEmbeddedStatement(db).get(projectPath, modelId);
|
|
162243
162246
|
return row?.count ?? 0;
|
|
162244
162247
|
}
|
|
162245
|
-
function clearProjectCommitEmbeddings(db, projectPath, modelId) {
|
|
162246
|
-
if (modelId) {
|
|
162247
|
-
return getClearProjectModelStatement2(db).run(modelId, projectPath).changes;
|
|
162248
|
-
}
|
|
162249
|
-
return getClearProjectStatement2(db).run(projectPath).changes;
|
|
162250
|
-
}
|
|
162251
162248
|
|
|
162252
162249
|
// ../plugin/src/features/magic-context/git-commits/storage-git-commits.ts
|
|
162253
162250
|
init_logger();
|
|
@@ -162549,6 +162546,7 @@ function invalidateMemory(projectPath, memoryId) {
|
|
|
162549
162546
|
}
|
|
162550
162547
|
|
|
162551
162548
|
// ../plugin/src/features/magic-context/session-project-storage.ts
|
|
162549
|
+
var SESSION_CHUNK_REPAIR_BATCH_SIZE = 100;
|
|
162552
162550
|
var upsertSessionProjectStatements = new WeakMap;
|
|
162553
162551
|
var repairSessionChunkProjectStatements = new WeakMap;
|
|
162554
162552
|
var repairProjectChunkProjectStatements = new WeakMap;
|
|
@@ -162570,9 +162568,14 @@ function getRepairSessionChunkProjectStatement(db) {
|
|
|
162570
162568
|
if (!stmt) {
|
|
162571
162569
|
stmt = db.prepare(`UPDATE compartment_chunk_embeddings
|
|
162572
162570
|
SET project_path = ?
|
|
162573
|
-
WHERE
|
|
162574
|
-
|
|
162575
|
-
|
|
162571
|
+
WHERE id IN (
|
|
162572
|
+
SELECT id
|
|
162573
|
+
FROM compartment_chunk_embeddings
|
|
162574
|
+
WHERE session_id = ?
|
|
162575
|
+
AND harness = ?
|
|
162576
|
+
AND project_path <> ?
|
|
162577
|
+
LIMIT ?
|
|
162578
|
+
)`);
|
|
162576
162579
|
repairSessionChunkProjectStatements.set(db, stmt);
|
|
162577
162580
|
}
|
|
162578
162581
|
return stmt;
|
|
@@ -162610,7 +162613,7 @@ function recordSessionProjectIdentity(db, sessionId, projectPath) {
|
|
|
162610
162613
|
const now = Date.now();
|
|
162611
162614
|
db.transaction(() => {
|
|
162612
162615
|
getUpsertSessionProjectStatement(db).run(sessionId, harness, projectPath, now);
|
|
162613
|
-
getRepairSessionChunkProjectStatement(db).run(projectPath, sessionId, harness, projectPath);
|
|
162616
|
+
getRepairSessionChunkProjectStatement(db).run(projectPath, sessionId, harness, projectPath, SESSION_CHUNK_REPAIR_BATCH_SIZE);
|
|
162614
162617
|
})();
|
|
162615
162618
|
}
|
|
162616
162619
|
function repairMisScopedCompartmentChunkEmbeddingsForProject(db, projectPath) {
|
|
@@ -162626,6 +162629,7 @@ var COMMIT_DRAIN_BATCH_SIZE = 16;
|
|
|
162626
162629
|
var COMMIT_DRAIN_MAX_PER_SWEEP = 500;
|
|
162627
162630
|
var CHUNK_DRAIN_BATCH_SIZE = 8;
|
|
162628
162631
|
var EMBEDDING_IDENTITY_GC_GRACE_MS = 14 * 24 * 60 * 60 * 1000;
|
|
162632
|
+
var STALE_EMBEDDING_GC_BATCH_SIZE = 250;
|
|
162629
162633
|
var MAX_WINDOWS_PER_EMBED_CALL = 2;
|
|
162630
162634
|
var SESSION_EMBED_LEASE_RENEWAL_MS = 60 * 1000;
|
|
162631
162635
|
var EMBED_SLICE_RETRY_ATTEMPTS = 3;
|
|
@@ -162854,6 +162858,55 @@ function staleModelsForScope(db, projectIdentity, scope, currentModelId, cutoff)
|
|
|
162854
162858
|
const rows = getStaleIdentityStatement(db, scope).all(projectIdentity, scope, currentModelId, cutoff);
|
|
162855
162859
|
return rows.map((row) => row.modelId).filter((modelId) => typeof modelId === "string");
|
|
162856
162860
|
}
|
|
162861
|
+
function deleteStaleEmbeddingBatch(db, scope, projectIdentity, modelId, limit) {
|
|
162862
|
+
if (scope === "memory") {
|
|
162863
|
+
return db.prepare(`DELETE FROM memory_embeddings
|
|
162864
|
+
WHERE rowid IN (
|
|
162865
|
+
SELECT me.rowid
|
|
162866
|
+
FROM memory_embeddings me
|
|
162867
|
+
JOIN memories m ON m.id = me.memory_id
|
|
162868
|
+
WHERE m.project_path = ? AND me.model_id = ?
|
|
162869
|
+
LIMIT ?
|
|
162870
|
+
)`).run(projectIdentity, modelId, limit).changes;
|
|
162871
|
+
}
|
|
162872
|
+
if (scope === "commit") {
|
|
162873
|
+
return db.prepare(`DELETE FROM git_commit_embeddings
|
|
162874
|
+
WHERE rowid IN (
|
|
162875
|
+
SELECT gce.rowid
|
|
162876
|
+
FROM git_commit_embeddings gce
|
|
162877
|
+
JOIN git_commits gc ON gc.sha = gce.sha
|
|
162878
|
+
WHERE gc.project_path = ? AND gce.model_id = ?
|
|
162879
|
+
LIMIT ?
|
|
162880
|
+
)`).run(projectIdentity, modelId, limit).changes;
|
|
162881
|
+
}
|
|
162882
|
+
return db.prepare(`DELETE FROM compartment_chunk_embeddings
|
|
162883
|
+
WHERE id IN (
|
|
162884
|
+
SELECT id
|
|
162885
|
+
FROM compartment_chunk_embeddings
|
|
162886
|
+
WHERE project_path = ? AND model_id = ?
|
|
162887
|
+
LIMIT ?
|
|
162888
|
+
)`).run(projectIdentity, modelId, limit).changes;
|
|
162889
|
+
}
|
|
162890
|
+
function hasStaleEmbeddingRows(db, scope, projectIdentity, modelId) {
|
|
162891
|
+
if (scope === "memory") {
|
|
162892
|
+
return Boolean(db.prepare(`SELECT 1
|
|
162893
|
+
FROM memory_embeddings me
|
|
162894
|
+
JOIN memories m ON m.id = me.memory_id
|
|
162895
|
+
WHERE m.project_path = ? AND me.model_id = ?
|
|
162896
|
+
LIMIT 1`).get(projectIdentity, modelId));
|
|
162897
|
+
}
|
|
162898
|
+
if (scope === "commit") {
|
|
162899
|
+
return Boolean(db.prepare(`SELECT 1
|
|
162900
|
+
FROM git_commit_embeddings gce
|
|
162901
|
+
JOIN git_commits gc ON gc.sha = gce.sha
|
|
162902
|
+
WHERE gc.project_path = ? AND gce.model_id = ?
|
|
162903
|
+
LIMIT 1`).get(projectIdentity, modelId));
|
|
162904
|
+
}
|
|
162905
|
+
return Boolean(db.prepare(`SELECT 1
|
|
162906
|
+
FROM compartment_chunk_embeddings
|
|
162907
|
+
WHERE project_path = ? AND model_id = ?
|
|
162908
|
+
LIMIT 1`).get(projectIdentity, modelId));
|
|
162909
|
+
}
|
|
162857
162910
|
function sweepStaleEmbeddingIdentitiesForProject(db, projectIdentity, now = Date.now()) {
|
|
162858
162911
|
const snapshot = getProjectEmbeddingSnapshot(projectIdentity);
|
|
162859
162912
|
const result = {
|
|
@@ -162868,24 +162921,45 @@ function sweepStaleEmbeddingIdentitiesForProject(db, projectIdentity, now = Date
|
|
|
162868
162921
|
return result;
|
|
162869
162922
|
const cutoff = now - EMBEDDING_IDENTITY_GC_GRACE_MS;
|
|
162870
162923
|
const deleteTracking = getDeleteActiveIdentityStatement(db);
|
|
162924
|
+
const scopes = [
|
|
162925
|
+
{
|
|
162926
|
+
scope: "memory",
|
|
162927
|
+
enabled: snapshot.enabled && snapshot.modelId !== "off",
|
|
162928
|
+
currentModelId: snapshot.modelId
|
|
162929
|
+
},
|
|
162930
|
+
{
|
|
162931
|
+
scope: "commit",
|
|
162932
|
+
enabled: snapshot.gitCommitEnabled && snapshot.modelId !== "off",
|
|
162933
|
+
currentModelId: snapshot.modelId
|
|
162934
|
+
},
|
|
162935
|
+
{
|
|
162936
|
+
scope: "chunk",
|
|
162937
|
+
enabled: snapshot.enabled && snapshot.chunkModelId !== "off",
|
|
162938
|
+
currentModelId: snapshot.chunkModelId
|
|
162939
|
+
}
|
|
162940
|
+
];
|
|
162941
|
+
let remainingBudget = STALE_EMBEDDING_GC_BATCH_SIZE;
|
|
162871
162942
|
db.exec("BEGIN IMMEDIATE");
|
|
162872
162943
|
try {
|
|
162873
|
-
|
|
162874
|
-
|
|
162875
|
-
|
|
162876
|
-
|
|
162877
|
-
|
|
162878
|
-
|
|
162879
|
-
|
|
162880
|
-
|
|
162881
|
-
|
|
162882
|
-
|
|
162883
|
-
|
|
162884
|
-
|
|
162885
|
-
|
|
162886
|
-
|
|
162887
|
-
|
|
162888
|
-
|
|
162944
|
+
for (const { scope, enabled, currentModelId } of scopes) {
|
|
162945
|
+
if (!enabled || remainingBudget === 0)
|
|
162946
|
+
continue;
|
|
162947
|
+
for (const modelId of staleModelsForScope(db, projectIdentity, scope, currentModelId, cutoff)) {
|
|
162948
|
+
if (remainingBudget === 0)
|
|
162949
|
+
break;
|
|
162950
|
+
const deleted = deleteStaleEmbeddingBatch(db, scope, projectIdentity, modelId, remainingBudget);
|
|
162951
|
+
remainingBudget -= deleted;
|
|
162952
|
+
if (scope === "memory")
|
|
162953
|
+
result.memoryRowsDeleted += deleted;
|
|
162954
|
+
else if (scope === "commit")
|
|
162955
|
+
result.commitRowsDeleted += deleted;
|
|
162956
|
+
else
|
|
162957
|
+
result.chunkRowsDeleted += deleted;
|
|
162958
|
+
if (!hasStaleEmbeddingRows(db, scope, projectIdentity, modelId)) {
|
|
162959
|
+
result.trackingRowsDeleted += deleteTracking.run(projectIdentity, scope, modelId).changes;
|
|
162960
|
+
} else if (deleted === 0) {
|
|
162961
|
+
remainingBudget = 0;
|
|
162962
|
+
}
|
|
162889
162963
|
}
|
|
162890
162964
|
}
|
|
162891
162965
|
db.exec("COMMIT");
|
|
@@ -162895,9 +162969,8 @@ function sweepStaleEmbeddingIdentitiesForProject(db, projectIdentity, now = Date
|
|
|
162895
162969
|
} catch {}
|
|
162896
162970
|
throw error51;
|
|
162897
162971
|
}
|
|
162898
|
-
if (result.memoryRowsDeleted > 0)
|
|
162972
|
+
if (result.memoryRowsDeleted > 0)
|
|
162899
162973
|
invalidateProject(projectIdentity);
|
|
162900
|
-
}
|
|
162901
162974
|
return result;
|
|
162902
162975
|
}
|
|
162903
162976
|
function registerProjectEmbedding(db, projectIdentity, config2, features, sourceDirectory) {
|
|
@@ -163117,16 +163190,27 @@ async function drainCommitBacklogForProject(db, projectIdentity, deadline) {
|
|
|
163117
163190
|
return 0;
|
|
163118
163191
|
}
|
|
163119
163192
|
let total = 0;
|
|
163193
|
+
let leaseLost = false;
|
|
163194
|
+
const renewal = setInterval(() => {
|
|
163195
|
+
try {
|
|
163196
|
+
if (!renewGitSweepLease(db, projectIdentity, holderId))
|
|
163197
|
+
leaseLost = true;
|
|
163198
|
+
} catch {}
|
|
163199
|
+
}, SESSION_EMBED_LEASE_RENEWAL_MS);
|
|
163200
|
+
renewal.unref?.();
|
|
163120
163201
|
try {
|
|
163121
|
-
while (Date.now() < deadline && total < COMMIT_DRAIN_MAX_PER_SWEEP) {
|
|
163202
|
+
while (!leaseLost && Date.now() < deadline && total < COMMIT_DRAIN_MAX_PER_SWEEP) {
|
|
163122
163203
|
const embedded = await embedCommitBatch(db, projectIdentity, COMMIT_DRAIN_BATCH_SIZE);
|
|
163123
|
-
if (
|
|
163204
|
+
if (!renewGitSweepLease(db, projectIdentity, holderId))
|
|
163205
|
+
leaseLost = true;
|
|
163206
|
+
if (leaseLost || embedded === 0)
|
|
163124
163207
|
break;
|
|
163125
163208
|
total += embedded;
|
|
163126
163209
|
if (embedded < COMMIT_DRAIN_BATCH_SIZE)
|
|
163127
163210
|
break;
|
|
163128
163211
|
}
|
|
163129
163212
|
} finally {
|
|
163213
|
+
clearInterval(renewal);
|
|
163130
163214
|
releaseGitSweepLease(db, projectIdentity, holderId);
|
|
163131
163215
|
}
|
|
163132
163216
|
return total;
|
|
@@ -163237,9 +163321,19 @@ async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, opt
|
|
|
163237
163321
|
const lease = acquireGitSweepLease(db, projectIdentity, holderId, { ignoreCooldown: true });
|
|
163238
163322
|
if (!lease.acquired)
|
|
163239
163323
|
return { status: "busy", embedded: 0, total };
|
|
163324
|
+
let leaseLost = false;
|
|
163325
|
+
const drainAbort = new AbortController;
|
|
163326
|
+
const forwardCallerAbort = () => drainAbort.abort();
|
|
163327
|
+
if (options?.signal?.aborted)
|
|
163328
|
+
drainAbort.abort();
|
|
163329
|
+
else
|
|
163330
|
+
options?.signal?.addEventListener("abort", forwardCallerAbort, { once: true });
|
|
163240
163331
|
const renewal = setInterval(() => {
|
|
163241
163332
|
try {
|
|
163242
|
-
renewGitSweepLease(db, projectIdentity, holderId)
|
|
163333
|
+
if (!renewGitSweepLease(db, projectIdentity, holderId)) {
|
|
163334
|
+
leaseLost = true;
|
|
163335
|
+
drainAbort.abort();
|
|
163336
|
+
}
|
|
163243
163337
|
} catch {}
|
|
163244
163338
|
}, SESSION_EMBED_LEASE_RENEWAL_MS);
|
|
163245
163339
|
renewal.unref?.();
|
|
@@ -163253,7 +163347,7 @@ async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, opt
|
|
|
163253
163347
|
try {
|
|
163254
163348
|
options?.onProgress?.({ embedded, total });
|
|
163255
163349
|
for (;; ) {
|
|
163256
|
-
if (
|
|
163350
|
+
if (leaseLost || drainAbort.signal.aborted) {
|
|
163257
163351
|
aborted2 = true;
|
|
163258
163352
|
break;
|
|
163259
163353
|
}
|
|
@@ -163264,7 +163358,13 @@ async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, opt
|
|
|
163264
163358
|
embedded: n,
|
|
163265
163359
|
noWork,
|
|
163266
163360
|
failed
|
|
163267
|
-
} = await embedCandidateChunkBatch(db, projectIdentity, snapshot.chunkModelId, candidates,
|
|
163361
|
+
} = await embedCandidateChunkBatch(db, projectIdentity, snapshot.chunkModelId, candidates, drainAbort.signal);
|
|
163362
|
+
if (leaseLost || !renewGitSweepLease(db, projectIdentity, holderId)) {
|
|
163363
|
+
leaseLost = true;
|
|
163364
|
+
drainAbort.abort();
|
|
163365
|
+
aborted2 = true;
|
|
163366
|
+
break;
|
|
163367
|
+
}
|
|
163268
163368
|
for (const id of noWork)
|
|
163269
163369
|
skipIds.push(id);
|
|
163270
163370
|
for (const id of failed)
|
|
@@ -163284,6 +163384,7 @@ async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, opt
|
|
|
163284
163384
|
}
|
|
163285
163385
|
} finally {
|
|
163286
163386
|
clearInterval(renewal);
|
|
163387
|
+
options?.signal?.removeEventListener("abort", forwardCallerAbort);
|
|
163287
163388
|
try {
|
|
163288
163389
|
releaseGitSweepLease(db, projectIdentity, holderId);
|
|
163289
163390
|
} catch (error51) {
|
|
@@ -164199,6 +164300,66 @@ function readRawSessionMessagesFromDb(db, sessionId) {
|
|
|
164199
164300
|
};
|
|
164200
164301
|
});
|
|
164201
164302
|
}
|
|
164303
|
+
function readRawSessionMessagePageFromDb(db, sessionId, afterOrdinal, limit, finalWatermark = Number.MAX_SAFE_INTEGER) {
|
|
164304
|
+
const remaining = Math.max(0, Math.floor(finalWatermark) - Math.floor(afterOrdinal));
|
|
164305
|
+
const pageSize = Math.min(Math.max(1, Math.floor(limit)), remaining);
|
|
164306
|
+
if (pageSize === 0)
|
|
164307
|
+
return [];
|
|
164308
|
+
const messageRows = db.prepare(`SELECT id, data, time_created, time_updated
|
|
164309
|
+
FROM message
|
|
164310
|
+
WHERE session_id = ?
|
|
164311
|
+
AND NOT (
|
|
164312
|
+
CASE WHEN json_valid(data) = 1
|
|
164313
|
+
THEN COALESCE(json_extract(data, '$.summary'), 0)
|
|
164314
|
+
ELSE 0 END = 1
|
|
164315
|
+
AND CASE WHEN json_valid(data) = 1
|
|
164316
|
+
THEN COALESCE(json_extract(data, '$.finish'), '')
|
|
164317
|
+
ELSE '' END = 'stop'
|
|
164318
|
+
)
|
|
164319
|
+
ORDER BY time_created ASC, id ASC
|
|
164320
|
+
LIMIT ? OFFSET ?`).all(sessionId, pageSize, Math.max(0, Math.floor(afterOrdinal))).filter(isRawMessageRow).map((row, index) => ({
|
|
164321
|
+
...row,
|
|
164322
|
+
ordinal: Math.floor(afterOrdinal) + index + 1
|
|
164323
|
+
}));
|
|
164324
|
+
if (messageRows.length === 0)
|
|
164325
|
+
return [];
|
|
164326
|
+
const placeholders2 = messageRows.map(() => "?").join(", ");
|
|
164327
|
+
const partRows = db.prepare(`SELECT message_id, data, time_updated
|
|
164328
|
+
FROM part
|
|
164329
|
+
WHERE session_id = ? AND message_id IN (${placeholders2})
|
|
164330
|
+
ORDER BY time_created ASC, id ASC`).all(sessionId, ...messageRows.map((row) => row.id)).filter(isRawPartRow);
|
|
164331
|
+
const partsByMessageId = new Map;
|
|
164332
|
+
for (const part of partRows) {
|
|
164333
|
+
const list = partsByMessageId.get(part.message_id) ?? [];
|
|
164334
|
+
list.push(attachRawPartVersion(parseJsonUnknown(part.data), part.time_updated));
|
|
164335
|
+
partsByMessageId.set(part.message_id, list);
|
|
164336
|
+
}
|
|
164337
|
+
return messageRows.map((row) => {
|
|
164338
|
+
const info = parseJsonRecord(row.data);
|
|
164339
|
+
return {
|
|
164340
|
+
ordinal: row.ordinal,
|
|
164341
|
+
id: row.id,
|
|
164342
|
+
role: typeof info?.role === "string" ? info.role : "unknown",
|
|
164343
|
+
parts: partsByMessageId.get(row.id) ?? [],
|
|
164344
|
+
createdAt: row.time_created ?? null,
|
|
164345
|
+
version: row.time_updated ?? null
|
|
164346
|
+
};
|
|
164347
|
+
});
|
|
164348
|
+
}
|
|
164349
|
+
function countRawSessionMessageOrdinalsFromDb(db, sessionId) {
|
|
164350
|
+
const row = db.prepare(`SELECT COUNT(*) AS count
|
|
164351
|
+
FROM message
|
|
164352
|
+
WHERE session_id = ?
|
|
164353
|
+
AND NOT (
|
|
164354
|
+
CASE WHEN json_valid(data) = 1
|
|
164355
|
+
THEN COALESCE(json_extract(data, '$.summary'), 0)
|
|
164356
|
+
ELSE 0 END = 1
|
|
164357
|
+
AND CASE WHEN json_valid(data) = 1
|
|
164358
|
+
THEN COALESCE(json_extract(data, '$.finish'), '')
|
|
164359
|
+
ELSE '' END = 'stop'
|
|
164360
|
+
)`).get(sessionId);
|
|
164361
|
+
return typeof row?.count === "number" ? row.count : 0;
|
|
164362
|
+
}
|
|
164202
164363
|
function isAnchorRow(row) {
|
|
164203
164364
|
return row !== null && typeof row === "object" && typeof row.time_created === "number" && typeof row.id === "string";
|
|
164204
164365
|
}
|
|
@@ -164506,6 +164667,31 @@ function readRawSessionMessages(sessionId) {
|
|
|
164506
164667
|
}
|
|
164507
164668
|
return readRawSessionMessagesFromSource(sessionId);
|
|
164508
164669
|
}
|
|
164670
|
+
function readRawSessionMessagePage(sessionId, afterOrdinal, limit, finalWatermark) {
|
|
164671
|
+
const provider2 = sessionProviders.get(sessionId);
|
|
164672
|
+
if (provider2?.readMessagePage) {
|
|
164673
|
+
return provider2.readMessagePage(afterOrdinal, limit, finalWatermark);
|
|
164674
|
+
}
|
|
164675
|
+
if (provider2) {
|
|
164676
|
+
return provider2.readMessages().filter((message) => message.ordinal > afterOrdinal && message.ordinal <= finalWatermark).slice(0, limit);
|
|
164677
|
+
}
|
|
164678
|
+
if (!openCodeDbExists())
|
|
164679
|
+
return [];
|
|
164680
|
+
return withReadOnlySessionDb((db) => readRawSessionMessagePageFromDb(db, sessionId, afterOrdinal, limit, finalWatermark));
|
|
164681
|
+
}
|
|
164682
|
+
function getRawSessionMessageOrdinalCount(sessionId) {
|
|
164683
|
+
const provider2 = sessionProviders.get(sessionId);
|
|
164684
|
+
if (provider2) {
|
|
164685
|
+
if (provider2.getMessageCount)
|
|
164686
|
+
return provider2.getMessageCount();
|
|
164687
|
+
return provider2.readMessages().length;
|
|
164688
|
+
}
|
|
164689
|
+
if (!openCodeDbExists())
|
|
164690
|
+
return 0;
|
|
164691
|
+
return withReadOnlySessionDb((db) => countRawSessionMessageOrdinalsFromDb(db, sessionId));
|
|
164692
|
+
}
|
|
164693
|
+
readRawSessionMessages.readPage = readRawSessionMessagePage;
|
|
164694
|
+
readRawSessionMessages.getCount = getRawSessionMessageOrdinalCount;
|
|
164509
164695
|
function primeTailRawMessageCache(args) {
|
|
164510
164696
|
const { sessionId, lastCompartmentEnd, anchorMessageId } = args;
|
|
164511
164697
|
if (!activeRawMessageCache)
|
|
@@ -170663,11 +170849,10 @@ async function normalizeVerificationFiles(args) {
|
|
|
170663
170849
|
// ../plugin/src/features/magic-context/message-index.ts
|
|
170664
170850
|
var lastIndexedStatements = new WeakMap;
|
|
170665
170851
|
var insertMessageStatements = new WeakMap;
|
|
170666
|
-
var
|
|
170667
|
-
var upsertCleanIndexStatements = new WeakMap;
|
|
170852
|
+
var upsertProgressStatements = new WeakMap;
|
|
170668
170853
|
var upsertDirtyFloorStatements = new WeakMap;
|
|
170669
170854
|
var deleteFtsStatements = new WeakMap;
|
|
170670
|
-
var
|
|
170855
|
+
var deleteFtsRangeStatements = new WeakMap;
|
|
170671
170856
|
var deleteIndexStatements = new WeakMap;
|
|
170672
170857
|
var countIndexedMessageStatements = new WeakMap;
|
|
170673
170858
|
function normalizeIndexText(text) {
|
|
@@ -170689,19 +170874,11 @@ function getInsertMessageStatement(db) {
|
|
|
170689
170874
|
}
|
|
170690
170875
|
return stmt;
|
|
170691
170876
|
}
|
|
170692
|
-
function
|
|
170693
|
-
let stmt =
|
|
170694
|
-
if (!stmt) {
|
|
170695
|
-
stmt = db.prepare("INSERT INTO message_history_index (session_id, last_indexed_ordinal, updated_at, harness) VALUES (?, ?, ?, ?) ON CONFLICT(session_id) DO UPDATE SET last_indexed_ordinal = excluded.last_indexed_ordinal, updated_at = excluded.updated_at");
|
|
170696
|
-
upsertIndexStatements.set(db, stmt);
|
|
170697
|
-
}
|
|
170698
|
-
return stmt;
|
|
170699
|
-
}
|
|
170700
|
-
function getUpsertCleanIndexStatement(db) {
|
|
170701
|
-
let stmt = upsertCleanIndexStatements.get(db);
|
|
170877
|
+
function getUpsertProgressStatement(db) {
|
|
170878
|
+
let stmt = upsertProgressStatements.get(db);
|
|
170702
170879
|
if (!stmt) {
|
|
170703
|
-
stmt = db.prepare("INSERT INTO message_history_index (session_id, last_indexed_ordinal, dirty_floor_ordinal, updated_at, harness) VALUES (?, ?,
|
|
170704
|
-
|
|
170880
|
+
stmt = db.prepare("INSERT INTO message_history_index (session_id, last_indexed_ordinal, dirty_floor_ordinal, updated_at, harness) VALUES (?, ?, ?, ?, ?) ON CONFLICT(session_id) DO UPDATE SET last_indexed_ordinal = excluded.last_indexed_ordinal, dirty_floor_ordinal = excluded.dirty_floor_ordinal, updated_at = excluded.updated_at");
|
|
170881
|
+
upsertProgressStatements.set(db, stmt);
|
|
170705
170882
|
}
|
|
170706
170883
|
return stmt;
|
|
170707
170884
|
}
|
|
@@ -170713,27 +170890,11 @@ function getUpsertDirtyFloorStatement(db) {
|
|
|
170713
170890
|
}
|
|
170714
170891
|
return stmt;
|
|
170715
170892
|
}
|
|
170716
|
-
function
|
|
170717
|
-
let stmt =
|
|
170718
|
-
if (!stmt) {
|
|
170719
|
-
stmt = db.prepare("DELETE FROM message_history_fts WHERE session_id = ?");
|
|
170720
|
-
deleteFtsStatements.set(db, stmt);
|
|
170721
|
-
}
|
|
170722
|
-
return stmt;
|
|
170723
|
-
}
|
|
170724
|
-
function getDeleteFtsFromOrdinalStatement(db) {
|
|
170725
|
-
let stmt = deleteFtsFromOrdinalStatements.get(db);
|
|
170893
|
+
function getDeleteFtsRangeStatement(db) {
|
|
170894
|
+
let stmt = deleteFtsRangeStatements.get(db);
|
|
170726
170895
|
if (!stmt) {
|
|
170727
|
-
stmt = db.prepare("DELETE FROM message_history_fts WHERE session_id = ? AND CAST(message_ordinal AS INTEGER)
|
|
170728
|
-
|
|
170729
|
-
}
|
|
170730
|
-
return stmt;
|
|
170731
|
-
}
|
|
170732
|
-
function getDeleteIndexStatement(db) {
|
|
170733
|
-
let stmt = deleteIndexStatements.get(db);
|
|
170734
|
-
if (!stmt) {
|
|
170735
|
-
stmt = db.prepare("DELETE FROM message_history_index WHERE session_id = ?");
|
|
170736
|
-
deleteIndexStatements.set(db, stmt);
|
|
170896
|
+
stmt = db.prepare("DELETE FROM message_history_fts WHERE session_id = ? AND CAST(message_ordinal AS INTEGER) BETWEEN ? AND ?");
|
|
170897
|
+
deleteFtsRangeStatements.set(db, stmt);
|
|
170737
170898
|
}
|
|
170738
170899
|
return stmt;
|
|
170739
170900
|
}
|
|
@@ -170761,16 +170922,17 @@ function isMessageAlreadyIndexed(db, sessionId, messageId) {
|
|
|
170761
170922
|
const row = getCountIndexedMessageStatement(db).get(sessionId, messageId);
|
|
170762
170923
|
return (typeof row?.count === "number" ? row.count : 0) > 0;
|
|
170763
170924
|
}
|
|
170764
|
-
function
|
|
170765
|
-
|
|
170766
|
-
getUpsertIndexStatement(db).run(sessionId, Math.max(current, ordinal), now, getHarness());
|
|
170925
|
+
function setIndexProgress(db, sessionId, watermark, dirtyFloor, now) {
|
|
170926
|
+
getUpsertProgressStatement(db).run(sessionId, Math.max(0, Math.floor(watermark)), dirtyFloor ?? 0, now, getHarness());
|
|
170767
170927
|
}
|
|
170768
|
-
function
|
|
170769
|
-
db
|
|
170770
|
-
|
|
170771
|
-
|
|
170772
|
-
|
|
170773
|
-
|
|
170928
|
+
function getMessageIndexReconciliationStartOrdinal(db, sessionId) {
|
|
170929
|
+
const watermark = getLastIndexedOrdinal(db, sessionId);
|
|
170930
|
+
const dirtyFloor = getDirtyIndexFloor(db, sessionId);
|
|
170931
|
+
return dirtyFloor === null ? watermark : Math.min(watermark, dirtyFloor - 1);
|
|
170932
|
+
}
|
|
170933
|
+
function isMessageIndexReconciledThrough(db, sessionId, finalWatermark) {
|
|
170934
|
+
const dirtyFloor = getDirtyIndexFloor(db, sessionId);
|
|
170935
|
+
return getLastIndexedOrdinal(db, sessionId) >= finalWatermark && dirtyFloor === null;
|
|
170774
170936
|
}
|
|
170775
170937
|
function getIndexableContent(role, parts) {
|
|
170776
170938
|
if (role === "user") {
|
|
@@ -170785,22 +170947,21 @@ function getIndexableContent(role, parts) {
|
|
|
170785
170947
|
return "";
|
|
170786
170948
|
}
|
|
170787
170949
|
function indexSingleMessageInTransaction(db, sessionId, message, now) {
|
|
170788
|
-
|
|
170789
|
-
|
|
170790
|
-
|
|
170791
|
-
}
|
|
170792
|
-
const content = getIndexableContent(message.role, message.parts);
|
|
170793
|
-
if (content.length === 0) {
|
|
170794
|
-
advanceIndexWatermark(db, sessionId, message.ordinal, now);
|
|
170950
|
+
const currentWatermark = getLastIndexedOrdinal(db, sessionId);
|
|
170951
|
+
const dirtyFloor = getDirtyIndexFloor(db, sessionId);
|
|
170952
|
+
if (message.ordinal !== currentWatermark + 1 || dirtyFloor !== null && dirtyFloor !== message.ordinal) {
|
|
170795
170953
|
return false;
|
|
170796
170954
|
}
|
|
170797
|
-
|
|
170798
|
-
|
|
170799
|
-
|
|
170955
|
+
let inserted = false;
|
|
170956
|
+
if (message.role === "user" || message.role === "assistant") {
|
|
170957
|
+
const content = getIndexableContent(message.role, message.parts);
|
|
170958
|
+
if (content.length > 0 && !isMessageAlreadyIndexed(db, sessionId, message.id)) {
|
|
170959
|
+
getInsertMessageStatement(db).run(sessionId, message.ordinal, message.id, message.role, content);
|
|
170960
|
+
inserted = true;
|
|
170961
|
+
}
|
|
170800
170962
|
}
|
|
170801
|
-
|
|
170802
|
-
|
|
170803
|
-
return true;
|
|
170963
|
+
setIndexProgress(db, sessionId, message.ordinal, null, now);
|
|
170964
|
+
return inserted;
|
|
170804
170965
|
}
|
|
170805
170966
|
function indexSingleMessage(db, sessionId, message) {
|
|
170806
170967
|
db.exec("BEGIN IMMEDIATE");
|
|
@@ -170818,38 +170979,44 @@ function indexSingleMessage(db, sessionId, message) {
|
|
|
170818
170979
|
}
|
|
170819
170980
|
}
|
|
170820
170981
|
}
|
|
170821
|
-
function indexMessagesAfterOrdinal(db, sessionId, messages,
|
|
170982
|
+
function indexMessagesAfterOrdinal(db, sessionId, messages, _lastIndexedOrdinal, finalWatermark = messages.length) {
|
|
170822
170983
|
const now = Date.now();
|
|
170823
170984
|
let inserted = 0;
|
|
170824
170985
|
db.exec("BEGIN IMMEDIATE");
|
|
170825
170986
|
let committed = false;
|
|
170826
170987
|
try {
|
|
170827
|
-
|
|
170988
|
+
const currentWatermark = getLastIndexedOrdinal(db, sessionId);
|
|
170828
170989
|
const dirtyFloor = getDirtyIndexFloor(db, sessionId);
|
|
170829
|
-
|
|
170830
|
-
|
|
170831
|
-
|
|
170832
|
-
getDeleteFtsFromOrdinalStatement(db).run(sessionId, rewindOrdinal);
|
|
170833
|
-
}
|
|
170834
|
-
effectiveWatermark = Math.min(effectiveWatermark, rewindOrdinal - 1);
|
|
170990
|
+
const effectiveWatermark = dirtyFloor === null ? currentWatermark : Math.min(currentWatermark, Math.max(0, dirtyFloor - 1));
|
|
170991
|
+
if (dirtyFloor !== null && dirtyFloor <= finalWatermark) {
|
|
170992
|
+
getDeleteFtsRangeStatement(db).run(sessionId, dirtyFloor, finalWatermark);
|
|
170835
170993
|
}
|
|
170836
|
-
const
|
|
170994
|
+
const messagesByOrdinal = new Map;
|
|
170837
170995
|
for (const message of messages) {
|
|
170838
|
-
if (message.ordinal <=
|
|
170839
|
-
|
|
170996
|
+
if (message.ordinal > effectiveWatermark && message.ordinal <= finalWatermark) {
|
|
170997
|
+
messagesByOrdinal.set(message.ordinal, message);
|
|
170840
170998
|
}
|
|
170841
|
-
|
|
170999
|
+
}
|
|
171000
|
+
let coveredWatermark = effectiveWatermark;
|
|
171001
|
+
while (coveredWatermark < finalWatermark && messagesByOrdinal.has(coveredWatermark + 1)) {
|
|
171002
|
+
coveredWatermark += 1;
|
|
171003
|
+
}
|
|
171004
|
+
for (let ordinal = effectiveWatermark + 1;ordinal <= coveredWatermark; ordinal++) {
|
|
171005
|
+
const message = messagesByOrdinal.get(ordinal);
|
|
171006
|
+
if (!message || message.role !== "user" && message.role !== "assistant") {
|
|
170842
171007
|
continue;
|
|
170843
171008
|
}
|
|
170844
171009
|
const content = getIndexableContent(message.role, message.parts);
|
|
170845
|
-
if (content.length === 0) {
|
|
171010
|
+
if (content.length === 0 || isMessageAlreadyIndexed(db, sessionId, message.id)) {
|
|
170846
171011
|
continue;
|
|
170847
171012
|
}
|
|
170848
|
-
|
|
170849
|
-
inserted
|
|
171013
|
+
getInsertMessageStatement(db).run(sessionId, message.ordinal, message.id, message.role, content);
|
|
171014
|
+
inserted += 1;
|
|
170850
171015
|
}
|
|
170851
|
-
const
|
|
170852
|
-
|
|
171016
|
+
const missingFloor = coveredWatermark < finalWatermark ? coveredWatermark + 1 : null;
|
|
171017
|
+
const preservedFloor = dirtyFloor !== null && dirtyFloor > finalWatermark ? dirtyFloor : null;
|
|
171018
|
+
const nextDirtyFloor = missingFloor === null ? preservedFloor : preservedFloor === null ? missingFloor : Math.min(missingFloor, preservedFloor);
|
|
171019
|
+
setIndexProgress(db, sessionId, coveredWatermark, nextDirtyFloor, now);
|
|
170853
171020
|
db.exec("COMMIT");
|
|
170854
171021
|
committed = true;
|
|
170855
171022
|
} finally {
|
|
@@ -171990,13 +172157,17 @@ function clearHistorianFailureState(db, sessionId) {
|
|
|
171990
172157
|
function normalizeDetectedLimitModelKey(modelKey) {
|
|
171991
172158
|
return typeof modelKey === "string" && modelKey.length > 0 ? modelKey : null;
|
|
171992
172159
|
}
|
|
172160
|
+
function normalizeEmergencyRecoveryOrigin(value) {
|
|
172161
|
+
return value === "provider_overflow" || value === "proactive_model_shrink" ? value : null;
|
|
172162
|
+
}
|
|
171993
172163
|
function getOverflowState(db, sessionId, modelKey) {
|
|
171994
|
-
const result = db.prepare("SELECT detected_context_limit, detected_context_limit_model_key, needs_emergency_recovery FROM session_meta WHERE session_id = ?").get(sessionId);
|
|
172164
|
+
const result = db.prepare("SELECT detected_context_limit, detected_context_limit_model_key, needs_emergency_recovery, emergency_recovery_origin FROM session_meta WHERE session_id = ?").get(sessionId);
|
|
171995
172165
|
if (!result) {
|
|
171996
172166
|
return {
|
|
171997
172167
|
detectedContextLimit: 0,
|
|
171998
172168
|
detectedContextLimitModelKey: null,
|
|
171999
|
-
needsEmergencyRecovery: false
|
|
172169
|
+
needsEmergencyRecovery: false,
|
|
172170
|
+
emergencyRecoveryOrigin: null
|
|
172000
172171
|
};
|
|
172001
172172
|
}
|
|
172002
172173
|
const storedModelKey = normalizeDetectedLimitModelKey(result.detected_context_limit_model_key);
|
|
@@ -172004,19 +172175,22 @@ function getOverflowState(db, sessionId, modelKey) {
|
|
|
172004
172175
|
const limit = typeof result.detected_context_limit === "number" && result.detected_context_limit > 0 ? result.detected_context_limit : 0;
|
|
172005
172176
|
const modelMatches = limit > 0 && requestedModelKey && storedModelKey ? requestedModelKey === storedModelKey : true;
|
|
172006
172177
|
const needs = typeof result.needs_emergency_recovery === "number" && result.needs_emergency_recovery > 0;
|
|
172178
|
+
const persistedOrigin = normalizeEmergencyRecoveryOrigin(result.emergency_recovery_origin);
|
|
172179
|
+
const recoveryOrigin = needs ? persistedOrigin ?? (limit > 0 ? "provider_overflow" : null) : null;
|
|
172007
172180
|
return {
|
|
172008
172181
|
detectedContextLimit: modelMatches ? limit : 0,
|
|
172009
172182
|
detectedContextLimitModelKey: storedModelKey,
|
|
172010
|
-
needsEmergencyRecovery: needs
|
|
172183
|
+
needsEmergencyRecovery: needs,
|
|
172184
|
+
emergencyRecoveryOrigin: recoveryOrigin
|
|
172011
172185
|
};
|
|
172012
172186
|
}
|
|
172013
|
-
function recordOverflowDetected(db, sessionId, reportedLimit, modelKey) {
|
|
172187
|
+
function recordOverflowDetected(db, sessionId, reportedLimit, modelKey, origin = "provider_overflow") {
|
|
172014
172188
|
db.transaction(() => {
|
|
172015
172189
|
ensureSessionMetaRow(db, sessionId);
|
|
172016
172190
|
if (typeof reportedLimit === "number" && reportedLimit > 0) {
|
|
172017
|
-
db.prepare("UPDATE session_meta SET detected_context_limit = ?, detected_context_limit_model_key = ?, needs_emergency_recovery = 1, observed_safe_input_tokens = 0, cache_alert_sent = 0 WHERE session_id = ?").run(reportedLimit, normalizeDetectedLimitModelKey(modelKey), sessionId);
|
|
172191
|
+
db.prepare("UPDATE session_meta SET detected_context_limit = ?, detected_context_limit_model_key = ?, needs_emergency_recovery = 1, emergency_recovery_origin = ?, observed_safe_input_tokens = 0, cache_alert_sent = 0 WHERE session_id = ?").run(reportedLimit, normalizeDetectedLimitModelKey(modelKey), origin, sessionId);
|
|
172018
172192
|
} else {
|
|
172019
|
-
db.prepare("UPDATE session_meta SET needs_emergency_recovery = 1, observed_safe_input_tokens = 0, cache_alert_sent = 0 WHERE session_id = ?").run(sessionId);
|
|
172193
|
+
db.prepare("UPDATE session_meta SET needs_emergency_recovery = 1, emergency_recovery_origin = ?, observed_safe_input_tokens = 0, cache_alert_sent = 0 WHERE session_id = ?").run(origin, sessionId);
|
|
172020
172194
|
}
|
|
172021
172195
|
})();
|
|
172022
172196
|
}
|
|
@@ -172024,9 +172198,9 @@ function clearEmergencyRecovery(db, sessionId) {
|
|
|
172024
172198
|
db.transaction(() => {
|
|
172025
172199
|
ensureSessionMetaRow(db, sessionId);
|
|
172026
172200
|
try {
|
|
172027
|
-
db.prepare("UPDATE session_meta SET needs_emergency_recovery = 0, recovery_no_eligible_head_count = 0 WHERE session_id = ?").run(sessionId);
|
|
172201
|
+
db.prepare("UPDATE session_meta SET needs_emergency_recovery = 0, emergency_recovery_origin = '', recovery_no_eligible_head_count = 0 WHERE session_id = ?").run(sessionId);
|
|
172028
172202
|
} catch {
|
|
172029
|
-
db.prepare("UPDATE session_meta SET needs_emergency_recovery = 0 WHERE session_id = ?").run(sessionId);
|
|
172203
|
+
db.prepare("UPDATE session_meta SET needs_emergency_recovery = 0, emergency_recovery_origin = '' WHERE session_id = ?").run(sessionId);
|
|
172030
172204
|
}
|
|
172031
172205
|
})();
|
|
172032
172206
|
}
|
|
@@ -175768,6 +175942,30 @@ class BoundedSessionMap {
|
|
|
175768
175942
|
// ../plugin/src/hooks/magic-context/inject-compartments.ts
|
|
175769
175943
|
init_logger();
|
|
175770
175944
|
|
|
175945
|
+
// ../plugin/src/hooks/magic-context/compartment-render-epoch.ts
|
|
175946
|
+
var COMPARTMENT_RENDER_EPOCH = "cre2";
|
|
175947
|
+
var EPOCH_COMPONENT_PREFIX = "|compartment-render:";
|
|
175948
|
+
function encodeCachedM0UpgradeIdentity(upgradeState, compartmentRenderEpoch = COMPARTMENT_RENDER_EPOCH) {
|
|
175949
|
+
if (compartmentRenderEpoch === null)
|
|
175950
|
+
return upgradeState;
|
|
175951
|
+
return `${upgradeState ?? ""}${EPOCH_COMPONENT_PREFIX}${compartmentRenderEpoch}`;
|
|
175952
|
+
}
|
|
175953
|
+
function decodeCachedM0UpgradeIdentity(value) {
|
|
175954
|
+
if (value === null) {
|
|
175955
|
+
return { upgradeState: null, compartmentRenderEpoch: null };
|
|
175956
|
+
}
|
|
175957
|
+
const componentIndex = value.lastIndexOf(EPOCH_COMPONENT_PREFIX);
|
|
175958
|
+
if (componentIndex < 0) {
|
|
175959
|
+
return { upgradeState: value, compartmentRenderEpoch: null };
|
|
175960
|
+
}
|
|
175961
|
+
const upgradeState = value.slice(0, componentIndex);
|
|
175962
|
+
const compartmentRenderEpoch = value.slice(componentIndex + EPOCH_COMPONENT_PREFIX.length);
|
|
175963
|
+
return {
|
|
175964
|
+
upgradeState: upgradeState.length > 0 ? upgradeState : null,
|
|
175965
|
+
compartmentRenderEpoch: compartmentRenderEpoch.length > 0 ? compartmentRenderEpoch : null
|
|
175966
|
+
};
|
|
175967
|
+
}
|
|
175968
|
+
|
|
175771
175969
|
// ../plugin/src/hooks/magic-context/decay-curve.ts
|
|
175772
175970
|
var H50 = 24;
|
|
175773
175971
|
var D = 25;
|
|
@@ -175837,10 +176035,13 @@ function formatDateRange(startDate, endDate) {
|
|
|
175837
176035
|
return `${startDate}→${endDate.slice(8)}`;
|
|
175838
176036
|
return `${startDate}→${endDate}`;
|
|
175839
176037
|
}
|
|
176038
|
+
function sanitizeCompartmentTitle(title) {
|
|
176039
|
+
return escapeXmlContent2(title.replace(/[\p{Cc}\p{Zl}\p{Zp}]+/gu, " "));
|
|
176040
|
+
}
|
|
175840
176041
|
function compartmentHeading(c) {
|
|
175841
176042
|
const dateRange = formatDateRange(c.startDate, c.endDate);
|
|
175842
176043
|
const dateSegment = dateRange ? ` · ${dateRange}` : "";
|
|
175843
|
-
return `## ${c.startMessage}-${c.endMessage}${dateSegment} · ${c.title}`;
|
|
176044
|
+
return `## ${c.startMessage}-${c.endMessage}${dateSegment} · ${sanitizeCompartmentTitle(c.title)}`;
|
|
175844
176045
|
}
|
|
175845
176046
|
function guardCompartmentBody(body) {
|
|
175846
176047
|
return body.replace(/^## /gm, " ## ");
|