@cortexkit/pi-magic-context 0.23.1 → 0.24.0
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 +2222 -287
- package/dist/subagent-entry.js +2071 -409
- package/package.json +1 -1
package/dist/subagent-entry.js
CHANGED
|
@@ -8164,9 +8164,470 @@ function isRelativeProjectFile(projectPath, relativePath) {
|
|
|
8164
8164
|
|
|
8165
8165
|
// ../plugin/src/features/magic-context/migrations.ts
|
|
8166
8166
|
init_logger();
|
|
8167
|
-
|
|
8167
|
+
|
|
8168
|
+
// ../plugin/src/features/magic-context/workspaces.ts
|
|
8169
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
8170
|
+
|
|
8171
|
+
// ../plugin/src/features/magic-context/memory/constants.ts
|
|
8172
|
+
var V2_MEMORY_CATEGORIES = [
|
|
8173
|
+
"PROJECT_RULES",
|
|
8174
|
+
"ARCHITECTURE",
|
|
8175
|
+
"CONSTRAINTS",
|
|
8176
|
+
"CONFIG_VALUES",
|
|
8177
|
+
"NAMING"
|
|
8178
|
+
];
|
|
8179
|
+
var PROMOTABLE_CATEGORIES = [
|
|
8180
|
+
"PROJECT_RULES",
|
|
8181
|
+
"ARCHITECTURE",
|
|
8182
|
+
"CONSTRAINTS",
|
|
8183
|
+
"CONFIG_VALUES",
|
|
8184
|
+
"NAMING",
|
|
8185
|
+
"ARCHITECTURE_DECISIONS",
|
|
8186
|
+
"CONFIG_DEFAULTS",
|
|
8187
|
+
"USER_PREFERENCES",
|
|
8188
|
+
"USER_DIRECTIVES",
|
|
8189
|
+
"ENVIRONMENT",
|
|
8190
|
+
"WORKFLOW_RULES",
|
|
8191
|
+
"KNOWN_ISSUES"
|
|
8192
|
+
];
|
|
8193
|
+
var CATEGORY_PRIORITY = [
|
|
8194
|
+
"PROJECT_RULES",
|
|
8195
|
+
"ARCHITECTURE",
|
|
8196
|
+
"CONSTRAINTS",
|
|
8197
|
+
"CONFIG_VALUES",
|
|
8198
|
+
"NAMING",
|
|
8199
|
+
"USER_DIRECTIVES",
|
|
8200
|
+
"USER_PREFERENCES",
|
|
8201
|
+
"CONFIG_DEFAULTS",
|
|
8202
|
+
"ARCHITECTURE_DECISIONS",
|
|
8203
|
+
"ENVIRONMENT",
|
|
8204
|
+
"WORKFLOW_RULES",
|
|
8205
|
+
"KNOWN_ISSUES"
|
|
8206
|
+
];
|
|
8207
|
+
var MEMORY_CATEGORY_ORDER_UNKNOWN = 99;
|
|
8208
|
+
var MEMORY_CATEGORY_ORDER_PRIORITY = CATEGORY_PRIORITY.reduce((acc, category, index) => {
|
|
8209
|
+
acc[category] = index;
|
|
8210
|
+
return acc;
|
|
8211
|
+
}, {});
|
|
8212
|
+
var MEMORY_CATEGORY_ORDER_SQL = `CASE category ${CATEGORY_PRIORITY.map((category, index) => `WHEN '${category}' THEN ${index}`).join(" ")} ELSE ${MEMORY_CATEGORY_ORDER_UNKNOWN} END`;
|
|
8213
|
+
var CATEGORY_DEFAULT_TTL = {
|
|
8214
|
+
WORKFLOW_RULES: 90 * 24 * 60 * 60 * 1000,
|
|
8215
|
+
KNOWN_ISSUES: 30 * 24 * 60 * 60 * 1000
|
|
8216
|
+
};
|
|
8217
|
+
|
|
8218
|
+
// ../plugin/src/features/magic-context/memory/project-identity.ts
|
|
8219
|
+
import { execFileSync } from "node:child_process";
|
|
8220
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
8221
|
+
import { statSync } from "node:fs";
|
|
8222
|
+
import path3 from "node:path";
|
|
8223
|
+
var GIT_TIMEOUT_MS = 5000;
|
|
8224
|
+
var identityCache = new Map;
|
|
8225
|
+
var directoryFallbackCache = new Map;
|
|
8226
|
+
|
|
8227
|
+
class ProjectIdentityError extends Error {
|
|
8228
|
+
errorClass;
|
|
8229
|
+
rawDirectory;
|
|
8230
|
+
constructor(errorClass, rawDirectory, message, cause) {
|
|
8231
|
+
super(message);
|
|
8232
|
+
this.name = "ProjectIdentityError";
|
|
8233
|
+
this.errorClass = errorClass;
|
|
8234
|
+
this.rawDirectory = rawDirectory;
|
|
8235
|
+
if (cause) {
|
|
8236
|
+
this.cause = cause;
|
|
8237
|
+
}
|
|
8238
|
+
}
|
|
8239
|
+
}
|
|
8240
|
+
function asError(error) {
|
|
8241
|
+
return error instanceof Error ? error : undefined;
|
|
8242
|
+
}
|
|
8243
|
+
function getErrorCode(error) {
|
|
8244
|
+
if (error === null || typeof error !== "object" || !("code" in error)) {
|
|
8245
|
+
return;
|
|
8246
|
+
}
|
|
8247
|
+
const code = error.code;
|
|
8248
|
+
return typeof code === "string" ? code : undefined;
|
|
8249
|
+
}
|
|
8250
|
+
function getErrorSignal(error) {
|
|
8251
|
+
if (error === null || typeof error !== "object" || !("signal" in error)) {
|
|
8252
|
+
return;
|
|
8253
|
+
}
|
|
8254
|
+
const signal = error.signal;
|
|
8255
|
+
return typeof signal === "string" ? signal : undefined;
|
|
8256
|
+
}
|
|
8257
|
+
function getErrorKilled(error) {
|
|
8258
|
+
if (error === null || typeof error !== "object" || !("killed" in error)) {
|
|
8259
|
+
return false;
|
|
8260
|
+
}
|
|
8261
|
+
return error.killed === true;
|
|
8262
|
+
}
|
|
8263
|
+
function getErrorStderr(error) {
|
|
8264
|
+
if (error === null || typeof error !== "object" || !("stderr" in error)) {
|
|
8265
|
+
return "";
|
|
8266
|
+
}
|
|
8267
|
+
const stderr = error.stderr;
|
|
8268
|
+
if (typeof stderr === "string") {
|
|
8269
|
+
return stderr;
|
|
8270
|
+
}
|
|
8271
|
+
if (Buffer.isBuffer(stderr)) {
|
|
8272
|
+
return stderr.toString("utf8");
|
|
8273
|
+
}
|
|
8274
|
+
return "";
|
|
8275
|
+
}
|
|
8276
|
+
function directoryFallback(directory) {
|
|
8277
|
+
const canonical = path3.resolve(directory);
|
|
8278
|
+
const hash = createHash2("md5").update(canonical, "utf8").digest("hex").slice(0, 12);
|
|
8279
|
+
return `dir:${hash}`;
|
|
8280
|
+
}
|
|
8281
|
+
function assertDirectoryUsable(canonicalDirectory, rawDirectory) {
|
|
8282
|
+
try {
|
|
8283
|
+
const stat = statSync(canonicalDirectory);
|
|
8284
|
+
if (!stat.isDirectory()) {
|
|
8285
|
+
throw new ProjectIdentityError("unknown", rawDirectory, `Project path is not a directory: ${canonicalDirectory}`);
|
|
8286
|
+
}
|
|
8287
|
+
} catch (error) {
|
|
8288
|
+
if (error instanceof ProjectIdentityError) {
|
|
8289
|
+
throw error;
|
|
8290
|
+
}
|
|
8291
|
+
const code = getErrorCode(error);
|
|
8292
|
+
if (code === "EACCES" || code === "EPERM") {
|
|
8293
|
+
throw new ProjectIdentityError("permission_denied", rawDirectory, `Permission denied while accessing project directory: ${canonicalDirectory}`, asError(error));
|
|
8294
|
+
}
|
|
8295
|
+
throw new ProjectIdentityError("unknown", rawDirectory, `Unable to access project directory: ${canonicalDirectory}`, asError(error));
|
|
8296
|
+
}
|
|
8297
|
+
}
|
|
8298
|
+
function isGitTimeoutError(error) {
|
|
8299
|
+
const code = getErrorCode(error);
|
|
8300
|
+
const signal = getErrorSignal(error);
|
|
8301
|
+
return code === "ETIMEDOUT" || signal === "SIGTERM" || signal === "SIGKILL" || getErrorKilled(error);
|
|
8302
|
+
}
|
|
8303
|
+
function classifyGitError(error, rawDirectory) {
|
|
8304
|
+
if (isGitTimeoutError(error)) {
|
|
8305
|
+
return new ProjectIdentityError("git_timeout", rawDirectory, `git rev-list timed out after ${GIT_TIMEOUT_MS}ms`, asError(error));
|
|
8306
|
+
}
|
|
8307
|
+
const code = getErrorCode(error);
|
|
8308
|
+
if (code === "ENOENT") {
|
|
8309
|
+
return new ProjectIdentityError("git_missing", rawDirectory, "git binary is not available in PATH", asError(error));
|
|
8310
|
+
}
|
|
8311
|
+
if (code === "EACCES" || code === "EPERM") {
|
|
8312
|
+
return new ProjectIdentityError("permission_denied", rawDirectory, "Permission denied while spawning git", asError(error));
|
|
8313
|
+
}
|
|
8314
|
+
const stderr = getErrorStderr(error).toLowerCase();
|
|
8315
|
+
if (stderr.includes("not a git repository") || stderr.includes("does not have any commits yet") || stderr.includes("ambiguous argument 'head'") || stderr.includes("unknown revision or path")) {
|
|
8316
|
+
return new ProjectIdentityError("not_git_repo", rawDirectory, "Directory has no git root commit; caller may use directory fallback", asError(error));
|
|
8317
|
+
}
|
|
8318
|
+
return new ProjectIdentityError("unknown", rawDirectory, "git rev-list failed while resolving project identity", asError(error));
|
|
8319
|
+
}
|
|
8320
|
+
function resolveProjectIdentityStrict(directory) {
|
|
8321
|
+
const canonical = path3.resolve(directory);
|
|
8322
|
+
const cached = identityCache.get(canonical);
|
|
8323
|
+
if (cached !== undefined) {
|
|
8324
|
+
return cached;
|
|
8325
|
+
}
|
|
8326
|
+
assertDirectoryUsable(canonical, directory);
|
|
8327
|
+
let output;
|
|
8328
|
+
try {
|
|
8329
|
+
output = execFileSync("git", ["rev-list", "--max-parents=0", "HEAD"], {
|
|
8330
|
+
cwd: canonical,
|
|
8331
|
+
encoding: "utf8",
|
|
8332
|
+
env: { ...process.env, LC_ALL: "C", LANG: "C" },
|
|
8333
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
8334
|
+
timeout: GIT_TIMEOUT_MS
|
|
8335
|
+
});
|
|
8336
|
+
} catch (error) {
|
|
8337
|
+
throw classifyGitError(error, directory);
|
|
8338
|
+
}
|
|
8339
|
+
const firstLine = output.split(`
|
|
8340
|
+
`)[0]?.trim() ?? "";
|
|
8341
|
+
const rootCommit = firstLine.slice(0, 64);
|
|
8342
|
+
if (rootCommit.length < 7) {
|
|
8343
|
+
throw new ProjectIdentityError("unknown", directory, "git rev-list returned no valid root commit hash");
|
|
8344
|
+
}
|
|
8345
|
+
const identity = `git:${rootCommit}`;
|
|
8346
|
+
identityCache.set(canonical, identity);
|
|
8347
|
+
return identity;
|
|
8348
|
+
}
|
|
8349
|
+
function shouldUseDirectoryFallback(error) {
|
|
8350
|
+
return error.errorClass === "not_git_repo" || error.errorClass === "unknown" && error.message.startsWith("Unable to access project directory:");
|
|
8351
|
+
}
|
|
8352
|
+
function resolveProjectIdentity(directory) {
|
|
8353
|
+
const canonical = path3.resolve(directory);
|
|
8354
|
+
const cachedFallback = directoryFallbackCache.get(canonical);
|
|
8355
|
+
if (cachedFallback !== undefined) {
|
|
8356
|
+
if (!hasGitDir(canonical)) {
|
|
8357
|
+
return cachedFallback;
|
|
8358
|
+
}
|
|
8359
|
+
directoryFallbackCache.delete(canonical);
|
|
8360
|
+
}
|
|
8361
|
+
try {
|
|
8362
|
+
return resolveProjectIdentityStrict(directory);
|
|
8363
|
+
} catch (error) {
|
|
8364
|
+
if (error instanceof ProjectIdentityError && shouldUseDirectoryFallback(error)) {
|
|
8365
|
+
const fallback = directoryFallback(canonical);
|
|
8366
|
+
if (!hasGitDir(canonical)) {
|
|
8367
|
+
directoryFallbackCache.set(canonical, fallback);
|
|
8368
|
+
}
|
|
8369
|
+
return fallback;
|
|
8370
|
+
}
|
|
8371
|
+
throw error;
|
|
8372
|
+
}
|
|
8373
|
+
}
|
|
8374
|
+
function hasGitDir(canonical) {
|
|
8375
|
+
try {
|
|
8376
|
+
statSync(path3.join(canonical, ".git"));
|
|
8377
|
+
return true;
|
|
8378
|
+
} catch {
|
|
8379
|
+
return false;
|
|
8380
|
+
}
|
|
8381
|
+
}
|
|
8382
|
+
function normalizeStoredProjectPath(rawOrStored) {
|
|
8383
|
+
if (rawOrStored.startsWith("git:") || rawOrStored.startsWith("dir:")) {
|
|
8384
|
+
return rawOrStored;
|
|
8385
|
+
}
|
|
8386
|
+
try {
|
|
8387
|
+
return resolveProjectIdentity(rawOrStored);
|
|
8388
|
+
} catch {
|
|
8389
|
+
return directoryFallback(rawOrStored);
|
|
8390
|
+
}
|
|
8391
|
+
}
|
|
8392
|
+
function storedPathBelongsToIdentity(storedProjectPath, projectIdentity) {
|
|
8393
|
+
return storedProjectPath === projectIdentity || normalizeStoredProjectPath(storedProjectPath) === projectIdentity;
|
|
8394
|
+
}
|
|
8395
|
+
// ../plugin/src/features/magic-context/workspaces.ts
|
|
8396
|
+
var VALID_SHARE_CATEGORIES = new Set(V2_MEMORY_CATEGORIES);
|
|
8397
|
+
function tableExists(db, tableName) {
|
|
8398
|
+
const row = db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name = ? LIMIT 1").get(tableName);
|
|
8399
|
+
return Boolean(row);
|
|
8400
|
+
}
|
|
8401
|
+
function columnExists(db, tableName, columnName) {
|
|
8402
|
+
const rows = db.prepare(`PRAGMA table_info(${tableName})`).all();
|
|
8403
|
+
return rows.some((row) => row.name === columnName);
|
|
8404
|
+
}
|
|
8405
|
+
function uniqueSorted(values) {
|
|
8406
|
+
return [...new Set(values)].sort((left, right) => left.localeCompare(right));
|
|
8407
|
+
}
|
|
8408
|
+
function placeholders(values) {
|
|
8409
|
+
return values.map(() => "?").join(", ");
|
|
8410
|
+
}
|
|
8411
|
+
function normalizeShareCategories(raw) {
|
|
8412
|
+
if (raw === null || raw === undefined)
|
|
8413
|
+
return null;
|
|
8414
|
+
if (typeof raw !== "string")
|
|
8415
|
+
return null;
|
|
8416
|
+
let parsed;
|
|
8417
|
+
try {
|
|
8418
|
+
parsed = JSON.parse(raw);
|
|
8419
|
+
} catch {
|
|
8420
|
+
return null;
|
|
8421
|
+
}
|
|
8422
|
+
if (!Array.isArray(parsed))
|
|
8423
|
+
return null;
|
|
8424
|
+
const categories = [];
|
|
8425
|
+
for (const value of parsed) {
|
|
8426
|
+
if (typeof value !== "string" || !VALID_SHARE_CATEGORIES.has(value)) {
|
|
8427
|
+
return null;
|
|
8428
|
+
}
|
|
8429
|
+
if (!categories.includes(value))
|
|
8430
|
+
categories.push(value);
|
|
8431
|
+
}
|
|
8432
|
+
return categories.sort((left, right) => left.localeCompare(right));
|
|
8433
|
+
}
|
|
8434
|
+
function selectWorkspaceShareCategories(db, identities) {
|
|
8435
|
+
const candidates = uniqueSorted(identities.filter((identity) => identity.length > 0));
|
|
8436
|
+
if (candidates.length === 0 || !tableExists(db, "workspace_members") || !tableExists(db, "workspaces") || !columnExists(db, "workspaces", "share_categories")) {
|
|
8437
|
+
return null;
|
|
8438
|
+
}
|
|
8439
|
+
const row = db.prepare(`SELECT workspace.share_categories AS shareCategories
|
|
8440
|
+
FROM workspace_members AS member
|
|
8441
|
+
JOIN workspaces AS workspace ON workspace.id = member.workspace_id
|
|
8442
|
+
WHERE member.project_path IN (${placeholders(candidates)})
|
|
8443
|
+
ORDER BY workspace.id ASC
|
|
8444
|
+
LIMIT 1`).get(...candidates);
|
|
8445
|
+
return normalizeShareCategories(row?.shareCategories ?? null);
|
|
8446
|
+
}
|
|
8447
|
+
function resolveWorkspaceShareCategories(db, projectIdentity) {
|
|
8448
|
+
return selectWorkspaceShareCategories(db, [projectIdentity]);
|
|
8449
|
+
}
|
|
8450
|
+
function resolveWorkspaceIdentitySet(db, projectIdentity) {
|
|
8451
|
+
if (!tableExists(db, "workspace_members")) {
|
|
8452
|
+
return { identities: [projectIdentity], namesByIdentity: new Map };
|
|
8453
|
+
}
|
|
8454
|
+
const rows = db.prepare(`SELECT member.project_path AS identity, member.display_name AS displayName
|
|
8455
|
+
FROM workspace_members AS anchor
|
|
8456
|
+
JOIN workspace_members AS member ON member.workspace_id = anchor.workspace_id
|
|
8457
|
+
WHERE anchor.project_path = ?
|
|
8458
|
+
ORDER BY member.display_name ASC, member.project_path ASC`).all(projectIdentity);
|
|
8459
|
+
if (rows.length === 0) {
|
|
8460
|
+
return { identities: [projectIdentity], namesByIdentity: new Map };
|
|
8461
|
+
}
|
|
8462
|
+
const namesByIdentity = new Map;
|
|
8463
|
+
const identities = [];
|
|
8464
|
+
for (const row of rows) {
|
|
8465
|
+
if (typeof row.identity !== "string" || row.identity.length === 0)
|
|
8466
|
+
continue;
|
|
8467
|
+
if (identities.includes(row.identity))
|
|
8468
|
+
continue;
|
|
8469
|
+
identities.push(row.identity);
|
|
8470
|
+
if (typeof row.displayName === "string" && row.displayName.length > 0) {
|
|
8471
|
+
namesByIdentity.set(row.identity, row.displayName);
|
|
8472
|
+
}
|
|
8473
|
+
}
|
|
8474
|
+
return identities.length > 0 ? { identities, namesByIdentity } : { identities: [projectIdentity], namesByIdentity: new Map };
|
|
8475
|
+
}
|
|
8476
|
+
function expandWorkspaceIdentitySetWithAliases(db, identities) {
|
|
8477
|
+
const canonical = uniqueSorted(identities.filter((identity) => identity.length > 0));
|
|
8478
|
+
const expanded = new Set(canonical);
|
|
8479
|
+
const canonicalIdentityByStoredPath = new Map;
|
|
8480
|
+
for (const identity of canonical) {
|
|
8481
|
+
canonicalIdentityByStoredPath.set(identity, identity);
|
|
8482
|
+
}
|
|
8483
|
+
if (canonical.length === 0 || !tableExists(db, "v22_identity_rekey_map")) {
|
|
8484
|
+
return { expandedIdentities: [...expanded], canonicalIdentityByStoredPath };
|
|
8485
|
+
}
|
|
8486
|
+
const rows = db.prepare(`SELECT old_project_path AS oldProjectPath, new_project_path AS newProjectPath
|
|
8487
|
+
FROM v22_identity_rekey_map
|
|
8488
|
+
WHERE new_project_path IN (${placeholders(canonical)})
|
|
8489
|
+
ORDER BY old_project_path ASC`).all(...canonical);
|
|
8490
|
+
for (const row of rows) {
|
|
8491
|
+
if (typeof row.oldProjectPath !== "string" || typeof row.newProjectPath !== "string") {
|
|
8492
|
+
continue;
|
|
8493
|
+
}
|
|
8494
|
+
if (!canonicalIdentityByStoredPath.has(row.newProjectPath))
|
|
8495
|
+
continue;
|
|
8496
|
+
expanded.add(row.oldProjectPath);
|
|
8497
|
+
canonicalIdentityByStoredPath.set(row.oldProjectPath, row.newProjectPath);
|
|
8498
|
+
}
|
|
8499
|
+
return { expandedIdentities: [...expanded], canonicalIdentityByStoredPath };
|
|
8500
|
+
}
|
|
8501
|
+
function resolveStoredPathWorkspaceIdentity(storedProjectPath, memberIdentities, canonicalIdentityByStoredPath) {
|
|
8502
|
+
const direct = canonicalIdentityByStoredPath.get(storedProjectPath);
|
|
8503
|
+
if (direct)
|
|
8504
|
+
return direct;
|
|
8505
|
+
const normalized = normalizeStoredProjectPath(storedProjectPath);
|
|
8506
|
+
const normalizedDirect = canonicalIdentityByStoredPath.get(normalized);
|
|
8507
|
+
if (normalizedDirect)
|
|
8508
|
+
return normalizedDirect;
|
|
8509
|
+
if (memberIdentities.includes(normalized))
|
|
8510
|
+
return normalized;
|
|
8511
|
+
for (const identity of memberIdentities) {
|
|
8512
|
+
if (storedPathBelongsToIdentity(storedProjectPath, identity)) {
|
|
8513
|
+
return identity;
|
|
8514
|
+
}
|
|
8515
|
+
}
|
|
8516
|
+
return null;
|
|
8517
|
+
}
|
|
8518
|
+
function storedPathBelongsToWorkspace(storedProjectPath, memberIdentities, expandedIdentities, canonicalIdentityByStoredPath) {
|
|
8519
|
+
if (expandedIdentities.includes(storedProjectPath))
|
|
8520
|
+
return true;
|
|
8521
|
+
return resolveStoredPathWorkspaceIdentity(storedProjectPath, memberIdentities, canonicalIdentityByStoredPath) !== null;
|
|
8522
|
+
}
|
|
8523
|
+
function sourceNameForMemory(storedProjectPath, ownIdentity, memberIdentities, namesByIdentity, canonicalIdentityByStoredPath) {
|
|
8524
|
+
const canonicalIdentity = resolveStoredPathWorkspaceIdentity(storedProjectPath, memberIdentities, canonicalIdentityByStoredPath);
|
|
8525
|
+
if (!canonicalIdentity || canonicalIdentity === ownIdentity)
|
|
8526
|
+
return;
|
|
8527
|
+
return namesByIdentity.get(canonicalIdentity);
|
|
8528
|
+
}
|
|
8529
|
+
function getEpochMap(db, identities) {
|
|
8530
|
+
if (identities.length === 0)
|
|
8531
|
+
return new Map;
|
|
8532
|
+
const rows = db.prepare(`SELECT project_path AS projectPath, project_memory_epoch AS epoch
|
|
8533
|
+
FROM project_state
|
|
8534
|
+
WHERE project_path IN (${placeholders(identities)})`).all(...identities);
|
|
8535
|
+
const epochs = new Map;
|
|
8536
|
+
for (const row of rows) {
|
|
8537
|
+
if (typeof row.projectPath !== "string" || typeof row.epoch !== "number")
|
|
8538
|
+
continue;
|
|
8539
|
+
epochs.set(row.projectPath, row.epoch);
|
|
8540
|
+
}
|
|
8541
|
+
return epochs;
|
|
8542
|
+
}
|
|
8543
|
+
function computeWorkspaceEpochFingerprint(db, identities) {
|
|
8544
|
+
const canonical = uniqueSorted(identities.filter((identity) => identity.length > 0));
|
|
8545
|
+
const epochs = getEpochMap(db, canonical);
|
|
8546
|
+
const shareCategories = selectWorkspaceShareCategories(db, canonical);
|
|
8547
|
+
const hash = createHash3("sha256");
|
|
8548
|
+
hash.update("share_categories", "utf8");
|
|
8549
|
+
hash.update("\x00");
|
|
8550
|
+
hash.update(shareCategories === null ? "ALL" : JSON.stringify(shareCategories), "utf8");
|
|
8551
|
+
hash.update(`
|
|
8552
|
+
`);
|
|
8553
|
+
for (const identity of canonical) {
|
|
8554
|
+
hash.update(identity, "utf8");
|
|
8555
|
+
hash.update("\x00");
|
|
8556
|
+
hash.update(String(epochs.get(identity) ?? 0), "utf8");
|
|
8557
|
+
hash.update(`
|
|
8558
|
+
`);
|
|
8559
|
+
}
|
|
8560
|
+
return hash.digest("hex");
|
|
8561
|
+
}
|
|
8562
|
+
function isInTransaction(db) {
|
|
8563
|
+
const candidate = db;
|
|
8564
|
+
return candidate.inTransaction === true || candidate.isTransaction === true;
|
|
8565
|
+
}
|
|
8566
|
+
function workspaceMembersForIdentity(db, identity) {
|
|
8567
|
+
if (!tableExists(db, "workspace_members"))
|
|
8568
|
+
return [identity];
|
|
8569
|
+
const rows = db.prepare(`SELECT member.project_path AS identity
|
|
8570
|
+
FROM workspace_members AS anchor
|
|
8571
|
+
JOIN workspace_members AS member ON member.workspace_id = anchor.workspace_id
|
|
8572
|
+
WHERE anchor.project_path = ?
|
|
8573
|
+
ORDER BY member.project_path ASC`).all(identity);
|
|
8574
|
+
const identities = rows.map((row) => typeof row.identity === "string" ? row.identity : "").filter((value) => value.length > 0);
|
|
8575
|
+
return identities.length > 0 ? uniqueSorted(identities) : [identity];
|
|
8576
|
+
}
|
|
8577
|
+
function bumpEpochRows(db, identities, now) {
|
|
8578
|
+
const stmt = db.prepare(`INSERT INTO project_state
|
|
8579
|
+
(project_path, project_memory_epoch, project_user_profile_version, updated_at)
|
|
8580
|
+
VALUES (?, 1, 0, ?)
|
|
8581
|
+
ON CONFLICT(project_path) DO UPDATE SET
|
|
8582
|
+
project_memory_epoch = project_memory_epoch + 1,
|
|
8583
|
+
updated_at = excluded.updated_at`);
|
|
8584
|
+
for (const identity of uniqueSorted(identities)) {
|
|
8585
|
+
stmt.run(identity, now);
|
|
8586
|
+
}
|
|
8587
|
+
}
|
|
8588
|
+
function bumpEpochsForWorkspaceMembers(db, identity, now = Date.now()) {
|
|
8589
|
+
const run = () => bumpEpochRows(db, workspaceMembersForIdentity(db, identity), now);
|
|
8590
|
+
if (isInTransaction(db)) {
|
|
8591
|
+
run();
|
|
8592
|
+
return;
|
|
8593
|
+
}
|
|
8594
|
+
db.exec("BEGIN IMMEDIATE");
|
|
8595
|
+
try {
|
|
8596
|
+
run();
|
|
8597
|
+
db.exec("COMMIT");
|
|
8598
|
+
} catch (error) {
|
|
8599
|
+
try {
|
|
8600
|
+
db.exec("ROLLBACK");
|
|
8601
|
+
} catch {}
|
|
8602
|
+
throw error;
|
|
8603
|
+
}
|
|
8604
|
+
}
|
|
8605
|
+
function bumpEpochsForWorkspaceMemberSet(db, identities, now = Date.now()) {
|
|
8606
|
+
const run = () => bumpEpochRows(db, identities, now);
|
|
8607
|
+
if (isInTransaction(db)) {
|
|
8608
|
+
run();
|
|
8609
|
+
return;
|
|
8610
|
+
}
|
|
8611
|
+
db.exec("BEGIN IMMEDIATE");
|
|
8612
|
+
try {
|
|
8613
|
+
run();
|
|
8614
|
+
db.exec("COMMIT");
|
|
8615
|
+
} catch (error) {
|
|
8616
|
+
try {
|
|
8617
|
+
db.exec("ROLLBACK");
|
|
8618
|
+
} catch {}
|
|
8619
|
+
throw error;
|
|
8620
|
+
}
|
|
8621
|
+
}
|
|
8622
|
+
|
|
8623
|
+
// ../plugin/src/features/magic-context/migrations.ts
|
|
8624
|
+
function tableExists2(db, name) {
|
|
8168
8625
|
return Boolean(db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name = ?").get(name));
|
|
8169
8626
|
}
|
|
8627
|
+
function columnExists2(db, table, column) {
|
|
8628
|
+
const rows = db.prepare(`PRAGMA table_info(${table})`).all();
|
|
8629
|
+
return rows.some((row) => row.name === column);
|
|
8630
|
+
}
|
|
8170
8631
|
var MIGRATIONS = [
|
|
8171
8632
|
{
|
|
8172
8633
|
version: 1,
|
|
@@ -8625,9 +9086,9 @@ var MIGRATIONS = [
|
|
|
8625
9086
|
version: 22,
|
|
8626
9087
|
description: "v2.0 cache architecture schema foundation",
|
|
8627
9088
|
up: (db) => {
|
|
8628
|
-
const hasSessionMetaTable =
|
|
8629
|
-
const hasCompartmentsTable =
|
|
8630
|
-
const hasMemoriesTable =
|
|
9089
|
+
const hasSessionMetaTable = tableExists2(db, "session_meta");
|
|
9090
|
+
const hasCompartmentsTable = tableExists2(db, "compartments");
|
|
9091
|
+
const hasMemoriesTable = tableExists2(db, "memories");
|
|
8631
9092
|
if (hasSessionMetaTable) {
|
|
8632
9093
|
ensureColumn(db, "session_meta", "cached_m0_bytes", "BLOB");
|
|
8633
9094
|
ensureColumn(db, "session_meta", "cached_m0_project_memory_epoch", "INTEGER");
|
|
@@ -8652,7 +9113,7 @@ var MIGRATIONS = [
|
|
|
8652
9113
|
ensureColumn(db, "compartments", "p1_embedding_model_id", "TEXT");
|
|
8653
9114
|
ensureColumn(db, "compartments", "legacy", "INTEGER NOT NULL DEFAULT 0");
|
|
8654
9115
|
}
|
|
8655
|
-
const hasRecompCompartmentsTable =
|
|
9116
|
+
const hasRecompCompartmentsTable = tableExists2(db, "recomp_compartments");
|
|
8656
9117
|
if (hasRecompCompartmentsTable) {
|
|
8657
9118
|
ensureColumn(db, "recomp_compartments", "p1", "TEXT");
|
|
8658
9119
|
ensureColumn(db, "recomp_compartments", "p2", "TEXT");
|
|
@@ -8963,6 +9424,156 @@ var MIGRATIONS = [
|
|
|
8963
9424
|
db.prepare("UPDATE session_meta SET force_emergency_bypass_used = 0 WHERE force_emergency_bypass_used IS NULL").run();
|
|
8964
9425
|
db.prepare("UPDATE session_meta SET last_usage_context_limit = 0 WHERE last_usage_context_limit IS NULL").run();
|
|
8965
9426
|
}
|
|
9427
|
+
},
|
|
9428
|
+
{
|
|
9429
|
+
version: 33,
|
|
9430
|
+
description: "Compartment chunk embeddings for semantic message-history search",
|
|
9431
|
+
up: (db) => {
|
|
9432
|
+
db.exec(`
|
|
9433
|
+
CREATE TABLE IF NOT EXISTS compartment_chunk_embeddings (
|
|
9434
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
9435
|
+
compartment_id INTEGER NOT NULL REFERENCES compartments(id) ON DELETE CASCADE,
|
|
9436
|
+
session_id TEXT NOT NULL,
|
|
9437
|
+
project_path TEXT NOT NULL,
|
|
9438
|
+
harness TEXT NOT NULL DEFAULT 'opencode',
|
|
9439
|
+
window_index INTEGER NOT NULL DEFAULT 0,
|
|
9440
|
+
start_ordinal INTEGER NOT NULL,
|
|
9441
|
+
end_ordinal INTEGER NOT NULL,
|
|
9442
|
+
chunk_hash TEXT NOT NULL,
|
|
9443
|
+
model_id TEXT NOT NULL,
|
|
9444
|
+
dims INTEGER NOT NULL,
|
|
9445
|
+
vector BLOB NOT NULL,
|
|
9446
|
+
created_at INTEGER NOT NULL,
|
|
9447
|
+
UNIQUE(compartment_id, window_index)
|
|
9448
|
+
);
|
|
9449
|
+
CREATE INDEX IF NOT EXISTS idx_cce_session
|
|
9450
|
+
ON compartment_chunk_embeddings(session_id);
|
|
9451
|
+
CREATE INDEX IF NOT EXISTS idx_cce_project_model
|
|
9452
|
+
ON compartment_chunk_embeddings(project_path, model_id);
|
|
9453
|
+
`);
|
|
9454
|
+
}
|
|
9455
|
+
},
|
|
9456
|
+
{
|
|
9457
|
+
version: 34,
|
|
9458
|
+
description: "workspace tables and m[0] workspace fingerprint cache reset",
|
|
9459
|
+
up: (db) => {
|
|
9460
|
+
db.exec(`
|
|
9461
|
+
CREATE TABLE IF NOT EXISTS workspaces (
|
|
9462
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
9463
|
+
name TEXT NOT NULL UNIQUE,
|
|
9464
|
+
created_at INTEGER NOT NULL,
|
|
9465
|
+
updated_at INTEGER NOT NULL
|
|
9466
|
+
);
|
|
9467
|
+
CREATE TABLE IF NOT EXISTS workspace_members (
|
|
9468
|
+
workspace_id INTEGER NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
|
|
9469
|
+
project_path TEXT NOT NULL,
|
|
9470
|
+
display_name TEXT NOT NULL,
|
|
9471
|
+
display_path TEXT NOT NULL,
|
|
9472
|
+
added_at INTEGER NOT NULL,
|
|
9473
|
+
PRIMARY KEY (workspace_id, project_path)
|
|
9474
|
+
);
|
|
9475
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_workspace_member_unique
|
|
9476
|
+
ON workspace_members(project_path);
|
|
9477
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_workspace_member_name
|
|
9478
|
+
ON workspace_members(workspace_id, display_name);
|
|
9479
|
+
`);
|
|
9480
|
+
const hasSessionMeta = db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name='session_meta' LIMIT 1").get();
|
|
9481
|
+
if (!hasSessionMeta)
|
|
9482
|
+
return;
|
|
9483
|
+
ensureColumn(db, "session_meta", "cached_m0_workspace_fingerprint", "TEXT");
|
|
9484
|
+
const columns = new Set(db.prepare("PRAGMA table_info(session_meta)").all().map((column) => column.name));
|
|
9485
|
+
const clears = [
|
|
9486
|
+
["cached_m0_bytes", null],
|
|
9487
|
+
["cached_m1_bytes", null],
|
|
9488
|
+
["cached_m0_project_memory_epoch", null],
|
|
9489
|
+
["cached_m0_workspace_fingerprint", null],
|
|
9490
|
+
["cached_m0_project_user_profile_version", null],
|
|
9491
|
+
["cached_m0_max_compartment_seq", null],
|
|
9492
|
+
["cached_m0_max_memory_id", null],
|
|
9493
|
+
["cached_m0_max_mutation_id", null],
|
|
9494
|
+
["cached_m0_max_memory_mutation_id", null],
|
|
9495
|
+
["cached_m0_project_docs_hash", null],
|
|
9496
|
+
["cached_m0_materialized_at", null],
|
|
9497
|
+
["cached_m0_session_facts_version", null],
|
|
9498
|
+
["cached_m0_upgrade_state", null],
|
|
9499
|
+
["cached_m0_system_hash", null],
|
|
9500
|
+
["cached_m0_tool_set_hash", null],
|
|
9501
|
+
["cached_m0_model_key", null],
|
|
9502
|
+
["cached_m0_last_baseline_end_message_id", null],
|
|
9503
|
+
["memory_block_cache", ""],
|
|
9504
|
+
["memory_block_ids", ""],
|
|
9505
|
+
["memory_block_count", 0]
|
|
9506
|
+
];
|
|
9507
|
+
const setClauses = [];
|
|
9508
|
+
const values = [];
|
|
9509
|
+
for (const [column, value] of clears) {
|
|
9510
|
+
if (!columns.has(column))
|
|
9511
|
+
continue;
|
|
9512
|
+
setClauses.push(`${column} = ?`);
|
|
9513
|
+
values.push(value);
|
|
9514
|
+
}
|
|
9515
|
+
if (setClauses.length > 0) {
|
|
9516
|
+
db.prepare(`UPDATE session_meta SET ${setClauses.join(", ")}`).run(...values);
|
|
9517
|
+
}
|
|
9518
|
+
}
|
|
9519
|
+
},
|
|
9520
|
+
{
|
|
9521
|
+
version: 35,
|
|
9522
|
+
description: "workspace per-category share defaults and epoch refresh",
|
|
9523
|
+
up: (db) => {
|
|
9524
|
+
db.exec(`
|
|
9525
|
+
CREATE TABLE IF NOT EXISTS workspaces (
|
|
9526
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
9527
|
+
name TEXT NOT NULL UNIQUE,
|
|
9528
|
+
created_at INTEGER NOT NULL,
|
|
9529
|
+
updated_at INTEGER NOT NULL,
|
|
9530
|
+
share_categories TEXT NOT NULL DEFAULT '["CONSTRAINTS"]'
|
|
9531
|
+
);
|
|
9532
|
+
`);
|
|
9533
|
+
if (!columnExists2(db, "workspaces", "share_categories")) {
|
|
9534
|
+
db.exec(`ALTER TABLE workspaces ADD COLUMN share_categories TEXT NOT NULL DEFAULT '["CONSTRAINTS"]'`);
|
|
9535
|
+
}
|
|
9536
|
+
db.prepare(`UPDATE workspaces
|
|
9537
|
+
SET share_categories = '["CONSTRAINTS"]'
|
|
9538
|
+
WHERE share_categories IS NULL OR share_categories = ''`).run();
|
|
9539
|
+
if (!tableExists2(db, "workspace_members"))
|
|
9540
|
+
return;
|
|
9541
|
+
const rows = db.prepare(`SELECT DISTINCT project_path AS identity
|
|
9542
|
+
FROM workspace_members
|
|
9543
|
+
WHERE project_path IS NOT NULL AND project_path <> ''
|
|
9544
|
+
ORDER BY project_path ASC`).all();
|
|
9545
|
+
const identities = rows.map((row) => typeof row.identity === "string" ? row.identity : "").filter((identity) => identity.length > 0);
|
|
9546
|
+
if (identities.length > 0) {
|
|
9547
|
+
bumpEpochsForWorkspaceMemberSet(db, identities, Date.now());
|
|
9548
|
+
}
|
|
9549
|
+
}
|
|
9550
|
+
},
|
|
9551
|
+
{
|
|
9552
|
+
version: 36,
|
|
9553
|
+
description: "session project ownership map for compartment chunk backfill scoping",
|
|
9554
|
+
up: (db) => {
|
|
9555
|
+
db.exec(`
|
|
9556
|
+
CREATE TABLE IF NOT EXISTS session_projects (
|
|
9557
|
+
session_id TEXT NOT NULL,
|
|
9558
|
+
harness TEXT NOT NULL DEFAULT 'opencode',
|
|
9559
|
+
project_path TEXT NOT NULL,
|
|
9560
|
+
updated_at INTEGER NOT NULL,
|
|
9561
|
+
PRIMARY KEY(session_id, harness)
|
|
9562
|
+
);
|
|
9563
|
+
CREATE INDEX IF NOT EXISTS idx_session_projects_project
|
|
9564
|
+
ON session_projects(project_path);
|
|
9565
|
+
`);
|
|
9566
|
+
const hasChunkTable = db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name='compartment_chunk_embeddings'").get();
|
|
9567
|
+
if (hasChunkTable) {
|
|
9568
|
+
db.exec(`
|
|
9569
|
+
INSERT OR IGNORE INTO session_projects (session_id, harness, project_path, updated_at)
|
|
9570
|
+
SELECT session_id, harness, MIN(project_path), 0
|
|
9571
|
+
FROM compartment_chunk_embeddings
|
|
9572
|
+
GROUP BY session_id, harness
|
|
9573
|
+
HAVING COUNT(DISTINCT project_path) = 1;
|
|
9574
|
+
`);
|
|
9575
|
+
}
|
|
9576
|
+
}
|
|
8966
9577
|
}
|
|
8967
9578
|
];
|
|
8968
9579
|
var LATEST_MIGRATION_VERSION = MIGRATIONS.reduce((max, m) => Math.max(max, m.version), 0);
|
|
@@ -141940,7 +142551,7 @@ var databases = new Map;
|
|
|
141940
142551
|
var persistenceByDatabase = new WeakMap;
|
|
141941
142552
|
var persistenceErrorByDatabase = new WeakMap;
|
|
141942
142553
|
var lastSchemaFenceRejection = null;
|
|
141943
|
-
var LATEST_SUPPORTED_VERSION =
|
|
142554
|
+
var LATEST_SUPPORTED_VERSION = 36;
|
|
141944
142555
|
function resolveDatabasePath(dbPathOverride) {
|
|
141945
142556
|
if (dbPathOverride) {
|
|
141946
142557
|
return { dbDir: dirname2(dbPathOverride), dbPath: dbPathOverride };
|
|
@@ -142106,6 +142717,35 @@ function initializeDatabase(db) {
|
|
|
142106
142717
|
);
|
|
142107
142718
|
CREATE INDEX IF NOT EXISTS idx_compartments_session ON compartments(session_id);
|
|
142108
142719
|
|
|
142720
|
+
CREATE TABLE IF NOT EXISTS compartment_chunk_embeddings (
|
|
142721
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
142722
|
+
compartment_id INTEGER NOT NULL REFERENCES compartments(id) ON DELETE CASCADE,
|
|
142723
|
+
session_id TEXT NOT NULL,
|
|
142724
|
+
project_path TEXT NOT NULL,
|
|
142725
|
+
harness TEXT NOT NULL DEFAULT 'opencode',
|
|
142726
|
+
window_index INTEGER NOT NULL DEFAULT 0,
|
|
142727
|
+
start_ordinal INTEGER NOT NULL,
|
|
142728
|
+
end_ordinal INTEGER NOT NULL,
|
|
142729
|
+
chunk_hash TEXT NOT NULL,
|
|
142730
|
+
model_id TEXT NOT NULL,
|
|
142731
|
+
dims INTEGER NOT NULL,
|
|
142732
|
+
vector BLOB NOT NULL,
|
|
142733
|
+
created_at INTEGER NOT NULL,
|
|
142734
|
+
UNIQUE(compartment_id, window_index)
|
|
142735
|
+
);
|
|
142736
|
+
CREATE INDEX IF NOT EXISTS idx_cce_session ON compartment_chunk_embeddings(session_id);
|
|
142737
|
+
CREATE INDEX IF NOT EXISTS idx_cce_project_model ON compartment_chunk_embeddings(project_path, model_id);
|
|
142738
|
+
|
|
142739
|
+
CREATE TABLE IF NOT EXISTS session_projects (
|
|
142740
|
+
session_id TEXT NOT NULL,
|
|
142741
|
+
harness TEXT NOT NULL DEFAULT 'opencode',
|
|
142742
|
+
project_path TEXT NOT NULL,
|
|
142743
|
+
updated_at INTEGER NOT NULL,
|
|
142744
|
+
PRIMARY KEY(session_id, harness)
|
|
142745
|
+
);
|
|
142746
|
+
CREATE INDEX IF NOT EXISTS idx_session_projects_project
|
|
142747
|
+
ON session_projects(project_path);
|
|
142748
|
+
|
|
142109
142749
|
CREATE TABLE IF NOT EXISTS compartment_events (
|
|
142110
142750
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
142111
142751
|
session_id TEXT NOT NULL,
|
|
@@ -142291,6 +142931,25 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
142291
142931
|
rekeyed_at INTEGER NOT NULL
|
|
142292
142932
|
);
|
|
142293
142933
|
|
|
142934
|
+
CREATE TABLE IF NOT EXISTS workspaces (
|
|
142935
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
142936
|
+
name TEXT NOT NULL UNIQUE,
|
|
142937
|
+
created_at INTEGER NOT NULL,
|
|
142938
|
+
updated_at INTEGER NOT NULL,
|
|
142939
|
+
share_categories TEXT NOT NULL DEFAULT '["CONSTRAINTS"]'
|
|
142940
|
+
);
|
|
142941
|
+
|
|
142942
|
+
CREATE TABLE IF NOT EXISTS workspace_members (
|
|
142943
|
+
workspace_id INTEGER NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
|
|
142944
|
+
project_path TEXT NOT NULL,
|
|
142945
|
+
display_name TEXT NOT NULL,
|
|
142946
|
+
display_path TEXT NOT NULL,
|
|
142947
|
+
added_at INTEGER NOT NULL,
|
|
142948
|
+
PRIMARY KEY (workspace_id, project_path)
|
|
142949
|
+
);
|
|
142950
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_workspace_member_unique ON workspace_members(project_path);
|
|
142951
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_workspace_member_name ON workspace_members(workspace_id, display_name);
|
|
142952
|
+
|
|
142294
142953
|
CREATE TABLE IF NOT EXISTS v22_backfill_failures (
|
|
142295
142954
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
142296
142955
|
table_name TEXT NOT NULL,
|
|
@@ -142402,6 +143061,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
142402
143061
|
deferred_execute_state TEXT,
|
|
142403
143062
|
cached_m0_bytes BLOB,
|
|
142404
143063
|
cached_m0_project_memory_epoch INTEGER,
|
|
143064
|
+
cached_m0_workspace_fingerprint TEXT,
|
|
142405
143065
|
cached_m0_project_user_profile_version INTEGER,
|
|
142406
143066
|
cached_m0_max_compartment_seq INTEGER,
|
|
142407
143067
|
cached_m0_max_memory_id INTEGER,
|
|
@@ -142560,6 +143220,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
142560
143220
|
ensureColumn(db, "session_meta", "cleared_reasoning_through_tag", "INTEGER DEFAULT 0");
|
|
142561
143221
|
ensureColumn(db, "session_meta", "stripped_placeholder_ids", "TEXT DEFAULT ''");
|
|
142562
143222
|
ensureColumn(db, "session_meta", "stale_reduce_stripped_ids", "TEXT DEFAULT ''");
|
|
143223
|
+
ensureColumn(db, "session_meta", "processed_image_stripped_ids", "TEXT DEFAULT ''");
|
|
142563
143224
|
ensureColumn(db, "compartments", "start_message_id", "TEXT DEFAULT ''");
|
|
142564
143225
|
ensureColumn(db, "compartments", "end_message_id", "TEXT DEFAULT ''");
|
|
142565
143226
|
ensureColumn(db, "memory_embeddings", "model_id", "TEXT");
|
|
@@ -142613,6 +143274,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
142613
143274
|
ensureColumn(db, "memories", "importance", "INTEGER");
|
|
142614
143275
|
ensureColumn(db, "session_meta", "cached_m0_bytes", "BLOB");
|
|
142615
143276
|
ensureColumn(db, "session_meta", "cached_m0_project_memory_epoch", "INTEGER");
|
|
143277
|
+
ensureColumn(db, "session_meta", "cached_m0_workspace_fingerprint", "TEXT");
|
|
142616
143278
|
ensureColumn(db, "session_meta", "cached_m0_project_user_profile_version", "INTEGER");
|
|
142617
143279
|
ensureColumn(db, "session_meta", "cached_m0_max_compartment_seq", "INTEGER");
|
|
142618
143280
|
ensureColumn(db, "session_meta", "cached_m0_max_memory_id", "INTEGER");
|
|
@@ -142644,6 +143306,15 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
142644
143306
|
project_user_profile_version INTEGER NOT NULL DEFAULT 0,
|
|
142645
143307
|
updated_at INTEGER NOT NULL DEFAULT 0
|
|
142646
143308
|
);
|
|
143309
|
+
CREATE TABLE IF NOT EXISTS session_projects (
|
|
143310
|
+
session_id TEXT NOT NULL,
|
|
143311
|
+
harness TEXT NOT NULL DEFAULT 'opencode',
|
|
143312
|
+
project_path TEXT NOT NULL,
|
|
143313
|
+
updated_at INTEGER NOT NULL,
|
|
143314
|
+
PRIMARY KEY(session_id, harness)
|
|
143315
|
+
);
|
|
143316
|
+
CREATE INDEX IF NOT EXISTS idx_session_projects_project
|
|
143317
|
+
ON session_projects(project_path);
|
|
142647
143318
|
CREATE TABLE IF NOT EXISTS m0_mutation_log (
|
|
142648
143319
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
142649
143320
|
session_id TEXT NOT NULL,
|
|
@@ -142671,6 +143342,23 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
142671
143342
|
new_project_path TEXT NOT NULL,
|
|
142672
143343
|
rekeyed_at INTEGER NOT NULL
|
|
142673
143344
|
);
|
|
143345
|
+
CREATE TABLE IF NOT EXISTS workspaces (
|
|
143346
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
143347
|
+
name TEXT NOT NULL UNIQUE,
|
|
143348
|
+
created_at INTEGER NOT NULL,
|
|
143349
|
+
updated_at INTEGER NOT NULL,
|
|
143350
|
+
share_categories TEXT NOT NULL DEFAULT '["CONSTRAINTS"]'
|
|
143351
|
+
);
|
|
143352
|
+
CREATE TABLE IF NOT EXISTS workspace_members (
|
|
143353
|
+
workspace_id INTEGER NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
|
|
143354
|
+
project_path TEXT NOT NULL,
|
|
143355
|
+
display_name TEXT NOT NULL,
|
|
143356
|
+
display_path TEXT NOT NULL,
|
|
143357
|
+
added_at INTEGER NOT NULL,
|
|
143358
|
+
PRIMARY KEY (workspace_id, project_path)
|
|
143359
|
+
);
|
|
143360
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_workspace_member_unique ON workspace_members(project_path);
|
|
143361
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_workspace_member_name ON workspace_members(workspace_id, display_name);
|
|
142674
143362
|
CREATE TABLE IF NOT EXISTS v22_backfill_failures (
|
|
142675
143363
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
142676
143364
|
table_name TEXT NOT NULL,
|
|
@@ -142692,6 +143380,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
142692
143380
|
ensureColumn(db, "recomp_compartments", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
|
|
142693
143381
|
ensureColumn(db, "recomp_facts", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
|
|
142694
143382
|
ensureColumn(db, "message_history_index", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
|
|
143383
|
+
ensureColumn(db, "workspaces", "share_categories", `TEXT NOT NULL DEFAULT '["CONSTRAINTS"]'`);
|
|
142695
143384
|
}
|
|
142696
143385
|
function healAllNullColumns(db) {
|
|
142697
143386
|
healNullTextColumns(db);
|
|
@@ -142730,6 +143419,7 @@ function healNullTextColumns(db) {
|
|
|
142730
143419
|
["system_prompt_hash", ""],
|
|
142731
143420
|
["stripped_placeholder_ids", ""],
|
|
142732
143421
|
["stale_reduce_stripped_ids", ""],
|
|
143422
|
+
["processed_image_stripped_ids", ""],
|
|
142733
143423
|
["memory_block_cache", ""],
|
|
142734
143424
|
["memory_block_ids", ""],
|
|
142735
143425
|
["compaction_marker_state", ""],
|
|
@@ -142774,7 +143464,7 @@ function healNullIntegerColumns(db) {
|
|
|
142774
143464
|
}
|
|
142775
143465
|
}
|
|
142776
143466
|
function ensureColumn(db, table, column, definition) {
|
|
142777
|
-
if (!/^[a-z][a-z0-9_]*$/.test(table) || !/^[a-z][a-z0-9_]*$/.test(column) || !/^[A-Z0-9_'(),[\]\s]+$/i.test(definition)) {
|
|
143467
|
+
if (!/^[a-z][a-z0-9_]*$/.test(table) || !/^[a-z][a-z0-9_]*$/.test(column) || !/^[A-Z0-9_"'(),[\]\s]+$/i.test(definition)) {
|
|
142778
143468
|
throw new Error(`Unsafe schema identifier: ${table}.${column} ${definition}`);
|
|
142779
143469
|
}
|
|
142780
143470
|
const rows = db.prepare(`PRAGMA table_info(${table})`).all();
|
|
@@ -143852,10 +144542,10 @@ function mergeDefs(...defs) {
|
|
|
143852
144542
|
function cloneDef(schema) {
|
|
143853
144543
|
return mergeDefs(schema._zod.def);
|
|
143854
144544
|
}
|
|
143855
|
-
function getElementAtPath(obj,
|
|
143856
|
-
if (!
|
|
144545
|
+
function getElementAtPath(obj, path4) {
|
|
144546
|
+
if (!path4)
|
|
143857
144547
|
return obj;
|
|
143858
|
-
return
|
|
144548
|
+
return path4.reduce((acc, key) => acc?.[key], obj);
|
|
143859
144549
|
}
|
|
143860
144550
|
function promiseAllObject(promisesObj) {
|
|
143861
144551
|
const keys = Object.keys(promisesObj);
|
|
@@ -144263,11 +144953,11 @@ function explicitlyAborted(x, startIndex = 0) {
|
|
|
144263
144953
|
}
|
|
144264
144954
|
return false;
|
|
144265
144955
|
}
|
|
144266
|
-
function prefixIssues(
|
|
144956
|
+
function prefixIssues(path4, issues) {
|
|
144267
144957
|
return issues.map((iss) => {
|
|
144268
144958
|
var _a2;
|
|
144269
144959
|
(_a2 = iss).path ?? (_a2.path = []);
|
|
144270
|
-
iss.path.unshift(
|
|
144960
|
+
iss.path.unshift(path4);
|
|
144271
144961
|
return iss;
|
|
144272
144962
|
});
|
|
144273
144963
|
}
|
|
@@ -144414,16 +145104,16 @@ function flattenError(error, mapper = (issue2) => issue2.message) {
|
|
|
144414
145104
|
}
|
|
144415
145105
|
function formatError(error, mapper = (issue2) => issue2.message) {
|
|
144416
145106
|
const fieldErrors = { _errors: [] };
|
|
144417
|
-
const processError = (error2,
|
|
145107
|
+
const processError = (error2, path4 = []) => {
|
|
144418
145108
|
for (const issue2 of error2.issues) {
|
|
144419
145109
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
144420
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
145110
|
+
issue2.errors.map((issues) => processError({ issues }, [...path4, ...issue2.path]));
|
|
144421
145111
|
} else if (issue2.code === "invalid_key") {
|
|
144422
|
-
processError({ issues: issue2.issues }, [...
|
|
145112
|
+
processError({ issues: issue2.issues }, [...path4, ...issue2.path]);
|
|
144423
145113
|
} else if (issue2.code === "invalid_element") {
|
|
144424
|
-
processError({ issues: issue2.issues }, [...
|
|
145114
|
+
processError({ issues: issue2.issues }, [...path4, ...issue2.path]);
|
|
144425
145115
|
} else {
|
|
144426
|
-
const fullpath = [...
|
|
145116
|
+
const fullpath = [...path4, ...issue2.path];
|
|
144427
145117
|
if (fullpath.length === 0) {
|
|
144428
145118
|
fieldErrors._errors.push(mapper(issue2));
|
|
144429
145119
|
} else {
|
|
@@ -144450,17 +145140,17 @@ function formatError(error, mapper = (issue2) => issue2.message) {
|
|
|
144450
145140
|
}
|
|
144451
145141
|
function treeifyError(error, mapper = (issue2) => issue2.message) {
|
|
144452
145142
|
const result = { errors: [] };
|
|
144453
|
-
const processError = (error2,
|
|
145143
|
+
const processError = (error2, path4 = []) => {
|
|
144454
145144
|
var _a2, _b;
|
|
144455
145145
|
for (const issue2 of error2.issues) {
|
|
144456
145146
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
144457
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
145147
|
+
issue2.errors.map((issues) => processError({ issues }, [...path4, ...issue2.path]));
|
|
144458
145148
|
} else if (issue2.code === "invalid_key") {
|
|
144459
|
-
processError({ issues: issue2.issues }, [...
|
|
145149
|
+
processError({ issues: issue2.issues }, [...path4, ...issue2.path]);
|
|
144460
145150
|
} else if (issue2.code === "invalid_element") {
|
|
144461
|
-
processError({ issues: issue2.issues }, [...
|
|
145151
|
+
processError({ issues: issue2.issues }, [...path4, ...issue2.path]);
|
|
144462
145152
|
} else {
|
|
144463
|
-
const fullpath = [...
|
|
145153
|
+
const fullpath = [...path4, ...issue2.path];
|
|
144464
145154
|
if (fullpath.length === 0) {
|
|
144465
145155
|
result.errors.push(mapper(issue2));
|
|
144466
145156
|
continue;
|
|
@@ -144492,8 +145182,8 @@ function treeifyError(error, mapper = (issue2) => issue2.message) {
|
|
|
144492
145182
|
}
|
|
144493
145183
|
function toDotPath(_path) {
|
|
144494
145184
|
const segs = [];
|
|
144495
|
-
const
|
|
144496
|
-
for (const seg of
|
|
145185
|
+
const path4 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
145186
|
+
for (const seg of path4) {
|
|
144497
145187
|
if (typeof seg === "number")
|
|
144498
145188
|
segs.push(`[${seg}]`);
|
|
144499
145189
|
else if (typeof seg === "symbol")
|
|
@@ -156952,13 +157642,13 @@ function resolveRef(ref, ctx) {
|
|
|
156952
157642
|
if (!ref.startsWith("#")) {
|
|
156953
157643
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
156954
157644
|
}
|
|
156955
|
-
const
|
|
156956
|
-
if (
|
|
157645
|
+
const path4 = ref.slice(1).split("/").filter(Boolean);
|
|
157646
|
+
if (path4.length === 0) {
|
|
156957
157647
|
return ctx.rootSchema;
|
|
156958
157648
|
}
|
|
156959
157649
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
156960
|
-
if (
|
|
156961
|
-
const key =
|
|
157650
|
+
if (path4[0] === defsKey) {
|
|
157651
|
+
const key = path4[1];
|
|
156962
157652
|
if (!key || !ctx.defs[key]) {
|
|
156963
157653
|
throw new Error(`Reference not found: ${ref}`);
|
|
156964
157654
|
}
|
|
@@ -157446,7 +158136,8 @@ var BaseEmbeddingConfigSchema = exports_external.object({
|
|
|
157446
158136
|
endpoint: exports_external.string().optional().describe("API endpoint URL. Required when provider is openai-compatible."),
|
|
157447
158137
|
api_key: exports_external.string().optional().describe("API key for remote embedding provider (optional)"),
|
|
157448
158138
|
input_type: exports_external.string().optional().describe("Optional input_type sent in the embedding request body. Required by some openai-compatible providers (e.g. NVIDIA NIM expects 'query' or 'passage'). Omitted from the request when unset."),
|
|
157449
|
-
truncate: exports_external.string().optional().describe("Optional truncate mode sent in the embedding request body (e.g. NVIDIA NIM accepts 'NONE' | 'START' | 'END'). Omitted from the request when unset.")
|
|
158139
|
+
truncate: exports_external.string().optional().describe("Optional truncate mode sent in the embedding request body (e.g. NVIDIA NIM accepts 'NONE' | 'START' | 'END'). Omitted from the request when unset."),
|
|
158140
|
+
max_input_tokens: exports_external.number().int().positive().optional().describe("Optional maximum input tokens for chunk embeddings. Defaults conservatively to 512 when omitted.")
|
|
157450
158141
|
}).superRefine((data, ctx) => {
|
|
157451
158142
|
if (data.provider === "openai-compatible" && !data.endpoint?.trim()) {
|
|
157452
158143
|
ctx.addIssue({
|
|
@@ -157467,7 +158158,8 @@ var EmbeddingConfigSchema = BaseEmbeddingConfigSchema.transform((data) => {
|
|
|
157467
158158
|
if (data.provider === "local") {
|
|
157468
158159
|
return {
|
|
157469
158160
|
provider: "local",
|
|
157470
|
-
model: data.model?.trim() || DEFAULT_LOCAL_EMBEDDING_MODEL
|
|
158161
|
+
model: data.model?.trim() || DEFAULT_LOCAL_EMBEDDING_MODEL,
|
|
158162
|
+
...data.max_input_tokens ? { max_input_tokens: data.max_input_tokens } : {}
|
|
157471
158163
|
};
|
|
157472
158164
|
}
|
|
157473
158165
|
if (data.provider === "openai-compatible") {
|
|
@@ -157480,7 +158172,8 @@ var EmbeddingConfigSchema = BaseEmbeddingConfigSchema.transform((data) => {
|
|
|
157480
158172
|
endpoint: data.endpoint?.trim() ?? "",
|
|
157481
158173
|
...apiKey ? { api_key: apiKey } : {},
|
|
157482
158174
|
...inputType ? { input_type: inputType } : {},
|
|
157483
|
-
...truncate ? { truncate } : {}
|
|
158175
|
+
...truncate ? { truncate } : {},
|
|
158176
|
+
...data.max_input_tokens ? { max_input_tokens: data.max_input_tokens } : {}
|
|
157484
158177
|
};
|
|
157485
158178
|
}
|
|
157486
158179
|
return { provider: "off" };
|
|
@@ -157726,31 +158419,31 @@ function getUserConfigPaths() {
|
|
|
157726
158419
|
return [`${basePath}.jsonc`, `${basePath}.json`];
|
|
157727
158420
|
}
|
|
157728
158421
|
function resolveFirstExisting(paths) {
|
|
157729
|
-
return paths.find((
|
|
158422
|
+
return paths.find((path4) => existsSync5(path4));
|
|
157730
158423
|
}
|
|
157731
|
-
function loadConfigFile(
|
|
158424
|
+
function loadConfigFile(path4, scope) {
|
|
157732
158425
|
try {
|
|
157733
|
-
const rawText = readFileSync3(
|
|
158426
|
+
const rawText = readFileSync3(path4, "utf-8");
|
|
157734
158427
|
const substituted = substituteConfigVariables({
|
|
157735
158428
|
text: rawText,
|
|
157736
|
-
configPath:
|
|
158429
|
+
configPath: path4,
|
|
157737
158430
|
isProjectConfig: scope === "project"
|
|
157738
158431
|
});
|
|
157739
158432
|
return {
|
|
157740
|
-
path:
|
|
158433
|
+
path: path4,
|
|
157741
158434
|
scope,
|
|
157742
158435
|
config: import_comment_json.parse(substituted.text),
|
|
157743
|
-
warnings: substituted.warnings.map((warning) => `${
|
|
158436
|
+
warnings: substituted.warnings.map((warning) => `${path4}: ${warning}`),
|
|
157744
158437
|
loadOutcome: substituted.warnings.length > 0 ? "substitution-failure" : "ok"
|
|
157745
158438
|
};
|
|
157746
158439
|
} catch (error51) {
|
|
157747
158440
|
const message = error51 instanceof Error ? error51.message : String(error51);
|
|
157748
158441
|
return {
|
|
157749
|
-
path:
|
|
158442
|
+
path: path4,
|
|
157750
158443
|
scope,
|
|
157751
158444
|
config: {},
|
|
157752
158445
|
warnings: [
|
|
157753
|
-
`${
|
|
158446
|
+
`${path4}: failed to load config: ${message}; using defaults for this file.`
|
|
157754
158447
|
],
|
|
157755
158448
|
loadOutcome: typeof error51.code === "string" ? "project-file-io-error" : "project-file-parse-error"
|
|
157756
158449
|
};
|
|
@@ -157916,9 +158609,9 @@ function bindSubstitutionFailures(loaded) {
|
|
|
157916
158609
|
}
|
|
157917
158610
|
const emptyPaths = collectEmptyStringPaths(loaded.config);
|
|
157918
158611
|
return loaded.warnings.map((message) => {
|
|
157919
|
-
const matchedPath = emptyPaths.find((
|
|
157920
|
-
const tail =
|
|
157921
|
-
return message.includes(
|
|
158612
|
+
const matchedPath = emptyPaths.find((path4) => {
|
|
158613
|
+
const tail = path4.split(".").at(-1) ?? path4;
|
|
158614
|
+
return message.includes(path4) || message.toLowerCase().includes(tail.toLowerCase());
|
|
157922
158615
|
});
|
|
157923
158616
|
return {
|
|
157924
158617
|
keyPath: matchedPath ?? "<unknown>",
|
|
@@ -158006,6 +158699,441 @@ function loadPiConfigDetailed(opts = {}) {
|
|
|
158006
158699
|
// ../plugin/src/features/magic-context/memory/embedding.ts
|
|
158007
158700
|
init_logger();
|
|
158008
158701
|
|
|
158702
|
+
// ../plugin/src/features/magic-context/compartment-chunk-embedding.ts
|
|
158703
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
158704
|
+
var DEFAULT_COMPARTMENT_CHUNK_MAX_INPUT_TOKENS = 512;
|
|
158705
|
+
var loadFtsRowsStatements = new WeakMap;
|
|
158706
|
+
var existingHashStatements = new WeakMap;
|
|
158707
|
+
var existingHashByProjectStatements = new WeakMap;
|
|
158708
|
+
var deleteByCompartmentStatements = new WeakMap;
|
|
158709
|
+
var insertEmbeddingStatements = new WeakMap;
|
|
158710
|
+
var distinctModelStatements = new WeakMap;
|
|
158711
|
+
var clearProjectStatements = new WeakMap;
|
|
158712
|
+
var clearProjectModelStatements = new WeakMap;
|
|
158713
|
+
var searchRowsStatements = new WeakMap;
|
|
158714
|
+
var searchRowsByModelStatements = new WeakMap;
|
|
158715
|
+
var backfillCandidateStatements = new WeakMap;
|
|
158716
|
+
function getLoadFtsRowsStatement(db) {
|
|
158717
|
+
let stmt = loadFtsRowsStatements.get(db);
|
|
158718
|
+
if (!stmt) {
|
|
158719
|
+
stmt = db.prepare(`SELECT message_ordinal AS messageOrdinal, role, content
|
|
158720
|
+
FROM message_history_fts
|
|
158721
|
+
WHERE session_id = ?
|
|
158722
|
+
AND message_ordinal >= ?
|
|
158723
|
+
AND message_ordinal <= ?
|
|
158724
|
+
AND role IN ('user', 'assistant')
|
|
158725
|
+
ORDER BY message_ordinal ASC`);
|
|
158726
|
+
loadFtsRowsStatements.set(db, stmt);
|
|
158727
|
+
}
|
|
158728
|
+
return stmt;
|
|
158729
|
+
}
|
|
158730
|
+
function getExistingHashStatement(db, scopedToProject) {
|
|
158731
|
+
const map2 = scopedToProject ? existingHashByProjectStatements : existingHashStatements;
|
|
158732
|
+
let stmt = map2.get(db);
|
|
158733
|
+
if (!stmt) {
|
|
158734
|
+
stmt = db.prepare(`SELECT window_index AS windowIndex, chunk_hash AS chunkHash
|
|
158735
|
+
FROM compartment_chunk_embeddings
|
|
158736
|
+
WHERE compartment_id = ?
|
|
158737
|
+
AND model_id = ?
|
|
158738
|
+
${scopedToProject ? "AND project_path = ?" : ""}
|
|
158739
|
+
ORDER BY window_index ASC`);
|
|
158740
|
+
map2.set(db, stmt);
|
|
158741
|
+
}
|
|
158742
|
+
return stmt;
|
|
158743
|
+
}
|
|
158744
|
+
function getDeleteByCompartmentStatement(db) {
|
|
158745
|
+
let stmt = deleteByCompartmentStatements.get(db);
|
|
158746
|
+
if (!stmt) {
|
|
158747
|
+
stmt = db.prepare("DELETE FROM compartment_chunk_embeddings WHERE compartment_id = ?");
|
|
158748
|
+
deleteByCompartmentStatements.set(db, stmt);
|
|
158749
|
+
}
|
|
158750
|
+
return stmt;
|
|
158751
|
+
}
|
|
158752
|
+
function getInsertEmbeddingStatement(db) {
|
|
158753
|
+
let stmt = insertEmbeddingStatements.get(db);
|
|
158754
|
+
if (!stmt) {
|
|
158755
|
+
stmt = db.prepare(`INSERT INTO compartment_chunk_embeddings (
|
|
158756
|
+
compartment_id, session_id, project_path, harness, window_index,
|
|
158757
|
+
start_ordinal, end_ordinal, chunk_hash, model_id, dims, vector, created_at
|
|
158758
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
158759
|
+
insertEmbeddingStatements.set(db, stmt);
|
|
158760
|
+
}
|
|
158761
|
+
return stmt;
|
|
158762
|
+
}
|
|
158763
|
+
function getDistinctModelStatement(db) {
|
|
158764
|
+
let stmt = distinctModelStatements.get(db);
|
|
158765
|
+
if (!stmt) {
|
|
158766
|
+
stmt = db.prepare(`SELECT DISTINCT model_id AS modelId
|
|
158767
|
+
FROM compartment_chunk_embeddings
|
|
158768
|
+
WHERE project_path = ?`);
|
|
158769
|
+
distinctModelStatements.set(db, stmt);
|
|
158770
|
+
}
|
|
158771
|
+
return stmt;
|
|
158772
|
+
}
|
|
158773
|
+
function getClearProjectStatement(db) {
|
|
158774
|
+
let stmt = clearProjectStatements.get(db);
|
|
158775
|
+
if (!stmt) {
|
|
158776
|
+
stmt = db.prepare("DELETE FROM compartment_chunk_embeddings WHERE project_path = ?");
|
|
158777
|
+
clearProjectStatements.set(db, stmt);
|
|
158778
|
+
}
|
|
158779
|
+
return stmt;
|
|
158780
|
+
}
|
|
158781
|
+
function getClearProjectModelStatement(db) {
|
|
158782
|
+
let stmt = clearProjectModelStatements.get(db);
|
|
158783
|
+
if (!stmt) {
|
|
158784
|
+
stmt = db.prepare("DELETE FROM compartment_chunk_embeddings WHERE project_path = ? AND model_id = ?");
|
|
158785
|
+
clearProjectModelStatements.set(db, stmt);
|
|
158786
|
+
}
|
|
158787
|
+
return stmt;
|
|
158788
|
+
}
|
|
158789
|
+
function getSearchRowsStatement(db, withModel) {
|
|
158790
|
+
const map2 = withModel ? searchRowsByModelStatements : searchRowsStatements;
|
|
158791
|
+
let stmt = map2.get(db);
|
|
158792
|
+
if (!stmt) {
|
|
158793
|
+
stmt = db.prepare(`SELECT e.compartment_id AS compartmentId,
|
|
158794
|
+
e.session_id AS sessionId,
|
|
158795
|
+
c.title AS title,
|
|
158796
|
+
c.start_message AS compartmentStart,
|
|
158797
|
+
c.end_message AS compartmentEnd,
|
|
158798
|
+
e.window_index AS windowIndex,
|
|
158799
|
+
e.start_ordinal AS windowStart,
|
|
158800
|
+
e.end_ordinal AS windowEnd,
|
|
158801
|
+
e.chunk_hash AS chunkHash,
|
|
158802
|
+
e.model_id AS modelId,
|
|
158803
|
+
e.dims AS dims,
|
|
158804
|
+
e.vector AS vector
|
|
158805
|
+
FROM compartment_chunk_embeddings e
|
|
158806
|
+
JOIN compartments c ON c.id = e.compartment_id
|
|
158807
|
+
WHERE e.session_id = ?
|
|
158808
|
+
AND e.project_path = ?
|
|
158809
|
+
${withModel ? "AND e.model_id = ?" : ""}
|
|
158810
|
+
ORDER BY e.compartment_id ASC, e.window_index ASC`);
|
|
158811
|
+
map2.set(db, stmt);
|
|
158812
|
+
}
|
|
158813
|
+
return stmt;
|
|
158814
|
+
}
|
|
158815
|
+
function isFinitePositiveInteger(value) {
|
|
158816
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0;
|
|
158817
|
+
}
|
|
158818
|
+
function normalizeCompartmentChunkMaxInputTokens(value) {
|
|
158819
|
+
if (!isFinitePositiveInteger(value)) {
|
|
158820
|
+
return DEFAULT_COMPARTMENT_CHUNK_MAX_INPUT_TOKENS;
|
|
158821
|
+
}
|
|
158822
|
+
return Math.max(1, Math.floor(value));
|
|
158823
|
+
}
|
|
158824
|
+
function normalizeContent(text) {
|
|
158825
|
+
return text.replace(/\s+/g, " ").trim();
|
|
158826
|
+
}
|
|
158827
|
+
function formatOrdinalRange(start, end) {
|
|
158828
|
+
return start === end ? `[${start}]` : `[${start}-${end}]`;
|
|
158829
|
+
}
|
|
158830
|
+
function rolePrefix(role) {
|
|
158831
|
+
if (role === "user")
|
|
158832
|
+
return "U";
|
|
158833
|
+
if (role === "assistant")
|
|
158834
|
+
return "A";
|
|
158835
|
+
return null;
|
|
158836
|
+
}
|
|
158837
|
+
function parseOrdinal(value) {
|
|
158838
|
+
const parsed = typeof value === "number" ? value : Number.parseInt(String(value ?? ""), 10);
|
|
158839
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
|
158840
|
+
}
|
|
158841
|
+
function parseCanonicalLineRange(line) {
|
|
158842
|
+
const match = /^\[(\d+)(?:-(\d+))?\]\s+[UA]:/.exec(line.trim());
|
|
158843
|
+
if (!match)
|
|
158844
|
+
return null;
|
|
158845
|
+
const start = Number.parseInt(match[1], 10);
|
|
158846
|
+
const end = match[2] ? Number.parseInt(match[2], 10) : start;
|
|
158847
|
+
if (!Number.isFinite(start) || !Number.isFinite(end))
|
|
158848
|
+
return null;
|
|
158849
|
+
return { start, end };
|
|
158850
|
+
}
|
|
158851
|
+
function hashChunkText(text) {
|
|
158852
|
+
return createHash4("sha256").update(text).digest("hex");
|
|
158853
|
+
}
|
|
158854
|
+
function vectorBlob(vector) {
|
|
158855
|
+
return new Uint8Array(vector.buffer, vector.byteOffset, vector.byteLength);
|
|
158856
|
+
}
|
|
158857
|
+
function toFloat32Array(blob) {
|
|
158858
|
+
if (blob instanceof Uint8Array) {
|
|
158859
|
+
const buffer2 = blob.buffer.slice(blob.byteOffset, blob.byteOffset + blob.byteLength);
|
|
158860
|
+
return new Float32Array(buffer2);
|
|
158861
|
+
}
|
|
158862
|
+
return new Float32Array(blob.slice(0));
|
|
158863
|
+
}
|
|
158864
|
+
function buildCanonicalChunkTextFromFts(db, sessionId, startOrdinal, endOrdinal) {
|
|
158865
|
+
if (endOrdinal < startOrdinal)
|
|
158866
|
+
return "";
|
|
158867
|
+
const rows = getLoadFtsRowsStatement(db).all(sessionId, startOrdinal, endOrdinal).map((row) => row);
|
|
158868
|
+
const lines = [];
|
|
158869
|
+
let current = null;
|
|
158870
|
+
const flush2 = () => {
|
|
158871
|
+
if (!current || current.parts.length === 0)
|
|
158872
|
+
return;
|
|
158873
|
+
lines.push(`${formatOrdinalRange(current.start, current.end)} ${current.role}: ${current.parts.join(" / ")}`);
|
|
158874
|
+
current = null;
|
|
158875
|
+
};
|
|
158876
|
+
for (const row of rows) {
|
|
158877
|
+
const ordinal = parseOrdinal(row.messageOrdinal);
|
|
158878
|
+
const prefix = rolePrefix(row.role);
|
|
158879
|
+
const content = typeof row.content === "string" ? normalizeContent(row.content) : "";
|
|
158880
|
+
if (ordinal === null || prefix === null || content.length === 0)
|
|
158881
|
+
continue;
|
|
158882
|
+
if (current && current.role === prefix) {
|
|
158883
|
+
current.end = ordinal;
|
|
158884
|
+
current.parts.push(content);
|
|
158885
|
+
continue;
|
|
158886
|
+
}
|
|
158887
|
+
flush2();
|
|
158888
|
+
current = { role: prefix, start: ordinal, end: ordinal, parts: [content] };
|
|
158889
|
+
}
|
|
158890
|
+
flush2();
|
|
158891
|
+
return lines.join(`
|
|
158892
|
+
`);
|
|
158893
|
+
}
|
|
158894
|
+
function canonicalizeInMemoryChunkTextForEmbedding(chunkText, startOrdinal, endOrdinal) {
|
|
158895
|
+
const lines = [];
|
|
158896
|
+
for (const rawLine of chunkText.split(/\r?\n/)) {
|
|
158897
|
+
const line = rawLine.trim();
|
|
158898
|
+
const match = /^(\[(\d+)(?:-(\d+))?\]\s+[UA]:)\s*(.*)$/.exec(line);
|
|
158899
|
+
if (!match)
|
|
158900
|
+
continue;
|
|
158901
|
+
const lineStart = Number.parseInt(match[2], 10);
|
|
158902
|
+
const lineEnd = match[3] ? Number.parseInt(match[3], 10) : lineStart;
|
|
158903
|
+
if (startOrdinal != null && lineEnd < startOrdinal)
|
|
158904
|
+
continue;
|
|
158905
|
+
if (endOrdinal != null && lineStart > endOrdinal)
|
|
158906
|
+
continue;
|
|
158907
|
+
const rawParts = match[4].split(" / ").map((part) => normalizeContent(part)).filter((part) => part.length > 0);
|
|
158908
|
+
const ordinalSpan = lineEnd - lineStart + 1;
|
|
158909
|
+
const roleLabel = match[1].slice(match[1].indexOf("]") + 2);
|
|
158910
|
+
if (ordinalSpan === rawParts.length) {
|
|
158911
|
+
const retained = rawParts.map((part, index) => ({ ordinal: lineStart + index, part })).filter(({ ordinal, part }) => {
|
|
158912
|
+
if (part.startsWith("TC:"))
|
|
158913
|
+
return false;
|
|
158914
|
+
if (startOrdinal != null && ordinal < startOrdinal)
|
|
158915
|
+
return false;
|
|
158916
|
+
if (endOrdinal != null && ordinal > endOrdinal)
|
|
158917
|
+
return false;
|
|
158918
|
+
return true;
|
|
158919
|
+
});
|
|
158920
|
+
if (retained.length === 0)
|
|
158921
|
+
continue;
|
|
158922
|
+
const retainedStart = retained[0].ordinal;
|
|
158923
|
+
const retainedEnd = retained[retained.length - 1].ordinal;
|
|
158924
|
+
lines.push(`${formatOrdinalRange(retainedStart, retainedEnd)} ${roleLabel} ${retained.map(({ part }) => part).join(" / ")}`);
|
|
158925
|
+
continue;
|
|
158926
|
+
}
|
|
158927
|
+
const parts = rawParts.filter((part) => !part.startsWith("TC:"));
|
|
158928
|
+
if (parts.length === 0)
|
|
158929
|
+
continue;
|
|
158930
|
+
lines.push(`${match[1]} ${parts.join(" / ")}`);
|
|
158931
|
+
}
|
|
158932
|
+
return lines.join(`
|
|
158933
|
+
`);
|
|
158934
|
+
}
|
|
158935
|
+
function chunkCanonicalText(canonicalText, startOrdinal, endOrdinal, maxInputTokens) {
|
|
158936
|
+
const lines = canonicalText.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0);
|
|
158937
|
+
if (lines.length === 0 || endOrdinal < startOrdinal)
|
|
158938
|
+
return [];
|
|
158939
|
+
const normalizedMax = normalizeCompartmentChunkMaxInputTokens(maxInputTokens);
|
|
158940
|
+
const fullText = lines.join(`
|
|
158941
|
+
`);
|
|
158942
|
+
if (estimateTokens(fullText) <= normalizedMax) {
|
|
158943
|
+
return [
|
|
158944
|
+
{
|
|
158945
|
+
windowIndex: 0,
|
|
158946
|
+
startOrdinal,
|
|
158947
|
+
endOrdinal,
|
|
158948
|
+
text: fullText,
|
|
158949
|
+
chunkHash: hashChunkText(fullText)
|
|
158950
|
+
}
|
|
158951
|
+
];
|
|
158952
|
+
}
|
|
158953
|
+
const windows = [];
|
|
158954
|
+
let currentLines = [];
|
|
158955
|
+
let currentStart = null;
|
|
158956
|
+
let currentEnd = null;
|
|
158957
|
+
let currentTokens = 0;
|
|
158958
|
+
const flush2 = () => {
|
|
158959
|
+
if (currentLines.length === 0 || currentStart === null || currentEnd === null)
|
|
158960
|
+
return;
|
|
158961
|
+
const text = currentLines.join(`
|
|
158962
|
+
`);
|
|
158963
|
+
windows.push({
|
|
158964
|
+
windowIndex: windows.length + 1,
|
|
158965
|
+
startOrdinal: currentStart,
|
|
158966
|
+
endOrdinal: currentEnd,
|
|
158967
|
+
text,
|
|
158968
|
+
chunkHash: hashChunkText(text)
|
|
158969
|
+
});
|
|
158970
|
+
currentLines = [];
|
|
158971
|
+
currentStart = null;
|
|
158972
|
+
currentEnd = null;
|
|
158973
|
+
currentTokens = 0;
|
|
158974
|
+
};
|
|
158975
|
+
for (const line of lines) {
|
|
158976
|
+
const range = parseCanonicalLineRange(line);
|
|
158977
|
+
const lineStart = range?.start ?? startOrdinal;
|
|
158978
|
+
const lineEnd = range?.end ?? lineStart;
|
|
158979
|
+
const lineTokens = estimateTokens(line);
|
|
158980
|
+
if (currentLines.length > 0 && currentTokens + lineTokens > normalizedMax) {
|
|
158981
|
+
flush2();
|
|
158982
|
+
}
|
|
158983
|
+
if (currentLines.length === 0) {
|
|
158984
|
+
currentStart = lineStart;
|
|
158985
|
+
}
|
|
158986
|
+
currentLines.push(line);
|
|
158987
|
+
currentEnd = lineEnd;
|
|
158988
|
+
currentTokens += lineTokens;
|
|
158989
|
+
}
|
|
158990
|
+
flush2();
|
|
158991
|
+
return windows;
|
|
158992
|
+
}
|
|
158993
|
+
function getExistingChunkHashes(db, compartmentId, modelId, projectPath) {
|
|
158994
|
+
const scoped = typeof projectPath === "string" && projectPath.length > 0;
|
|
158995
|
+
const rows = scoped ? getExistingHashStatement(db, true).all(compartmentId, modelId, projectPath) : getExistingHashStatement(db, false).all(compartmentId, modelId);
|
|
158996
|
+
return new Map(rows.filter((row) => typeof row.windowIndex === "number" && typeof row.chunkHash === "string").map((row) => [row.windowIndex, row.chunkHash]));
|
|
158997
|
+
}
|
|
158998
|
+
function chunkEmbeddingWindowsAreCurrent(db, compartmentId, modelId, windows, projectPath) {
|
|
158999
|
+
const existing = getExistingChunkHashes(db, compartmentId, modelId, projectPath);
|
|
159000
|
+
if (existing.size !== windows.length)
|
|
159001
|
+
return false;
|
|
159002
|
+
return windows.every((window) => existing.get(window.windowIndex) === window.chunkHash);
|
|
159003
|
+
}
|
|
159004
|
+
function replaceCompartmentChunkEmbeddings(db, rows) {
|
|
159005
|
+
if (rows.length === 0)
|
|
159006
|
+
return;
|
|
159007
|
+
const compartmentId = rows[0].compartmentId;
|
|
159008
|
+
const now = Date.now();
|
|
159009
|
+
db.transaction(() => {
|
|
159010
|
+
getDeleteByCompartmentStatement(db).run(compartmentId);
|
|
159011
|
+
const insert = getInsertEmbeddingStatement(db);
|
|
159012
|
+
for (const row of rows) {
|
|
159013
|
+
insert.run(row.compartmentId, row.sessionId, row.projectPath, getHarness(), row.window.windowIndex, row.window.startOrdinal, row.window.endOrdinal, row.window.chunkHash, row.modelId, row.vector.length, vectorBlob(row.vector), row.createdAt ?? now);
|
|
159014
|
+
}
|
|
159015
|
+
})();
|
|
159016
|
+
}
|
|
159017
|
+
function getDistinctChunkEmbeddingModelIds(db, projectPath) {
|
|
159018
|
+
const rows = getDistinctModelStatement(db).all(projectPath);
|
|
159019
|
+
return new Set(rows.map((row) => typeof row.modelId === "string" ? row.modelId : null));
|
|
159020
|
+
}
|
|
159021
|
+
function clearChunkEmbeddingsForProject(db, projectPath, modelId) {
|
|
159022
|
+
if (modelId) {
|
|
159023
|
+
return getClearProjectModelStatement(db).run(projectPath, modelId).changes;
|
|
159024
|
+
}
|
|
159025
|
+
return getClearProjectStatement(db).run(projectPath).changes;
|
|
159026
|
+
}
|
|
159027
|
+
function loadCompartmentChunkEmbeddingsForSearch(db, sessionId, projectPath, modelId) {
|
|
159028
|
+
const rows = modelId ? getSearchRowsStatement(db, true).all(sessionId, projectPath, modelId) : getSearchRowsStatement(db, false).all(sessionId, projectPath);
|
|
159029
|
+
return rows.filter((row) => typeof row.compartmentId === "number" && typeof row.sessionId === "string" && typeof row.title === "string" && typeof row.compartmentStart === "number" && typeof row.compartmentEnd === "number" && typeof row.windowIndex === "number" && typeof row.windowStart === "number" && typeof row.windowEnd === "number" && typeof row.chunkHash === "string" && typeof row.modelId === "string" && typeof row.dims === "number" && (row.vector instanceof Uint8Array || row.vector instanceof ArrayBuffer)).map((row) => ({
|
|
159030
|
+
compartmentId: row.compartmentId,
|
|
159031
|
+
sessionId: row.sessionId,
|
|
159032
|
+
title: row.title,
|
|
159033
|
+
startOrdinal: row.compartmentStart,
|
|
159034
|
+
endOrdinal: row.compartmentEnd,
|
|
159035
|
+
windowIndex: row.windowIndex,
|
|
159036
|
+
windowStartOrdinal: row.windowStart,
|
|
159037
|
+
windowEndOrdinal: row.windowEnd,
|
|
159038
|
+
chunkHash: row.chunkHash,
|
|
159039
|
+
modelId: row.modelId,
|
|
159040
|
+
dims: row.dims,
|
|
159041
|
+
vector: toFloat32Array(row.vector)
|
|
159042
|
+
}));
|
|
159043
|
+
}
|
|
159044
|
+
function mapBackfillCandidateRows(rows) {
|
|
159045
|
+
return rows.filter((row) => {
|
|
159046
|
+
if (row === null || typeof row !== "object")
|
|
159047
|
+
return false;
|
|
159048
|
+
const candidate = row;
|
|
159049
|
+
return typeof candidate.id === "number" && typeof candidate.sessionId === "string" && typeof candidate.startMessage === "number" && typeof candidate.endMessage === "number" && typeof candidate.title === "string";
|
|
159050
|
+
}).map((row) => ({
|
|
159051
|
+
id: row.id,
|
|
159052
|
+
sessionId: row.sessionId,
|
|
159053
|
+
startMessage: row.startMessage,
|
|
159054
|
+
endMessage: row.endMessage,
|
|
159055
|
+
title: row.title
|
|
159056
|
+
}));
|
|
159057
|
+
}
|
|
159058
|
+
var sessionBackfillCandidateStatements = new WeakMap;
|
|
159059
|
+
function loadUnembeddedSessionChunkCandidates(db, projectPath, sessionId, modelId, limit, excludeIds) {
|
|
159060
|
+
if (excludeIds && excludeIds.length > 0) {
|
|
159061
|
+
const placeholders2 = excludeIds.map(() => "?").join(", ");
|
|
159062
|
+
const stmt2 = db.prepare(`SELECT c.id AS id,
|
|
159063
|
+
c.session_id AS sessionId,
|
|
159064
|
+
c.start_message AS startMessage,
|
|
159065
|
+
c.end_message AS endMessage,
|
|
159066
|
+
c.title AS title
|
|
159067
|
+
FROM compartments c
|
|
159068
|
+
JOIN session_projects sp
|
|
159069
|
+
ON sp.session_id = c.session_id
|
|
159070
|
+
AND sp.harness = c.harness
|
|
159071
|
+
AND sp.project_path = ?
|
|
159072
|
+
WHERE c.session_id = ?
|
|
159073
|
+
AND c.start_message IS NOT NULL
|
|
159074
|
+
AND c.end_message IS NOT NULL
|
|
159075
|
+
AND c.id NOT IN (${placeholders2})
|
|
159076
|
+
AND NOT EXISTS (
|
|
159077
|
+
SELECT 1
|
|
159078
|
+
FROM compartment_chunk_embeddings current
|
|
159079
|
+
WHERE current.compartment_id = c.id
|
|
159080
|
+
AND current.project_path = ?
|
|
159081
|
+
AND current.model_id = ?
|
|
159082
|
+
)
|
|
159083
|
+
ORDER BY c.start_message ASC, c.id ASC
|
|
159084
|
+
LIMIT ?`);
|
|
159085
|
+
const rows2 = stmt2.all(projectPath, sessionId, ...excludeIds, projectPath, modelId, Math.max(1, limit));
|
|
159086
|
+
return mapBackfillCandidateRows(rows2);
|
|
159087
|
+
}
|
|
159088
|
+
let stmt = sessionBackfillCandidateStatements.get(db);
|
|
159089
|
+
if (!stmt) {
|
|
159090
|
+
stmt = db.prepare(`SELECT c.id AS id,
|
|
159091
|
+
c.session_id AS sessionId,
|
|
159092
|
+
c.start_message AS startMessage,
|
|
159093
|
+
c.end_message AS endMessage,
|
|
159094
|
+
c.title AS title
|
|
159095
|
+
FROM compartments c
|
|
159096
|
+
JOIN session_projects sp
|
|
159097
|
+
ON sp.session_id = c.session_id
|
|
159098
|
+
AND sp.harness = c.harness
|
|
159099
|
+
AND sp.project_path = ?
|
|
159100
|
+
WHERE c.session_id = ?
|
|
159101
|
+
AND c.start_message IS NOT NULL
|
|
159102
|
+
AND c.end_message IS NOT NULL
|
|
159103
|
+
AND NOT EXISTS (
|
|
159104
|
+
SELECT 1
|
|
159105
|
+
FROM compartment_chunk_embeddings current
|
|
159106
|
+
WHERE current.compartment_id = c.id
|
|
159107
|
+
AND current.project_path = ?
|
|
159108
|
+
AND current.model_id = ?
|
|
159109
|
+
)
|
|
159110
|
+
ORDER BY c.start_message ASC, c.id ASC
|
|
159111
|
+
LIMIT ?`);
|
|
159112
|
+
sessionBackfillCandidateStatements.set(db, stmt);
|
|
159113
|
+
}
|
|
159114
|
+
const rows = stmt.all(projectPath, sessionId, projectPath, modelId, Math.max(1, limit));
|
|
159115
|
+
return mapBackfillCandidateRows(rows);
|
|
159116
|
+
}
|
|
159117
|
+
function countUnembeddedSessionCompartments(db, projectPath, sessionId, modelId) {
|
|
159118
|
+
const row = db.prepare(`SELECT COUNT(*) AS n
|
|
159119
|
+
FROM compartments c
|
|
159120
|
+
JOIN session_projects sp
|
|
159121
|
+
ON sp.session_id = c.session_id
|
|
159122
|
+
AND sp.harness = c.harness
|
|
159123
|
+
AND sp.project_path = ?
|
|
159124
|
+
WHERE c.session_id = ?
|
|
159125
|
+
AND c.start_message IS NOT NULL
|
|
159126
|
+
AND c.end_message IS NOT NULL
|
|
159127
|
+
AND NOT EXISTS (
|
|
159128
|
+
SELECT 1
|
|
159129
|
+
FROM compartment_chunk_embeddings current
|
|
159130
|
+
WHERE current.compartment_id = c.id
|
|
159131
|
+
AND current.project_path = ?
|
|
159132
|
+
AND current.model_id = ?
|
|
159133
|
+
)`).get(projectPath, sessionId, projectPath, modelId);
|
|
159134
|
+
return typeof row?.n === "number" ? row.n : 0;
|
|
159135
|
+
}
|
|
159136
|
+
|
|
158009
159137
|
// ../plugin/src/features/magic-context/memory/cosine-similarity.ts
|
|
158010
159138
|
function cosineSimilarity(a, b) {
|
|
158011
159139
|
if (a.length !== b.length) {
|
|
@@ -158024,13 +159152,13 @@ function cosineSimilarity(a, b) {
|
|
|
158024
159152
|
}
|
|
158025
159153
|
|
|
158026
159154
|
// ../plugin/src/features/magic-context/memory/normalize-hash.ts
|
|
158027
|
-
import { createHash as
|
|
159155
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
158028
159156
|
function normalizeMemoryContent(content) {
|
|
158029
159157
|
return content.toLowerCase().replace(/\s+/g, " ").trim();
|
|
158030
159158
|
}
|
|
158031
159159
|
function computeNormalizedHash(content) {
|
|
158032
159160
|
const normalized = normalizeMemoryContent(content);
|
|
158033
|
-
return
|
|
159161
|
+
return createHash5("md5").update(normalized).digest("hex");
|
|
158034
159162
|
}
|
|
158035
159163
|
|
|
158036
159164
|
// ../plugin/src/features/magic-context/memory/embedding-identity.ts
|
|
@@ -158174,19 +159302,19 @@ function isArrayLikeNumber(value) {
|
|
|
158174
159302
|
}
|
|
158175
159303
|
return arr.length === 0 || typeof arr[0] === "number";
|
|
158176
159304
|
}
|
|
158177
|
-
function
|
|
159305
|
+
function toFloat32Array2(values) {
|
|
158178
159306
|
return values instanceof Float32Array ? new Float32Array(values) : Float32Array.from(Array.from(values));
|
|
158179
159307
|
}
|
|
158180
159308
|
function extractBatchEmbeddings(result, expectedCount) {
|
|
158181
159309
|
const { data } = result;
|
|
158182
159310
|
if (Array.isArray(data) && data.length === expectedCount && data.every((entry) => typeof entry !== "number" && isArrayLikeNumber(entry))) {
|
|
158183
|
-
return data.map((entry) =>
|
|
159311
|
+
return data.map((entry) => toFloat32Array2(entry));
|
|
158184
159312
|
}
|
|
158185
159313
|
if (!isArrayLikeNumber(data)) {
|
|
158186
159314
|
log("[magic-context] embedding batch returned unexpected data shape");
|
|
158187
159315
|
return Array.from({ length: expectedCount }, () => null);
|
|
158188
159316
|
}
|
|
158189
|
-
const flatData =
|
|
159317
|
+
const flatData = toFloat32Array2(data);
|
|
158190
159318
|
const dimension = result.dims?.at(-1) ?? flatData.length / expectedCount;
|
|
158191
159319
|
if (!Number.isInteger(dimension) || dimension <= 0 || flatData.length !== expectedCount * dimension) {
|
|
158192
159320
|
log("[magic-context] embedding batch returned invalid dimensions");
|
|
@@ -158201,6 +159329,7 @@ function extractBatchEmbeddings(result, expectedCount) {
|
|
|
158201
159329
|
|
|
158202
159330
|
class LocalEmbeddingProvider {
|
|
158203
159331
|
modelId;
|
|
159332
|
+
maxInputTokens;
|
|
158204
159333
|
model;
|
|
158205
159334
|
pipeline = null;
|
|
158206
159335
|
initPromise = null;
|
|
@@ -158208,8 +159337,9 @@ class LocalEmbeddingProvider {
|
|
|
158208
159337
|
disposing = false;
|
|
158209
159338
|
disposePromise = null;
|
|
158210
159339
|
inFlightWaiters = [];
|
|
158211
|
-
constructor(model = DEFAULT_LOCAL_EMBEDDING_MODEL) {
|
|
159340
|
+
constructor(model = DEFAULT_LOCAL_EMBEDDING_MODEL, maxInputTokens = 512) {
|
|
158212
159341
|
this.model = model;
|
|
159342
|
+
this.maxInputTokens = maxInputTokens;
|
|
158213
159343
|
this.modelId = getEmbeddingProviderIdentity({ provider: "local", model });
|
|
158214
159344
|
}
|
|
158215
159345
|
async initialize() {
|
|
@@ -158464,6 +159594,7 @@ var FETCH_TIMEOUT_MS = 30000;
|
|
|
158464
159594
|
|
|
158465
159595
|
class OpenAICompatibleEmbeddingProvider {
|
|
158466
159596
|
modelId;
|
|
159597
|
+
maxInputTokens;
|
|
158467
159598
|
endpoint;
|
|
158468
159599
|
model;
|
|
158469
159600
|
apiKey;
|
|
@@ -158480,11 +159611,13 @@ class OpenAICompatibleEmbeddingProvider {
|
|
|
158480
159611
|
this.apiKey = options.apiKey?.trim() ?? "";
|
|
158481
159612
|
this.inputType = options.inputType?.trim() ?? "";
|
|
158482
159613
|
this.truncate = options.truncate?.trim() ?? "";
|
|
159614
|
+
this.maxInputTokens = typeof options.maxInputTokens === "number" && Number.isFinite(options.maxInputTokens) ? Math.max(1, Math.floor(options.maxInputTokens)) : 512;
|
|
158483
159615
|
this.modelId = getEmbeddingProviderIdentity({
|
|
158484
159616
|
provider: "openai-compatible",
|
|
158485
159617
|
endpoint: this.endpoint,
|
|
158486
159618
|
model: this.model,
|
|
158487
|
-
...this.apiKey ? { api_key: this.apiKey } : {}
|
|
159619
|
+
...this.apiKey ? { api_key: this.apiKey } : {},
|
|
159620
|
+
...this.inputType ? { input_type: this.inputType } : {}
|
|
158488
159621
|
});
|
|
158489
159622
|
}
|
|
158490
159623
|
async initialize() {
|
|
@@ -158688,9 +159821,9 @@ function isEmbeddingRow(row) {
|
|
|
158688
159821
|
if (row === null || typeof row !== "object")
|
|
158689
159822
|
return false;
|
|
158690
159823
|
const candidate = row;
|
|
158691
|
-
return typeof candidate.memoryId === "number" && isEmbeddingBlob(candidate.embedding);
|
|
159824
|
+
return typeof candidate.memoryId === "number" && isEmbeddingBlob(candidate.embedding) && (candidate.modelId === null || typeof candidate.modelId === "string");
|
|
158692
159825
|
}
|
|
158693
|
-
function
|
|
159826
|
+
function toFloat32Array3(blob) {
|
|
158694
159827
|
if (blob instanceof Uint8Array) {
|
|
158695
159828
|
const buffer2 = blob.buffer.slice(blob.byteOffset, blob.byteOffset + blob.byteLength);
|
|
158696
159829
|
return new Float32Array(buffer2);
|
|
@@ -158708,7 +159841,7 @@ function getSaveEmbeddingStatement(db) {
|
|
|
158708
159841
|
function getLoadAllEmbeddingsStatement(db) {
|
|
158709
159842
|
let stmt = loadAllEmbeddingsStatements.get(db);
|
|
158710
159843
|
if (!stmt) {
|
|
158711
|
-
stmt = db.prepare("SELECT memory_embeddings.memory_id AS memoryId, memory_embeddings.embedding AS embedding FROM memory_embeddings INNER JOIN memories ON memories.id = memory_embeddings.memory_id WHERE memories.project_path = ? ORDER BY memory_embeddings.memory_id ASC");
|
|
159844
|
+
stmt = db.prepare("SELECT memory_embeddings.memory_id AS memoryId, memory_embeddings.embedding AS embedding, memory_embeddings.model_id AS modelId FROM memory_embeddings INNER JOIN memories ON memories.id = memory_embeddings.memory_id WHERE memories.project_path = ? ORDER BY memory_embeddings.memory_id ASC");
|
|
158712
159845
|
loadAllEmbeddingsStatements.set(db, stmt);
|
|
158713
159846
|
}
|
|
158714
159847
|
return stmt;
|
|
@@ -158737,7 +159870,10 @@ function loadAllEmbeddings(db, projectPath) {
|
|
|
158737
159870
|
const rows = getLoadAllEmbeddingsStatement(db).all(projectPath).filter(isEmbeddingRow);
|
|
158738
159871
|
const embeddings = new Map;
|
|
158739
159872
|
for (const row of rows) {
|
|
158740
|
-
embeddings.set(row.memoryId,
|
|
159873
|
+
embeddings.set(row.memoryId, {
|
|
159874
|
+
embedding: toFloat32Array3(row.embedding),
|
|
159875
|
+
modelId: row.modelId
|
|
159876
|
+
});
|
|
158741
159877
|
}
|
|
158742
159878
|
return embeddings;
|
|
158743
159879
|
}
|
|
@@ -158750,7 +159886,7 @@ function getDistinctStoredModelIds(db, projectPath) {
|
|
|
158750
159886
|
}
|
|
158751
159887
|
|
|
158752
159888
|
// ../plugin/src/features/magic-context/project-embedding-registry.ts
|
|
158753
|
-
import { createHash as
|
|
159889
|
+
import { createHash as createHash6, randomUUID } from "node:crypto";
|
|
158754
159890
|
init_logger();
|
|
158755
159891
|
|
|
158756
159892
|
// ../plugin/src/features/magic-context/git-commits/storage-git-commit-embeddings.ts
|
|
@@ -158758,7 +159894,7 @@ var saveStatements = new WeakMap;
|
|
|
158758
159894
|
var loadProjectStatements = new WeakMap;
|
|
158759
159895
|
var loadUnembeddedStatements = new WeakMap;
|
|
158760
159896
|
var countEmbeddedStatements = new WeakMap;
|
|
158761
|
-
var
|
|
159897
|
+
var clearProjectStatements2 = new WeakMap;
|
|
158762
159898
|
var distinctModelIdStatements = new WeakMap;
|
|
158763
159899
|
function getSaveStatement(db) {
|
|
158764
159900
|
let stmt = saveStatements.get(db);
|
|
@@ -158806,12 +159942,12 @@ function getCountEmbeddedStatement(db) {
|
|
|
158806
159942
|
}
|
|
158807
159943
|
return stmt;
|
|
158808
159944
|
}
|
|
158809
|
-
function
|
|
158810
|
-
let stmt =
|
|
159945
|
+
function getClearProjectStatement2(db) {
|
|
159946
|
+
let stmt = clearProjectStatements2.get(db);
|
|
158811
159947
|
if (!stmt) {
|
|
158812
159948
|
stmt = db.prepare(`DELETE FROM git_commit_embeddings
|
|
158813
159949
|
WHERE sha IN (SELECT sha FROM git_commits WHERE project_path = ?)`);
|
|
158814
|
-
|
|
159950
|
+
clearProjectStatements2.set(db, stmt);
|
|
158815
159951
|
}
|
|
158816
159952
|
return stmt;
|
|
158817
159953
|
}
|
|
@@ -158847,7 +159983,7 @@ function countEmbeddedCommits(db, projectPath) {
|
|
|
158847
159983
|
return row?.count ?? 0;
|
|
158848
159984
|
}
|
|
158849
159985
|
function clearProjectCommitEmbeddings(db, projectPath) {
|
|
158850
|
-
return
|
|
159986
|
+
return getClearProjectStatement2(db).run(projectPath).changes;
|
|
158851
159987
|
}
|
|
158852
159988
|
function getDistinctCommitEmbeddingModelIds(db, projectPath) {
|
|
158853
159989
|
const rows = getDistinctModelIdStatement(db).all(projectPath);
|
|
@@ -159011,9 +160147,83 @@ function invalidateMemory(projectPath, memoryId) {
|
|
|
159011
160147
|
cached2?.embeddings.delete(memoryId);
|
|
159012
160148
|
}
|
|
159013
160149
|
|
|
160150
|
+
// ../plugin/src/features/magic-context/session-project-storage.ts
|
|
160151
|
+
var upsertSessionProjectStatements = new WeakMap;
|
|
160152
|
+
var repairSessionChunkProjectStatements = new WeakMap;
|
|
160153
|
+
var repairProjectChunkProjectStatements = new WeakMap;
|
|
160154
|
+
function getUpsertSessionProjectStatement(db) {
|
|
160155
|
+
let stmt = upsertSessionProjectStatements.get(db);
|
|
160156
|
+
if (!stmt) {
|
|
160157
|
+
stmt = db.prepare(`INSERT INTO session_projects (session_id, harness, project_path, updated_at)
|
|
160158
|
+
VALUES (?, ?, ?, ?)
|
|
160159
|
+
ON CONFLICT(session_id, harness) DO UPDATE SET
|
|
160160
|
+
project_path = excluded.project_path,
|
|
160161
|
+
updated_at = excluded.updated_at
|
|
160162
|
+
WHERE session_projects.project_path <> excluded.project_path`);
|
|
160163
|
+
upsertSessionProjectStatements.set(db, stmt);
|
|
160164
|
+
}
|
|
160165
|
+
return stmt;
|
|
160166
|
+
}
|
|
160167
|
+
function getRepairSessionChunkProjectStatement(db) {
|
|
160168
|
+
let stmt = repairSessionChunkProjectStatements.get(db);
|
|
160169
|
+
if (!stmt) {
|
|
160170
|
+
stmt = db.prepare(`UPDATE compartment_chunk_embeddings
|
|
160171
|
+
SET project_path = ?
|
|
160172
|
+
WHERE session_id = ?
|
|
160173
|
+
AND harness = ?
|
|
160174
|
+
AND project_path <> ?`);
|
|
160175
|
+
repairSessionChunkProjectStatements.set(db, stmt);
|
|
160176
|
+
}
|
|
160177
|
+
return stmt;
|
|
160178
|
+
}
|
|
160179
|
+
function getRepairProjectChunkProjectStatement(db) {
|
|
160180
|
+
let stmt = repairProjectChunkProjectStatements.get(db);
|
|
160181
|
+
if (!stmt) {
|
|
160182
|
+
stmt = db.prepare(`UPDATE compartment_chunk_embeddings
|
|
160183
|
+
SET project_path = (
|
|
160184
|
+
SELECT sp.project_path
|
|
160185
|
+
FROM session_projects sp
|
|
160186
|
+
WHERE sp.session_id = compartment_chunk_embeddings.session_id
|
|
160187
|
+
AND sp.harness = compartment_chunk_embeddings.harness
|
|
160188
|
+
LIMIT 1
|
|
160189
|
+
)
|
|
160190
|
+
WHERE EXISTS (
|
|
160191
|
+
SELECT 1
|
|
160192
|
+
FROM session_projects sp
|
|
160193
|
+
WHERE sp.session_id = compartment_chunk_embeddings.session_id
|
|
160194
|
+
AND sp.harness = compartment_chunk_embeddings.harness
|
|
160195
|
+
AND sp.project_path <> compartment_chunk_embeddings.project_path
|
|
160196
|
+
AND (
|
|
160197
|
+
sp.project_path = ?
|
|
160198
|
+
OR compartment_chunk_embeddings.project_path = ?
|
|
160199
|
+
)
|
|
160200
|
+
)`);
|
|
160201
|
+
repairProjectChunkProjectStatements.set(db, stmt);
|
|
160202
|
+
}
|
|
160203
|
+
return stmt;
|
|
160204
|
+
}
|
|
160205
|
+
function recordSessionProjectIdentity(db, sessionId, projectPath) {
|
|
160206
|
+
if (!sessionId || !projectPath)
|
|
160207
|
+
return;
|
|
160208
|
+
const harness = getHarness();
|
|
160209
|
+
const now = Date.now();
|
|
160210
|
+
db.transaction(() => {
|
|
160211
|
+
getUpsertSessionProjectStatement(db).run(sessionId, harness, projectPath, now);
|
|
160212
|
+
getRepairSessionChunkProjectStatement(db).run(projectPath, sessionId, harness, projectPath);
|
|
160213
|
+
})();
|
|
160214
|
+
}
|
|
160215
|
+
function repairMisScopedCompartmentChunkEmbeddingsForProject(db, projectPath) {
|
|
160216
|
+
if (!projectPath)
|
|
160217
|
+
return 0;
|
|
160218
|
+
return getRepairProjectChunkProjectStatement(db).run(projectPath, projectPath).changes;
|
|
160219
|
+
}
|
|
160220
|
+
|
|
159014
160221
|
// ../plugin/src/features/magic-context/project-embedding-registry.ts
|
|
159015
160222
|
var OFF_PROVIDER_IDENTITY = "embedding-provider:off";
|
|
159016
160223
|
var SWEEP_MAX_WALL_CLOCK_MS = 10 * 60 * 1000;
|
|
160224
|
+
var CHUNK_DRAIN_BATCH_SIZE = 8;
|
|
160225
|
+
var MAX_WINDOWS_PER_EMBED_CALL = 16;
|
|
160226
|
+
var SESSION_EMBED_LEASE_RENEWAL_MS = 60 * 1000;
|
|
159017
160227
|
var projectRegistrations = new Map;
|
|
159018
160228
|
var loadUnembeddedMemoriesStatements = new WeakMap;
|
|
159019
160229
|
var globalRegistrationGeneration = 0;
|
|
@@ -159022,7 +160232,10 @@ function resolveEmbeddingConfig(config2) {
|
|
|
159022
160232
|
if (!config2 || config2.provider === "local") {
|
|
159023
160233
|
return {
|
|
159024
160234
|
provider: "local",
|
|
159025
|
-
model: config2?.model?.trim() || DEFAULT_LOCAL_EMBEDDING_MODEL
|
|
160235
|
+
model: config2?.model?.trim() || DEFAULT_LOCAL_EMBEDDING_MODEL,
|
|
160236
|
+
...config2?.max_input_tokens ? {
|
|
160237
|
+
max_input_tokens: normalizeCompartmentChunkMaxInputTokens(config2.max_input_tokens)
|
|
160238
|
+
} : {}
|
|
159026
160239
|
};
|
|
159027
160240
|
}
|
|
159028
160241
|
if (config2.provider === "openai-compatible") {
|
|
@@ -159035,7 +160248,10 @@ function resolveEmbeddingConfig(config2) {
|
|
|
159035
160248
|
endpoint: config2.endpoint.trim(),
|
|
159036
160249
|
...apiKey ? { api_key: apiKey } : {},
|
|
159037
160250
|
...inputType ? { input_type: inputType } : {},
|
|
159038
|
-
...truncate ? { truncate } : {}
|
|
160251
|
+
...truncate ? { truncate } : {},
|
|
160252
|
+
...config2.max_input_tokens ? {
|
|
160253
|
+
max_input_tokens: normalizeCompartmentChunkMaxInputTokens(config2.max_input_tokens)
|
|
160254
|
+
} : {}
|
|
159039
160255
|
};
|
|
159040
160256
|
}
|
|
159041
160257
|
return { provider: "off" };
|
|
@@ -159053,10 +160269,11 @@ function createProvider(config2) {
|
|
|
159053
160269
|
model: config2.model,
|
|
159054
160270
|
apiKey: config2.api_key,
|
|
159055
160271
|
inputType: config2.input_type,
|
|
159056
|
-
truncate: config2.truncate
|
|
160272
|
+
truncate: config2.truncate,
|
|
160273
|
+
maxInputTokens: config2.max_input_tokens
|
|
159057
160274
|
});
|
|
159058
160275
|
}
|
|
159059
|
-
return new LocalEmbeddingProvider(config2.model);
|
|
160276
|
+
return new LocalEmbeddingProvider(config2.model, config2.max_input_tokens);
|
|
159060
160277
|
}
|
|
159061
160278
|
function stableStringify2(value) {
|
|
159062
160279
|
if (Array.isArray(value)) {
|
|
@@ -159069,7 +160286,7 @@ function stableStringify2(value) {
|
|
|
159069
160286
|
return JSON.stringify(value);
|
|
159070
160287
|
}
|
|
159071
160288
|
function sha256Prefix(value, length = 16) {
|
|
159072
|
-
return
|
|
160289
|
+
return createHash6("sha256").update(value).digest("hex").slice(0, length);
|
|
159073
160290
|
}
|
|
159074
160291
|
function getRuntimeFingerprint(config2) {
|
|
159075
160292
|
if (config2.provider === "off") {
|
|
@@ -159077,6 +160294,18 @@ function getRuntimeFingerprint(config2) {
|
|
|
159077
160294
|
}
|
|
159078
160295
|
return `${getEmbeddingProviderIdentity(config2)}:${sha256Prefix(stableStringify2(config2))}`;
|
|
159079
160296
|
}
|
|
160297
|
+
function getChunkEmbeddingModelId(config2, providerIdentity) {
|
|
160298
|
+
if (config2.provider === "off") {
|
|
160299
|
+
return OFF_PROVIDER_IDENTITY;
|
|
160300
|
+
}
|
|
160301
|
+
const chunkIdentity = {
|
|
160302
|
+
providerIdentity,
|
|
160303
|
+
chunkerVersion: 1,
|
|
160304
|
+
maxInputTokens: normalizeCompartmentChunkMaxInputTokens("max_input_tokens" in config2 ? config2.max_input_tokens : undefined),
|
|
160305
|
+
truncate: config2.provider === "openai-compatible" ? config2.truncate ?? "" : ""
|
|
160306
|
+
};
|
|
160307
|
+
return `${providerIdentity}:chunk:${sha256Prefix(stableStringify2(chunkIdentity))}`;
|
|
160308
|
+
}
|
|
159080
160309
|
function sameFeatures(a, b) {
|
|
159081
160310
|
return a.memoryEnabled === b.memoryEnabled && a.gitCommitEnabled === b.gitCommitEnabled;
|
|
159082
160311
|
}
|
|
@@ -159093,7 +160322,8 @@ function snapshotFor(registration) {
|
|
|
159093
160322
|
features: { ...registration.features },
|
|
159094
160323
|
enabled,
|
|
159095
160324
|
gitCommitEnabled,
|
|
159096
|
-
modelId: registration.observationMode || !providerIsOn ? "off" : registration.modelId
|
|
160325
|
+
modelId: registration.observationMode || !providerIsOn ? "off" : registration.modelId,
|
|
160326
|
+
chunkModelId: registration.observationMode || !providerIsOn ? "off" : registration.chunkModelId
|
|
159097
160327
|
};
|
|
159098
160328
|
}
|
|
159099
160329
|
function disposeProvider(provider) {
|
|
@@ -159113,7 +160343,7 @@ function anyStoredModelIdIsStale(storedIds, currentId) {
|
|
|
159113
160343
|
}
|
|
159114
160344
|
return false;
|
|
159115
160345
|
}
|
|
159116
|
-
function maybeWipeStaleEmbeddings(db, projectIdentity, currentProviderIdentity, features) {
|
|
160346
|
+
function maybeWipeStaleEmbeddings(db, projectIdentity, currentProviderIdentity, currentChunkIdentity, features) {
|
|
159117
160347
|
if (currentProviderIdentity === OFF_PROVIDER_IDENTITY) {
|
|
159118
160348
|
return false;
|
|
159119
160349
|
}
|
|
@@ -159134,6 +160364,14 @@ function maybeWipeStaleEmbeddings(db, projectIdentity, currentProviderIdentity,
|
|
|
159134
160364
|
wiped = true;
|
|
159135
160365
|
}
|
|
159136
160366
|
}
|
|
160367
|
+
if (features.memoryEnabled) {
|
|
160368
|
+
repairMisScopedCompartmentChunkEmbeddingsForProject(db, projectIdentity);
|
|
160369
|
+
const chunkIds = getDistinctChunkEmbeddingModelIds(db, projectIdentity);
|
|
160370
|
+
if (anyStoredModelIdIsStale(chunkIds, currentChunkIdentity)) {
|
|
160371
|
+
clearChunkEmbeddingsForProject(db, projectIdentity);
|
|
160372
|
+
wiped = true;
|
|
160373
|
+
}
|
|
160374
|
+
}
|
|
159137
160375
|
})();
|
|
159138
160376
|
return wiped;
|
|
159139
160377
|
}
|
|
@@ -159141,10 +160379,11 @@ function registerProjectEmbeddingAndMaybeWipe(db, projectIdentity, config2, feat
|
|
|
159141
160379
|
const resolvedConfig = resolveEmbeddingConfig(config2);
|
|
159142
160380
|
const providerIdentity = getEmbeddingProviderIdentity(resolvedConfig);
|
|
159143
160381
|
const runtimeFingerprint = getRuntimeFingerprint(resolvedConfig);
|
|
160382
|
+
const chunkModelId = getChunkEmbeddingModelId(resolvedConfig, providerIdentity);
|
|
159144
160383
|
const prior = projectRegistrations.get(projectIdentity);
|
|
159145
160384
|
const canReuseProvider = prior !== undefined && !prior.observationMode && prior.runtimeFingerprint === runtimeFingerprint && prior.providerIdentity === providerIdentity;
|
|
159146
|
-
const wiped = maybeWipeStaleEmbeddings(db, projectIdentity, providerIdentity, features);
|
|
159147
|
-
const generationChanged = prior === undefined || prior.observationMode || prior.runtimeFingerprint !== runtimeFingerprint || !sameFeatures(prior.features, features) || wiped;
|
|
160385
|
+
const wiped = maybeWipeStaleEmbeddings(db, projectIdentity, providerIdentity, chunkModelId, features);
|
|
160386
|
+
const generationChanged = prior === undefined || prior.observationMode || prior.runtimeFingerprint !== runtimeFingerprint || prior.chunkModelId !== chunkModelId || !sameFeatures(prior.features, features) || wiped;
|
|
159148
160387
|
const generation = generationChanged ? ++globalRegistrationGeneration : prior.generation;
|
|
159149
160388
|
const registration = {
|
|
159150
160389
|
projectIdentity,
|
|
@@ -159156,6 +160395,7 @@ function registerProjectEmbeddingAndMaybeWipe(db, projectIdentity, config2, feat
|
|
|
159156
160395
|
generation,
|
|
159157
160396
|
features: { ...features },
|
|
159158
160397
|
modelId: providerIdentity === OFF_PROVIDER_IDENTITY ? "off" : providerIdentity,
|
|
160398
|
+
chunkModelId: providerIdentity === OFF_PROVIDER_IDENTITY ? "off" : chunkModelId,
|
|
159159
160399
|
observationMode: false
|
|
159160
160400
|
};
|
|
159161
160401
|
projectRegistrations.set(projectIdentity, registration);
|
|
@@ -159178,6 +160418,7 @@ function registerProjectInObservationMode(db, projectIdentity, sourceDirectory,
|
|
|
159178
160418
|
generation,
|
|
159179
160419
|
features: { memoryEnabled: false, gitCommitEnabled: false },
|
|
159180
160420
|
modelId: "off",
|
|
160421
|
+
chunkModelId: "off",
|
|
159181
160422
|
observationMode: true
|
|
159182
160423
|
};
|
|
159183
160424
|
projectRegistrations.set(projectIdentity, registration);
|
|
@@ -159188,6 +160429,15 @@ function getProjectEmbeddingSnapshot(projectIdentity) {
|
|
|
159188
160429
|
const registration = projectRegistrations.get(projectIdentity);
|
|
159189
160430
|
return registration ? snapshotFor(registration) : null;
|
|
159190
160431
|
}
|
|
160432
|
+
function getProjectChunkEmbeddingModelId(projectIdentity) {
|
|
160433
|
+
const registration = projectRegistrations.get(projectIdentity);
|
|
160434
|
+
return registration && !registration.observationMode ? registration.chunkModelId : "off";
|
|
160435
|
+
}
|
|
160436
|
+
function getProjectEmbeddingMaxInputTokens(projectIdentity) {
|
|
160437
|
+
const registration = projectRegistrations.get(projectIdentity);
|
|
160438
|
+
const configMax = registration?.config && "max_input_tokens" in registration.config ? registration.config.max_input_tokens : undefined;
|
|
160439
|
+
return normalizeCompartmentChunkMaxInputTokens(registration?.provider?.maxInputTokens ?? configMax);
|
|
160440
|
+
}
|
|
159191
160441
|
function getOrCreateProjectProvider(registration) {
|
|
159192
160442
|
if (registration.providerIdentity === OFF_PROVIDER_IDENTITY || registration.observationMode) {
|
|
159193
160443
|
return null;
|
|
@@ -159282,6 +160532,131 @@ async function embedUnembeddedMemoriesForProject(db, projectIdentity, batchSize
|
|
|
159282
160532
|
return 0;
|
|
159283
160533
|
}
|
|
159284
160534
|
}
|
|
160535
|
+
async function embedCandidateChunkBatch(db, projectIdentity, modelId, candidates, signal) {
|
|
160536
|
+
const noWork = [];
|
|
160537
|
+
if (candidates.length === 0)
|
|
160538
|
+
return { embedded: 0, noWork };
|
|
160539
|
+
const maxInputTokens = getProjectEmbeddingMaxInputTokens(projectIdentity);
|
|
160540
|
+
const prepared = [];
|
|
160541
|
+
for (const candidate of candidates) {
|
|
160542
|
+
const canonicalText = buildCanonicalChunkTextFromFts(db, candidate.sessionId, candidate.startMessage, candidate.endMessage);
|
|
160543
|
+
if (canonicalText.length === 0) {
|
|
160544
|
+
noWork.push(candidate.id);
|
|
160545
|
+
continue;
|
|
160546
|
+
}
|
|
160547
|
+
const windows = chunkCanonicalText(canonicalText, candidate.startMessage, candidate.endMessage, maxInputTokens);
|
|
160548
|
+
if (windows.length === 0 || chunkEmbeddingWindowsAreCurrent(db, candidate.id, modelId, windows, projectIdentity)) {
|
|
160549
|
+
noWork.push(candidate.id);
|
|
160550
|
+
continue;
|
|
160551
|
+
}
|
|
160552
|
+
prepared.push({ candidate, windows });
|
|
160553
|
+
}
|
|
160554
|
+
if (prepared.length === 0)
|
|
160555
|
+
return { embedded: 0, noWork };
|
|
160556
|
+
let embedded = 0;
|
|
160557
|
+
let i = 0;
|
|
160558
|
+
while (i < prepared.length) {
|
|
160559
|
+
if (signal?.aborted)
|
|
160560
|
+
break;
|
|
160561
|
+
const slice = [];
|
|
160562
|
+
let windowCount = 0;
|
|
160563
|
+
do {
|
|
160564
|
+
const item = prepared[i];
|
|
160565
|
+
slice.push(item);
|
|
160566
|
+
windowCount += item.windows.length;
|
|
160567
|
+
i += 1;
|
|
160568
|
+
} while (i < prepared.length && windowCount + prepared[i].windows.length <= MAX_WINDOWS_PER_EMBED_CALL);
|
|
160569
|
+
const texts = [];
|
|
160570
|
+
for (const item of slice)
|
|
160571
|
+
texts.push(...item.windows.map((w) => w.text));
|
|
160572
|
+
try {
|
|
160573
|
+
const result = await embedBatchForProject(projectIdentity, texts, signal);
|
|
160574
|
+
if (!result)
|
|
160575
|
+
continue;
|
|
160576
|
+
if (signal?.aborted)
|
|
160577
|
+
break;
|
|
160578
|
+
let offset = 0;
|
|
160579
|
+
for (const item of slice) {
|
|
160580
|
+
const vectors = result.vectors.slice(offset, offset + item.windows.length);
|
|
160581
|
+
offset += item.windows.length;
|
|
160582
|
+
if (vectors.length !== item.windows.length || vectors.some((v) => !v)) {
|
|
160583
|
+
continue;
|
|
160584
|
+
}
|
|
160585
|
+
const rows = item.windows.map((window, index) => ({
|
|
160586
|
+
compartmentId: item.candidate.id,
|
|
160587
|
+
sessionId: item.candidate.sessionId,
|
|
160588
|
+
projectPath: projectIdentity,
|
|
160589
|
+
window,
|
|
160590
|
+
modelId,
|
|
160591
|
+
vector: vectors[index]
|
|
160592
|
+
}));
|
|
160593
|
+
replaceCompartmentChunkEmbeddings(db, rows);
|
|
160594
|
+
embedded += 1;
|
|
160595
|
+
}
|
|
160596
|
+
} catch (error51) {
|
|
160597
|
+
log("[magic-context] failed to proactively embed compartment chunks:", error51);
|
|
160598
|
+
}
|
|
160599
|
+
}
|
|
160600
|
+
return { embedded, noWork };
|
|
160601
|
+
}
|
|
160602
|
+
async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, options) {
|
|
160603
|
+
const snapshot = getProjectEmbeddingSnapshot(projectIdentity);
|
|
160604
|
+
if (!snapshot?.enabled || snapshot.chunkModelId === "off") {
|
|
160605
|
+
return { status: "disabled", embedded: 0, total: 0 };
|
|
160606
|
+
}
|
|
160607
|
+
recordSessionProjectIdentity(db, sessionId, projectIdentity);
|
|
160608
|
+
const total = countUnembeddedSessionCompartments(db, projectIdentity, sessionId, snapshot.chunkModelId);
|
|
160609
|
+
if (total === 0)
|
|
160610
|
+
return { status: "nothing", embedded: 0, total: 0 };
|
|
160611
|
+
const holderId = `session-embed-${randomUUID()}`;
|
|
160612
|
+
const lease = acquireGitSweepLease(db, projectIdentity, holderId, { ignoreCooldown: true });
|
|
160613
|
+
if (!lease.acquired)
|
|
160614
|
+
return { status: "busy", embedded: 0, total };
|
|
160615
|
+
const renewal = setInterval(() => {
|
|
160616
|
+
try {
|
|
160617
|
+
renewGitSweepLease(db, projectIdentity, holderId);
|
|
160618
|
+
} catch {}
|
|
160619
|
+
}, SESSION_EMBED_LEASE_RENEWAL_MS);
|
|
160620
|
+
renewal.unref?.();
|
|
160621
|
+
const batchSize = Math.max(1, options?.batchSize ?? CHUNK_DRAIN_BATCH_SIZE);
|
|
160622
|
+
const skipIds = [];
|
|
160623
|
+
let embedded = 0;
|
|
160624
|
+
let aborted2 = false;
|
|
160625
|
+
let providerStalled = false;
|
|
160626
|
+
try {
|
|
160627
|
+
options?.onProgress?.({ embedded, total });
|
|
160628
|
+
for (;; ) {
|
|
160629
|
+
if (options?.signal?.aborted) {
|
|
160630
|
+
aborted2 = true;
|
|
160631
|
+
break;
|
|
160632
|
+
}
|
|
160633
|
+
const candidates = loadUnembeddedSessionChunkCandidates(db, projectIdentity, sessionId, snapshot.chunkModelId, batchSize, skipIds);
|
|
160634
|
+
if (candidates.length === 0)
|
|
160635
|
+
break;
|
|
160636
|
+
const { embedded: n, noWork } = await embedCandidateChunkBatch(db, projectIdentity, snapshot.chunkModelId, candidates, options?.signal);
|
|
160637
|
+
for (const id of noWork)
|
|
160638
|
+
skipIds.push(id);
|
|
160639
|
+
if (n === 0 && noWork.length === 0) {
|
|
160640
|
+
providerStalled = true;
|
|
160641
|
+
break;
|
|
160642
|
+
}
|
|
160643
|
+
embedded += n;
|
|
160644
|
+
options?.onProgress?.({ embedded: Math.min(embedded, total), total });
|
|
160645
|
+
await new Promise((resolve3) => setTimeout(resolve3, 0));
|
|
160646
|
+
}
|
|
160647
|
+
} finally {
|
|
160648
|
+
clearInterval(renewal);
|
|
160649
|
+
releaseGitSweepLease(db, projectIdentity, holderId);
|
|
160650
|
+
}
|
|
160651
|
+
if (aborted2)
|
|
160652
|
+
return { status: "aborted", embedded, total };
|
|
160653
|
+
if (providerStalled) {
|
|
160654
|
+
const remaining = Math.max(0, countUnembeddedSessionCompartments(db, projectIdentity, sessionId, snapshot.chunkModelId) - skipIds.length);
|
|
160655
|
+
if (remaining > 0)
|
|
160656
|
+
return { status: "stalled", embedded, total, remaining };
|
|
160657
|
+
}
|
|
160658
|
+
return { status: "done", embedded, total };
|
|
160659
|
+
}
|
|
159285
160660
|
|
|
159286
160661
|
// ../plugin/src/features/magic-context/memory/embedding.ts
|
|
159287
160662
|
var DEFAULT_EMBEDDING_CONFIG = {
|
|
@@ -159301,10 +160676,11 @@ function createProvider2(config2) {
|
|
|
159301
160676
|
model: config2.model,
|
|
159302
160677
|
apiKey: config2.api_key,
|
|
159303
160678
|
inputType: config2.input_type,
|
|
159304
|
-
truncate: config2.truncate
|
|
160679
|
+
truncate: config2.truncate,
|
|
160680
|
+
maxInputTokens: config2.max_input_tokens
|
|
159305
160681
|
});
|
|
159306
160682
|
}
|
|
159307
|
-
return new LocalEmbeddingProvider(config2.model);
|
|
160683
|
+
return new LocalEmbeddingProvider(config2.model, config2.max_input_tokens);
|
|
159308
160684
|
}
|
|
159309
160685
|
function getOrCreateProvider() {
|
|
159310
160686
|
if (provider) {
|
|
@@ -159328,186 +160704,8 @@ async function embedText(text, signal) {
|
|
|
159328
160704
|
}
|
|
159329
160705
|
var SWEEP_MAX_WALL_CLOCK_MS2 = 10 * 60 * 1000;
|
|
159330
160706
|
|
|
159331
|
-
// ../plugin/src/features/magic-context/memory/project-identity.ts
|
|
159332
|
-
import { execFileSync } from "node:child_process";
|
|
159333
|
-
import { createHash as createHash4 } from "node:crypto";
|
|
159334
|
-
import { statSync } from "node:fs";
|
|
159335
|
-
import path3 from "node:path";
|
|
159336
|
-
var GIT_TIMEOUT_MS = 5000;
|
|
159337
|
-
var identityCache = new Map;
|
|
159338
|
-
var directoryFallbackCache = new Map;
|
|
159339
|
-
|
|
159340
|
-
class ProjectIdentityError extends Error {
|
|
159341
|
-
errorClass;
|
|
159342
|
-
rawDirectory;
|
|
159343
|
-
constructor(errorClass, rawDirectory, message, cause) {
|
|
159344
|
-
super(message);
|
|
159345
|
-
this.name = "ProjectIdentityError";
|
|
159346
|
-
this.errorClass = errorClass;
|
|
159347
|
-
this.rawDirectory = rawDirectory;
|
|
159348
|
-
if (cause) {
|
|
159349
|
-
this.cause = cause;
|
|
159350
|
-
}
|
|
159351
|
-
}
|
|
159352
|
-
}
|
|
159353
|
-
function asError(error51) {
|
|
159354
|
-
return error51 instanceof Error ? error51 : undefined;
|
|
159355
|
-
}
|
|
159356
|
-
function getErrorCode(error51) {
|
|
159357
|
-
if (error51 === null || typeof error51 !== "object" || !("code" in error51)) {
|
|
159358
|
-
return;
|
|
159359
|
-
}
|
|
159360
|
-
const code = error51.code;
|
|
159361
|
-
return typeof code === "string" ? code : undefined;
|
|
159362
|
-
}
|
|
159363
|
-
function getErrorSignal(error51) {
|
|
159364
|
-
if (error51 === null || typeof error51 !== "object" || !("signal" in error51)) {
|
|
159365
|
-
return;
|
|
159366
|
-
}
|
|
159367
|
-
const signal = error51.signal;
|
|
159368
|
-
return typeof signal === "string" ? signal : undefined;
|
|
159369
|
-
}
|
|
159370
|
-
function getErrorKilled(error51) {
|
|
159371
|
-
if (error51 === null || typeof error51 !== "object" || !("killed" in error51)) {
|
|
159372
|
-
return false;
|
|
159373
|
-
}
|
|
159374
|
-
return error51.killed === true;
|
|
159375
|
-
}
|
|
159376
|
-
function getErrorStderr(error51) {
|
|
159377
|
-
if (error51 === null || typeof error51 !== "object" || !("stderr" in error51)) {
|
|
159378
|
-
return "";
|
|
159379
|
-
}
|
|
159380
|
-
const stderr = error51.stderr;
|
|
159381
|
-
if (typeof stderr === "string") {
|
|
159382
|
-
return stderr;
|
|
159383
|
-
}
|
|
159384
|
-
if (Buffer.isBuffer(stderr)) {
|
|
159385
|
-
return stderr.toString("utf8");
|
|
159386
|
-
}
|
|
159387
|
-
return "";
|
|
159388
|
-
}
|
|
159389
|
-
function directoryFallback(directory) {
|
|
159390
|
-
const canonical = path3.resolve(directory);
|
|
159391
|
-
const hash2 = createHash4("md5").update(canonical, "utf8").digest("hex").slice(0, 12);
|
|
159392
|
-
return `dir:${hash2}`;
|
|
159393
|
-
}
|
|
159394
|
-
function assertDirectoryUsable(canonicalDirectory, rawDirectory) {
|
|
159395
|
-
try {
|
|
159396
|
-
const stat2 = statSync(canonicalDirectory);
|
|
159397
|
-
if (!stat2.isDirectory()) {
|
|
159398
|
-
throw new ProjectIdentityError("unknown", rawDirectory, `Project path is not a directory: ${canonicalDirectory}`);
|
|
159399
|
-
}
|
|
159400
|
-
} catch (error51) {
|
|
159401
|
-
if (error51 instanceof ProjectIdentityError) {
|
|
159402
|
-
throw error51;
|
|
159403
|
-
}
|
|
159404
|
-
const code = getErrorCode(error51);
|
|
159405
|
-
if (code === "EACCES" || code === "EPERM") {
|
|
159406
|
-
throw new ProjectIdentityError("permission_denied", rawDirectory, `Permission denied while accessing project directory: ${canonicalDirectory}`, asError(error51));
|
|
159407
|
-
}
|
|
159408
|
-
throw new ProjectIdentityError("unknown", rawDirectory, `Unable to access project directory: ${canonicalDirectory}`, asError(error51));
|
|
159409
|
-
}
|
|
159410
|
-
}
|
|
159411
|
-
function isGitTimeoutError(error51) {
|
|
159412
|
-
const code = getErrorCode(error51);
|
|
159413
|
-
const signal = getErrorSignal(error51);
|
|
159414
|
-
return code === "ETIMEDOUT" || signal === "SIGTERM" || signal === "SIGKILL" || getErrorKilled(error51);
|
|
159415
|
-
}
|
|
159416
|
-
function classifyGitError(error51, rawDirectory) {
|
|
159417
|
-
if (isGitTimeoutError(error51)) {
|
|
159418
|
-
return new ProjectIdentityError("git_timeout", rawDirectory, `git rev-list timed out after ${GIT_TIMEOUT_MS}ms`, asError(error51));
|
|
159419
|
-
}
|
|
159420
|
-
const code = getErrorCode(error51);
|
|
159421
|
-
if (code === "ENOENT") {
|
|
159422
|
-
return new ProjectIdentityError("git_missing", rawDirectory, "git binary is not available in PATH", asError(error51));
|
|
159423
|
-
}
|
|
159424
|
-
if (code === "EACCES" || code === "EPERM") {
|
|
159425
|
-
return new ProjectIdentityError("permission_denied", rawDirectory, "Permission denied while spawning git", asError(error51));
|
|
159426
|
-
}
|
|
159427
|
-
const stderr = getErrorStderr(error51).toLowerCase();
|
|
159428
|
-
if (stderr.includes("not a git repository") || stderr.includes("does not have any commits yet") || stderr.includes("ambiguous argument 'head'") || stderr.includes("unknown revision or path")) {
|
|
159429
|
-
return new ProjectIdentityError("not_git_repo", rawDirectory, "Directory has no git root commit; caller may use directory fallback", asError(error51));
|
|
159430
|
-
}
|
|
159431
|
-
return new ProjectIdentityError("unknown", rawDirectory, "git rev-list failed while resolving project identity", asError(error51));
|
|
159432
|
-
}
|
|
159433
|
-
function resolveProjectIdentityStrict(directory) {
|
|
159434
|
-
const canonical = path3.resolve(directory);
|
|
159435
|
-
const cached2 = identityCache.get(canonical);
|
|
159436
|
-
if (cached2 !== undefined) {
|
|
159437
|
-
return cached2;
|
|
159438
|
-
}
|
|
159439
|
-
assertDirectoryUsable(canonical, directory);
|
|
159440
|
-
let output;
|
|
159441
|
-
try {
|
|
159442
|
-
output = execFileSync("git", ["rev-list", "--max-parents=0", "HEAD"], {
|
|
159443
|
-
cwd: canonical,
|
|
159444
|
-
encoding: "utf8",
|
|
159445
|
-
env: { ...process.env, LC_ALL: "C", LANG: "C" },
|
|
159446
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
159447
|
-
timeout: GIT_TIMEOUT_MS
|
|
159448
|
-
});
|
|
159449
|
-
} catch (error51) {
|
|
159450
|
-
throw classifyGitError(error51, directory);
|
|
159451
|
-
}
|
|
159452
|
-
const firstLine = output.split(`
|
|
159453
|
-
`)[0]?.trim() ?? "";
|
|
159454
|
-
const rootCommit = firstLine.slice(0, 64);
|
|
159455
|
-
if (rootCommit.length < 7) {
|
|
159456
|
-
throw new ProjectIdentityError("unknown", directory, "git rev-list returned no valid root commit hash");
|
|
159457
|
-
}
|
|
159458
|
-
const identity = `git:${rootCommit}`;
|
|
159459
|
-
identityCache.set(canonical, identity);
|
|
159460
|
-
return identity;
|
|
159461
|
-
}
|
|
159462
|
-
function shouldUseDirectoryFallback(error51) {
|
|
159463
|
-
return error51.errorClass === "not_git_repo" || error51.errorClass === "unknown" && error51.message.startsWith("Unable to access project directory:");
|
|
159464
|
-
}
|
|
159465
|
-
function resolveProjectIdentity(directory) {
|
|
159466
|
-
const canonical = path3.resolve(directory);
|
|
159467
|
-
const cachedFallback = directoryFallbackCache.get(canonical);
|
|
159468
|
-
if (cachedFallback !== undefined) {
|
|
159469
|
-
if (!hasGitDir(canonical)) {
|
|
159470
|
-
return cachedFallback;
|
|
159471
|
-
}
|
|
159472
|
-
directoryFallbackCache.delete(canonical);
|
|
159473
|
-
}
|
|
159474
|
-
try {
|
|
159475
|
-
return resolveProjectIdentityStrict(directory);
|
|
159476
|
-
} catch (error51) {
|
|
159477
|
-
if (error51 instanceof ProjectIdentityError && shouldUseDirectoryFallback(error51)) {
|
|
159478
|
-
const fallback = directoryFallback(canonical);
|
|
159479
|
-
if (!hasGitDir(canonical)) {
|
|
159480
|
-
directoryFallbackCache.set(canonical, fallback);
|
|
159481
|
-
}
|
|
159482
|
-
return fallback;
|
|
159483
|
-
}
|
|
159484
|
-
throw error51;
|
|
159485
|
-
}
|
|
159486
|
-
}
|
|
159487
|
-
function hasGitDir(canonical) {
|
|
159488
|
-
try {
|
|
159489
|
-
statSync(path3.join(canonical, ".git"));
|
|
159490
|
-
return true;
|
|
159491
|
-
} catch {
|
|
159492
|
-
return false;
|
|
159493
|
-
}
|
|
159494
|
-
}
|
|
159495
|
-
function normalizeStoredProjectPath(rawOrStored) {
|
|
159496
|
-
if (rawOrStored.startsWith("git:") || rawOrStored.startsWith("dir:")) {
|
|
159497
|
-
return rawOrStored;
|
|
159498
|
-
}
|
|
159499
|
-
try {
|
|
159500
|
-
return resolveProjectIdentity(rawOrStored);
|
|
159501
|
-
} catch {
|
|
159502
|
-
return directoryFallback(rawOrStored);
|
|
159503
|
-
}
|
|
159504
|
-
}
|
|
159505
|
-
function storedPathBelongsToIdentity(storedProjectPath, projectIdentity) {
|
|
159506
|
-
return storedProjectPath === projectIdentity || normalizeStoredProjectPath(storedProjectPath) === projectIdentity;
|
|
159507
|
-
}
|
|
159508
|
-
|
|
159509
160707
|
// ../plugin/src/plugin/embedding-bootstrap-helpers.ts
|
|
159510
|
-
import { createHash as
|
|
160708
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
159511
160709
|
init_logger();
|
|
159512
160710
|
var EMBEDDING_AFFECTING_KEYS = new Set([
|
|
159513
160711
|
"embedding.api_key",
|
|
@@ -159529,7 +160727,7 @@ var EMBEDDING_WARNING_TERMS = [
|
|
|
159529
160727
|
];
|
|
159530
160728
|
var loggedFailureSignatures = new Map;
|
|
159531
160729
|
function sha256Prefix2(value, length = 16) {
|
|
159532
|
-
return
|
|
160730
|
+
return createHash7("sha256").update(value).digest("hex").slice(0, length);
|
|
159533
160731
|
}
|
|
159534
160732
|
function warningLooksEmbeddingRelated(message) {
|
|
159535
160733
|
const lower = message.toLowerCase();
|
|
@@ -159690,6 +160888,7 @@ var SESSION_META_SELECT_COLUMNS = [
|
|
|
159690
160888
|
"cached_m0_bytes",
|
|
159691
160889
|
"cached_m1_bytes",
|
|
159692
160890
|
"cached_m0_project_memory_epoch",
|
|
160891
|
+
"cached_m0_workspace_fingerprint",
|
|
159693
160892
|
"cached_m0_project_user_profile_version",
|
|
159694
160893
|
"cached_m0_max_compartment_seq",
|
|
159695
160894
|
"cached_m0_max_memory_id",
|
|
@@ -159737,6 +160936,7 @@ var META_COLUMNS = {
|
|
|
159737
160936
|
cachedM0Bytes: "cached_m0_bytes",
|
|
159738
160937
|
cachedM1Bytes: "cached_m1_bytes",
|
|
159739
160938
|
cachedM0ProjectMemoryEpoch: "cached_m0_project_memory_epoch",
|
|
160939
|
+
cachedM0WorkspaceFingerprint: "cached_m0_workspace_fingerprint",
|
|
159740
160940
|
cachedM0ProjectUserProfileVersion: "cached_m0_project_user_profile_version",
|
|
159741
160941
|
cachedM0MaxCompartmentSeq: "cached_m0_max_compartment_seq",
|
|
159742
160942
|
cachedM0MaxMemoryId: "cached_m0_max_memory_id",
|
|
@@ -159766,6 +160966,7 @@ var NULL_BIND_META_KEYS = new Set([
|
|
|
159766
160966
|
"cachedM0Bytes",
|
|
159767
160967
|
"cachedM1Bytes",
|
|
159768
160968
|
"cachedM0ProjectMemoryEpoch",
|
|
160969
|
+
"cachedM0WorkspaceFingerprint",
|
|
159769
160970
|
"cachedM0ProjectUserProfileVersion",
|
|
159770
160971
|
"cachedM0MaxCompartmentSeq",
|
|
159771
160972
|
"cachedM0MaxMemoryId",
|
|
@@ -159799,7 +161000,7 @@ function isSessionMetaRow(row) {
|
|
|
159799
161000
|
if (row === null || typeof row !== "object")
|
|
159800
161001
|
return false;
|
|
159801
161002
|
const r = row;
|
|
159802
|
-
return typeof r.session_id === "string" && typeof r.last_response_time === "number" && isStringOrNull(r.cache_ttl) && typeof r.counter === "number" && typeof r.last_nudge_tokens === "number" && isStringOrNull(r.last_nudge_band) && isStringOrNull(r.last_transform_error) && typeof r.is_subagent === "number" && typeof r.last_context_percentage === "number" && typeof r.last_input_tokens === "number" && isNumberOrNull(r.observed_safe_input_tokens) && isNumberOrNull(r.cache_alert_sent) && isNumberOrNull(r.times_execute_threshold_reached) && isNumberOrNull(r.compartment_in_progress) && (r.system_prompt_hash === null || typeof r.system_prompt_hash === "string" || typeof r.system_prompt_hash === "number") && isNumberOrNull(r.system_prompt_tokens) && isNumberOrNull(r.conversation_tokens) && isNumberOrNull(r.tool_call_tokens) && isNumberOrNull(r.cleared_reasoning_through_tag) && isStringOrNull(r.last_todo_state) && isBlobOrNull(r.cached_m0_bytes) && isBlobOrNull(r.cached_m1_bytes) && isNumberOrNull(r.cached_m0_project_memory_epoch) && isNumberOrNull(r.cached_m0_project_user_profile_version) && isNumberOrNull(r.cached_m0_max_compartment_seq) && isNumberOrNull(r.cached_m0_max_memory_id) && isNumberOrNull(r.cached_m0_max_mutation_id) && isNumberOrNull(r.cached_m0_max_memory_mutation_id) && isStringOrNull(r.cached_m0_project_docs_hash) && isNumberOrNull(r.cached_m0_materialized_at) && isNumberOrNull(r.cached_m0_session_facts_version) && isStringOrNull(r.cached_m0_upgrade_state) && isStringOrNull(r.cached_m0_system_hash) && isStringOrNull(r.cached_m0_tool_set_hash) && isStringOrNull(r.cached_m0_model_key) && isStringOrNull(r.last_observed_model_key) && isNumberOrNull(r.last_usage_context_limit) && isNumberOrNull(r.prior_boundary_ordinal) && isNumberOrNull(r.protected_tail_policy_version) && isNumberOrNull(r.protected_tail_drain_window_started_at) && isNumberOrNull(r.protected_tail_drain_tokens) && isNumberOrNull(r.recovery_no_eligible_head_count) && isNumberOrNull(r.force_emergency_bypass_window_start) && isNumberOrNull(r.force_emergency_bypass_used) && isNumberOrNull(r.upgrade_reminded_at) && isNumberOrNull(r.pi_stable_id_scheme);
|
|
161003
|
+
return typeof r.session_id === "string" && typeof r.last_response_time === "number" && isStringOrNull(r.cache_ttl) && typeof r.counter === "number" && typeof r.last_nudge_tokens === "number" && isStringOrNull(r.last_nudge_band) && isStringOrNull(r.last_transform_error) && typeof r.is_subagent === "number" && typeof r.last_context_percentage === "number" && typeof r.last_input_tokens === "number" && isNumberOrNull(r.observed_safe_input_tokens) && isNumberOrNull(r.cache_alert_sent) && isNumberOrNull(r.times_execute_threshold_reached) && isNumberOrNull(r.compartment_in_progress) && (r.system_prompt_hash === null || typeof r.system_prompt_hash === "string" || typeof r.system_prompt_hash === "number") && isNumberOrNull(r.system_prompt_tokens) && isNumberOrNull(r.conversation_tokens) && isNumberOrNull(r.tool_call_tokens) && isNumberOrNull(r.cleared_reasoning_through_tag) && isStringOrNull(r.last_todo_state) && isBlobOrNull(r.cached_m0_bytes) && isBlobOrNull(r.cached_m1_bytes) && isNumberOrNull(r.cached_m0_project_memory_epoch) && isStringOrNull(r.cached_m0_workspace_fingerprint) && isNumberOrNull(r.cached_m0_project_user_profile_version) && isNumberOrNull(r.cached_m0_max_compartment_seq) && isNumberOrNull(r.cached_m0_max_memory_id) && isNumberOrNull(r.cached_m0_max_mutation_id) && isNumberOrNull(r.cached_m0_max_memory_mutation_id) && isStringOrNull(r.cached_m0_project_docs_hash) && isNumberOrNull(r.cached_m0_materialized_at) && isNumberOrNull(r.cached_m0_session_facts_version) && isStringOrNull(r.cached_m0_upgrade_state) && isStringOrNull(r.cached_m0_system_hash) && isStringOrNull(r.cached_m0_tool_set_hash) && isStringOrNull(r.cached_m0_model_key) && isStringOrNull(r.last_observed_model_key) && isNumberOrNull(r.last_usage_context_limit) && isNumberOrNull(r.prior_boundary_ordinal) && isNumberOrNull(r.protected_tail_policy_version) && isNumberOrNull(r.protected_tail_drain_window_started_at) && isNumberOrNull(r.protected_tail_drain_tokens) && isNumberOrNull(r.recovery_no_eligible_head_count) && isNumberOrNull(r.force_emergency_bypass_window_start) && isNumberOrNull(r.force_emergency_bypass_used) && isNumberOrNull(r.upgrade_reminded_at) && isNumberOrNull(r.pi_stable_id_scheme);
|
|
159803
161004
|
}
|
|
159804
161005
|
function getDefaultSessionMeta(sessionId) {
|
|
159805
161006
|
return {
|
|
@@ -159826,6 +161027,7 @@ function getDefaultSessionMeta(sessionId) {
|
|
|
159826
161027
|
cachedM0Bytes: null,
|
|
159827
161028
|
cachedM1Bytes: null,
|
|
159828
161029
|
cachedM0ProjectMemoryEpoch: null,
|
|
161030
|
+
cachedM0WorkspaceFingerprint: null,
|
|
159829
161031
|
cachedM0ProjectUserProfileVersion: null,
|
|
159830
161032
|
cachedM0MaxCompartmentSeq: null,
|
|
159831
161033
|
cachedM0MaxMemoryId: null,
|
|
@@ -159888,6 +161090,7 @@ function toSessionMeta(row) {
|
|
|
159888
161090
|
cachedM0Bytes: toBufferOrNull(row.cached_m0_bytes),
|
|
159889
161091
|
cachedM1Bytes: toBufferOrNull(row.cached_m1_bytes),
|
|
159890
161092
|
cachedM0ProjectMemoryEpoch: numOrNull(row.cached_m0_project_memory_epoch),
|
|
161093
|
+
cachedM0WorkspaceFingerprint: stringOrNull(row.cached_m0_workspace_fingerprint),
|
|
159891
161094
|
cachedM0ProjectUserProfileVersion: numOrNull(row.cached_m0_project_user_profile_version),
|
|
159892
161095
|
cachedM0MaxCompartmentSeq: numOrNull(row.cached_m0_max_compartment_seq),
|
|
159893
161096
|
cachedM0MaxMemoryId: numOrNull(row.cached_m0_max_memory_id),
|
|
@@ -159918,6 +161121,7 @@ function persistCachedM0(db, sessionId, payload) {
|
|
|
159918
161121
|
db.prepare(`UPDATE session_meta SET
|
|
159919
161122
|
cached_m0_bytes = ?,
|
|
159920
161123
|
cached_m0_project_memory_epoch = ?,
|
|
161124
|
+
cached_m0_workspace_fingerprint = ?,
|
|
159921
161125
|
cached_m0_project_user_profile_version = ?,
|
|
159922
161126
|
cached_m0_max_compartment_seq = ?,
|
|
159923
161127
|
cached_m0_max_memory_id = ?,
|
|
@@ -159930,7 +161134,7 @@ function persistCachedM0(db, sessionId, payload) {
|
|
|
159930
161134
|
cached_m0_upgrade_state = ?,
|
|
159931
161135
|
cached_m0_system_hash = ?,
|
|
159932
161136
|
cached_m0_model_key = ?
|
|
159933
|
-
WHERE session_id = ?`).run(Buffer2.from(payload.m0Bytes), payload.projectMemoryEpoch, payload.projectUserProfileVersion, payload.maxCompartmentSeq, payload.maxMemoryId, payload.maxMutationId, payload.maxMemoryMutationId ?? null, payload.m1Bytes ? Buffer2.from(payload.m1Bytes) : null, payload.projectDocsHash, payload.materializedAt, payload.sessionFactsVersion, payload.upgradeState, payload.systemHash ?? "", payload.modelKey ?? "", sessionId);
|
|
161137
|
+
WHERE session_id = ?`).run(Buffer2.from(payload.m0Bytes), payload.projectMemoryEpoch, payload.workspaceFingerprint ?? null, payload.projectUserProfileVersion, payload.maxCompartmentSeq, payload.maxMemoryId, payload.maxMutationId, payload.maxMemoryMutationId ?? null, payload.m1Bytes ? Buffer2.from(payload.m1Bytes) : null, payload.projectDocsHash, payload.materializedAt, payload.sessionFactsVersion, payload.upgradeState, payload.systemHash ?? "", payload.modelKey ?? "", sessionId);
|
|
159934
161138
|
}
|
|
159935
161139
|
function clearCachedM0M1(db, sessionId) {
|
|
159936
161140
|
ensureSessionMetaRow(db, sessionId);
|
|
@@ -159939,6 +161143,7 @@ function clearCachedM0M1(db, sessionId) {
|
|
|
159939
161143
|
["cached_m0_bytes", null],
|
|
159940
161144
|
["cached_m1_bytes", null],
|
|
159941
161145
|
["cached_m0_project_memory_epoch", null],
|
|
161146
|
+
["cached_m0_workspace_fingerprint", null],
|
|
159942
161147
|
["cached_m0_project_user_profile_version", null],
|
|
159943
161148
|
["cached_m0_max_compartment_seq", null],
|
|
159944
161149
|
["cached_m0_max_memory_id", null],
|
|
@@ -160274,8 +161479,8 @@ function readRawSessionTailFromDb(db, sessionId, baseOrdinal, anchorMessageId) {
|
|
|
160274
161479
|
const CHUNK = 800;
|
|
160275
161480
|
for (let i = 0;i < ids.length; i += CHUNK) {
|
|
160276
161481
|
const slice = ids.slice(i, i + CHUNK);
|
|
160277
|
-
const
|
|
160278
|
-
const partRows = db.prepare(`SELECT message_id, data, time_updated FROM part WHERE session_id = ? AND message_id IN (${
|
|
161482
|
+
const placeholders2 = slice.map(() => "?").join(",");
|
|
161483
|
+
const partRows = db.prepare(`SELECT message_id, data, time_updated FROM part WHERE session_id = ? AND message_id IN (${placeholders2}) ORDER BY time_created ASC, id ASC`).all(sessionId, ...slice).filter(isRawPartRow);
|
|
160279
161484
|
for (const part of partRows) {
|
|
160280
161485
|
const list = partsByMessageId.get(part.message_id) ?? [];
|
|
160281
161486
|
list.push(attachRawPartVersion(parseJsonUnknown(part.data), part.time_updated));
|
|
@@ -165172,53 +166377,6 @@ function createCtxExpandTool(deps) {
|
|
|
165172
166377
|
}
|
|
165173
166378
|
};
|
|
165174
166379
|
}
|
|
165175
|
-
|
|
165176
|
-
// ../plugin/src/features/magic-context/memory/constants.ts
|
|
165177
|
-
var V2_MEMORY_CATEGORIES = [
|
|
165178
|
-
"PROJECT_RULES",
|
|
165179
|
-
"ARCHITECTURE",
|
|
165180
|
-
"CONSTRAINTS",
|
|
165181
|
-
"CONFIG_VALUES",
|
|
165182
|
-
"NAMING"
|
|
165183
|
-
];
|
|
165184
|
-
var PROMOTABLE_CATEGORIES = [
|
|
165185
|
-
"PROJECT_RULES",
|
|
165186
|
-
"ARCHITECTURE",
|
|
165187
|
-
"CONSTRAINTS",
|
|
165188
|
-
"CONFIG_VALUES",
|
|
165189
|
-
"NAMING",
|
|
165190
|
-
"ARCHITECTURE_DECISIONS",
|
|
165191
|
-
"CONFIG_DEFAULTS",
|
|
165192
|
-
"USER_PREFERENCES",
|
|
165193
|
-
"USER_DIRECTIVES",
|
|
165194
|
-
"ENVIRONMENT",
|
|
165195
|
-
"WORKFLOW_RULES",
|
|
165196
|
-
"KNOWN_ISSUES"
|
|
165197
|
-
];
|
|
165198
|
-
var CATEGORY_PRIORITY = [
|
|
165199
|
-
"PROJECT_RULES",
|
|
165200
|
-
"ARCHITECTURE",
|
|
165201
|
-
"CONSTRAINTS",
|
|
165202
|
-
"CONFIG_VALUES",
|
|
165203
|
-
"NAMING",
|
|
165204
|
-
"USER_DIRECTIVES",
|
|
165205
|
-
"USER_PREFERENCES",
|
|
165206
|
-
"CONFIG_DEFAULTS",
|
|
165207
|
-
"ARCHITECTURE_DECISIONS",
|
|
165208
|
-
"ENVIRONMENT",
|
|
165209
|
-
"WORKFLOW_RULES",
|
|
165210
|
-
"KNOWN_ISSUES"
|
|
165211
|
-
];
|
|
165212
|
-
var MEMORY_CATEGORY_ORDER_UNKNOWN = 99;
|
|
165213
|
-
var MEMORY_CATEGORY_ORDER_PRIORITY = CATEGORY_PRIORITY.reduce((acc, category, index) => {
|
|
165214
|
-
acc[category] = index;
|
|
165215
|
-
return acc;
|
|
165216
|
-
}, {});
|
|
165217
|
-
var MEMORY_CATEGORY_ORDER_SQL = `CASE category ${CATEGORY_PRIORITY.map((category, index) => `WHEN '${category}' THEN ${index}`).join(" ")} ELSE ${MEMORY_CATEGORY_ORDER_UNKNOWN} END`;
|
|
165218
|
-
var CATEGORY_DEFAULT_TTL = {
|
|
165219
|
-
WORKFLOW_RULES: 90 * 24 * 60 * 60 * 1000,
|
|
165220
|
-
KNOWN_ISSUES: 30 * 24 * 60 * 60 * 1000
|
|
165221
|
-
};
|
|
165222
166380
|
// ../plugin/src/features/magic-context/memory/embedding-backfill.ts
|
|
165223
166381
|
init_logger();
|
|
165224
166382
|
async function ensureMemoryEmbeddings(args) {
|
|
@@ -165243,7 +166401,7 @@ async function ensureMemoryEmbeddings(args) {
|
|
|
165243
166401
|
continue;
|
|
165244
166402
|
}
|
|
165245
166403
|
saveEmbedding(args.db, memory2.id, embedding, result.modelId);
|
|
165246
|
-
staged.set(memory2.id, embedding);
|
|
166404
|
+
staged.set(memory2.id, { embedding, modelId: result.modelId });
|
|
165247
166405
|
}
|
|
165248
166406
|
})();
|
|
165249
166407
|
const currentSnapshot = getProjectEmbeddingSnapshot(args.projectIdentity);
|
|
@@ -165446,8 +166604,8 @@ function getMemoriesByProjectStatement(db, statuses) {
|
|
|
165446
166604
|
}
|
|
165447
166605
|
let stmt = statements.get(db);
|
|
165448
166606
|
if (!stmt) {
|
|
165449
|
-
const
|
|
165450
|
-
stmt = db.prepare(`SELECT ${getMemorySelectColumns(db)} FROM memories WHERE project_path = ? AND status IN (${
|
|
166607
|
+
const placeholders2 = statuses.map(() => "?").join(", ");
|
|
166608
|
+
stmt = db.prepare(`SELECT ${getMemorySelectColumns(db)} FROM memories WHERE project_path = ? AND status IN (${placeholders2}) AND (expires_at IS NULL OR expires_at > ?) ORDER BY category ASC, updated_at DESC, id ASC`);
|
|
165451
166609
|
statements.set(db, stmt);
|
|
165452
166610
|
}
|
|
165453
166611
|
return stmt;
|
|
@@ -165592,6 +166750,97 @@ function getMemoriesByProject(db, projectPath, statuses = ["active", "permanent"
|
|
|
165592
166750
|
const rows = getMemoriesByProjectStatement(db, statuses).all(projectPath, ...statuses, expiryCutoff).filter(isMemoryRow);
|
|
165593
166751
|
return rows.map(toMemory);
|
|
165594
166752
|
}
|
|
166753
|
+
function sqlPlaceholders(values) {
|
|
166754
|
+
return values.map(() => "?").join(", ");
|
|
166755
|
+
}
|
|
166756
|
+
function uniqueValues(values) {
|
|
166757
|
+
return [...new Set(values.filter((value) => value.length > 0))];
|
|
166758
|
+
}
|
|
166759
|
+
function buildWorkspaceMemorySqlFilter(args) {
|
|
166760
|
+
if (args.shareCategories === null || args.shareCategories === undefined) {
|
|
166761
|
+
return { clause: "", params: [], active: false };
|
|
166762
|
+
}
|
|
166763
|
+
const identities = uniqueValues(args.identities);
|
|
166764
|
+
const identitySet = new Set(identities);
|
|
166765
|
+
const ownSet = new Set(uniqueValues(args.ownIdentities ?? []).filter((identity) => identitySet.has(identity)));
|
|
166766
|
+
const foreignIdentities = identities.filter((identity) => !ownSet.has(identity));
|
|
166767
|
+
if (foreignIdentities.length === 0) {
|
|
166768
|
+
return { clause: "", params: [], active: false };
|
|
166769
|
+
}
|
|
166770
|
+
const ownIdentities = identities.filter((identity) => ownSet.has(identity));
|
|
166771
|
+
const shareCategories = uniqueValues([...args.shareCategories]);
|
|
166772
|
+
const qualifier = args.tableName ? `${args.tableName}.` : "";
|
|
166773
|
+
const predicates = [];
|
|
166774
|
+
const params = [];
|
|
166775
|
+
if (ownIdentities.length > 0) {
|
|
166776
|
+
predicates.push(`${qualifier}project_path IN (${sqlPlaceholders(ownIdentities)})`);
|
|
166777
|
+
params.push(...ownIdentities);
|
|
166778
|
+
}
|
|
166779
|
+
if (foreignIdentities.length > 0 && shareCategories.length > 0) {
|
|
166780
|
+
predicates.push(`(${qualifier}project_path IN (${sqlPlaceholders(foreignIdentities)}) AND ${qualifier}category IN (${sqlPlaceholders(shareCategories)}))`);
|
|
166781
|
+
params.push(...foreignIdentities, ...shareCategories);
|
|
166782
|
+
}
|
|
166783
|
+
if (predicates.length === 0) {
|
|
166784
|
+
return { clause: " AND 0 = 1", params: [], active: true };
|
|
166785
|
+
}
|
|
166786
|
+
return { clause: ` AND (${predicates.join(" OR ")})`, params, active: true };
|
|
166787
|
+
}
|
|
166788
|
+
function getMemoriesByProjects(db, projectPaths, statuses = ["active", "permanent"], expiryCutoff = Date.now(), ownIdentities, shareCategories) {
|
|
166789
|
+
const identities = uniqueValues(projectPaths);
|
|
166790
|
+
if (identities.length === 0 || statuses.length === 0)
|
|
166791
|
+
return [];
|
|
166792
|
+
const sharingFilter = buildWorkspaceMemorySqlFilter({
|
|
166793
|
+
identities,
|
|
166794
|
+
ownIdentities,
|
|
166795
|
+
shareCategories
|
|
166796
|
+
});
|
|
166797
|
+
if (identities.length === 1 && !sharingFilter.active) {
|
|
166798
|
+
return getMemoriesByProject(db, identities[0], statuses, expiryCutoff);
|
|
166799
|
+
}
|
|
166800
|
+
const rows = db.prepare(`SELECT ${getMemorySelectColumns(db)}
|
|
166801
|
+
FROM memories
|
|
166802
|
+
WHERE project_path IN (${sqlPlaceholders(identities)})
|
|
166803
|
+
AND status IN (${sqlPlaceholders(statuses)})
|
|
166804
|
+
AND (expires_at IS NULL OR expires_at > ?)${sharingFilter.clause}
|
|
166805
|
+
ORDER BY category ASC, updated_at DESC, id ASC`).all(...identities, ...statuses, expiryCutoff, ...sharingFilter.params).filter(isMemoryRow);
|
|
166806
|
+
return rows.map(toMemory);
|
|
166807
|
+
}
|
|
166808
|
+
function getMaxMemoryIdForProjects(db, projectPaths, ownIdentities, shareCategories) {
|
|
166809
|
+
const identities = uniqueValues(projectPaths);
|
|
166810
|
+
if (identities.length === 0)
|
|
166811
|
+
return 0;
|
|
166812
|
+
const sharingFilter = buildWorkspaceMemorySqlFilter({
|
|
166813
|
+
identities,
|
|
166814
|
+
ownIdentities,
|
|
166815
|
+
shareCategories
|
|
166816
|
+
});
|
|
166817
|
+
if (identities.length === 1 && !sharingFilter.active) {
|
|
166818
|
+
const row2 = db.prepare("SELECT COALESCE(MAX(id), 0) AS max_id FROM memories WHERE project_path = ?").get(identities[0]);
|
|
166819
|
+
return typeof row2?.max_id === "number" ? row2.max_id : 0;
|
|
166820
|
+
}
|
|
166821
|
+
const row = db.prepare(`SELECT COALESCE(MAX(id), 0) AS max_id
|
|
166822
|
+
FROM memories
|
|
166823
|
+
WHERE project_path IN (${sqlPlaceholders(identities)})${sharingFilter.clause}`).get(...identities, ...sharingFilter.params);
|
|
166824
|
+
return typeof row?.max_id === "number" ? row.max_id : 0;
|
|
166825
|
+
}
|
|
166826
|
+
function readNewMemoriesForM1Union(db, projectPaths, afterId, expiryCutoff, ownIdentities, shareCategories) {
|
|
166827
|
+
const identities = uniqueValues(projectPaths);
|
|
166828
|
+
if (identities.length === 0)
|
|
166829
|
+
return [];
|
|
166830
|
+
const sharingFilter = buildWorkspaceMemorySqlFilter({
|
|
166831
|
+
identities,
|
|
166832
|
+
ownIdentities,
|
|
166833
|
+
shareCategories
|
|
166834
|
+
});
|
|
166835
|
+
const rows = db.prepare(`SELECT ${getMemorySelectColumns(db)}
|
|
166836
|
+
FROM memories
|
|
166837
|
+
WHERE project_path IN (${sqlPlaceholders(identities)})
|
|
166838
|
+
AND id > ?
|
|
166839
|
+
AND status IN ('active', 'permanent')
|
|
166840
|
+
AND (expires_at IS NULL OR expires_at > ?)${sharingFilter.clause}
|
|
166841
|
+
ORDER BY ${MEMORY_CATEGORY_ORDER_SQL}, id ASC`).all(...identities, afterId, expiryCutoff, ...sharingFilter.params).filter(isMemoryRow);
|
|
166842
|
+
return rows.map(toMemory);
|
|
166843
|
+
}
|
|
165595
166844
|
function getAllActiveMemoriesForMigration(db, projectPath) {
|
|
165596
166845
|
const rows = getActiveMemoriesNoExpiryStatement(db).all(projectPath).filter(isMemoryRow);
|
|
165597
166846
|
return rows.map(toMemory);
|
|
@@ -165759,6 +167008,7 @@ async function embedAndStoreMemory(db, sessionId, projectPath, memoryId, content
|
|
|
165759
167008
|
// ../plugin/src/features/magic-context/memory/storage-memory-fts.ts
|
|
165760
167009
|
var DEFAULT_SEARCH_LIMIT = 10;
|
|
165761
167010
|
var searchStatements = new WeakMap;
|
|
167011
|
+
var unionSearchStatements = new Map;
|
|
165762
167012
|
function getSearchStatement(db) {
|
|
165763
167013
|
let stmt = searchStatements.get(db);
|
|
165764
167014
|
if (!stmt) {
|
|
@@ -165767,6 +167017,23 @@ function getSearchStatement(db) {
|
|
|
165767
167017
|
}
|
|
165768
167018
|
return stmt;
|
|
165769
167019
|
}
|
|
167020
|
+
function getUnionSearchStatement(db, arity) {
|
|
167021
|
+
let statements = unionSearchStatements.get(arity);
|
|
167022
|
+
if (!statements) {
|
|
167023
|
+
statements = new WeakMap;
|
|
167024
|
+
unionSearchStatements.set(arity, statements);
|
|
167025
|
+
}
|
|
167026
|
+
let stmt = statements.get(db);
|
|
167027
|
+
if (!stmt) {
|
|
167028
|
+
const placeholders2 = Array.from({ length: arity }, () => "?").join(", ");
|
|
167029
|
+
stmt = db.prepare(`SELECT ${getMemorySelectColumns(db)} FROM memories_fts INNER JOIN memories ON memories.id = memories_fts.rowid WHERE memories.project_path IN (${placeholders2}) AND memories.status IN ('active', 'permanent') AND (memories.expires_at IS NULL OR memories.expires_at > ?) AND memories_fts MATCH ? ORDER BY bm25(memories_fts), memories.updated_at DESC, memories.id ASC LIMIT ?`);
|
|
167030
|
+
statements.set(db, stmt);
|
|
167031
|
+
}
|
|
167032
|
+
return stmt;
|
|
167033
|
+
}
|
|
167034
|
+
function uniqueProjectPaths(projectPaths) {
|
|
167035
|
+
return [...new Set(projectPaths.filter((path4) => path4.length > 0))];
|
|
167036
|
+
}
|
|
165770
167037
|
function sanitizeFtsQuery(query) {
|
|
165771
167038
|
const tokens = query.split(/\s+/).filter((token) => token.length > 0);
|
|
165772
167039
|
if (tokens.length === 0)
|
|
@@ -165785,6 +167052,28 @@ function searchMemoriesFTS(db, projectPath, query, limit = DEFAULT_SEARCH_LIMIT)
|
|
|
165785
167052
|
const rows = getSearchStatement(db).all(projectPath, Date.now(), sanitized, limit).filter(isMemoryRow);
|
|
165786
167053
|
return rows.map(toMemory);
|
|
165787
167054
|
}
|
|
167055
|
+
function searchMemoriesFTSUnion(db, projectPaths, query, limit = DEFAULT_SEARCH_LIMIT, ownIdentities, shareCategories) {
|
|
167056
|
+
const identities = uniqueProjectPaths(projectPaths);
|
|
167057
|
+
if (identities.length === 0)
|
|
167058
|
+
return [];
|
|
167059
|
+
const sharingFilter = buildWorkspaceMemorySqlFilter({
|
|
167060
|
+
identities,
|
|
167061
|
+
ownIdentities,
|
|
167062
|
+
shareCategories,
|
|
167063
|
+
tableName: "memories"
|
|
167064
|
+
});
|
|
167065
|
+
if (identities.length === 1 && !sharingFilter.active) {
|
|
167066
|
+
return searchMemoriesFTS(db, identities[0], query, limit);
|
|
167067
|
+
}
|
|
167068
|
+
const trimmedQuery = query.trim();
|
|
167069
|
+
if (trimmedQuery.length === 0 || limit <= 0)
|
|
167070
|
+
return [];
|
|
167071
|
+
const sanitized = sanitizeFtsQuery(trimmedQuery);
|
|
167072
|
+
if (sanitized.length === 0)
|
|
167073
|
+
return [];
|
|
167074
|
+
const rows = sharingFilter.active ? db.prepare(`SELECT ${getMemorySelectColumns(db)} FROM memories_fts INNER JOIN memories ON memories.id = memories_fts.rowid WHERE memories.project_path IN (${identities.map(() => "?").join(", ")}) AND memories.status IN ('active', 'permanent') AND (memories.expires_at IS NULL OR memories.expires_at > ?) AND memories_fts MATCH ?${sharingFilter.clause} ORDER BY bm25(memories_fts), memories.updated_at DESC, memories.id ASC LIMIT ?`).all(...identities, Date.now(), sanitized, ...sharingFilter.params, limit).filter(isMemoryRow) : getUnionSearchStatement(db, identities.length).all(...identities, Date.now(), sanitized, limit).filter(isMemoryRow);
|
|
167075
|
+
return rows.map(toMemory);
|
|
167076
|
+
}
|
|
165788
167077
|
// ../plugin/src/features/magic-context/message-index.ts
|
|
165789
167078
|
var lastIndexedStatements = new WeakMap;
|
|
165790
167079
|
var insertMessageStatements = new WeakMap;
|
|
@@ -165931,7 +167220,7 @@ function indexMessagesAfterOrdinal(db, sessionId, messages, lastIndexedOrdinal,
|
|
|
165931
167220
|
return inserted;
|
|
165932
167221
|
}
|
|
165933
167222
|
// ../plugin/src/features/magic-context/project-docs-hash.ts
|
|
165934
|
-
import { createHash as
|
|
167223
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
165935
167224
|
import { lstatSync, readFileSync as readFileSync4, statSync as statSync2 } from "node:fs";
|
|
165936
167225
|
import path4 from "node:path";
|
|
165937
167226
|
var PROJECT_DOC_FILES = ["ARCHITECTURE.md", "STRUCTURE.md"];
|
|
@@ -166029,7 +167318,7 @@ function hashCanonicalPieces(hashPieces) {
|
|
|
166029
167318
|
if (hashPieces.length === 0) {
|
|
166030
167319
|
return "";
|
|
166031
167320
|
}
|
|
166032
|
-
return
|
|
167321
|
+
return createHash8("sha256").update(hashPieces.join(PROJECT_DOCS_DELIMITER), "utf8").digest("hex");
|
|
166033
167322
|
}
|
|
166034
167323
|
function readProjectDocsCanonical(projectDirectory) {
|
|
166035
167324
|
const canonicalDirectory = path4.resolve(projectDirectory);
|
|
@@ -166135,18 +167424,13 @@ function getMemoryMutation(db, id) {
|
|
|
166135
167424
|
WHERE id = ?`).get(id);
|
|
166136
167425
|
return row ? toMemoryMutation(row) : null;
|
|
166137
167426
|
}
|
|
166138
|
-
function
|
|
166139
|
-
|
|
166140
|
-
|
|
166141
|
-
|
|
166142
|
-
|
|
166143
|
-
|
|
166144
|
-
|
|
166145
|
-
FROM memory_mutation_log
|
|
166146
|
-
WHERE project_path = ?
|
|
166147
|
-
AND id > ?
|
|
166148
|
-
AND target_memory_id IN (${placeholders})
|
|
166149
|
-
ORDER BY id ASC`).all(projectPath, afterId ?? 0, ...uniqueIds);
|
|
167427
|
+
function uniqueProjectPaths2(projectPaths) {
|
|
167428
|
+
return [...new Set(projectPaths.filter((path5) => path5.length > 0))];
|
|
167429
|
+
}
|
|
167430
|
+
function placeholders2(values) {
|
|
167431
|
+
return values.map(() => "?").join(", ");
|
|
167432
|
+
}
|
|
167433
|
+
function coalesceMutations(rows) {
|
|
166150
167434
|
const chosenByTarget = new Map;
|
|
166151
167435
|
for (const dbRow of rows) {
|
|
166152
167436
|
const candidate = toMemoryMutation(dbRow);
|
|
@@ -166167,10 +167451,54 @@ function getMemoryMutationsForRender(db, projectPath, afterId, renderedMemoryIds
|
|
|
166167
167451
|
}
|
|
166168
167452
|
return [...chosenByTarget.values()].sort((left, right) => left.id - right.id);
|
|
166169
167453
|
}
|
|
167454
|
+
function getMemoryMutationsForRender(db, projectPath, afterId, renderedMemoryIds) {
|
|
167455
|
+
if (renderedMemoryIds.length === 0)
|
|
167456
|
+
return [];
|
|
167457
|
+
const uniqueIds = [...new Set(renderedMemoryIds)].sort((left, right) => left - right);
|
|
167458
|
+
const placeholders3 = uniqueIds.map(() => "?").join(", ");
|
|
167459
|
+
const rows = db.prepare(`SELECT id, project_path, mutation_type, target_memory_id,
|
|
167460
|
+
superseded_by_id, category, new_content, queued_at
|
|
167461
|
+
FROM memory_mutation_log
|
|
167462
|
+
WHERE project_path = ?
|
|
167463
|
+
AND id > ?
|
|
167464
|
+
AND target_memory_id IN (${placeholders3})
|
|
167465
|
+
ORDER BY id ASC`).all(projectPath, afterId ?? 0, ...uniqueIds);
|
|
167466
|
+
return coalesceMutations(rows);
|
|
167467
|
+
}
|
|
167468
|
+
function getMemoryMutationsForRenderByProjects(db, projectPaths, afterId, renderedMemoryIds) {
|
|
167469
|
+
if (renderedMemoryIds.length === 0)
|
|
167470
|
+
return [];
|
|
167471
|
+
const identities = uniqueProjectPaths2(projectPaths);
|
|
167472
|
+
if (identities.length === 0)
|
|
167473
|
+
return [];
|
|
167474
|
+
if (identities.length === 1) {
|
|
167475
|
+
return getMemoryMutationsForRender(db, identities[0], afterId, renderedMemoryIds);
|
|
167476
|
+
}
|
|
167477
|
+
const uniqueIds = [...new Set(renderedMemoryIds)].sort((left, right) => left - right);
|
|
167478
|
+
const rows = db.prepare(`SELECT id, project_path, mutation_type, target_memory_id,
|
|
167479
|
+
superseded_by_id, category, new_content, queued_at
|
|
167480
|
+
FROM memory_mutation_log
|
|
167481
|
+
WHERE project_path IN (${placeholders2(identities)})
|
|
167482
|
+
AND id > ?
|
|
167483
|
+
AND target_memory_id IN (${placeholders2(uniqueIds)})
|
|
167484
|
+
ORDER BY id ASC`).all(...identities, afterId ?? 0, ...uniqueIds);
|
|
167485
|
+
return coalesceMutations(rows);
|
|
167486
|
+
}
|
|
166170
167487
|
function getMaxMemoryMutationId(db, projectPath) {
|
|
166171
167488
|
const row = db.prepare("SELECT MAX(id) AS max_id FROM memory_mutation_log WHERE project_path = ?").get(projectPath);
|
|
166172
167489
|
return row?.max_id ?? null;
|
|
166173
167490
|
}
|
|
167491
|
+
function getMaxMemoryMutationIdForProjects(db, projectPaths) {
|
|
167492
|
+
const identities = uniqueProjectPaths2(projectPaths);
|
|
167493
|
+
if (identities.length === 0)
|
|
167494
|
+
return null;
|
|
167495
|
+
if (identities.length === 1)
|
|
167496
|
+
return getMaxMemoryMutationId(db, identities[0]);
|
|
167497
|
+
const row = db.prepare(`SELECT MAX(id) AS max_id
|
|
167498
|
+
FROM memory_mutation_log
|
|
167499
|
+
WHERE project_path IN (${placeholders2(identities)})`).get(...identities);
|
|
167500
|
+
return row?.max_id ?? null;
|
|
167501
|
+
}
|
|
166174
167502
|
// ../plugin/src/features/magic-context/storage-meta-persisted.ts
|
|
166175
167503
|
init_logger();
|
|
166176
167504
|
var CAS_RETRY_LIMIT = 5;
|
|
@@ -167016,9 +168344,9 @@ function buildStatusClause(status) {
|
|
|
167016
168344
|
if (statuses.length === 0) {
|
|
167017
168345
|
return null;
|
|
167018
168346
|
}
|
|
167019
|
-
const
|
|
168347
|
+
const placeholders3 = statuses.map(() => "?").join(", ");
|
|
167020
168348
|
return {
|
|
167021
|
-
sql: `status IN (${
|
|
168349
|
+
sql: `status IN (${placeholders3})`,
|
|
167022
168350
|
params: statuses
|
|
167023
168351
|
};
|
|
167024
168352
|
}
|
|
@@ -167214,19 +168542,6 @@ function getProjectState(db, projectPath) {
|
|
|
167214
168542
|
WHERE project_path = ?`).get(projectPath);
|
|
167215
168543
|
return row ? toProjectState(row) : null;
|
|
167216
168544
|
}
|
|
167217
|
-
function bumpProjectMemoryEpoch(db, projectPath, now = Date.now()) {
|
|
167218
|
-
db.prepare(`INSERT INTO project_state
|
|
167219
|
-
(project_path, project_memory_epoch, project_user_profile_version, updated_at)
|
|
167220
|
-
VALUES (?, 1, 0, ?)
|
|
167221
|
-
ON CONFLICT(project_path) DO UPDATE SET
|
|
167222
|
-
project_memory_epoch = project_memory_epoch + 1,
|
|
167223
|
-
updated_at = excluded.updated_at`).run(projectPath, now);
|
|
167224
|
-
const state = getProjectState(db, projectPath);
|
|
167225
|
-
if (!state) {
|
|
167226
|
-
throw new Error(`Failed to bump project memory epoch for ${projectPath}`);
|
|
167227
|
-
}
|
|
167228
|
-
return state;
|
|
167229
|
-
}
|
|
167230
168545
|
function bumpProjectUserProfileVersion(db, projectPath = GLOBAL_USER_PROFILE_PROJECT_PATH, now = Date.now()) {
|
|
167231
168546
|
db.prepare(`INSERT INTO project_state
|
|
167232
168547
|
(project_path, project_memory_epoch, project_user_profile_version, updated_at)
|
|
@@ -167260,8 +168575,8 @@ function getSourceContents(db, sessionId, tagIds) {
|
|
|
167260
168575
|
if (tagIds.length === 0) {
|
|
167261
168576
|
return new Map;
|
|
167262
168577
|
}
|
|
167263
|
-
const
|
|
167264
|
-
const rows = db.prepare(`SELECT tag_id, content FROM source_contents WHERE session_id = ? AND tag_id IN (${
|
|
168578
|
+
const placeholders3 = tagIds.map(() => "?").join(", ");
|
|
168579
|
+
const rows = db.prepare(`SELECT tag_id, content FROM source_contents WHERE session_id = ? AND tag_id IN (${placeholders3})`).all(sessionId, ...tagIds).filter(isSourceContentRow);
|
|
167265
168580
|
const sources = new Map;
|
|
167266
168581
|
for (const row of rows) {
|
|
167267
168582
|
sources.set(row.tag_id, row.content);
|
|
@@ -167551,8 +168866,8 @@ function getTagsByNumbers(db, sessionId, tagNumbers) {
|
|
|
167551
168866
|
}
|
|
167552
168867
|
return all;
|
|
167553
168868
|
}
|
|
167554
|
-
const
|
|
167555
|
-
const rows = db.prepare(`SELECT ${TAG_SELECT_COLUMNS} FROM tags WHERE session_id = ? AND tag_number IN (${
|
|
168869
|
+
const placeholders3 = tagNumbers.map(() => "?").join(",");
|
|
168870
|
+
const rows = db.prepare(`SELECT ${TAG_SELECT_COLUMNS} FROM tags WHERE session_id = ? AND tag_number IN (${placeholders3}) ORDER BY tag_number ASC, id ASC`).all(sessionId, ...tagNumbers).filter(isTagRow);
|
|
167556
168871
|
return rows.map(toTagEntry);
|
|
167557
168872
|
}
|
|
167558
168873
|
var getToolTagNumberByOwnerStatements = new WeakMap;
|
|
@@ -167742,6 +169057,23 @@ function createCtxMemoryTool(deps) {
|
|
|
167742
169057
|
}
|
|
167743
169058
|
const projectIdentity = resolveProjectIdentity(ctx.cwd);
|
|
167744
169059
|
await deps.ensureProjectRegistered?.(ctx.cwd, deps.db);
|
|
169060
|
+
const workspaceIdentitySet = resolveWorkspaceIdentitySet(deps.db, projectIdentity);
|
|
169061
|
+
const expandedWorkspace = expandWorkspaceIdentitySetWithAliases(deps.db, workspaceIdentitySet.identities);
|
|
169062
|
+
const workspaceVisibleIdentities = workspaceIdentitySet.identities.length > 1 ? expandedWorkspace.expandedIdentities : workspaceIdentitySet.identities;
|
|
169063
|
+
const targetIdentityForStoredPath = (rawProjectPath) => workspaceIdentitySet.identities.length > 1 ? resolveStoredPathWorkspaceIdentity(rawProjectPath, workspaceIdentitySet.identities, expandedWorkspace.canonicalIdentityByStoredPath) ?? normalizeStoredProjectPath(rawProjectPath) : normalizeStoredProjectPath(rawProjectPath);
|
|
169064
|
+
const toolShareCategories = workspaceIdentitySet.identities.length > 1 ? resolveWorkspaceShareCategories(deps.db, projectIdentity) : null;
|
|
169065
|
+
const memoryVisibleToTool = (memory2) => {
|
|
169066
|
+
if (workspaceIdentitySet.identities.length <= 1) {
|
|
169067
|
+
return storedPathBelongsToIdentity(memory2.projectPath, projectIdentity);
|
|
169068
|
+
}
|
|
169069
|
+
if (!storedPathBelongsToWorkspace(memory2.projectPath, workspaceIdentitySet.identities, workspaceVisibleIdentities, expandedWorkspace.canonicalIdentityByStoredPath)) {
|
|
169070
|
+
return false;
|
|
169071
|
+
}
|
|
169072
|
+
const isOwn = targetIdentityForStoredPath(memory2.projectPath) === projectIdentity;
|
|
169073
|
+
if (isOwn)
|
|
169074
|
+
return true;
|
|
169075
|
+
return toolShareCategories === null || toolShareCategories.includes(memory2.category);
|
|
169076
|
+
};
|
|
167745
169077
|
const snapshot = getProjectEmbeddingSnapshot(projectIdentity);
|
|
167746
169078
|
if (snapshot ? !snapshot.features.memoryEnabled : deps.memoryEnabled === false) {
|
|
167747
169079
|
return err2("Cross-session memory is disabled for this project.");
|
|
@@ -167788,28 +169120,34 @@ function createCtxMemoryTool(deps) {
|
|
|
167788
169120
|
return err2("Error: 'content' is required when action is 'update'.");
|
|
167789
169121
|
}
|
|
167790
169122
|
const memory2 = getMemoryById(deps.db, updateId);
|
|
167791
|
-
if (!memory2 || !
|
|
169123
|
+
if (!memory2 || !memoryVisibleToTool(memory2)) {
|
|
167792
169124
|
return err2(`Error: Memory with ID ${updateId} was not found.`);
|
|
167793
169125
|
}
|
|
167794
169126
|
if (!dreamerAllowed && !isPrimaryMutableMemory(memory2)) {
|
|
167795
169127
|
return err2(inactiveMemoryError(updateId, "updating"));
|
|
167796
169128
|
}
|
|
167797
169129
|
const normalizedHash = computeNormalizedHash(content);
|
|
167798
|
-
const
|
|
169130
|
+
const targetIdentity = targetIdentityForStoredPath(memory2.projectPath);
|
|
169131
|
+
const duplicate = getMemoryByHash(deps.db, targetIdentity, memory2.category, normalizedHash);
|
|
167799
169132
|
if (duplicate && duplicate.id !== memory2.id) {
|
|
167800
169133
|
return err2(`Error: Memory content already exists as ID ${duplicate.id}; merge or archive duplicates instead.`);
|
|
167801
169134
|
}
|
|
167802
169135
|
deps.db.transaction(() => {
|
|
167803
169136
|
updateMemoryContent(deps.db, memory2.id, content, normalizedHash);
|
|
167804
169137
|
queueMemoryMutation(deps.db, {
|
|
167805
|
-
projectPath:
|
|
169138
|
+
projectPath: targetIdentity,
|
|
167806
169139
|
mutationType: "update",
|
|
167807
169140
|
targetMemoryId: memory2.id,
|
|
167808
169141
|
category: memory2.category,
|
|
167809
169142
|
newContent: content
|
|
167810
169143
|
});
|
|
167811
169144
|
})();
|
|
167812
|
-
queueEmbedding({
|
|
169145
|
+
queueEmbedding({
|
|
169146
|
+
deps,
|
|
169147
|
+
projectIdentity: targetIdentity,
|
|
169148
|
+
memoryId: memory2.id,
|
|
169149
|
+
content
|
|
169150
|
+
});
|
|
167813
169151
|
return ok2(`Updated memory [ID: ${memory2.id}] in ${memory2.category}.`);
|
|
167814
169152
|
}
|
|
167815
169153
|
if (params.action === "merge") {
|
|
@@ -167829,7 +169167,7 @@ function createCtxMemoryTool(deps) {
|
|
|
167829
169167
|
return err2("Error: One or more source memories were not found.");
|
|
167830
169168
|
}
|
|
167831
169169
|
if (!dreamerAllowed) {
|
|
167832
|
-
const foreign = sourceMemories.find((memory2) => !
|
|
169170
|
+
const foreign = sourceMemories.find((memory2) => !memoryVisibleToTool(memory2));
|
|
167833
169171
|
if (foreign) {
|
|
167834
169172
|
return err2(`Error: Memory with ID ${foreign.id} was not found.`);
|
|
167835
169173
|
}
|
|
@@ -167918,26 +169256,36 @@ function createCtxMemoryTool(deps) {
|
|
|
167918
169256
|
return ok2(`Merged memories [${ids.join(", ")}] into canonical memory [ID: ${canonicalMemory.id}] in ${category}; superseded [${supersededIds.join(", ")}].`);
|
|
167919
169257
|
}
|
|
167920
169258
|
if (params.action === "archive") {
|
|
167921
|
-
const
|
|
167922
|
-
if (!
|
|
169259
|
+
const rawArchiveIds = params.ids;
|
|
169260
|
+
if (!rawArchiveIds || rawArchiveIds.length === 0 || !rawArchiveIds.every(Number.isInteger)) {
|
|
167923
169261
|
return err2("Error: 'ids' must contain at least one integer memory ID when action is 'archive'.");
|
|
167924
169262
|
}
|
|
169263
|
+
const archiveIds = [...new Set(rawArchiveIds)];
|
|
167925
169264
|
for (const memoryId of archiveIds) {
|
|
167926
169265
|
const memory2 = getMemoryById(deps.db, memoryId);
|
|
167927
|
-
if (!memory2 || !
|
|
169266
|
+
if (!memory2 || !memoryVisibleToTool(memory2)) {
|
|
167928
169267
|
return err2(`Error: Memory with ID ${memoryId} was not found.`);
|
|
167929
169268
|
}
|
|
167930
169269
|
if (!dreamerAllowed && !isPrimaryMutableMemory(memory2)) {
|
|
167931
169270
|
return err2(inactiveMemoryError(memoryId, "archiving"));
|
|
167932
169271
|
}
|
|
167933
169272
|
}
|
|
169273
|
+
const targets = archiveIds.map((memoryId) => {
|
|
169274
|
+
const memory2 = getMemoryById(deps.db, memoryId);
|
|
169275
|
+
if (!memory2)
|
|
169276
|
+
throw new Error(`validated memory ${memoryId} disappeared`);
|
|
169277
|
+
return {
|
|
169278
|
+
memoryId,
|
|
169279
|
+
projectIdentity: targetIdentityForStoredPath(memory2.projectPath)
|
|
169280
|
+
};
|
|
169281
|
+
});
|
|
167934
169282
|
deps.db.transaction(() => {
|
|
167935
|
-
for (const
|
|
167936
|
-
archiveMemory(deps.db, memoryId, params.reason);
|
|
169283
|
+
for (const target2 of targets) {
|
|
169284
|
+
archiveMemory(deps.db, target2.memoryId, params.reason);
|
|
167937
169285
|
queueMemoryMutation(deps.db, {
|
|
167938
|
-
projectPath: projectIdentity,
|
|
169286
|
+
projectPath: target2.projectIdentity,
|
|
167939
169287
|
mutationType: "archive",
|
|
167940
|
-
targetMemoryId: memoryId
|
|
169288
|
+
targetMemoryId: target2.memoryId
|
|
167941
169289
|
});
|
|
167942
169290
|
}
|
|
167943
169291
|
})();
|
|
@@ -168909,6 +170257,37 @@ function previewText(text) {
|
|
|
168909
170257
|
}
|
|
168910
170258
|
return `${normalized.slice(0, RESULT_PREVIEW_LIMIT - 1).trimEnd()}…`;
|
|
168911
170259
|
}
|
|
170260
|
+
function resolveSearchWorkspaceContext(db, projectPath, identitySet) {
|
|
170261
|
+
const resolved = identitySet ?? resolveWorkspaceIdentitySet(db, projectPath);
|
|
170262
|
+
const isWorkspaced = resolved.identities.length > 1;
|
|
170263
|
+
const expanded = expandWorkspaceIdentitySetWithAliases(db, resolved.identities);
|
|
170264
|
+
const expandedIdentities = isWorkspaced ? expanded.expandedIdentities : resolved.identities;
|
|
170265
|
+
const canonicalIdentityByStoredPath = isWorkspaced ? expanded.canonicalIdentityByStoredPath : new Map(resolved.identities.map((identity) => [identity, identity]));
|
|
170266
|
+
const ownIdentities = expandedIdentities.filter((identity) => canonicalIdentityByStoredPath.get(identity) === projectPath);
|
|
170267
|
+
return {
|
|
170268
|
+
identities: resolved.identities,
|
|
170269
|
+
expandedIdentities,
|
|
170270
|
+
ownIdentities,
|
|
170271
|
+
shareCategories: isWorkspaced ? resolveWorkspaceShareCategories(db, projectPath) : null,
|
|
170272
|
+
namesByIdentity: resolved.namesByIdentity,
|
|
170273
|
+
canonicalIdentityByStoredPath,
|
|
170274
|
+
isWorkspaced
|
|
170275
|
+
};
|
|
170276
|
+
}
|
|
170277
|
+
function memoryWorkspaceIdentity(memory2, workspace) {
|
|
170278
|
+
return resolveStoredPathWorkspaceIdentity(memory2.projectPath, workspace.identities, workspace.canonicalIdentityByStoredPath);
|
|
170279
|
+
}
|
|
170280
|
+
function sourceNamesForSearchMemories(args) {
|
|
170281
|
+
if (!args.workspace.isWorkspaced)
|
|
170282
|
+
return;
|
|
170283
|
+
const sourceNames = new Map;
|
|
170284
|
+
for (const memory2 of args.memories) {
|
|
170285
|
+
const source = sourceNameForMemory(memory2.projectPath, args.projectPath, args.workspace.identities, args.workspace.namesByIdentity, args.workspace.canonicalIdentityByStoredPath);
|
|
170286
|
+
if (source)
|
|
170287
|
+
sourceNames.set(memory2.id, source);
|
|
170288
|
+
}
|
|
170289
|
+
return sourceNames.size > 0 ? sourceNames : undefined;
|
|
170290
|
+
}
|
|
168912
170291
|
function getMessageSearchStatement(db) {
|
|
168913
170292
|
let stmt = messageSearchStatements.get(db);
|
|
168914
170293
|
if (!stmt) {
|
|
@@ -168917,6 +170296,30 @@ function getMessageSearchStatement(db) {
|
|
|
168917
170296
|
}
|
|
168918
170297
|
return stmt;
|
|
168919
170298
|
}
|
|
170299
|
+
var ftsRowCountStatements = new WeakMap;
|
|
170300
|
+
var ftsMatchCountStatements = new WeakMap;
|
|
170301
|
+
function getSessionFtsRowCount(db, sessionId) {
|
|
170302
|
+
let stmt = ftsRowCountStatements.get(db);
|
|
170303
|
+
if (!stmt) {
|
|
170304
|
+
stmt = db.prepare("SELECT COUNT(*) AS n FROM message_history_fts WHERE session_id = ?");
|
|
170305
|
+
ftsRowCountStatements.set(db, stmt);
|
|
170306
|
+
}
|
|
170307
|
+
const row = stmt.get(sessionId);
|
|
170308
|
+
return typeof row?.n === "number" ? row.n : 0;
|
|
170309
|
+
}
|
|
170310
|
+
function countSessionFtsMatches(db, sessionId, ftsQuery) {
|
|
170311
|
+
let stmt = ftsMatchCountStatements.get(db);
|
|
170312
|
+
if (!stmt) {
|
|
170313
|
+
stmt = db.prepare("SELECT COUNT(*) AS n FROM message_history_fts WHERE session_id = ? AND message_history_fts MATCH ?");
|
|
170314
|
+
ftsMatchCountStatements.set(db, stmt);
|
|
170315
|
+
}
|
|
170316
|
+
try {
|
|
170317
|
+
const row = stmt.get(sessionId, ftsQuery);
|
|
170318
|
+
return typeof row?.n === "number" ? row.n : 0;
|
|
170319
|
+
} catch {
|
|
170320
|
+
return 0;
|
|
170321
|
+
}
|
|
170322
|
+
}
|
|
168920
170323
|
function getMessageOrdinal(value) {
|
|
168921
170324
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
168922
170325
|
return value;
|
|
@@ -168932,25 +170335,63 @@ async function getSemanticScores(args) {
|
|
|
168932
170335
|
if (!args.queryEmbedding || args.memories.length === 0) {
|
|
168933
170336
|
return semanticScores;
|
|
168934
170337
|
}
|
|
168935
|
-
|
|
168936
|
-
|
|
168937
|
-
|
|
168938
|
-
|
|
168939
|
-
|
|
168940
|
-
|
|
168941
|
-
|
|
170338
|
+
if (!args.workspace?.isWorkspaced) {
|
|
170339
|
+
const cachedEmbeddings = getProjectEmbeddings(args.db, args.projectPath);
|
|
170340
|
+
const embeddings = await ensureMemoryEmbeddings({
|
|
170341
|
+
db: args.db,
|
|
170342
|
+
projectIdentity: args.projectPath,
|
|
170343
|
+
memories: args.memories,
|
|
170344
|
+
existingEmbeddings: cachedEmbeddings
|
|
170345
|
+
});
|
|
170346
|
+
for (const memory2 of args.memories) {
|
|
170347
|
+
const memoryEmbedding = embeddings.get(memory2.id);
|
|
170348
|
+
if (!memoryEmbedding) {
|
|
170349
|
+
continue;
|
|
170350
|
+
}
|
|
170351
|
+
semanticScores.set(memory2.id, normalizeCosineScore(cosineSimilarity(args.queryEmbedding, memoryEmbedding.embedding)));
|
|
170352
|
+
}
|
|
170353
|
+
return semanticScores;
|
|
170354
|
+
}
|
|
170355
|
+
if (!args.queryModelId || args.queryModelId === "off") {
|
|
170356
|
+
return semanticScores;
|
|
170357
|
+
}
|
|
170358
|
+
const workspace = args.workspace;
|
|
170359
|
+
const memoriesByIdentity = new Map;
|
|
168942
170360
|
for (const memory2 of args.memories) {
|
|
168943
|
-
const
|
|
168944
|
-
if (!
|
|
170361
|
+
const identity = memoryWorkspaceIdentity(memory2, workspace);
|
|
170362
|
+
if (!identity)
|
|
170363
|
+
continue;
|
|
170364
|
+
const list = memoriesByIdentity.get(identity) ?? [];
|
|
170365
|
+
list.push(memory2);
|
|
170366
|
+
memoriesByIdentity.set(identity, list);
|
|
170367
|
+
}
|
|
170368
|
+
const ownMemories = memoriesByIdentity.get(args.projectPath) ?? [];
|
|
170369
|
+
if (ownMemories.length > 0) {
|
|
170370
|
+
const ownEmbeddings = getProjectEmbeddings(args.db, args.projectPath);
|
|
170371
|
+
await ensureMemoryEmbeddings({
|
|
170372
|
+
db: args.db,
|
|
170373
|
+
projectIdentity: args.projectPath,
|
|
170374
|
+
memories: ownMemories,
|
|
170375
|
+
existingEmbeddings: ownEmbeddings
|
|
170376
|
+
});
|
|
170377
|
+
}
|
|
170378
|
+
for (const identity of workspace.identities) {
|
|
170379
|
+
const memberMemories = memoriesByIdentity.get(identity) ?? [];
|
|
170380
|
+
if (memberMemories.length === 0)
|
|
168945
170381
|
continue;
|
|
170382
|
+
const cachedEmbeddings = getProjectEmbeddings(args.db, identity);
|
|
170383
|
+
for (const memory2 of memberMemories) {
|
|
170384
|
+
const memoryEmbedding = cachedEmbeddings.get(memory2.id);
|
|
170385
|
+
if (!memoryEmbedding || memoryEmbedding.modelId !== args.queryModelId)
|
|
170386
|
+
continue;
|
|
170387
|
+
semanticScores.set(memory2.id, normalizeCosineScore(cosineSimilarity(args.queryEmbedding, memoryEmbedding.embedding)));
|
|
168946
170388
|
}
|
|
168947
|
-
semanticScores.set(memory2.id, normalizeCosineScore(cosineSimilarity(args.queryEmbedding, memoryEmbedding)));
|
|
168948
170389
|
}
|
|
168949
170390
|
return semanticScores;
|
|
168950
170391
|
}
|
|
168951
170392
|
function getFtsMatches(args) {
|
|
168952
170393
|
try {
|
|
168953
|
-
return searchMemoriesFTS(args.db, args.projectPath, args.query, args.limit);
|
|
170394
|
+
return args.workspace?.isWorkspaced ? searchMemoriesFTSUnion(args.db, args.workspace.expandedIdentities, args.query, args.limit, args.workspace.ownIdentities, args.workspace.shareCategories) : searchMemoriesFTS(args.db, args.projectPath, args.query, args.limit);
|
|
168954
170395
|
} catch (error51) {
|
|
168955
170396
|
log(`[search] FTS query failed for "${args.query}": ${error51 instanceof Error ? error51.message : String(error51)}`);
|
|
168956
170397
|
return [];
|
|
@@ -168964,8 +170405,11 @@ function selectSemanticCandidates(args) {
|
|
|
168964
170405
|
return args.memories;
|
|
168965
170406
|
}
|
|
168966
170407
|
const candidateIds = new Set(args.ftsMatches.map((memory2) => memory2.id));
|
|
168967
|
-
const
|
|
168968
|
-
|
|
170408
|
+
const embeddingProjects = args.workspace?.isWorkspaced ? args.workspace.identities : [args.projectPath];
|
|
170409
|
+
for (const projectPath of embeddingProjects) {
|
|
170410
|
+
const cachedEmbeddings = peekProjectEmbeddings(projectPath);
|
|
170411
|
+
if (!cachedEmbeddings)
|
|
170412
|
+
continue;
|
|
168969
170413
|
for (const memoryId of cachedEmbeddings.keys()) {
|
|
168970
170414
|
candidateIds.add(memoryId);
|
|
168971
170415
|
}
|
|
@@ -169007,7 +170451,8 @@ function mergeMemoryResults(args) {
|
|
|
169007
170451
|
score,
|
|
169008
170452
|
memoryId: memory2.id,
|
|
169009
170453
|
category: memory2.category,
|
|
169010
|
-
matchType
|
|
170454
|
+
matchType,
|
|
170455
|
+
sourceName: args.sourceNameByMemoryId?.get(memory2.id)
|
|
169011
170456
|
});
|
|
169012
170457
|
}
|
|
169013
170458
|
return results.sort((left, right) => {
|
|
@@ -169021,7 +170466,7 @@ async function searchMemories(args) {
|
|
|
169021
170466
|
if (!args.memoryEnabled) {
|
|
169022
170467
|
return [];
|
|
169023
170468
|
}
|
|
169024
|
-
const memories = getMemoriesByProject(args.db, args.projectPath);
|
|
170469
|
+
const memories = args.workspace?.isWorkspaced ? getMemoriesByProjects(args.db, args.workspace.expandedIdentities, ["active", "permanent"], Date.now(), args.workspace.ownIdentities, args.workspace.shareCategories) : getMemoriesByProject(args.db, args.projectPath);
|
|
169025
170470
|
if (memories.length === 0) {
|
|
169026
170471
|
return [];
|
|
169027
170472
|
}
|
|
@@ -169029,26 +170474,43 @@ async function searchMemories(args) {
|
|
|
169029
170474
|
db: args.db,
|
|
169030
170475
|
projectPath: args.projectPath,
|
|
169031
170476
|
query: args.query,
|
|
169032
|
-
limit: FTS_SEMANTIC_CANDIDATE_LIMIT
|
|
170477
|
+
limit: FTS_SEMANTIC_CANDIDATE_LIMIT,
|
|
170478
|
+
workspace: args.workspace
|
|
169033
170479
|
});
|
|
169034
170480
|
const ftsScores = getFtsScores(ftsMatches);
|
|
169035
170481
|
const semanticCandidates = selectSemanticCandidates({
|
|
169036
170482
|
memories,
|
|
169037
170483
|
projectPath: args.projectPath,
|
|
169038
|
-
ftsMatches
|
|
170484
|
+
ftsMatches,
|
|
170485
|
+
workspace: args.workspace
|
|
169039
170486
|
});
|
|
169040
170487
|
const semanticScores = await getSemanticScores({
|
|
169041
170488
|
db: args.db,
|
|
169042
170489
|
projectPath: args.projectPath,
|
|
169043
170490
|
memories: semanticCandidates,
|
|
169044
|
-
queryEmbedding: args.queryEmbedding
|
|
170491
|
+
queryEmbedding: args.queryEmbedding,
|
|
170492
|
+
queryModelId: args.queryModelId,
|
|
170493
|
+
workspace: args.workspace
|
|
169045
170494
|
});
|
|
169046
170495
|
return mergeMemoryResults({
|
|
169047
170496
|
memories,
|
|
169048
170497
|
semanticScores,
|
|
169049
170498
|
ftsScores,
|
|
169050
170499
|
limit: args.limit,
|
|
169051
|
-
visibleMemoryIds: args.visibleMemoryIds
|
|
170500
|
+
visibleMemoryIds: args.visibleMemoryIds,
|
|
170501
|
+
sourceNameByMemoryId: sourceNamesForSearchMemories({
|
|
170502
|
+
memories,
|
|
170503
|
+
projectPath: args.projectPath,
|
|
170504
|
+
workspace: args.workspace ?? {
|
|
170505
|
+
identities: [args.projectPath],
|
|
170506
|
+
expandedIdentities: [args.projectPath],
|
|
170507
|
+
namesByIdentity: new Map,
|
|
170508
|
+
canonicalIdentityByStoredPath: new Map([[args.projectPath, args.projectPath]]),
|
|
170509
|
+
ownIdentities: [args.projectPath],
|
|
170510
|
+
shareCategories: null,
|
|
170511
|
+
isWorkspaced: false
|
|
170512
|
+
}
|
|
170513
|
+
})
|
|
169052
170514
|
});
|
|
169053
170515
|
}
|
|
169054
170516
|
function linearDecayScore(rank, total) {
|
|
@@ -169079,7 +170541,13 @@ function runMessageFtsQuery(db, sessionId, ftsQuery, fetchLimit, cutoff) {
|
|
|
169079
170541
|
return result;
|
|
169080
170542
|
}
|
|
169081
170543
|
var RRF_K = 60;
|
|
169082
|
-
var
|
|
170544
|
+
var VERBATIM_RANK_BONUS = 1 / RRF_K;
|
|
170545
|
+
var IDF_FALLOFF = 100;
|
|
170546
|
+
function probeDiscriminationWeight(df, corpusSize) {
|
|
170547
|
+
if (corpusSize <= 0 || df <= 0)
|
|
170548
|
+
return 1;
|
|
170549
|
+
return 1 / (1 + IDF_FALLOFF * df / corpusSize);
|
|
170550
|
+
}
|
|
169083
170551
|
function searchMessages(args) {
|
|
169084
170552
|
const cutoff = args.maxOrdinal != null && args.maxOrdinal >= 0 ? args.maxOrdinal : null;
|
|
169085
170553
|
const fetchLimit = args.maxOrdinal != null && args.maxOrdinal >= 0 ? args.limit * 3 : args.limit;
|
|
@@ -169096,20 +170564,31 @@ function searchMessages(args) {
|
|
|
169096
170564
|
role: row.role
|
|
169097
170565
|
}));
|
|
169098
170566
|
}
|
|
170567
|
+
const corpusSize = getSessionFtsRowCount(args.db, args.sessionId);
|
|
169099
170568
|
const queryLists = [];
|
|
169100
170569
|
if (baseQuery.length > 0) {
|
|
169101
|
-
queryLists.push(
|
|
170570
|
+
queryLists.push({
|
|
170571
|
+
rows: runMessageFtsQuery(args.db, args.sessionId, baseQuery, fetchLimit, cutoff),
|
|
170572
|
+
weight: 1
|
|
170573
|
+
});
|
|
169102
170574
|
}
|
|
170575
|
+
const probeWeights = new Map;
|
|
169103
170576
|
for (const probe of probes) {
|
|
169104
170577
|
const probeQuery = sanitizeFtsQuery(probe);
|
|
169105
170578
|
if (probeQuery.length === 0)
|
|
169106
170579
|
continue;
|
|
169107
|
-
|
|
170580
|
+
const df = countSessionFtsMatches(args.db, args.sessionId, probeQuery);
|
|
170581
|
+
const weight = probeDiscriminationWeight(df, corpusSize);
|
|
170582
|
+
probeWeights.set(probe, weight);
|
|
170583
|
+
queryLists.push({
|
|
170584
|
+
rows: runMessageFtsQuery(args.db, args.sessionId, probeQuery, fetchLimit, cutoff),
|
|
170585
|
+
weight
|
|
170586
|
+
});
|
|
169108
170587
|
}
|
|
169109
170588
|
const fused = new Map;
|
|
169110
170589
|
for (const list of queryLists) {
|
|
169111
|
-
list.forEach((row, rank) => {
|
|
169112
|
-
const rrf =
|
|
170590
|
+
list.rows.forEach((row, rank) => {
|
|
170591
|
+
const rrf = list.weight / (RRF_K + rank);
|
|
169113
170592
|
const existing = fused.get(row.messageId);
|
|
169114
170593
|
if (existing) {
|
|
169115
170594
|
existing.score += rrf;
|
|
@@ -169119,26 +170598,107 @@ function searchMessages(args) {
|
|
|
169119
170598
|
});
|
|
169120
170599
|
}
|
|
169121
170600
|
for (const entry of fused.values()) {
|
|
169122
|
-
|
|
169123
|
-
|
|
170601
|
+
let best = 0;
|
|
170602
|
+
for (const probe of probes) {
|
|
170603
|
+
const weight = probeWeights.get(probe) ?? 0;
|
|
170604
|
+
if (weight > best && containsProbeVerbatim(entry.row.content, [probe])) {
|
|
170605
|
+
best = weight;
|
|
170606
|
+
}
|
|
170607
|
+
}
|
|
170608
|
+
if (best > 0) {
|
|
170609
|
+
entry.score += best * VERBATIM_RANK_BONUS;
|
|
169124
170610
|
}
|
|
169125
170611
|
}
|
|
169126
170612
|
const ranked = [...fused.values()].sort((a, b) => b.score !== a.score ? b.score - a.score : a.row.messageOrdinal - b.row.messageOrdinal).slice(0, args.limit);
|
|
169127
|
-
|
|
169128
|
-
return ranked.map((entry) => ({
|
|
170613
|
+
return ranked.map((entry, rank) => ({
|
|
169129
170614
|
source: "message",
|
|
169130
170615
|
content: previewText(entry.row.content),
|
|
169131
|
-
score:
|
|
170616
|
+
score: linearDecayScore(rank, ranked.length),
|
|
169132
170617
|
messageOrdinal: entry.row.messageOrdinal,
|
|
169133
170618
|
messageId: entry.row.messageId,
|
|
169134
170619
|
role: entry.row.role
|
|
169135
170620
|
}));
|
|
169136
170621
|
}
|
|
170622
|
+
function searchCompartmentChunks(args) {
|
|
170623
|
+
if (!args.queryEmbedding || args.limit <= 0)
|
|
170624
|
+
return [];
|
|
170625
|
+
const cutoff = args.maxOrdinal != null && args.maxOrdinal >= 0 ? args.maxOrdinal : null;
|
|
170626
|
+
const rows = loadCompartmentChunkEmbeddingsForSearch(args.db, args.sessionId, args.projectPath, args.modelId);
|
|
170627
|
+
if (rows.length === 0)
|
|
170628
|
+
return [];
|
|
170629
|
+
const byCompartment = new Map;
|
|
170630
|
+
for (const row of rows) {
|
|
170631
|
+
if (cutoff !== null && row.endOrdinal > cutoff) {
|
|
170632
|
+
continue;
|
|
170633
|
+
}
|
|
170634
|
+
const score = normalizeCosineScore(cosineSimilarity(args.queryEmbedding, row.vector));
|
|
170635
|
+
if (score <= 0)
|
|
170636
|
+
continue;
|
|
170637
|
+
const existing = byCompartment.get(row.compartmentId);
|
|
170638
|
+
if (!existing || score > existing.score) {
|
|
170639
|
+
byCompartment.set(row.compartmentId, { row, score });
|
|
170640
|
+
}
|
|
170641
|
+
}
|
|
170642
|
+
return [...byCompartment.values()].sort((left, right) => right.score !== left.score ? right.score - left.score : left.row.startOrdinal - right.row.startOrdinal).slice(0, args.limit).map(({ row, score }) => ({
|
|
170643
|
+
source: "compartment",
|
|
170644
|
+
content: previewText(row.title),
|
|
170645
|
+
score: score * SINGLE_SOURCE_PENALTY,
|
|
170646
|
+
compartmentId: row.compartmentId,
|
|
170647
|
+
sessionId: row.sessionId,
|
|
170648
|
+
title: row.title,
|
|
170649
|
+
startOrdinal: row.startOrdinal,
|
|
170650
|
+
endOrdinal: row.endOrdinal,
|
|
170651
|
+
matchType: "semantic"
|
|
170652
|
+
}));
|
|
170653
|
+
}
|
|
170654
|
+
function mergeMessageAndCompartmentResults(args) {
|
|
170655
|
+
if (args.compartments.length === 0)
|
|
170656
|
+
return args.messages;
|
|
170657
|
+
if (args.messages.length === 0)
|
|
170658
|
+
return args.compartments;
|
|
170659
|
+
const fused = new Map;
|
|
170660
|
+
const add = (key, result, score, tieOrdinal) => {
|
|
170661
|
+
const existing = fused.get(key);
|
|
170662
|
+
if (existing) {
|
|
170663
|
+
existing.score += score;
|
|
170664
|
+
return existing;
|
|
170665
|
+
}
|
|
170666
|
+
const entry = { result, score, tieOrdinal, snippetScore: -1 };
|
|
170667
|
+
fused.set(key, entry);
|
|
170668
|
+
return entry;
|
|
170669
|
+
};
|
|
170670
|
+
args.compartments.forEach((compartment, rank) => {
|
|
170671
|
+
add(`compartment:${compartment.compartmentId}`, compartment, 1 / (RRF_K + rank), compartment.startOrdinal);
|
|
170672
|
+
});
|
|
170673
|
+
for (const [rank, message] of args.messages.entries()) {
|
|
170674
|
+
const containing = args.compartments.find((compartment) => message.messageOrdinal >= compartment.startOrdinal && message.messageOrdinal <= compartment.endOrdinal);
|
|
170675
|
+
const contribution = 1 / (RRF_K + rank);
|
|
170676
|
+
if (!containing) {
|
|
170677
|
+
add(`message:${message.messageId}`, message, contribution, message.messageOrdinal);
|
|
170678
|
+
continue;
|
|
170679
|
+
}
|
|
170680
|
+
const entry = add(`compartment:${containing.compartmentId}`, containing, contribution, containing.startOrdinal);
|
|
170681
|
+
if (message.score > entry.snippetScore && entry.result.source === "compartment") {
|
|
170682
|
+
entry.snippetScore = message.score;
|
|
170683
|
+
entry.result = {
|
|
170684
|
+
...entry.result,
|
|
170685
|
+
matchType: "hybrid",
|
|
170686
|
+
snippet: message.content
|
|
170687
|
+
};
|
|
170688
|
+
}
|
|
170689
|
+
}
|
|
170690
|
+
const ranked = [...fused.values()].sort((left, right) => right.score !== left.score ? right.score - left.score : left.tieOrdinal - right.tieOrdinal).slice(0, args.limit);
|
|
170691
|
+
return ranked.map((entry, rank) => ({
|
|
170692
|
+
...entry.result,
|
|
170693
|
+
score: linearDecayScore(rank, ranked.length)
|
|
170694
|
+
}));
|
|
170695
|
+
}
|
|
169137
170696
|
function getSourceBoost(result) {
|
|
169138
170697
|
switch (result.source) {
|
|
169139
170698
|
case "memory":
|
|
169140
170699
|
return MEMORY_SOURCE_BOOST;
|
|
169141
170700
|
case "message":
|
|
170701
|
+
case "compartment":
|
|
169142
170702
|
return MESSAGE_SOURCE_BOOST;
|
|
169143
170703
|
case "git_commit":
|
|
169144
170704
|
return GIT_COMMIT_SOURCE_BOOST;
|
|
@@ -169156,6 +170716,9 @@ function compareUnifiedResults(left, right) {
|
|
|
169156
170716
|
if (left.source === "message" && right.source === "message") {
|
|
169157
170717
|
return left.messageOrdinal - right.messageOrdinal;
|
|
169158
170718
|
}
|
|
170719
|
+
if (left.source === "compartment" && right.source === "compartment") {
|
|
170720
|
+
return left.startOrdinal - right.startOrdinal;
|
|
170721
|
+
}
|
|
169159
170722
|
if (left.source === "git_commit" && right.source === "git_commit") {
|
|
169160
170723
|
return right.committedAtMs - left.committedAtMs;
|
|
169161
170724
|
}
|
|
@@ -169206,10 +170769,12 @@ async function unifiedSearch(db, sessionId, projectPath, query, options3 = {}) {
|
|
|
169206
170769
|
const isEmbeddingRuntimeEnabled = options3.isEmbeddingRuntimeEnabled ?? isEmbeddingEnabled;
|
|
169207
170770
|
const gitCommitsEnabled = options3.gitCommitsEnabled ?? false;
|
|
169208
170771
|
const activeSources = resolveSources(options3.sources);
|
|
169209
|
-
const
|
|
170772
|
+
const memoryFeatureEnabled = options3.memoryEnabled ?? true;
|
|
170773
|
+
const runMemory = activeSources.has("memory") && memoryFeatureEnabled;
|
|
169210
170774
|
const runMessages = activeSources.has("message");
|
|
169211
170775
|
const runGitCommits = activeSources.has("git_commit") && gitCommitsEnabled;
|
|
169212
|
-
const
|
|
170776
|
+
const runCompartmentChunks = runMessages && memoryFeatureEnabled && embeddingEnabled;
|
|
170777
|
+
const needsEmbedding = (runMemory || runGitCommits || runCompartmentChunks) && embeddingEnabled && isEmbeddingRuntimeEnabled();
|
|
169213
170778
|
const queryEmbeddingPromise = needsEmbedding ? embedQuery(trimmedQuery, options3.signal).catch((error51) => {
|
|
169214
170779
|
log(`[search] query embedding failed: ${error51 instanceof Error ? error51.message : String(error51)}`);
|
|
169215
170780
|
return null;
|
|
@@ -169225,6 +170790,24 @@ async function unifiedSearch(db, sessionId, projectPath, query, options3 = {}) {
|
|
|
169225
170790
|
probes: messageProbes
|
|
169226
170791
|
}) : [];
|
|
169227
170792
|
const queryEmbedding = await queryEmbeddingPromise;
|
|
170793
|
+
const workspace = resolveSearchWorkspaceContext(db, projectPath);
|
|
170794
|
+
const embeddingSnapshot = getProjectEmbeddingSnapshot(projectPath);
|
|
170795
|
+
const embeddingModelId = embeddingSnapshot?.modelId;
|
|
170796
|
+
const chunkModelId = embeddingSnapshot?.chunkModelId;
|
|
170797
|
+
const compartmentResults = runCompartmentChunks ? searchCompartmentChunks({
|
|
170798
|
+
db,
|
|
170799
|
+
sessionId,
|
|
170800
|
+
projectPath,
|
|
170801
|
+
queryEmbedding,
|
|
170802
|
+
limit: tierLimit,
|
|
170803
|
+
maxOrdinal: options3.maxMessageOrdinal,
|
|
170804
|
+
modelId: chunkModelId && chunkModelId !== "off" ? chunkModelId : null
|
|
170805
|
+
}) : [];
|
|
170806
|
+
const messageLikeResults = mergeMessageAndCompartmentResults({
|
|
170807
|
+
messages: messageResults,
|
|
170808
|
+
compartments: compartmentResults,
|
|
170809
|
+
limit: tierLimit
|
|
170810
|
+
});
|
|
169228
170811
|
const [memoryResults, gitCommitResults] = await Promise.all([
|
|
169229
170812
|
runMemory ? searchMemories({
|
|
169230
170813
|
db,
|
|
@@ -169233,6 +170816,8 @@ async function unifiedSearch(db, sessionId, projectPath, query, options3 = {}) {
|
|
|
169233
170816
|
limit: tierLimit,
|
|
169234
170817
|
memoryEnabled: true,
|
|
169235
170818
|
queryEmbedding,
|
|
170819
|
+
queryModelId: embeddingModelId && embeddingModelId !== "off" ? embeddingModelId : null,
|
|
170820
|
+
workspace,
|
|
169236
170821
|
visibleMemoryIds: options3.visibleMemoryIds
|
|
169237
170822
|
}) : Promise.resolve([]),
|
|
169238
170823
|
runGitCommits ? Promise.resolve(searchGitCommits({
|
|
@@ -169243,7 +170828,7 @@ async function unifiedSearch(db, sessionId, projectPath, query, options3 = {}) {
|
|
|
169243
170828
|
queryEmbedding
|
|
169244
170829
|
})) : Promise.resolve([])
|
|
169245
170830
|
]);
|
|
169246
|
-
const results = [...memoryResults, ...
|
|
170831
|
+
const results = [...memoryResults, ...messageLikeResults, ...gitCommitResults].sort(compareUnifiedResults).slice(0, limit);
|
|
169247
170832
|
const countRetrievals = options3.countRetrievals ?? true;
|
|
169248
170833
|
if (countRetrievals) {
|
|
169249
170834
|
const memoryIds = results.filter((result) => result.source === "memory").map((result) => result.memoryId);
|
|
@@ -169284,8 +170869,8 @@ function getUserMemoryCandidates(db) {
|
|
|
169284
170869
|
function deleteUserMemoryCandidates(db, ids) {
|
|
169285
170870
|
if (ids.length === 0)
|
|
169286
170871
|
return;
|
|
169287
|
-
const
|
|
169288
|
-
db.prepare(`DELETE FROM user_memory_candidates WHERE id IN (${
|
|
170872
|
+
const placeholders3 = ids.map(() => "?").join(",");
|
|
170873
|
+
db.prepare(`DELETE FROM user_memory_candidates WHERE id IN (${placeholders3})`).run(...ids);
|
|
169289
170874
|
}
|
|
169290
170875
|
function insertUserMemory(db, content, sourceCandidateIds) {
|
|
169291
170876
|
const now = Date.now();
|
|
@@ -169837,26 +171422,40 @@ ${sections.join(`
|
|
|
169837
171422
|
var DEFAULT_MEMORY_BUDGET_TOKENS = 8000;
|
|
169838
171423
|
var MEMORY_BLOCK_WRAPPER_TOKENS = 6;
|
|
169839
171424
|
var DEFAULT_USER_PROFILE_BUDGET_TOKENS = 4000;
|
|
171425
|
+
function memoryCanonicalIdentity(memory2, workspace) {
|
|
171426
|
+
return resolveStoredPathWorkspaceIdentity(memory2.projectPath, workspace.identities, workspace.canonicalIdentityByStoredPath);
|
|
171427
|
+
}
|
|
171428
|
+
function memorySelectionOrder(left, right) {
|
|
171429
|
+
if (left.status === "permanent" && right.status !== "permanent")
|
|
171430
|
+
return -1;
|
|
171431
|
+
if (right.status === "permanent" && left.status !== "permanent")
|
|
171432
|
+
return 1;
|
|
171433
|
+
const leftImportance = left.importance ?? Number.NEGATIVE_INFINITY;
|
|
171434
|
+
const rightImportance = right.importance ?? Number.NEGATIVE_INFINITY;
|
|
171435
|
+
const importanceDiff = rightImportance - leftImportance;
|
|
171436
|
+
if (importanceDiff !== 0)
|
|
171437
|
+
return importanceDiff;
|
|
171438
|
+
return left.id - right.id;
|
|
171439
|
+
}
|
|
171440
|
+
function memoryRenderOrder(left, right) {
|
|
171441
|
+
const aPriority = MEMORY_CATEGORY_ORDER_PRIORITY[left.category] ?? MEMORY_CATEGORY_ORDER_UNKNOWN;
|
|
171442
|
+
const bPriority = MEMORY_CATEGORY_ORDER_PRIORITY[right.category] ?? MEMORY_CATEGORY_ORDER_UNKNOWN;
|
|
171443
|
+
const categoryDiff = aPriority - bPriority;
|
|
171444
|
+
if (categoryDiff !== 0)
|
|
171445
|
+
return categoryDiff;
|
|
171446
|
+
return left.id - right.id;
|
|
171447
|
+
}
|
|
169840
171448
|
var maxCompartmentSeqStatements = new WeakMap;
|
|
169841
171449
|
var maxMemoryIdStatements = new WeakMap;
|
|
169842
171450
|
var legacyCompartmentCountStatements = new WeakMap;
|
|
169843
171451
|
var m0CompartmentStatements = new WeakMap;
|
|
169844
171452
|
var newCompartmentStatements = new WeakMap;
|
|
169845
|
-
function trimMemoriesToBudgetV2(sessionId, memories, budgetTokens) {
|
|
169846
|
-
const selectionOrder = [...memories].sort(
|
|
169847
|
-
if (a.status === "permanent" && b.status !== "permanent")
|
|
169848
|
-
return -1;
|
|
169849
|
-
if (b.status === "permanent" && a.status !== "permanent")
|
|
169850
|
-
return 1;
|
|
169851
|
-
const importanceDiff = (b.importance ?? 50) - (a.importance ?? 50);
|
|
169852
|
-
if (importanceDiff !== 0)
|
|
169853
|
-
return importanceDiff;
|
|
169854
|
-
return a.id - b.id;
|
|
169855
|
-
});
|
|
171453
|
+
function trimMemoriesToBudgetV2(sessionId, memories, budgetTokens, renderOptions = {}) {
|
|
171454
|
+
const selectionOrder = [...memories].sort(memorySelectionOrder);
|
|
169856
171455
|
const selected = [];
|
|
169857
171456
|
let usedTokens = MEMORY_BLOCK_WRAPPER_TOKENS;
|
|
169858
171457
|
for (const memory2 of selectionOrder) {
|
|
169859
|
-
const memoryTokens = estimateTokens(renderMemoryLineV2(memory2));
|
|
171458
|
+
const memoryTokens = estimateTokens(renderMemoryLineV2(memory2, renderOptions.sourceNameByMemoryId?.get(memory2.id)));
|
|
169860
171459
|
if (usedTokens + memoryTokens > budgetTokens)
|
|
169861
171460
|
continue;
|
|
169862
171461
|
selected.push(memory2);
|
|
@@ -169865,16 +171464,70 @@ function trimMemoriesToBudgetV2(sessionId, memories, budgetTokens) {
|
|
|
169865
171464
|
if (selected.length < memories.length) {
|
|
169866
171465
|
sessionLog(sessionId, `v2 trimmed memories from ${memories.length} to ${selected.length} to fit injection budget of ${budgetTokens} tokens`);
|
|
169867
171466
|
}
|
|
169868
|
-
const renderOrder = [...selected].sort(
|
|
169869
|
-
const aPriority = MEMORY_CATEGORY_ORDER_PRIORITY[a.category] ?? MEMORY_CATEGORY_ORDER_UNKNOWN;
|
|
169870
|
-
const bPriority = MEMORY_CATEGORY_ORDER_PRIORITY[b.category] ?? MEMORY_CATEGORY_ORDER_UNKNOWN;
|
|
169871
|
-
const categoryDiff = aPriority - bPriority;
|
|
169872
|
-
if (categoryDiff !== 0)
|
|
169873
|
-
return categoryDiff;
|
|
169874
|
-
return a.id - b.id;
|
|
169875
|
-
});
|
|
171467
|
+
const renderOrder = [...selected].sort(memoryRenderOrder);
|
|
169876
171468
|
return { selected, renderOrder };
|
|
169877
171469
|
}
|
|
171470
|
+
function trimWorkspaceMemoriesToBudgetV2(sessionId, memories, budgetTokens, workspace, renderOptions = {}) {
|
|
171471
|
+
if (!workspace.isWorkspaced) {
|
|
171472
|
+
return trimMemoriesToBudgetV2(sessionId, memories, budgetTokens, renderOptions);
|
|
171473
|
+
}
|
|
171474
|
+
const selected = [];
|
|
171475
|
+
const selectedIds = new Set;
|
|
171476
|
+
let usedTokens = MEMORY_BLOCK_WRAPPER_TOKENS;
|
|
171477
|
+
const tokenCost = (memory2) => estimateTokens(renderMemoryLineV2(memory2, renderOptions.sourceNameByMemoryId?.get(memory2.id)));
|
|
171478
|
+
const trySelect = (memory2) => {
|
|
171479
|
+
if (selectedIds.has(memory2.id))
|
|
171480
|
+
return false;
|
|
171481
|
+
const tokens = tokenCost(memory2);
|
|
171482
|
+
if (usedTokens + tokens > budgetTokens)
|
|
171483
|
+
return false;
|
|
171484
|
+
selected.push(memory2);
|
|
171485
|
+
selectedIds.add(memory2.id);
|
|
171486
|
+
usedTokens += tokens;
|
|
171487
|
+
return true;
|
|
171488
|
+
};
|
|
171489
|
+
for (const memory2 of memories.filter((candidate) => candidate.status === "permanent").sort(memorySelectionOrder)) {
|
|
171490
|
+
trySelect(memory2);
|
|
171491
|
+
}
|
|
171492
|
+
const remainingAfterPermanent = Math.max(0, budgetTokens - usedTokens);
|
|
171493
|
+
const floorTokens = remainingAfterPermanent / Math.max(1, workspace.identities.length);
|
|
171494
|
+
const byIdentity = new Map;
|
|
171495
|
+
for (const memory2 of memories) {
|
|
171496
|
+
if (memory2.status === "permanent")
|
|
171497
|
+
continue;
|
|
171498
|
+
const identity = memoryCanonicalIdentity(memory2, workspace);
|
|
171499
|
+
if (!identity)
|
|
171500
|
+
continue;
|
|
171501
|
+
const list = byIdentity.get(identity) ?? [];
|
|
171502
|
+
list.push(memory2);
|
|
171503
|
+
byIdentity.set(identity, list);
|
|
171504
|
+
}
|
|
171505
|
+
for (const identity of workspace.identities) {
|
|
171506
|
+
let memberTokens = 0;
|
|
171507
|
+
const candidates2 = (byIdentity.get(identity) ?? []).sort(memorySelectionOrder);
|
|
171508
|
+
for (const memory2 of candidates2) {
|
|
171509
|
+
if (selectedIds.has(memory2.id))
|
|
171510
|
+
continue;
|
|
171511
|
+
const tokens = tokenCost(memory2);
|
|
171512
|
+
if (memberTokens + tokens > floorTokens)
|
|
171513
|
+
continue;
|
|
171514
|
+
if (usedTokens + tokens > budgetTokens)
|
|
171515
|
+
continue;
|
|
171516
|
+
selected.push(memory2);
|
|
171517
|
+
selectedIds.add(memory2.id);
|
|
171518
|
+
usedTokens += tokens;
|
|
171519
|
+
memberTokens += tokens;
|
|
171520
|
+
}
|
|
171521
|
+
}
|
|
171522
|
+
const remaining = memories.filter((memory2) => !selectedIds.has(memory2.id)).sort(memorySelectionOrder);
|
|
171523
|
+
for (const memory2 of remaining) {
|
|
171524
|
+
trySelect(memory2);
|
|
171525
|
+
}
|
|
171526
|
+
if (selected.length < memories.length) {
|
|
171527
|
+
sessionLog(sessionId, `v2 trimmed memories from ${memories.length} to ${selected.length} to fit injection budget of ${budgetTokens} tokens`);
|
|
171528
|
+
}
|
|
171529
|
+
return { selected, renderOrder: [...selected].sort(memoryRenderOrder) };
|
|
171530
|
+
}
|
|
169878
171531
|
function trimUserMemoriesToBudget(memories, budgetTokens) {
|
|
169879
171532
|
const selected = [];
|
|
169880
171533
|
let usedTokens = 0;
|
|
@@ -169887,15 +171540,16 @@ function trimUserMemoriesToBudget(memories, budgetTokens) {
|
|
|
169887
171540
|
}
|
|
169888
171541
|
return selected;
|
|
169889
171542
|
}
|
|
169890
|
-
function renderMemoryLineV2(memory2) {
|
|
169891
|
-
|
|
171543
|
+
function renderMemoryLineV2(memory2, sourceName) {
|
|
171544
|
+
const sourceAttr = sourceName ? ` source="${escapeXmlAttr(sourceName)}"` : "";
|
|
171545
|
+
return ` <memory id="${memory2.id}" category="${escapeXmlAttr(memory2.category)}"${sourceAttr} importance="${memory2.importance ?? 50}">${escapeXmlContent(memory2.content)}</memory>`;
|
|
169892
171546
|
}
|
|
169893
|
-
function renderMemoryBlockV2(memories, wrapper = "project-memory") {
|
|
171547
|
+
function renderMemoryBlockV2(memories, wrapper = "project-memory", renderOptions = {}) {
|
|
169894
171548
|
if (memories.length === 0)
|
|
169895
171549
|
return "";
|
|
169896
171550
|
const lines = [`<${wrapper}>`];
|
|
169897
171551
|
for (const memory2 of memories) {
|
|
169898
|
-
lines.push(renderMemoryLineV2(memory2));
|
|
171552
|
+
lines.push(renderMemoryLineV2(memory2, renderOptions.sourceNameByMemoryId?.get(memory2.id)));
|
|
169899
171553
|
}
|
|
169900
171554
|
lines.push(`</${wrapper}>`);
|
|
169901
171555
|
return lines.join(`
|
|
@@ -169960,8 +171614,9 @@ function formatAge(committedAtMs) {
|
|
|
169960
171614
|
}
|
|
169961
171615
|
function formatResult(result, index) {
|
|
169962
171616
|
if (result.source === "memory") {
|
|
171617
|
+
const source = result.sourceName ? ` source=${result.sourceName}` : "";
|
|
169963
171618
|
return [
|
|
169964
|
-
`[${index}] [memory] score=${result.score.toFixed(2)} id=${result.memoryId} category=${result.category} match=${result.matchType}`,
|
|
171619
|
+
`[${index}] [memory] score=${result.score.toFixed(2)} id=${result.memoryId} category=${result.category}${source} match=${result.matchType}`,
|
|
169965
171620
|
result.content
|
|
169966
171621
|
].join(`
|
|
169967
171622
|
`);
|
|
@@ -169971,6 +171626,13 @@ function formatResult(result, index) {
|
|
|
169971
171626
|
`[${index}] [git_commit] score=${result.score.toFixed(2)} sha=${result.shortSha} ${formatAge(result.committedAtMs)} match=${result.matchType}`,
|
|
169972
171627
|
result.content
|
|
169973
171628
|
].join(`
|
|
171629
|
+
`);
|
|
171630
|
+
}
|
|
171631
|
+
if (result.source === "compartment") {
|
|
171632
|
+
return [
|
|
171633
|
+
`[${index}] [message] score=${result.score.toFixed(2)} compartment_id=${result.compartmentId} range=${result.startOrdinal}-${result.endOrdinal} match=${result.matchType} title=${result.title}`,
|
|
171634
|
+
result.snippet ? `Snippet: ${result.snippet}` : result.content
|
|
171635
|
+
].join(`
|
|
169974
171636
|
`);
|
|
169975
171637
|
}
|
|
169976
171638
|
const expandStart = Math.max(1, result.messageOrdinal - 3);
|
|
@@ -169986,7 +171648,7 @@ function formatSearchResults(query, results) {
|
|
|
169986
171648
|
return `No results found for "${query}" across memories, git commits, or message history.`;
|
|
169987
171649
|
}
|
|
169988
171650
|
const bodyParts = results.map((result, index) => formatResult(result, index + 1));
|
|
169989
|
-
if (results.some((result) => result.source === "message")) {
|
|
171651
|
+
if (results.some((result) => result.source === "message" || result.source === "compartment")) {
|
|
169990
171652
|
bodyParts.push("Use ctx_expand(start, end) with the range from any message result above to read the full conversation context.");
|
|
169991
171653
|
}
|
|
169992
171654
|
const body = bodyParts.join(`
|