@wolfx/pi-magic-context 0.23.0 → 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 +2243 -307
- package/dist/subagent-entry.js +2071 -410
- 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();
|
|
@@ -143856,10 +144546,10 @@ function mergeDefs(...defs) {
|
|
|
143856
144546
|
function cloneDef(schema) {
|
|
143857
144547
|
return mergeDefs(schema._zod.def);
|
|
143858
144548
|
}
|
|
143859
|
-
function getElementAtPath(obj,
|
|
143860
|
-
if (!
|
|
144549
|
+
function getElementAtPath(obj, path4) {
|
|
144550
|
+
if (!path4)
|
|
143861
144551
|
return obj;
|
|
143862
|
-
return
|
|
144552
|
+
return path4.reduce((acc, key) => acc?.[key], obj);
|
|
143863
144553
|
}
|
|
143864
144554
|
function promiseAllObject(promisesObj) {
|
|
143865
144555
|
const keys = Object.keys(promisesObj);
|
|
@@ -144267,11 +144957,11 @@ function explicitlyAborted(x, startIndex = 0) {
|
|
|
144267
144957
|
}
|
|
144268
144958
|
return false;
|
|
144269
144959
|
}
|
|
144270
|
-
function prefixIssues(
|
|
144960
|
+
function prefixIssues(path4, issues) {
|
|
144271
144961
|
return issues.map((iss) => {
|
|
144272
144962
|
var _a2;
|
|
144273
144963
|
(_a2 = iss).path ?? (_a2.path = []);
|
|
144274
|
-
iss.path.unshift(
|
|
144964
|
+
iss.path.unshift(path4);
|
|
144275
144965
|
return iss;
|
|
144276
144966
|
});
|
|
144277
144967
|
}
|
|
@@ -144418,16 +145108,16 @@ function flattenError(error, mapper = (issue2) => issue2.message) {
|
|
|
144418
145108
|
}
|
|
144419
145109
|
function formatError(error, mapper = (issue2) => issue2.message) {
|
|
144420
145110
|
const fieldErrors = { _errors: [] };
|
|
144421
|
-
const processError = (error2,
|
|
145111
|
+
const processError = (error2, path4 = []) => {
|
|
144422
145112
|
for (const issue2 of error2.issues) {
|
|
144423
145113
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
144424
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
145114
|
+
issue2.errors.map((issues) => processError({ issues }, [...path4, ...issue2.path]));
|
|
144425
145115
|
} else if (issue2.code === "invalid_key") {
|
|
144426
|
-
processError({ issues: issue2.issues }, [...
|
|
145116
|
+
processError({ issues: issue2.issues }, [...path4, ...issue2.path]);
|
|
144427
145117
|
} else if (issue2.code === "invalid_element") {
|
|
144428
|
-
processError({ issues: issue2.issues }, [...
|
|
145118
|
+
processError({ issues: issue2.issues }, [...path4, ...issue2.path]);
|
|
144429
145119
|
} else {
|
|
144430
|
-
const fullpath = [...
|
|
145120
|
+
const fullpath = [...path4, ...issue2.path];
|
|
144431
145121
|
if (fullpath.length === 0) {
|
|
144432
145122
|
fieldErrors._errors.push(mapper(issue2));
|
|
144433
145123
|
} else {
|
|
@@ -144454,17 +145144,17 @@ function formatError(error, mapper = (issue2) => issue2.message) {
|
|
|
144454
145144
|
}
|
|
144455
145145
|
function treeifyError(error, mapper = (issue2) => issue2.message) {
|
|
144456
145146
|
const result = { errors: [] };
|
|
144457
|
-
const processError = (error2,
|
|
145147
|
+
const processError = (error2, path4 = []) => {
|
|
144458
145148
|
var _a2, _b;
|
|
144459
145149
|
for (const issue2 of error2.issues) {
|
|
144460
145150
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
144461
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
145151
|
+
issue2.errors.map((issues) => processError({ issues }, [...path4, ...issue2.path]));
|
|
144462
145152
|
} else if (issue2.code === "invalid_key") {
|
|
144463
|
-
processError({ issues: issue2.issues }, [...
|
|
145153
|
+
processError({ issues: issue2.issues }, [...path4, ...issue2.path]);
|
|
144464
145154
|
} else if (issue2.code === "invalid_element") {
|
|
144465
|
-
processError({ issues: issue2.issues }, [...
|
|
145155
|
+
processError({ issues: issue2.issues }, [...path4, ...issue2.path]);
|
|
144466
145156
|
} else {
|
|
144467
|
-
const fullpath = [...
|
|
145157
|
+
const fullpath = [...path4, ...issue2.path];
|
|
144468
145158
|
if (fullpath.length === 0) {
|
|
144469
145159
|
result.errors.push(mapper(issue2));
|
|
144470
145160
|
continue;
|
|
@@ -144496,8 +145186,8 @@ function treeifyError(error, mapper = (issue2) => issue2.message) {
|
|
|
144496
145186
|
}
|
|
144497
145187
|
function toDotPath(_path) {
|
|
144498
145188
|
const segs = [];
|
|
144499
|
-
const
|
|
144500
|
-
for (const seg of
|
|
145189
|
+
const path4 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
145190
|
+
for (const seg of path4) {
|
|
144501
145191
|
if (typeof seg === "number")
|
|
144502
145192
|
segs.push(`[${seg}]`);
|
|
144503
145193
|
else if (typeof seg === "symbol")
|
|
@@ -156956,13 +157646,13 @@ function resolveRef(ref, ctx) {
|
|
|
156956
157646
|
if (!ref.startsWith("#")) {
|
|
156957
157647
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
156958
157648
|
}
|
|
156959
|
-
const
|
|
156960
|
-
if (
|
|
157649
|
+
const path4 = ref.slice(1).split("/").filter(Boolean);
|
|
157650
|
+
if (path4.length === 0) {
|
|
156961
157651
|
return ctx.rootSchema;
|
|
156962
157652
|
}
|
|
156963
157653
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
156964
|
-
if (
|
|
156965
|
-
const key =
|
|
157654
|
+
if (path4[0] === defsKey) {
|
|
157655
|
+
const key = path4[1];
|
|
156966
157656
|
if (!key || !ctx.defs[key]) {
|
|
156967
157657
|
throw new Error(`Reference not found: ${ref}`);
|
|
156968
157658
|
}
|
|
@@ -157450,7 +158140,8 @@ var BaseEmbeddingConfigSchema = exports_external.object({
|
|
|
157450
158140
|
endpoint: exports_external.string().optional().describe("API endpoint URL. Required when provider is openai-compatible."),
|
|
157451
158141
|
api_key: exports_external.string().optional().describe("API key for remote embedding provider (optional)"),
|
|
157452
158142
|
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."),
|
|
157453
|
-
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.")
|
|
158143
|
+
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."),
|
|
158144
|
+
max_input_tokens: exports_external.number().int().positive().optional().describe("Optional maximum input tokens for chunk embeddings. Defaults conservatively to 512 when omitted.")
|
|
157454
158145
|
}).superRefine((data, ctx) => {
|
|
157455
158146
|
if (data.provider === "openai-compatible" && !data.endpoint?.trim()) {
|
|
157456
158147
|
ctx.addIssue({
|
|
@@ -157471,7 +158162,8 @@ var EmbeddingConfigSchema = BaseEmbeddingConfigSchema.transform((data) => {
|
|
|
157471
158162
|
if (data.provider === "local") {
|
|
157472
158163
|
return {
|
|
157473
158164
|
provider: "local",
|
|
157474
|
-
model: data.model?.trim() || DEFAULT_LOCAL_EMBEDDING_MODEL
|
|
158165
|
+
model: data.model?.trim() || DEFAULT_LOCAL_EMBEDDING_MODEL,
|
|
158166
|
+
...data.max_input_tokens ? { max_input_tokens: data.max_input_tokens } : {}
|
|
157475
158167
|
};
|
|
157476
158168
|
}
|
|
157477
158169
|
if (data.provider === "openai-compatible") {
|
|
@@ -157484,7 +158176,8 @@ var EmbeddingConfigSchema = BaseEmbeddingConfigSchema.transform((data) => {
|
|
|
157484
158176
|
endpoint: data.endpoint?.trim() ?? "",
|
|
157485
158177
|
...apiKey ? { api_key: apiKey } : {},
|
|
157486
158178
|
...inputType ? { input_type: inputType } : {},
|
|
157487
|
-
...truncate ? { truncate } : {}
|
|
158179
|
+
...truncate ? { truncate } : {},
|
|
158180
|
+
...data.max_input_tokens ? { max_input_tokens: data.max_input_tokens } : {}
|
|
157488
158181
|
};
|
|
157489
158182
|
}
|
|
157490
158183
|
return { provider: "off" };
|
|
@@ -157733,31 +158426,31 @@ function getUserConfigPaths() {
|
|
|
157733
158426
|
return [`${basePath}.jsonc`, `${basePath}.json`];
|
|
157734
158427
|
}
|
|
157735
158428
|
function resolveFirstExisting(paths) {
|
|
157736
|
-
return paths.find((
|
|
158429
|
+
return paths.find((path4) => existsSync5(path4));
|
|
157737
158430
|
}
|
|
157738
|
-
function loadConfigFile(
|
|
158431
|
+
function loadConfigFile(path4, scope) {
|
|
157739
158432
|
try {
|
|
157740
|
-
const rawText = readFileSync3(
|
|
158433
|
+
const rawText = readFileSync3(path4, "utf-8");
|
|
157741
158434
|
const substituted = substituteConfigVariables({
|
|
157742
158435
|
text: rawText,
|
|
157743
|
-
configPath:
|
|
158436
|
+
configPath: path4,
|
|
157744
158437
|
isProjectConfig: scope === "project"
|
|
157745
158438
|
});
|
|
157746
158439
|
return {
|
|
157747
|
-
path:
|
|
158440
|
+
path: path4,
|
|
157748
158441
|
scope,
|
|
157749
158442
|
config: import_comment_json.parse(substituted.text),
|
|
157750
|
-
warnings: substituted.warnings.map((warning) => `${
|
|
158443
|
+
warnings: substituted.warnings.map((warning) => `${path4}: ${warning}`),
|
|
157751
158444
|
loadOutcome: substituted.warnings.length > 0 ? "substitution-failure" : "ok"
|
|
157752
158445
|
};
|
|
157753
158446
|
} catch (error51) {
|
|
157754
158447
|
const message = error51 instanceof Error ? error51.message : String(error51);
|
|
157755
158448
|
return {
|
|
157756
|
-
path:
|
|
158449
|
+
path: path4,
|
|
157757
158450
|
scope,
|
|
157758
158451
|
config: {},
|
|
157759
158452
|
warnings: [
|
|
157760
|
-
`${
|
|
158453
|
+
`${path4}: failed to load config: ${message}; using defaults for this file.`
|
|
157761
158454
|
],
|
|
157762
158455
|
loadOutcome: typeof error51.code === "string" ? "project-file-io-error" : "project-file-parse-error"
|
|
157763
158456
|
};
|
|
@@ -157923,9 +158616,9 @@ function bindSubstitutionFailures(loaded) {
|
|
|
157923
158616
|
}
|
|
157924
158617
|
const emptyPaths = collectEmptyStringPaths(loaded.config);
|
|
157925
158618
|
return loaded.warnings.map((message) => {
|
|
157926
|
-
const matchedPath = emptyPaths.find((
|
|
157927
|
-
const tail =
|
|
157928
|
-
return message.includes(
|
|
158619
|
+
const matchedPath = emptyPaths.find((path4) => {
|
|
158620
|
+
const tail = path4.split(".").at(-1) ?? path4;
|
|
158621
|
+
return message.includes(path4) || message.toLowerCase().includes(tail.toLowerCase());
|
|
157929
158622
|
});
|
|
157930
158623
|
return {
|
|
157931
158624
|
keyPath: matchedPath ?? "<unknown>",
|
|
@@ -158013,6 +158706,441 @@ function loadPiConfigDetailed(opts = {}) {
|
|
|
158013
158706
|
// ../plugin/src/features/magic-context/memory/embedding.ts
|
|
158014
158707
|
init_logger();
|
|
158015
158708
|
|
|
158709
|
+
// ../plugin/src/features/magic-context/compartment-chunk-embedding.ts
|
|
158710
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
158711
|
+
var DEFAULT_COMPARTMENT_CHUNK_MAX_INPUT_TOKENS = 512;
|
|
158712
|
+
var loadFtsRowsStatements = new WeakMap;
|
|
158713
|
+
var existingHashStatements = new WeakMap;
|
|
158714
|
+
var existingHashByProjectStatements = new WeakMap;
|
|
158715
|
+
var deleteByCompartmentStatements = new WeakMap;
|
|
158716
|
+
var insertEmbeddingStatements = new WeakMap;
|
|
158717
|
+
var distinctModelStatements = new WeakMap;
|
|
158718
|
+
var clearProjectStatements = new WeakMap;
|
|
158719
|
+
var clearProjectModelStatements = new WeakMap;
|
|
158720
|
+
var searchRowsStatements = new WeakMap;
|
|
158721
|
+
var searchRowsByModelStatements = new WeakMap;
|
|
158722
|
+
var backfillCandidateStatements = new WeakMap;
|
|
158723
|
+
function getLoadFtsRowsStatement(db) {
|
|
158724
|
+
let stmt = loadFtsRowsStatements.get(db);
|
|
158725
|
+
if (!stmt) {
|
|
158726
|
+
stmt = db.prepare(`SELECT message_ordinal AS messageOrdinal, role, content
|
|
158727
|
+
FROM message_history_fts
|
|
158728
|
+
WHERE session_id = ?
|
|
158729
|
+
AND message_ordinal >= ?
|
|
158730
|
+
AND message_ordinal <= ?
|
|
158731
|
+
AND role IN ('user', 'assistant')
|
|
158732
|
+
ORDER BY message_ordinal ASC`);
|
|
158733
|
+
loadFtsRowsStatements.set(db, stmt);
|
|
158734
|
+
}
|
|
158735
|
+
return stmt;
|
|
158736
|
+
}
|
|
158737
|
+
function getExistingHashStatement(db, scopedToProject) {
|
|
158738
|
+
const map2 = scopedToProject ? existingHashByProjectStatements : existingHashStatements;
|
|
158739
|
+
let stmt = map2.get(db);
|
|
158740
|
+
if (!stmt) {
|
|
158741
|
+
stmt = db.prepare(`SELECT window_index AS windowIndex, chunk_hash AS chunkHash
|
|
158742
|
+
FROM compartment_chunk_embeddings
|
|
158743
|
+
WHERE compartment_id = ?
|
|
158744
|
+
AND model_id = ?
|
|
158745
|
+
${scopedToProject ? "AND project_path = ?" : ""}
|
|
158746
|
+
ORDER BY window_index ASC`);
|
|
158747
|
+
map2.set(db, stmt);
|
|
158748
|
+
}
|
|
158749
|
+
return stmt;
|
|
158750
|
+
}
|
|
158751
|
+
function getDeleteByCompartmentStatement(db) {
|
|
158752
|
+
let stmt = deleteByCompartmentStatements.get(db);
|
|
158753
|
+
if (!stmt) {
|
|
158754
|
+
stmt = db.prepare("DELETE FROM compartment_chunk_embeddings WHERE compartment_id = ?");
|
|
158755
|
+
deleteByCompartmentStatements.set(db, stmt);
|
|
158756
|
+
}
|
|
158757
|
+
return stmt;
|
|
158758
|
+
}
|
|
158759
|
+
function getInsertEmbeddingStatement(db) {
|
|
158760
|
+
let stmt = insertEmbeddingStatements.get(db);
|
|
158761
|
+
if (!stmt) {
|
|
158762
|
+
stmt = db.prepare(`INSERT INTO compartment_chunk_embeddings (
|
|
158763
|
+
compartment_id, session_id, project_path, harness, window_index,
|
|
158764
|
+
start_ordinal, end_ordinal, chunk_hash, model_id, dims, vector, created_at
|
|
158765
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
158766
|
+
insertEmbeddingStatements.set(db, stmt);
|
|
158767
|
+
}
|
|
158768
|
+
return stmt;
|
|
158769
|
+
}
|
|
158770
|
+
function getDistinctModelStatement(db) {
|
|
158771
|
+
let stmt = distinctModelStatements.get(db);
|
|
158772
|
+
if (!stmt) {
|
|
158773
|
+
stmt = db.prepare(`SELECT DISTINCT model_id AS modelId
|
|
158774
|
+
FROM compartment_chunk_embeddings
|
|
158775
|
+
WHERE project_path = ?`);
|
|
158776
|
+
distinctModelStatements.set(db, stmt);
|
|
158777
|
+
}
|
|
158778
|
+
return stmt;
|
|
158779
|
+
}
|
|
158780
|
+
function getClearProjectStatement(db) {
|
|
158781
|
+
let stmt = clearProjectStatements.get(db);
|
|
158782
|
+
if (!stmt) {
|
|
158783
|
+
stmt = db.prepare("DELETE FROM compartment_chunk_embeddings WHERE project_path = ?");
|
|
158784
|
+
clearProjectStatements.set(db, stmt);
|
|
158785
|
+
}
|
|
158786
|
+
return stmt;
|
|
158787
|
+
}
|
|
158788
|
+
function getClearProjectModelStatement(db) {
|
|
158789
|
+
let stmt = clearProjectModelStatements.get(db);
|
|
158790
|
+
if (!stmt) {
|
|
158791
|
+
stmt = db.prepare("DELETE FROM compartment_chunk_embeddings WHERE project_path = ? AND model_id = ?");
|
|
158792
|
+
clearProjectModelStatements.set(db, stmt);
|
|
158793
|
+
}
|
|
158794
|
+
return stmt;
|
|
158795
|
+
}
|
|
158796
|
+
function getSearchRowsStatement(db, withModel) {
|
|
158797
|
+
const map2 = withModel ? searchRowsByModelStatements : searchRowsStatements;
|
|
158798
|
+
let stmt = map2.get(db);
|
|
158799
|
+
if (!stmt) {
|
|
158800
|
+
stmt = db.prepare(`SELECT e.compartment_id AS compartmentId,
|
|
158801
|
+
e.session_id AS sessionId,
|
|
158802
|
+
c.title AS title,
|
|
158803
|
+
c.start_message AS compartmentStart,
|
|
158804
|
+
c.end_message AS compartmentEnd,
|
|
158805
|
+
e.window_index AS windowIndex,
|
|
158806
|
+
e.start_ordinal AS windowStart,
|
|
158807
|
+
e.end_ordinal AS windowEnd,
|
|
158808
|
+
e.chunk_hash AS chunkHash,
|
|
158809
|
+
e.model_id AS modelId,
|
|
158810
|
+
e.dims AS dims,
|
|
158811
|
+
e.vector AS vector
|
|
158812
|
+
FROM compartment_chunk_embeddings e
|
|
158813
|
+
JOIN compartments c ON c.id = e.compartment_id
|
|
158814
|
+
WHERE e.session_id = ?
|
|
158815
|
+
AND e.project_path = ?
|
|
158816
|
+
${withModel ? "AND e.model_id = ?" : ""}
|
|
158817
|
+
ORDER BY e.compartment_id ASC, e.window_index ASC`);
|
|
158818
|
+
map2.set(db, stmt);
|
|
158819
|
+
}
|
|
158820
|
+
return stmt;
|
|
158821
|
+
}
|
|
158822
|
+
function isFinitePositiveInteger(value) {
|
|
158823
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0;
|
|
158824
|
+
}
|
|
158825
|
+
function normalizeCompartmentChunkMaxInputTokens(value) {
|
|
158826
|
+
if (!isFinitePositiveInteger(value)) {
|
|
158827
|
+
return DEFAULT_COMPARTMENT_CHUNK_MAX_INPUT_TOKENS;
|
|
158828
|
+
}
|
|
158829
|
+
return Math.max(1, Math.floor(value));
|
|
158830
|
+
}
|
|
158831
|
+
function normalizeContent(text) {
|
|
158832
|
+
return text.replace(/\s+/g, " ").trim();
|
|
158833
|
+
}
|
|
158834
|
+
function formatOrdinalRange(start, end) {
|
|
158835
|
+
return start === end ? `[${start}]` : `[${start}-${end}]`;
|
|
158836
|
+
}
|
|
158837
|
+
function rolePrefix(role) {
|
|
158838
|
+
if (role === "user")
|
|
158839
|
+
return "U";
|
|
158840
|
+
if (role === "assistant")
|
|
158841
|
+
return "A";
|
|
158842
|
+
return null;
|
|
158843
|
+
}
|
|
158844
|
+
function parseOrdinal(value) {
|
|
158845
|
+
const parsed = typeof value === "number" ? value : Number.parseInt(String(value ?? ""), 10);
|
|
158846
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
|
158847
|
+
}
|
|
158848
|
+
function parseCanonicalLineRange(line) {
|
|
158849
|
+
const match = /^\[(\d+)(?:-(\d+))?\]\s+[UA]:/.exec(line.trim());
|
|
158850
|
+
if (!match)
|
|
158851
|
+
return null;
|
|
158852
|
+
const start = Number.parseInt(match[1], 10);
|
|
158853
|
+
const end = match[2] ? Number.parseInt(match[2], 10) : start;
|
|
158854
|
+
if (!Number.isFinite(start) || !Number.isFinite(end))
|
|
158855
|
+
return null;
|
|
158856
|
+
return { start, end };
|
|
158857
|
+
}
|
|
158858
|
+
function hashChunkText(text) {
|
|
158859
|
+
return createHash4("sha256").update(text).digest("hex");
|
|
158860
|
+
}
|
|
158861
|
+
function vectorBlob(vector) {
|
|
158862
|
+
return new Uint8Array(vector.buffer, vector.byteOffset, vector.byteLength);
|
|
158863
|
+
}
|
|
158864
|
+
function toFloat32Array(blob) {
|
|
158865
|
+
if (blob instanceof Uint8Array) {
|
|
158866
|
+
const buffer2 = blob.buffer.slice(blob.byteOffset, blob.byteOffset + blob.byteLength);
|
|
158867
|
+
return new Float32Array(buffer2);
|
|
158868
|
+
}
|
|
158869
|
+
return new Float32Array(blob.slice(0));
|
|
158870
|
+
}
|
|
158871
|
+
function buildCanonicalChunkTextFromFts(db, sessionId, startOrdinal, endOrdinal) {
|
|
158872
|
+
if (endOrdinal < startOrdinal)
|
|
158873
|
+
return "";
|
|
158874
|
+
const rows = getLoadFtsRowsStatement(db).all(sessionId, startOrdinal, endOrdinal).map((row) => row);
|
|
158875
|
+
const lines = [];
|
|
158876
|
+
let current = null;
|
|
158877
|
+
const flush2 = () => {
|
|
158878
|
+
if (!current || current.parts.length === 0)
|
|
158879
|
+
return;
|
|
158880
|
+
lines.push(`${formatOrdinalRange(current.start, current.end)} ${current.role}: ${current.parts.join(" / ")}`);
|
|
158881
|
+
current = null;
|
|
158882
|
+
};
|
|
158883
|
+
for (const row of rows) {
|
|
158884
|
+
const ordinal = parseOrdinal(row.messageOrdinal);
|
|
158885
|
+
const prefix = rolePrefix(row.role);
|
|
158886
|
+
const content = typeof row.content === "string" ? normalizeContent(row.content) : "";
|
|
158887
|
+
if (ordinal === null || prefix === null || content.length === 0)
|
|
158888
|
+
continue;
|
|
158889
|
+
if (current && current.role === prefix) {
|
|
158890
|
+
current.end = ordinal;
|
|
158891
|
+
current.parts.push(content);
|
|
158892
|
+
continue;
|
|
158893
|
+
}
|
|
158894
|
+
flush2();
|
|
158895
|
+
current = { role: prefix, start: ordinal, end: ordinal, parts: [content] };
|
|
158896
|
+
}
|
|
158897
|
+
flush2();
|
|
158898
|
+
return lines.join(`
|
|
158899
|
+
`);
|
|
158900
|
+
}
|
|
158901
|
+
function canonicalizeInMemoryChunkTextForEmbedding(chunkText, startOrdinal, endOrdinal) {
|
|
158902
|
+
const lines = [];
|
|
158903
|
+
for (const rawLine of chunkText.split(/\r?\n/)) {
|
|
158904
|
+
const line = rawLine.trim();
|
|
158905
|
+
const match = /^(\[(\d+)(?:-(\d+))?\]\s+[UA]:)\s*(.*)$/.exec(line);
|
|
158906
|
+
if (!match)
|
|
158907
|
+
continue;
|
|
158908
|
+
const lineStart = Number.parseInt(match[2], 10);
|
|
158909
|
+
const lineEnd = match[3] ? Number.parseInt(match[3], 10) : lineStart;
|
|
158910
|
+
if (startOrdinal != null && lineEnd < startOrdinal)
|
|
158911
|
+
continue;
|
|
158912
|
+
if (endOrdinal != null && lineStart > endOrdinal)
|
|
158913
|
+
continue;
|
|
158914
|
+
const rawParts = match[4].split(" / ").map((part) => normalizeContent(part)).filter((part) => part.length > 0);
|
|
158915
|
+
const ordinalSpan = lineEnd - lineStart + 1;
|
|
158916
|
+
const roleLabel = match[1].slice(match[1].indexOf("]") + 2);
|
|
158917
|
+
if (ordinalSpan === rawParts.length) {
|
|
158918
|
+
const retained = rawParts.map((part, index) => ({ ordinal: lineStart + index, part })).filter(({ ordinal, part }) => {
|
|
158919
|
+
if (part.startsWith("TC:"))
|
|
158920
|
+
return false;
|
|
158921
|
+
if (startOrdinal != null && ordinal < startOrdinal)
|
|
158922
|
+
return false;
|
|
158923
|
+
if (endOrdinal != null && ordinal > endOrdinal)
|
|
158924
|
+
return false;
|
|
158925
|
+
return true;
|
|
158926
|
+
});
|
|
158927
|
+
if (retained.length === 0)
|
|
158928
|
+
continue;
|
|
158929
|
+
const retainedStart = retained[0].ordinal;
|
|
158930
|
+
const retainedEnd = retained[retained.length - 1].ordinal;
|
|
158931
|
+
lines.push(`${formatOrdinalRange(retainedStart, retainedEnd)} ${roleLabel} ${retained.map(({ part }) => part).join(" / ")}`);
|
|
158932
|
+
continue;
|
|
158933
|
+
}
|
|
158934
|
+
const parts = rawParts.filter((part) => !part.startsWith("TC:"));
|
|
158935
|
+
if (parts.length === 0)
|
|
158936
|
+
continue;
|
|
158937
|
+
lines.push(`${match[1]} ${parts.join(" / ")}`);
|
|
158938
|
+
}
|
|
158939
|
+
return lines.join(`
|
|
158940
|
+
`);
|
|
158941
|
+
}
|
|
158942
|
+
function chunkCanonicalText(canonicalText, startOrdinal, endOrdinal, maxInputTokens) {
|
|
158943
|
+
const lines = canonicalText.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0);
|
|
158944
|
+
if (lines.length === 0 || endOrdinal < startOrdinal)
|
|
158945
|
+
return [];
|
|
158946
|
+
const normalizedMax = normalizeCompartmentChunkMaxInputTokens(maxInputTokens);
|
|
158947
|
+
const fullText = lines.join(`
|
|
158948
|
+
`);
|
|
158949
|
+
if (estimateTokens(fullText) <= normalizedMax) {
|
|
158950
|
+
return [
|
|
158951
|
+
{
|
|
158952
|
+
windowIndex: 0,
|
|
158953
|
+
startOrdinal,
|
|
158954
|
+
endOrdinal,
|
|
158955
|
+
text: fullText,
|
|
158956
|
+
chunkHash: hashChunkText(fullText)
|
|
158957
|
+
}
|
|
158958
|
+
];
|
|
158959
|
+
}
|
|
158960
|
+
const windows = [];
|
|
158961
|
+
let currentLines = [];
|
|
158962
|
+
let currentStart = null;
|
|
158963
|
+
let currentEnd = null;
|
|
158964
|
+
let currentTokens = 0;
|
|
158965
|
+
const flush2 = () => {
|
|
158966
|
+
if (currentLines.length === 0 || currentStart === null || currentEnd === null)
|
|
158967
|
+
return;
|
|
158968
|
+
const text = currentLines.join(`
|
|
158969
|
+
`);
|
|
158970
|
+
windows.push({
|
|
158971
|
+
windowIndex: windows.length + 1,
|
|
158972
|
+
startOrdinal: currentStart,
|
|
158973
|
+
endOrdinal: currentEnd,
|
|
158974
|
+
text,
|
|
158975
|
+
chunkHash: hashChunkText(text)
|
|
158976
|
+
});
|
|
158977
|
+
currentLines = [];
|
|
158978
|
+
currentStart = null;
|
|
158979
|
+
currentEnd = null;
|
|
158980
|
+
currentTokens = 0;
|
|
158981
|
+
};
|
|
158982
|
+
for (const line of lines) {
|
|
158983
|
+
const range = parseCanonicalLineRange(line);
|
|
158984
|
+
const lineStart = range?.start ?? startOrdinal;
|
|
158985
|
+
const lineEnd = range?.end ?? lineStart;
|
|
158986
|
+
const lineTokens = estimateTokens(line);
|
|
158987
|
+
if (currentLines.length > 0 && currentTokens + lineTokens > normalizedMax) {
|
|
158988
|
+
flush2();
|
|
158989
|
+
}
|
|
158990
|
+
if (currentLines.length === 0) {
|
|
158991
|
+
currentStart = lineStart;
|
|
158992
|
+
}
|
|
158993
|
+
currentLines.push(line);
|
|
158994
|
+
currentEnd = lineEnd;
|
|
158995
|
+
currentTokens += lineTokens;
|
|
158996
|
+
}
|
|
158997
|
+
flush2();
|
|
158998
|
+
return windows;
|
|
158999
|
+
}
|
|
159000
|
+
function getExistingChunkHashes(db, compartmentId, modelId, projectPath) {
|
|
159001
|
+
const scoped = typeof projectPath === "string" && projectPath.length > 0;
|
|
159002
|
+
const rows = scoped ? getExistingHashStatement(db, true).all(compartmentId, modelId, projectPath) : getExistingHashStatement(db, false).all(compartmentId, modelId);
|
|
159003
|
+
return new Map(rows.filter((row) => typeof row.windowIndex === "number" && typeof row.chunkHash === "string").map((row) => [row.windowIndex, row.chunkHash]));
|
|
159004
|
+
}
|
|
159005
|
+
function chunkEmbeddingWindowsAreCurrent(db, compartmentId, modelId, windows, projectPath) {
|
|
159006
|
+
const existing = getExistingChunkHashes(db, compartmentId, modelId, projectPath);
|
|
159007
|
+
if (existing.size !== windows.length)
|
|
159008
|
+
return false;
|
|
159009
|
+
return windows.every((window) => existing.get(window.windowIndex) === window.chunkHash);
|
|
159010
|
+
}
|
|
159011
|
+
function replaceCompartmentChunkEmbeddings(db, rows) {
|
|
159012
|
+
if (rows.length === 0)
|
|
159013
|
+
return;
|
|
159014
|
+
const compartmentId = rows[0].compartmentId;
|
|
159015
|
+
const now = Date.now();
|
|
159016
|
+
db.transaction(() => {
|
|
159017
|
+
getDeleteByCompartmentStatement(db).run(compartmentId);
|
|
159018
|
+
const insert = getInsertEmbeddingStatement(db);
|
|
159019
|
+
for (const row of rows) {
|
|
159020
|
+
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);
|
|
159021
|
+
}
|
|
159022
|
+
})();
|
|
159023
|
+
}
|
|
159024
|
+
function getDistinctChunkEmbeddingModelIds(db, projectPath) {
|
|
159025
|
+
const rows = getDistinctModelStatement(db).all(projectPath);
|
|
159026
|
+
return new Set(rows.map((row) => typeof row.modelId === "string" ? row.modelId : null));
|
|
159027
|
+
}
|
|
159028
|
+
function clearChunkEmbeddingsForProject(db, projectPath, modelId) {
|
|
159029
|
+
if (modelId) {
|
|
159030
|
+
return getClearProjectModelStatement(db).run(projectPath, modelId).changes;
|
|
159031
|
+
}
|
|
159032
|
+
return getClearProjectStatement(db).run(projectPath).changes;
|
|
159033
|
+
}
|
|
159034
|
+
function loadCompartmentChunkEmbeddingsForSearch(db, sessionId, projectPath, modelId) {
|
|
159035
|
+
const rows = modelId ? getSearchRowsStatement(db, true).all(sessionId, projectPath, modelId) : getSearchRowsStatement(db, false).all(sessionId, projectPath);
|
|
159036
|
+
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) => ({
|
|
159037
|
+
compartmentId: row.compartmentId,
|
|
159038
|
+
sessionId: row.sessionId,
|
|
159039
|
+
title: row.title,
|
|
159040
|
+
startOrdinal: row.compartmentStart,
|
|
159041
|
+
endOrdinal: row.compartmentEnd,
|
|
159042
|
+
windowIndex: row.windowIndex,
|
|
159043
|
+
windowStartOrdinal: row.windowStart,
|
|
159044
|
+
windowEndOrdinal: row.windowEnd,
|
|
159045
|
+
chunkHash: row.chunkHash,
|
|
159046
|
+
modelId: row.modelId,
|
|
159047
|
+
dims: row.dims,
|
|
159048
|
+
vector: toFloat32Array(row.vector)
|
|
159049
|
+
}));
|
|
159050
|
+
}
|
|
159051
|
+
function mapBackfillCandidateRows(rows) {
|
|
159052
|
+
return rows.filter((row) => {
|
|
159053
|
+
if (row === null || typeof row !== "object")
|
|
159054
|
+
return false;
|
|
159055
|
+
const candidate = row;
|
|
159056
|
+
return typeof candidate.id === "number" && typeof candidate.sessionId === "string" && typeof candidate.startMessage === "number" && typeof candidate.endMessage === "number" && typeof candidate.title === "string";
|
|
159057
|
+
}).map((row) => ({
|
|
159058
|
+
id: row.id,
|
|
159059
|
+
sessionId: row.sessionId,
|
|
159060
|
+
startMessage: row.startMessage,
|
|
159061
|
+
endMessage: row.endMessage,
|
|
159062
|
+
title: row.title
|
|
159063
|
+
}));
|
|
159064
|
+
}
|
|
159065
|
+
var sessionBackfillCandidateStatements = new WeakMap;
|
|
159066
|
+
function loadUnembeddedSessionChunkCandidates(db, projectPath, sessionId, modelId, limit, excludeIds) {
|
|
159067
|
+
if (excludeIds && excludeIds.length > 0) {
|
|
159068
|
+
const placeholders2 = excludeIds.map(() => "?").join(", ");
|
|
159069
|
+
const stmt2 = db.prepare(`SELECT c.id AS id,
|
|
159070
|
+
c.session_id AS sessionId,
|
|
159071
|
+
c.start_message AS startMessage,
|
|
159072
|
+
c.end_message AS endMessage,
|
|
159073
|
+
c.title AS title
|
|
159074
|
+
FROM compartments c
|
|
159075
|
+
JOIN session_projects sp
|
|
159076
|
+
ON sp.session_id = c.session_id
|
|
159077
|
+
AND sp.harness = c.harness
|
|
159078
|
+
AND sp.project_path = ?
|
|
159079
|
+
WHERE c.session_id = ?
|
|
159080
|
+
AND c.start_message IS NOT NULL
|
|
159081
|
+
AND c.end_message IS NOT NULL
|
|
159082
|
+
AND c.id NOT IN (${placeholders2})
|
|
159083
|
+
AND NOT EXISTS (
|
|
159084
|
+
SELECT 1
|
|
159085
|
+
FROM compartment_chunk_embeddings current
|
|
159086
|
+
WHERE current.compartment_id = c.id
|
|
159087
|
+
AND current.project_path = ?
|
|
159088
|
+
AND current.model_id = ?
|
|
159089
|
+
)
|
|
159090
|
+
ORDER BY c.start_message ASC, c.id ASC
|
|
159091
|
+
LIMIT ?`);
|
|
159092
|
+
const rows2 = stmt2.all(projectPath, sessionId, ...excludeIds, projectPath, modelId, Math.max(1, limit));
|
|
159093
|
+
return mapBackfillCandidateRows(rows2);
|
|
159094
|
+
}
|
|
159095
|
+
let stmt = sessionBackfillCandidateStatements.get(db);
|
|
159096
|
+
if (!stmt) {
|
|
159097
|
+
stmt = db.prepare(`SELECT c.id AS id,
|
|
159098
|
+
c.session_id AS sessionId,
|
|
159099
|
+
c.start_message AS startMessage,
|
|
159100
|
+
c.end_message AS endMessage,
|
|
159101
|
+
c.title AS title
|
|
159102
|
+
FROM compartments c
|
|
159103
|
+
JOIN session_projects sp
|
|
159104
|
+
ON sp.session_id = c.session_id
|
|
159105
|
+
AND sp.harness = c.harness
|
|
159106
|
+
AND sp.project_path = ?
|
|
159107
|
+
WHERE c.session_id = ?
|
|
159108
|
+
AND c.start_message IS NOT NULL
|
|
159109
|
+
AND c.end_message IS NOT NULL
|
|
159110
|
+
AND NOT EXISTS (
|
|
159111
|
+
SELECT 1
|
|
159112
|
+
FROM compartment_chunk_embeddings current
|
|
159113
|
+
WHERE current.compartment_id = c.id
|
|
159114
|
+
AND current.project_path = ?
|
|
159115
|
+
AND current.model_id = ?
|
|
159116
|
+
)
|
|
159117
|
+
ORDER BY c.start_message ASC, c.id ASC
|
|
159118
|
+
LIMIT ?`);
|
|
159119
|
+
sessionBackfillCandidateStatements.set(db, stmt);
|
|
159120
|
+
}
|
|
159121
|
+
const rows = stmt.all(projectPath, sessionId, projectPath, modelId, Math.max(1, limit));
|
|
159122
|
+
return mapBackfillCandidateRows(rows);
|
|
159123
|
+
}
|
|
159124
|
+
function countUnembeddedSessionCompartments(db, projectPath, sessionId, modelId) {
|
|
159125
|
+
const row = db.prepare(`SELECT COUNT(*) AS n
|
|
159126
|
+
FROM compartments c
|
|
159127
|
+
JOIN session_projects sp
|
|
159128
|
+
ON sp.session_id = c.session_id
|
|
159129
|
+
AND sp.harness = c.harness
|
|
159130
|
+
AND sp.project_path = ?
|
|
159131
|
+
WHERE c.session_id = ?
|
|
159132
|
+
AND c.start_message IS NOT NULL
|
|
159133
|
+
AND c.end_message IS NOT NULL
|
|
159134
|
+
AND NOT EXISTS (
|
|
159135
|
+
SELECT 1
|
|
159136
|
+
FROM compartment_chunk_embeddings current
|
|
159137
|
+
WHERE current.compartment_id = c.id
|
|
159138
|
+
AND current.project_path = ?
|
|
159139
|
+
AND current.model_id = ?
|
|
159140
|
+
)`).get(projectPath, sessionId, projectPath, modelId);
|
|
159141
|
+
return typeof row?.n === "number" ? row.n : 0;
|
|
159142
|
+
}
|
|
159143
|
+
|
|
158016
159144
|
// ../plugin/src/features/magic-context/memory/cosine-similarity.ts
|
|
158017
159145
|
function cosineSimilarity(a, b) {
|
|
158018
159146
|
if (a.length !== b.length) {
|
|
@@ -158031,13 +159159,13 @@ function cosineSimilarity(a, b) {
|
|
|
158031
159159
|
}
|
|
158032
159160
|
|
|
158033
159161
|
// ../plugin/src/features/magic-context/memory/normalize-hash.ts
|
|
158034
|
-
import { createHash as
|
|
159162
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
158035
159163
|
function normalizeMemoryContent(content) {
|
|
158036
159164
|
return content.toLowerCase().replace(/\s+/g, " ").trim();
|
|
158037
159165
|
}
|
|
158038
159166
|
function computeNormalizedHash(content) {
|
|
158039
159167
|
const normalized = normalizeMemoryContent(content);
|
|
158040
|
-
return
|
|
159168
|
+
return createHash5("md5").update(normalized).digest("hex");
|
|
158041
159169
|
}
|
|
158042
159170
|
|
|
158043
159171
|
// ../plugin/src/features/magic-context/memory/embedding-identity.ts
|
|
@@ -158181,19 +159309,19 @@ function isArrayLikeNumber(value) {
|
|
|
158181
159309
|
}
|
|
158182
159310
|
return arr.length === 0 || typeof arr[0] === "number";
|
|
158183
159311
|
}
|
|
158184
|
-
function
|
|
159312
|
+
function toFloat32Array2(values) {
|
|
158185
159313
|
return values instanceof Float32Array ? new Float32Array(values) : Float32Array.from(Array.from(values));
|
|
158186
159314
|
}
|
|
158187
159315
|
function extractBatchEmbeddings(result, expectedCount) {
|
|
158188
159316
|
const { data } = result;
|
|
158189
159317
|
if (Array.isArray(data) && data.length === expectedCount && data.every((entry) => typeof entry !== "number" && isArrayLikeNumber(entry))) {
|
|
158190
|
-
return data.map((entry) =>
|
|
159318
|
+
return data.map((entry) => toFloat32Array2(entry));
|
|
158191
159319
|
}
|
|
158192
159320
|
if (!isArrayLikeNumber(data)) {
|
|
158193
159321
|
log("[magic-context] embedding batch returned unexpected data shape");
|
|
158194
159322
|
return Array.from({ length: expectedCount }, () => null);
|
|
158195
159323
|
}
|
|
158196
|
-
const flatData =
|
|
159324
|
+
const flatData = toFloat32Array2(data);
|
|
158197
159325
|
const dimension = result.dims?.at(-1) ?? flatData.length / expectedCount;
|
|
158198
159326
|
if (!Number.isInteger(dimension) || dimension <= 0 || flatData.length !== expectedCount * dimension) {
|
|
158199
159327
|
log("[magic-context] embedding batch returned invalid dimensions");
|
|
@@ -158208,6 +159336,7 @@ function extractBatchEmbeddings(result, expectedCount) {
|
|
|
158208
159336
|
|
|
158209
159337
|
class LocalEmbeddingProvider {
|
|
158210
159338
|
modelId;
|
|
159339
|
+
maxInputTokens;
|
|
158211
159340
|
model;
|
|
158212
159341
|
pipeline = null;
|
|
158213
159342
|
initPromise = null;
|
|
@@ -158215,8 +159344,9 @@ class LocalEmbeddingProvider {
|
|
|
158215
159344
|
disposing = false;
|
|
158216
159345
|
disposePromise = null;
|
|
158217
159346
|
inFlightWaiters = [];
|
|
158218
|
-
constructor(model = DEFAULT_LOCAL_EMBEDDING_MODEL) {
|
|
159347
|
+
constructor(model = DEFAULT_LOCAL_EMBEDDING_MODEL, maxInputTokens = 512) {
|
|
158219
159348
|
this.model = model;
|
|
159349
|
+
this.maxInputTokens = maxInputTokens;
|
|
158220
159350
|
this.modelId = getEmbeddingProviderIdentity({ provider: "local", model });
|
|
158221
159351
|
}
|
|
158222
159352
|
async initialize() {
|
|
@@ -158471,6 +159601,7 @@ var FETCH_TIMEOUT_MS = 30000;
|
|
|
158471
159601
|
|
|
158472
159602
|
class OpenAICompatibleEmbeddingProvider {
|
|
158473
159603
|
modelId;
|
|
159604
|
+
maxInputTokens;
|
|
158474
159605
|
endpoint;
|
|
158475
159606
|
model;
|
|
158476
159607
|
apiKey;
|
|
@@ -158487,11 +159618,13 @@ class OpenAICompatibleEmbeddingProvider {
|
|
|
158487
159618
|
this.apiKey = options.apiKey?.trim() ?? "";
|
|
158488
159619
|
this.inputType = options.inputType?.trim() ?? "";
|
|
158489
159620
|
this.truncate = options.truncate?.trim() ?? "";
|
|
159621
|
+
this.maxInputTokens = typeof options.maxInputTokens === "number" && Number.isFinite(options.maxInputTokens) ? Math.max(1, Math.floor(options.maxInputTokens)) : 512;
|
|
158490
159622
|
this.modelId = getEmbeddingProviderIdentity({
|
|
158491
159623
|
provider: "openai-compatible",
|
|
158492
159624
|
endpoint: this.endpoint,
|
|
158493
159625
|
model: this.model,
|
|
158494
|
-
...this.apiKey ? { api_key: this.apiKey } : {}
|
|
159626
|
+
...this.apiKey ? { api_key: this.apiKey } : {},
|
|
159627
|
+
...this.inputType ? { input_type: this.inputType } : {}
|
|
158495
159628
|
});
|
|
158496
159629
|
}
|
|
158497
159630
|
async initialize() {
|
|
@@ -158695,9 +159828,9 @@ function isEmbeddingRow(row) {
|
|
|
158695
159828
|
if (row === null || typeof row !== "object")
|
|
158696
159829
|
return false;
|
|
158697
159830
|
const candidate = row;
|
|
158698
|
-
return typeof candidate.memoryId === "number" && isEmbeddingBlob(candidate.embedding);
|
|
159831
|
+
return typeof candidate.memoryId === "number" && isEmbeddingBlob(candidate.embedding) && (candidate.modelId === null || typeof candidate.modelId === "string");
|
|
158699
159832
|
}
|
|
158700
|
-
function
|
|
159833
|
+
function toFloat32Array3(blob) {
|
|
158701
159834
|
if (blob instanceof Uint8Array) {
|
|
158702
159835
|
const buffer2 = blob.buffer.slice(blob.byteOffset, blob.byteOffset + blob.byteLength);
|
|
158703
159836
|
return new Float32Array(buffer2);
|
|
@@ -158715,7 +159848,7 @@ function getSaveEmbeddingStatement(db) {
|
|
|
158715
159848
|
function getLoadAllEmbeddingsStatement(db) {
|
|
158716
159849
|
let stmt = loadAllEmbeddingsStatements.get(db);
|
|
158717
159850
|
if (!stmt) {
|
|
158718
|
-
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");
|
|
159851
|
+
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");
|
|
158719
159852
|
loadAllEmbeddingsStatements.set(db, stmt);
|
|
158720
159853
|
}
|
|
158721
159854
|
return stmt;
|
|
@@ -158744,7 +159877,10 @@ function loadAllEmbeddings(db, projectPath) {
|
|
|
158744
159877
|
const rows = getLoadAllEmbeddingsStatement(db).all(projectPath).filter(isEmbeddingRow);
|
|
158745
159878
|
const embeddings = new Map;
|
|
158746
159879
|
for (const row of rows) {
|
|
158747
|
-
embeddings.set(row.memoryId,
|
|
159880
|
+
embeddings.set(row.memoryId, {
|
|
159881
|
+
embedding: toFloat32Array3(row.embedding),
|
|
159882
|
+
modelId: row.modelId
|
|
159883
|
+
});
|
|
158748
159884
|
}
|
|
158749
159885
|
return embeddings;
|
|
158750
159886
|
}
|
|
@@ -158757,7 +159893,7 @@ function getDistinctStoredModelIds(db, projectPath) {
|
|
|
158757
159893
|
}
|
|
158758
159894
|
|
|
158759
159895
|
// ../plugin/src/features/magic-context/project-embedding-registry.ts
|
|
158760
|
-
import { createHash as
|
|
159896
|
+
import { createHash as createHash6, randomUUID } from "node:crypto";
|
|
158761
159897
|
init_logger();
|
|
158762
159898
|
|
|
158763
159899
|
// ../plugin/src/features/magic-context/git-commits/storage-git-commit-embeddings.ts
|
|
@@ -158765,7 +159901,7 @@ var saveStatements = new WeakMap;
|
|
|
158765
159901
|
var loadProjectStatements = new WeakMap;
|
|
158766
159902
|
var loadUnembeddedStatements = new WeakMap;
|
|
158767
159903
|
var countEmbeddedStatements = new WeakMap;
|
|
158768
|
-
var
|
|
159904
|
+
var clearProjectStatements2 = new WeakMap;
|
|
158769
159905
|
var distinctModelIdStatements = new WeakMap;
|
|
158770
159906
|
function getSaveStatement(db) {
|
|
158771
159907
|
let stmt = saveStatements.get(db);
|
|
@@ -158813,12 +159949,12 @@ function getCountEmbeddedStatement(db) {
|
|
|
158813
159949
|
}
|
|
158814
159950
|
return stmt;
|
|
158815
159951
|
}
|
|
158816
|
-
function
|
|
158817
|
-
let stmt =
|
|
159952
|
+
function getClearProjectStatement2(db) {
|
|
159953
|
+
let stmt = clearProjectStatements2.get(db);
|
|
158818
159954
|
if (!stmt) {
|
|
158819
159955
|
stmt = db.prepare(`DELETE FROM git_commit_embeddings
|
|
158820
159956
|
WHERE sha IN (SELECT sha FROM git_commits WHERE project_path = ?)`);
|
|
158821
|
-
|
|
159957
|
+
clearProjectStatements2.set(db, stmt);
|
|
158822
159958
|
}
|
|
158823
159959
|
return stmt;
|
|
158824
159960
|
}
|
|
@@ -158854,7 +159990,7 @@ function countEmbeddedCommits(db, projectPath) {
|
|
|
158854
159990
|
return row?.count ?? 0;
|
|
158855
159991
|
}
|
|
158856
159992
|
function clearProjectCommitEmbeddings(db, projectPath) {
|
|
158857
|
-
return
|
|
159993
|
+
return getClearProjectStatement2(db).run(projectPath).changes;
|
|
158858
159994
|
}
|
|
158859
159995
|
function getDistinctCommitEmbeddingModelIds(db, projectPath) {
|
|
158860
159996
|
const rows = getDistinctModelIdStatement(db).all(projectPath);
|
|
@@ -159018,9 +160154,83 @@ function invalidateMemory(projectPath, memoryId) {
|
|
|
159018
160154
|
cached2?.embeddings.delete(memoryId);
|
|
159019
160155
|
}
|
|
159020
160156
|
|
|
160157
|
+
// ../plugin/src/features/magic-context/session-project-storage.ts
|
|
160158
|
+
var upsertSessionProjectStatements = new WeakMap;
|
|
160159
|
+
var repairSessionChunkProjectStatements = new WeakMap;
|
|
160160
|
+
var repairProjectChunkProjectStatements = new WeakMap;
|
|
160161
|
+
function getUpsertSessionProjectStatement(db) {
|
|
160162
|
+
let stmt = upsertSessionProjectStatements.get(db);
|
|
160163
|
+
if (!stmt) {
|
|
160164
|
+
stmt = db.prepare(`INSERT INTO session_projects (session_id, harness, project_path, updated_at)
|
|
160165
|
+
VALUES (?, ?, ?, ?)
|
|
160166
|
+
ON CONFLICT(session_id, harness) DO UPDATE SET
|
|
160167
|
+
project_path = excluded.project_path,
|
|
160168
|
+
updated_at = excluded.updated_at
|
|
160169
|
+
WHERE session_projects.project_path <> excluded.project_path`);
|
|
160170
|
+
upsertSessionProjectStatements.set(db, stmt);
|
|
160171
|
+
}
|
|
160172
|
+
return stmt;
|
|
160173
|
+
}
|
|
160174
|
+
function getRepairSessionChunkProjectStatement(db) {
|
|
160175
|
+
let stmt = repairSessionChunkProjectStatements.get(db);
|
|
160176
|
+
if (!stmt) {
|
|
160177
|
+
stmt = db.prepare(`UPDATE compartment_chunk_embeddings
|
|
160178
|
+
SET project_path = ?
|
|
160179
|
+
WHERE session_id = ?
|
|
160180
|
+
AND harness = ?
|
|
160181
|
+
AND project_path <> ?`);
|
|
160182
|
+
repairSessionChunkProjectStatements.set(db, stmt);
|
|
160183
|
+
}
|
|
160184
|
+
return stmt;
|
|
160185
|
+
}
|
|
160186
|
+
function getRepairProjectChunkProjectStatement(db) {
|
|
160187
|
+
let stmt = repairProjectChunkProjectStatements.get(db);
|
|
160188
|
+
if (!stmt) {
|
|
160189
|
+
stmt = db.prepare(`UPDATE compartment_chunk_embeddings
|
|
160190
|
+
SET project_path = (
|
|
160191
|
+
SELECT sp.project_path
|
|
160192
|
+
FROM session_projects sp
|
|
160193
|
+
WHERE sp.session_id = compartment_chunk_embeddings.session_id
|
|
160194
|
+
AND sp.harness = compartment_chunk_embeddings.harness
|
|
160195
|
+
LIMIT 1
|
|
160196
|
+
)
|
|
160197
|
+
WHERE EXISTS (
|
|
160198
|
+
SELECT 1
|
|
160199
|
+
FROM session_projects sp
|
|
160200
|
+
WHERE sp.session_id = compartment_chunk_embeddings.session_id
|
|
160201
|
+
AND sp.harness = compartment_chunk_embeddings.harness
|
|
160202
|
+
AND sp.project_path <> compartment_chunk_embeddings.project_path
|
|
160203
|
+
AND (
|
|
160204
|
+
sp.project_path = ?
|
|
160205
|
+
OR compartment_chunk_embeddings.project_path = ?
|
|
160206
|
+
)
|
|
160207
|
+
)`);
|
|
160208
|
+
repairProjectChunkProjectStatements.set(db, stmt);
|
|
160209
|
+
}
|
|
160210
|
+
return stmt;
|
|
160211
|
+
}
|
|
160212
|
+
function recordSessionProjectIdentity(db, sessionId, projectPath) {
|
|
160213
|
+
if (!sessionId || !projectPath)
|
|
160214
|
+
return;
|
|
160215
|
+
const harness = getHarness();
|
|
160216
|
+
const now = Date.now();
|
|
160217
|
+
db.transaction(() => {
|
|
160218
|
+
getUpsertSessionProjectStatement(db).run(sessionId, harness, projectPath, now);
|
|
160219
|
+
getRepairSessionChunkProjectStatement(db).run(projectPath, sessionId, harness, projectPath);
|
|
160220
|
+
})();
|
|
160221
|
+
}
|
|
160222
|
+
function repairMisScopedCompartmentChunkEmbeddingsForProject(db, projectPath) {
|
|
160223
|
+
if (!projectPath)
|
|
160224
|
+
return 0;
|
|
160225
|
+
return getRepairProjectChunkProjectStatement(db).run(projectPath, projectPath).changes;
|
|
160226
|
+
}
|
|
160227
|
+
|
|
159021
160228
|
// ../plugin/src/features/magic-context/project-embedding-registry.ts
|
|
159022
160229
|
var OFF_PROVIDER_IDENTITY = "embedding-provider:off";
|
|
159023
160230
|
var SWEEP_MAX_WALL_CLOCK_MS = 10 * 60 * 1000;
|
|
160231
|
+
var CHUNK_DRAIN_BATCH_SIZE = 8;
|
|
160232
|
+
var MAX_WINDOWS_PER_EMBED_CALL = 16;
|
|
160233
|
+
var SESSION_EMBED_LEASE_RENEWAL_MS = 60 * 1000;
|
|
159024
160234
|
var projectRegistrations = new Map;
|
|
159025
160235
|
var loadUnembeddedMemoriesStatements = new WeakMap;
|
|
159026
160236
|
var globalRegistrationGeneration = 0;
|
|
@@ -159029,7 +160239,10 @@ function resolveEmbeddingConfig(config2) {
|
|
|
159029
160239
|
if (!config2 || config2.provider === "local") {
|
|
159030
160240
|
return {
|
|
159031
160241
|
provider: "local",
|
|
159032
|
-
model: config2?.model?.trim() || DEFAULT_LOCAL_EMBEDDING_MODEL
|
|
160242
|
+
model: config2?.model?.trim() || DEFAULT_LOCAL_EMBEDDING_MODEL,
|
|
160243
|
+
...config2?.max_input_tokens ? {
|
|
160244
|
+
max_input_tokens: normalizeCompartmentChunkMaxInputTokens(config2.max_input_tokens)
|
|
160245
|
+
} : {}
|
|
159033
160246
|
};
|
|
159034
160247
|
}
|
|
159035
160248
|
if (config2.provider === "openai-compatible") {
|
|
@@ -159042,7 +160255,10 @@ function resolveEmbeddingConfig(config2) {
|
|
|
159042
160255
|
endpoint: config2.endpoint.trim(),
|
|
159043
160256
|
...apiKey ? { api_key: apiKey } : {},
|
|
159044
160257
|
...inputType ? { input_type: inputType } : {},
|
|
159045
|
-
...truncate ? { truncate } : {}
|
|
160258
|
+
...truncate ? { truncate } : {},
|
|
160259
|
+
...config2.max_input_tokens ? {
|
|
160260
|
+
max_input_tokens: normalizeCompartmentChunkMaxInputTokens(config2.max_input_tokens)
|
|
160261
|
+
} : {}
|
|
159046
160262
|
};
|
|
159047
160263
|
}
|
|
159048
160264
|
return { provider: "off" };
|
|
@@ -159060,10 +160276,11 @@ function createProvider(config2) {
|
|
|
159060
160276
|
model: config2.model,
|
|
159061
160277
|
apiKey: config2.api_key,
|
|
159062
160278
|
inputType: config2.input_type,
|
|
159063
|
-
truncate: config2.truncate
|
|
160279
|
+
truncate: config2.truncate,
|
|
160280
|
+
maxInputTokens: config2.max_input_tokens
|
|
159064
160281
|
});
|
|
159065
160282
|
}
|
|
159066
|
-
return new LocalEmbeddingProvider(config2.model);
|
|
160283
|
+
return new LocalEmbeddingProvider(config2.model, config2.max_input_tokens);
|
|
159067
160284
|
}
|
|
159068
160285
|
function stableStringify2(value) {
|
|
159069
160286
|
if (Array.isArray(value)) {
|
|
@@ -159076,7 +160293,7 @@ function stableStringify2(value) {
|
|
|
159076
160293
|
return JSON.stringify(value);
|
|
159077
160294
|
}
|
|
159078
160295
|
function sha256Prefix(value, length = 16) {
|
|
159079
|
-
return
|
|
160296
|
+
return createHash6("sha256").update(value).digest("hex").slice(0, length);
|
|
159080
160297
|
}
|
|
159081
160298
|
function getRuntimeFingerprint(config2) {
|
|
159082
160299
|
if (config2.provider === "off") {
|
|
@@ -159084,6 +160301,18 @@ function getRuntimeFingerprint(config2) {
|
|
|
159084
160301
|
}
|
|
159085
160302
|
return `${getEmbeddingProviderIdentity(config2)}:${sha256Prefix(stableStringify2(config2))}`;
|
|
159086
160303
|
}
|
|
160304
|
+
function getChunkEmbeddingModelId(config2, providerIdentity) {
|
|
160305
|
+
if (config2.provider === "off") {
|
|
160306
|
+
return OFF_PROVIDER_IDENTITY;
|
|
160307
|
+
}
|
|
160308
|
+
const chunkIdentity = {
|
|
160309
|
+
providerIdentity,
|
|
160310
|
+
chunkerVersion: 1,
|
|
160311
|
+
maxInputTokens: normalizeCompartmentChunkMaxInputTokens("max_input_tokens" in config2 ? config2.max_input_tokens : undefined),
|
|
160312
|
+
truncate: config2.provider === "openai-compatible" ? config2.truncate ?? "" : ""
|
|
160313
|
+
};
|
|
160314
|
+
return `${providerIdentity}:chunk:${sha256Prefix(stableStringify2(chunkIdentity))}`;
|
|
160315
|
+
}
|
|
159087
160316
|
function sameFeatures(a, b) {
|
|
159088
160317
|
return a.memoryEnabled === b.memoryEnabled && a.gitCommitEnabled === b.gitCommitEnabled;
|
|
159089
160318
|
}
|
|
@@ -159100,7 +160329,8 @@ function snapshotFor(registration) {
|
|
|
159100
160329
|
features: { ...registration.features },
|
|
159101
160330
|
enabled,
|
|
159102
160331
|
gitCommitEnabled,
|
|
159103
|
-
modelId: registration.observationMode || !providerIsOn ? "off" : registration.modelId
|
|
160332
|
+
modelId: registration.observationMode || !providerIsOn ? "off" : registration.modelId,
|
|
160333
|
+
chunkModelId: registration.observationMode || !providerIsOn ? "off" : registration.chunkModelId
|
|
159104
160334
|
};
|
|
159105
160335
|
}
|
|
159106
160336
|
function disposeProvider(provider) {
|
|
@@ -159120,7 +160350,7 @@ function anyStoredModelIdIsStale(storedIds, currentId) {
|
|
|
159120
160350
|
}
|
|
159121
160351
|
return false;
|
|
159122
160352
|
}
|
|
159123
|
-
function maybeWipeStaleEmbeddings(db, projectIdentity, currentProviderIdentity, features) {
|
|
160353
|
+
function maybeWipeStaleEmbeddings(db, projectIdentity, currentProviderIdentity, currentChunkIdentity, features) {
|
|
159124
160354
|
if (currentProviderIdentity === OFF_PROVIDER_IDENTITY) {
|
|
159125
160355
|
return false;
|
|
159126
160356
|
}
|
|
@@ -159141,6 +160371,14 @@ function maybeWipeStaleEmbeddings(db, projectIdentity, currentProviderIdentity,
|
|
|
159141
160371
|
wiped = true;
|
|
159142
160372
|
}
|
|
159143
160373
|
}
|
|
160374
|
+
if (features.memoryEnabled) {
|
|
160375
|
+
repairMisScopedCompartmentChunkEmbeddingsForProject(db, projectIdentity);
|
|
160376
|
+
const chunkIds = getDistinctChunkEmbeddingModelIds(db, projectIdentity);
|
|
160377
|
+
if (anyStoredModelIdIsStale(chunkIds, currentChunkIdentity)) {
|
|
160378
|
+
clearChunkEmbeddingsForProject(db, projectIdentity);
|
|
160379
|
+
wiped = true;
|
|
160380
|
+
}
|
|
160381
|
+
}
|
|
159144
160382
|
})();
|
|
159145
160383
|
return wiped;
|
|
159146
160384
|
}
|
|
@@ -159148,10 +160386,11 @@ function registerProjectEmbeddingAndMaybeWipe(db, projectIdentity, config2, feat
|
|
|
159148
160386
|
const resolvedConfig = resolveEmbeddingConfig(config2);
|
|
159149
160387
|
const providerIdentity = getEmbeddingProviderIdentity(resolvedConfig);
|
|
159150
160388
|
const runtimeFingerprint = getRuntimeFingerprint(resolvedConfig);
|
|
160389
|
+
const chunkModelId = getChunkEmbeddingModelId(resolvedConfig, providerIdentity);
|
|
159151
160390
|
const prior = projectRegistrations.get(projectIdentity);
|
|
159152
160391
|
const canReuseProvider = prior !== undefined && !prior.observationMode && prior.runtimeFingerprint === runtimeFingerprint && prior.providerIdentity === providerIdentity;
|
|
159153
|
-
const wiped = maybeWipeStaleEmbeddings(db, projectIdentity, providerIdentity, features);
|
|
159154
|
-
const generationChanged = prior === undefined || prior.observationMode || prior.runtimeFingerprint !== runtimeFingerprint || !sameFeatures(prior.features, features) || wiped;
|
|
160392
|
+
const wiped = maybeWipeStaleEmbeddings(db, projectIdentity, providerIdentity, chunkModelId, features);
|
|
160393
|
+
const generationChanged = prior === undefined || prior.observationMode || prior.runtimeFingerprint !== runtimeFingerprint || prior.chunkModelId !== chunkModelId || !sameFeatures(prior.features, features) || wiped;
|
|
159155
160394
|
const generation = generationChanged ? ++globalRegistrationGeneration : prior.generation;
|
|
159156
160395
|
const registration = {
|
|
159157
160396
|
projectIdentity,
|
|
@@ -159163,6 +160402,7 @@ function registerProjectEmbeddingAndMaybeWipe(db, projectIdentity, config2, feat
|
|
|
159163
160402
|
generation,
|
|
159164
160403
|
features: { ...features },
|
|
159165
160404
|
modelId: providerIdentity === OFF_PROVIDER_IDENTITY ? "off" : providerIdentity,
|
|
160405
|
+
chunkModelId: providerIdentity === OFF_PROVIDER_IDENTITY ? "off" : chunkModelId,
|
|
159166
160406
|
observationMode: false
|
|
159167
160407
|
};
|
|
159168
160408
|
projectRegistrations.set(projectIdentity, registration);
|
|
@@ -159185,6 +160425,7 @@ function registerProjectInObservationMode(db, projectIdentity, sourceDirectory,
|
|
|
159185
160425
|
generation,
|
|
159186
160426
|
features: { memoryEnabled: false, gitCommitEnabled: false },
|
|
159187
160427
|
modelId: "off",
|
|
160428
|
+
chunkModelId: "off",
|
|
159188
160429
|
observationMode: true
|
|
159189
160430
|
};
|
|
159190
160431
|
projectRegistrations.set(projectIdentity, registration);
|
|
@@ -159195,6 +160436,15 @@ function getProjectEmbeddingSnapshot(projectIdentity) {
|
|
|
159195
160436
|
const registration = projectRegistrations.get(projectIdentity);
|
|
159196
160437
|
return registration ? snapshotFor(registration) : null;
|
|
159197
160438
|
}
|
|
160439
|
+
function getProjectChunkEmbeddingModelId(projectIdentity) {
|
|
160440
|
+
const registration = projectRegistrations.get(projectIdentity);
|
|
160441
|
+
return registration && !registration.observationMode ? registration.chunkModelId : "off";
|
|
160442
|
+
}
|
|
160443
|
+
function getProjectEmbeddingMaxInputTokens(projectIdentity) {
|
|
160444
|
+
const registration = projectRegistrations.get(projectIdentity);
|
|
160445
|
+
const configMax = registration?.config && "max_input_tokens" in registration.config ? registration.config.max_input_tokens : undefined;
|
|
160446
|
+
return normalizeCompartmentChunkMaxInputTokens(registration?.provider?.maxInputTokens ?? configMax);
|
|
160447
|
+
}
|
|
159198
160448
|
function getOrCreateProjectProvider(registration) {
|
|
159199
160449
|
if (registration.providerIdentity === OFF_PROVIDER_IDENTITY || registration.observationMode) {
|
|
159200
160450
|
return null;
|
|
@@ -159289,6 +160539,131 @@ async function embedUnembeddedMemoriesForProject(db, projectIdentity, batchSize
|
|
|
159289
160539
|
return 0;
|
|
159290
160540
|
}
|
|
159291
160541
|
}
|
|
160542
|
+
async function embedCandidateChunkBatch(db, projectIdentity, modelId, candidates, signal) {
|
|
160543
|
+
const noWork = [];
|
|
160544
|
+
if (candidates.length === 0)
|
|
160545
|
+
return { embedded: 0, noWork };
|
|
160546
|
+
const maxInputTokens = getProjectEmbeddingMaxInputTokens(projectIdentity);
|
|
160547
|
+
const prepared = [];
|
|
160548
|
+
for (const candidate of candidates) {
|
|
160549
|
+
const canonicalText = buildCanonicalChunkTextFromFts(db, candidate.sessionId, candidate.startMessage, candidate.endMessage);
|
|
160550
|
+
if (canonicalText.length === 0) {
|
|
160551
|
+
noWork.push(candidate.id);
|
|
160552
|
+
continue;
|
|
160553
|
+
}
|
|
160554
|
+
const windows = chunkCanonicalText(canonicalText, candidate.startMessage, candidate.endMessage, maxInputTokens);
|
|
160555
|
+
if (windows.length === 0 || chunkEmbeddingWindowsAreCurrent(db, candidate.id, modelId, windows, projectIdentity)) {
|
|
160556
|
+
noWork.push(candidate.id);
|
|
160557
|
+
continue;
|
|
160558
|
+
}
|
|
160559
|
+
prepared.push({ candidate, windows });
|
|
160560
|
+
}
|
|
160561
|
+
if (prepared.length === 0)
|
|
160562
|
+
return { embedded: 0, noWork };
|
|
160563
|
+
let embedded = 0;
|
|
160564
|
+
let i = 0;
|
|
160565
|
+
while (i < prepared.length) {
|
|
160566
|
+
if (signal?.aborted)
|
|
160567
|
+
break;
|
|
160568
|
+
const slice = [];
|
|
160569
|
+
let windowCount = 0;
|
|
160570
|
+
do {
|
|
160571
|
+
const item = prepared[i];
|
|
160572
|
+
slice.push(item);
|
|
160573
|
+
windowCount += item.windows.length;
|
|
160574
|
+
i += 1;
|
|
160575
|
+
} while (i < prepared.length && windowCount + prepared[i].windows.length <= MAX_WINDOWS_PER_EMBED_CALL);
|
|
160576
|
+
const texts = [];
|
|
160577
|
+
for (const item of slice)
|
|
160578
|
+
texts.push(...item.windows.map((w) => w.text));
|
|
160579
|
+
try {
|
|
160580
|
+
const result = await embedBatchForProject(projectIdentity, texts, signal);
|
|
160581
|
+
if (!result)
|
|
160582
|
+
continue;
|
|
160583
|
+
if (signal?.aborted)
|
|
160584
|
+
break;
|
|
160585
|
+
let offset = 0;
|
|
160586
|
+
for (const item of slice) {
|
|
160587
|
+
const vectors = result.vectors.slice(offset, offset + item.windows.length);
|
|
160588
|
+
offset += item.windows.length;
|
|
160589
|
+
if (vectors.length !== item.windows.length || vectors.some((v) => !v)) {
|
|
160590
|
+
continue;
|
|
160591
|
+
}
|
|
160592
|
+
const rows = item.windows.map((window, index) => ({
|
|
160593
|
+
compartmentId: item.candidate.id,
|
|
160594
|
+
sessionId: item.candidate.sessionId,
|
|
160595
|
+
projectPath: projectIdentity,
|
|
160596
|
+
window,
|
|
160597
|
+
modelId,
|
|
160598
|
+
vector: vectors[index]
|
|
160599
|
+
}));
|
|
160600
|
+
replaceCompartmentChunkEmbeddings(db, rows);
|
|
160601
|
+
embedded += 1;
|
|
160602
|
+
}
|
|
160603
|
+
} catch (error51) {
|
|
160604
|
+
log("[magic-context] failed to proactively embed compartment chunks:", error51);
|
|
160605
|
+
}
|
|
160606
|
+
}
|
|
160607
|
+
return { embedded, noWork };
|
|
160608
|
+
}
|
|
160609
|
+
async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, options) {
|
|
160610
|
+
const snapshot = getProjectEmbeddingSnapshot(projectIdentity);
|
|
160611
|
+
if (!snapshot?.enabled || snapshot.chunkModelId === "off") {
|
|
160612
|
+
return { status: "disabled", embedded: 0, total: 0 };
|
|
160613
|
+
}
|
|
160614
|
+
recordSessionProjectIdentity(db, sessionId, projectIdentity);
|
|
160615
|
+
const total = countUnembeddedSessionCompartments(db, projectIdentity, sessionId, snapshot.chunkModelId);
|
|
160616
|
+
if (total === 0)
|
|
160617
|
+
return { status: "nothing", embedded: 0, total: 0 };
|
|
160618
|
+
const holderId = `session-embed-${randomUUID()}`;
|
|
160619
|
+
const lease = acquireGitSweepLease(db, projectIdentity, holderId, { ignoreCooldown: true });
|
|
160620
|
+
if (!lease.acquired)
|
|
160621
|
+
return { status: "busy", embedded: 0, total };
|
|
160622
|
+
const renewal = setInterval(() => {
|
|
160623
|
+
try {
|
|
160624
|
+
renewGitSweepLease(db, projectIdentity, holderId);
|
|
160625
|
+
} catch {}
|
|
160626
|
+
}, SESSION_EMBED_LEASE_RENEWAL_MS);
|
|
160627
|
+
renewal.unref?.();
|
|
160628
|
+
const batchSize = Math.max(1, options?.batchSize ?? CHUNK_DRAIN_BATCH_SIZE);
|
|
160629
|
+
const skipIds = [];
|
|
160630
|
+
let embedded = 0;
|
|
160631
|
+
let aborted2 = false;
|
|
160632
|
+
let providerStalled = false;
|
|
160633
|
+
try {
|
|
160634
|
+
options?.onProgress?.({ embedded, total });
|
|
160635
|
+
for (;; ) {
|
|
160636
|
+
if (options?.signal?.aborted) {
|
|
160637
|
+
aborted2 = true;
|
|
160638
|
+
break;
|
|
160639
|
+
}
|
|
160640
|
+
const candidates = loadUnembeddedSessionChunkCandidates(db, projectIdentity, sessionId, snapshot.chunkModelId, batchSize, skipIds);
|
|
160641
|
+
if (candidates.length === 0)
|
|
160642
|
+
break;
|
|
160643
|
+
const { embedded: n, noWork } = await embedCandidateChunkBatch(db, projectIdentity, snapshot.chunkModelId, candidates, options?.signal);
|
|
160644
|
+
for (const id of noWork)
|
|
160645
|
+
skipIds.push(id);
|
|
160646
|
+
if (n === 0 && noWork.length === 0) {
|
|
160647
|
+
providerStalled = true;
|
|
160648
|
+
break;
|
|
160649
|
+
}
|
|
160650
|
+
embedded += n;
|
|
160651
|
+
options?.onProgress?.({ embedded: Math.min(embedded, total), total });
|
|
160652
|
+
await new Promise((resolve3) => setTimeout(resolve3, 0));
|
|
160653
|
+
}
|
|
160654
|
+
} finally {
|
|
160655
|
+
clearInterval(renewal);
|
|
160656
|
+
releaseGitSweepLease(db, projectIdentity, holderId);
|
|
160657
|
+
}
|
|
160658
|
+
if (aborted2)
|
|
160659
|
+
return { status: "aborted", embedded, total };
|
|
160660
|
+
if (providerStalled) {
|
|
160661
|
+
const remaining = Math.max(0, countUnembeddedSessionCompartments(db, projectIdentity, sessionId, snapshot.chunkModelId) - skipIds.length);
|
|
160662
|
+
if (remaining > 0)
|
|
160663
|
+
return { status: "stalled", embedded, total, remaining };
|
|
160664
|
+
}
|
|
160665
|
+
return { status: "done", embedded, total };
|
|
160666
|
+
}
|
|
159292
160667
|
|
|
159293
160668
|
// ../plugin/src/features/magic-context/memory/embedding.ts
|
|
159294
160669
|
var DEFAULT_EMBEDDING_CONFIG = {
|
|
@@ -159308,10 +160683,11 @@ function createProvider2(config2) {
|
|
|
159308
160683
|
model: config2.model,
|
|
159309
160684
|
apiKey: config2.api_key,
|
|
159310
160685
|
inputType: config2.input_type,
|
|
159311
|
-
truncate: config2.truncate
|
|
160686
|
+
truncate: config2.truncate,
|
|
160687
|
+
maxInputTokens: config2.max_input_tokens
|
|
159312
160688
|
});
|
|
159313
160689
|
}
|
|
159314
|
-
return new LocalEmbeddingProvider(config2.model);
|
|
160690
|
+
return new LocalEmbeddingProvider(config2.model, config2.max_input_tokens);
|
|
159315
160691
|
}
|
|
159316
160692
|
function getOrCreateProvider() {
|
|
159317
160693
|
if (provider) {
|
|
@@ -159335,186 +160711,8 @@ async function embedText(text, signal) {
|
|
|
159335
160711
|
}
|
|
159336
160712
|
var SWEEP_MAX_WALL_CLOCK_MS2 = 10 * 60 * 1000;
|
|
159337
160713
|
|
|
159338
|
-
// ../plugin/src/features/magic-context/memory/project-identity.ts
|
|
159339
|
-
import { execFileSync } from "node:child_process";
|
|
159340
|
-
import { createHash as createHash4 } from "node:crypto";
|
|
159341
|
-
import { statSync } from "node:fs";
|
|
159342
|
-
import path3 from "node:path";
|
|
159343
|
-
var GIT_TIMEOUT_MS = 5000;
|
|
159344
|
-
var identityCache = new Map;
|
|
159345
|
-
var directoryFallbackCache = new Map;
|
|
159346
|
-
|
|
159347
|
-
class ProjectIdentityError extends Error {
|
|
159348
|
-
errorClass;
|
|
159349
|
-
rawDirectory;
|
|
159350
|
-
constructor(errorClass, rawDirectory, message, cause) {
|
|
159351
|
-
super(message);
|
|
159352
|
-
this.name = "ProjectIdentityError";
|
|
159353
|
-
this.errorClass = errorClass;
|
|
159354
|
-
this.rawDirectory = rawDirectory;
|
|
159355
|
-
if (cause) {
|
|
159356
|
-
this.cause = cause;
|
|
159357
|
-
}
|
|
159358
|
-
}
|
|
159359
|
-
}
|
|
159360
|
-
function asError(error51) {
|
|
159361
|
-
return error51 instanceof Error ? error51 : undefined;
|
|
159362
|
-
}
|
|
159363
|
-
function getErrorCode(error51) {
|
|
159364
|
-
if (error51 === null || typeof error51 !== "object" || !("code" in error51)) {
|
|
159365
|
-
return;
|
|
159366
|
-
}
|
|
159367
|
-
const code = error51.code;
|
|
159368
|
-
return typeof code === "string" ? code : undefined;
|
|
159369
|
-
}
|
|
159370
|
-
function getErrorSignal(error51) {
|
|
159371
|
-
if (error51 === null || typeof error51 !== "object" || !("signal" in error51)) {
|
|
159372
|
-
return;
|
|
159373
|
-
}
|
|
159374
|
-
const signal = error51.signal;
|
|
159375
|
-
return typeof signal === "string" ? signal : undefined;
|
|
159376
|
-
}
|
|
159377
|
-
function getErrorKilled(error51) {
|
|
159378
|
-
if (error51 === null || typeof error51 !== "object" || !("killed" in error51)) {
|
|
159379
|
-
return false;
|
|
159380
|
-
}
|
|
159381
|
-
return error51.killed === true;
|
|
159382
|
-
}
|
|
159383
|
-
function getErrorStderr(error51) {
|
|
159384
|
-
if (error51 === null || typeof error51 !== "object" || !("stderr" in error51)) {
|
|
159385
|
-
return "";
|
|
159386
|
-
}
|
|
159387
|
-
const stderr = error51.stderr;
|
|
159388
|
-
if (typeof stderr === "string") {
|
|
159389
|
-
return stderr;
|
|
159390
|
-
}
|
|
159391
|
-
if (Buffer.isBuffer(stderr)) {
|
|
159392
|
-
return stderr.toString("utf8");
|
|
159393
|
-
}
|
|
159394
|
-
return "";
|
|
159395
|
-
}
|
|
159396
|
-
function directoryFallback(directory) {
|
|
159397
|
-
const canonical = path3.resolve(directory);
|
|
159398
|
-
const hash2 = createHash4("md5").update(canonical, "utf8").digest("hex").slice(0, 12);
|
|
159399
|
-
return `dir:${hash2}`;
|
|
159400
|
-
}
|
|
159401
|
-
function assertDirectoryUsable(canonicalDirectory, rawDirectory) {
|
|
159402
|
-
try {
|
|
159403
|
-
const stat2 = statSync(canonicalDirectory);
|
|
159404
|
-
if (!stat2.isDirectory()) {
|
|
159405
|
-
throw new ProjectIdentityError("unknown", rawDirectory, `Project path is not a directory: ${canonicalDirectory}`);
|
|
159406
|
-
}
|
|
159407
|
-
} catch (error51) {
|
|
159408
|
-
if (error51 instanceof ProjectIdentityError) {
|
|
159409
|
-
throw error51;
|
|
159410
|
-
}
|
|
159411
|
-
const code = getErrorCode(error51);
|
|
159412
|
-
if (code === "EACCES" || code === "EPERM") {
|
|
159413
|
-
throw new ProjectIdentityError("permission_denied", rawDirectory, `Permission denied while accessing project directory: ${canonicalDirectory}`, asError(error51));
|
|
159414
|
-
}
|
|
159415
|
-
throw new ProjectIdentityError("unknown", rawDirectory, `Unable to access project directory: ${canonicalDirectory}`, asError(error51));
|
|
159416
|
-
}
|
|
159417
|
-
}
|
|
159418
|
-
function isGitTimeoutError(error51) {
|
|
159419
|
-
const code = getErrorCode(error51);
|
|
159420
|
-
const signal = getErrorSignal(error51);
|
|
159421
|
-
return code === "ETIMEDOUT" || signal === "SIGTERM" || signal === "SIGKILL" || getErrorKilled(error51);
|
|
159422
|
-
}
|
|
159423
|
-
function classifyGitError(error51, rawDirectory) {
|
|
159424
|
-
if (isGitTimeoutError(error51)) {
|
|
159425
|
-
return new ProjectIdentityError("git_timeout", rawDirectory, `git rev-list timed out after ${GIT_TIMEOUT_MS}ms`, asError(error51));
|
|
159426
|
-
}
|
|
159427
|
-
const code = getErrorCode(error51);
|
|
159428
|
-
if (code === "ENOENT") {
|
|
159429
|
-
return new ProjectIdentityError("git_missing", rawDirectory, "git binary is not available in PATH", asError(error51));
|
|
159430
|
-
}
|
|
159431
|
-
if (code === "EACCES" || code === "EPERM") {
|
|
159432
|
-
return new ProjectIdentityError("permission_denied", rawDirectory, "Permission denied while spawning git", asError(error51));
|
|
159433
|
-
}
|
|
159434
|
-
const stderr = getErrorStderr(error51).toLowerCase();
|
|
159435
|
-
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")) {
|
|
159436
|
-
return new ProjectIdentityError("not_git_repo", rawDirectory, "Directory has no git root commit; caller may use directory fallback", asError(error51));
|
|
159437
|
-
}
|
|
159438
|
-
return new ProjectIdentityError("unknown", rawDirectory, "git rev-list failed while resolving project identity", asError(error51));
|
|
159439
|
-
}
|
|
159440
|
-
function resolveProjectIdentityStrict(directory) {
|
|
159441
|
-
const canonical = path3.resolve(directory);
|
|
159442
|
-
const cached2 = identityCache.get(canonical);
|
|
159443
|
-
if (cached2 !== undefined) {
|
|
159444
|
-
return cached2;
|
|
159445
|
-
}
|
|
159446
|
-
assertDirectoryUsable(canonical, directory);
|
|
159447
|
-
let output;
|
|
159448
|
-
try {
|
|
159449
|
-
output = execFileSync("git", ["rev-list", "--max-parents=0", "HEAD"], {
|
|
159450
|
-
cwd: canonical,
|
|
159451
|
-
encoding: "utf8",
|
|
159452
|
-
env: { ...process.env, LC_ALL: "C", LANG: "C" },
|
|
159453
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
159454
|
-
timeout: GIT_TIMEOUT_MS
|
|
159455
|
-
});
|
|
159456
|
-
} catch (error51) {
|
|
159457
|
-
throw classifyGitError(error51, directory);
|
|
159458
|
-
}
|
|
159459
|
-
const firstLine = output.split(`
|
|
159460
|
-
`)[0]?.trim() ?? "";
|
|
159461
|
-
const rootCommit = firstLine.slice(0, 64);
|
|
159462
|
-
if (rootCommit.length < 7) {
|
|
159463
|
-
throw new ProjectIdentityError("unknown", directory, "git rev-list returned no valid root commit hash");
|
|
159464
|
-
}
|
|
159465
|
-
const identity = `git:${rootCommit}`;
|
|
159466
|
-
identityCache.set(canonical, identity);
|
|
159467
|
-
return identity;
|
|
159468
|
-
}
|
|
159469
|
-
function shouldUseDirectoryFallback(error51) {
|
|
159470
|
-
return error51.errorClass === "not_git_repo" || error51.errorClass === "unknown" && error51.message.startsWith("Unable to access project directory:");
|
|
159471
|
-
}
|
|
159472
|
-
function resolveProjectIdentity(directory) {
|
|
159473
|
-
const canonical = path3.resolve(directory);
|
|
159474
|
-
const cachedFallback = directoryFallbackCache.get(canonical);
|
|
159475
|
-
if (cachedFallback !== undefined) {
|
|
159476
|
-
if (!hasGitDir(canonical)) {
|
|
159477
|
-
return cachedFallback;
|
|
159478
|
-
}
|
|
159479
|
-
directoryFallbackCache.delete(canonical);
|
|
159480
|
-
}
|
|
159481
|
-
try {
|
|
159482
|
-
return resolveProjectIdentityStrict(directory);
|
|
159483
|
-
} catch (error51) {
|
|
159484
|
-
if (error51 instanceof ProjectIdentityError && shouldUseDirectoryFallback(error51)) {
|
|
159485
|
-
const fallback = directoryFallback(canonical);
|
|
159486
|
-
if (!hasGitDir(canonical)) {
|
|
159487
|
-
directoryFallbackCache.set(canonical, fallback);
|
|
159488
|
-
}
|
|
159489
|
-
return fallback;
|
|
159490
|
-
}
|
|
159491
|
-
throw error51;
|
|
159492
|
-
}
|
|
159493
|
-
}
|
|
159494
|
-
function hasGitDir(canonical) {
|
|
159495
|
-
try {
|
|
159496
|
-
statSync(path3.join(canonical, ".git"));
|
|
159497
|
-
return true;
|
|
159498
|
-
} catch {
|
|
159499
|
-
return false;
|
|
159500
|
-
}
|
|
159501
|
-
}
|
|
159502
|
-
function normalizeStoredProjectPath(rawOrStored) {
|
|
159503
|
-
if (rawOrStored.startsWith("git:") || rawOrStored.startsWith("dir:")) {
|
|
159504
|
-
return rawOrStored;
|
|
159505
|
-
}
|
|
159506
|
-
try {
|
|
159507
|
-
return resolveProjectIdentity(rawOrStored);
|
|
159508
|
-
} catch {
|
|
159509
|
-
return directoryFallback(rawOrStored);
|
|
159510
|
-
}
|
|
159511
|
-
}
|
|
159512
|
-
function storedPathBelongsToIdentity(storedProjectPath, projectIdentity) {
|
|
159513
|
-
return storedProjectPath === projectIdentity || normalizeStoredProjectPath(storedProjectPath) === projectIdentity;
|
|
159514
|
-
}
|
|
159515
|
-
|
|
159516
160714
|
// ../plugin/src/plugin/embedding-bootstrap-helpers.ts
|
|
159517
|
-
import { createHash as
|
|
160715
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
159518
160716
|
init_logger();
|
|
159519
160717
|
var EMBEDDING_AFFECTING_KEYS = new Set([
|
|
159520
160718
|
"embedding.api_key",
|
|
@@ -159536,7 +160734,7 @@ var EMBEDDING_WARNING_TERMS = [
|
|
|
159536
160734
|
];
|
|
159537
160735
|
var loggedFailureSignatures = new Map;
|
|
159538
160736
|
function sha256Prefix2(value, length = 16) {
|
|
159539
|
-
return
|
|
160737
|
+
return createHash7("sha256").update(value).digest("hex").slice(0, length);
|
|
159540
160738
|
}
|
|
159541
160739
|
function warningLooksEmbeddingRelated(message) {
|
|
159542
160740
|
const lower = message.toLowerCase();
|
|
@@ -159697,6 +160895,7 @@ var SESSION_META_SELECT_COLUMNS = [
|
|
|
159697
160895
|
"cached_m0_bytes",
|
|
159698
160896
|
"cached_m1_bytes",
|
|
159699
160897
|
"cached_m0_project_memory_epoch",
|
|
160898
|
+
"cached_m0_workspace_fingerprint",
|
|
159700
160899
|
"cached_m0_project_user_profile_version",
|
|
159701
160900
|
"cached_m0_max_compartment_seq",
|
|
159702
160901
|
"cached_m0_max_memory_id",
|
|
@@ -159744,6 +160943,7 @@ var META_COLUMNS = {
|
|
|
159744
160943
|
cachedM0Bytes: "cached_m0_bytes",
|
|
159745
160944
|
cachedM1Bytes: "cached_m1_bytes",
|
|
159746
160945
|
cachedM0ProjectMemoryEpoch: "cached_m0_project_memory_epoch",
|
|
160946
|
+
cachedM0WorkspaceFingerprint: "cached_m0_workspace_fingerprint",
|
|
159747
160947
|
cachedM0ProjectUserProfileVersion: "cached_m0_project_user_profile_version",
|
|
159748
160948
|
cachedM0MaxCompartmentSeq: "cached_m0_max_compartment_seq",
|
|
159749
160949
|
cachedM0MaxMemoryId: "cached_m0_max_memory_id",
|
|
@@ -159773,6 +160973,7 @@ var NULL_BIND_META_KEYS = new Set([
|
|
|
159773
160973
|
"cachedM0Bytes",
|
|
159774
160974
|
"cachedM1Bytes",
|
|
159775
160975
|
"cachedM0ProjectMemoryEpoch",
|
|
160976
|
+
"cachedM0WorkspaceFingerprint",
|
|
159776
160977
|
"cachedM0ProjectUserProfileVersion",
|
|
159777
160978
|
"cachedM0MaxCompartmentSeq",
|
|
159778
160979
|
"cachedM0MaxMemoryId",
|
|
@@ -159806,7 +161007,7 @@ function isSessionMetaRow(row) {
|
|
|
159806
161007
|
if (row === null || typeof row !== "object")
|
|
159807
161008
|
return false;
|
|
159808
161009
|
const r = row;
|
|
159809
|
-
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);
|
|
161010
|
+
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);
|
|
159810
161011
|
}
|
|
159811
161012
|
function getDefaultSessionMeta(sessionId) {
|
|
159812
161013
|
return {
|
|
@@ -159833,6 +161034,7 @@ function getDefaultSessionMeta(sessionId) {
|
|
|
159833
161034
|
cachedM0Bytes: null,
|
|
159834
161035
|
cachedM1Bytes: null,
|
|
159835
161036
|
cachedM0ProjectMemoryEpoch: null,
|
|
161037
|
+
cachedM0WorkspaceFingerprint: null,
|
|
159836
161038
|
cachedM0ProjectUserProfileVersion: null,
|
|
159837
161039
|
cachedM0MaxCompartmentSeq: null,
|
|
159838
161040
|
cachedM0MaxMemoryId: null,
|
|
@@ -159895,6 +161097,7 @@ function toSessionMeta(row) {
|
|
|
159895
161097
|
cachedM0Bytes: toBufferOrNull(row.cached_m0_bytes),
|
|
159896
161098
|
cachedM1Bytes: toBufferOrNull(row.cached_m1_bytes),
|
|
159897
161099
|
cachedM0ProjectMemoryEpoch: numOrNull(row.cached_m0_project_memory_epoch),
|
|
161100
|
+
cachedM0WorkspaceFingerprint: stringOrNull(row.cached_m0_workspace_fingerprint),
|
|
159898
161101
|
cachedM0ProjectUserProfileVersion: numOrNull(row.cached_m0_project_user_profile_version),
|
|
159899
161102
|
cachedM0MaxCompartmentSeq: numOrNull(row.cached_m0_max_compartment_seq),
|
|
159900
161103
|
cachedM0MaxMemoryId: numOrNull(row.cached_m0_max_memory_id),
|
|
@@ -159925,6 +161128,7 @@ function persistCachedM0(db, sessionId, payload) {
|
|
|
159925
161128
|
db.prepare(`UPDATE session_meta SET
|
|
159926
161129
|
cached_m0_bytes = ?,
|
|
159927
161130
|
cached_m0_project_memory_epoch = ?,
|
|
161131
|
+
cached_m0_workspace_fingerprint = ?,
|
|
159928
161132
|
cached_m0_project_user_profile_version = ?,
|
|
159929
161133
|
cached_m0_max_compartment_seq = ?,
|
|
159930
161134
|
cached_m0_max_memory_id = ?,
|
|
@@ -159936,9 +161140,8 @@ function persistCachedM0(db, sessionId, payload) {
|
|
|
159936
161140
|
cached_m0_session_facts_version = ?,
|
|
159937
161141
|
cached_m0_upgrade_state = ?,
|
|
159938
161142
|
cached_m0_system_hash = ?,
|
|
159939
|
-
cached_m0_tool_set_hash = ?,
|
|
159940
161143
|
cached_m0_model_key = ?
|
|
159941
|
-
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.
|
|
161144
|
+
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);
|
|
159942
161145
|
}
|
|
159943
161146
|
function clearCachedM0M1(db, sessionId) {
|
|
159944
161147
|
ensureSessionMetaRow(db, sessionId);
|
|
@@ -159947,6 +161150,7 @@ function clearCachedM0M1(db, sessionId) {
|
|
|
159947
161150
|
["cached_m0_bytes", null],
|
|
159948
161151
|
["cached_m1_bytes", null],
|
|
159949
161152
|
["cached_m0_project_memory_epoch", null],
|
|
161153
|
+
["cached_m0_workspace_fingerprint", null],
|
|
159950
161154
|
["cached_m0_project_user_profile_version", null],
|
|
159951
161155
|
["cached_m0_max_compartment_seq", null],
|
|
159952
161156
|
["cached_m0_max_memory_id", null],
|
|
@@ -160282,8 +161486,8 @@ function readRawSessionTailFromDb(db, sessionId, baseOrdinal, anchorMessageId) {
|
|
|
160282
161486
|
const CHUNK = 800;
|
|
160283
161487
|
for (let i = 0;i < ids.length; i += CHUNK) {
|
|
160284
161488
|
const slice = ids.slice(i, i + CHUNK);
|
|
160285
|
-
const
|
|
160286
|
-
const partRows = db.prepare(`SELECT message_id, data, time_updated FROM part WHERE session_id = ? AND message_id IN (${
|
|
161489
|
+
const placeholders2 = slice.map(() => "?").join(",");
|
|
161490
|
+
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);
|
|
160287
161491
|
for (const part of partRows) {
|
|
160288
161492
|
const list = partsByMessageId.get(part.message_id) ?? [];
|
|
160289
161493
|
list.push(attachRawPartVersion(parseJsonUnknown(part.data), part.time_updated));
|
|
@@ -165180,53 +166384,6 @@ function createCtxExpandTool(deps) {
|
|
|
165180
166384
|
}
|
|
165181
166385
|
};
|
|
165182
166386
|
}
|
|
165183
|
-
|
|
165184
|
-
// ../plugin/src/features/magic-context/memory/constants.ts
|
|
165185
|
-
var V2_MEMORY_CATEGORIES = [
|
|
165186
|
-
"PROJECT_RULES",
|
|
165187
|
-
"ARCHITECTURE",
|
|
165188
|
-
"CONSTRAINTS",
|
|
165189
|
-
"CONFIG_VALUES",
|
|
165190
|
-
"NAMING"
|
|
165191
|
-
];
|
|
165192
|
-
var PROMOTABLE_CATEGORIES = [
|
|
165193
|
-
"PROJECT_RULES",
|
|
165194
|
-
"ARCHITECTURE",
|
|
165195
|
-
"CONSTRAINTS",
|
|
165196
|
-
"CONFIG_VALUES",
|
|
165197
|
-
"NAMING",
|
|
165198
|
-
"ARCHITECTURE_DECISIONS",
|
|
165199
|
-
"CONFIG_DEFAULTS",
|
|
165200
|
-
"USER_PREFERENCES",
|
|
165201
|
-
"USER_DIRECTIVES",
|
|
165202
|
-
"ENVIRONMENT",
|
|
165203
|
-
"WORKFLOW_RULES",
|
|
165204
|
-
"KNOWN_ISSUES"
|
|
165205
|
-
];
|
|
165206
|
-
var CATEGORY_PRIORITY = [
|
|
165207
|
-
"PROJECT_RULES",
|
|
165208
|
-
"ARCHITECTURE",
|
|
165209
|
-
"CONSTRAINTS",
|
|
165210
|
-
"CONFIG_VALUES",
|
|
165211
|
-
"NAMING",
|
|
165212
|
-
"USER_DIRECTIVES",
|
|
165213
|
-
"USER_PREFERENCES",
|
|
165214
|
-
"CONFIG_DEFAULTS",
|
|
165215
|
-
"ARCHITECTURE_DECISIONS",
|
|
165216
|
-
"ENVIRONMENT",
|
|
165217
|
-
"WORKFLOW_RULES",
|
|
165218
|
-
"KNOWN_ISSUES"
|
|
165219
|
-
];
|
|
165220
|
-
var MEMORY_CATEGORY_ORDER_UNKNOWN = 99;
|
|
165221
|
-
var MEMORY_CATEGORY_ORDER_PRIORITY = CATEGORY_PRIORITY.reduce((acc, category, index) => {
|
|
165222
|
-
acc[category] = index;
|
|
165223
|
-
return acc;
|
|
165224
|
-
}, {});
|
|
165225
|
-
var MEMORY_CATEGORY_ORDER_SQL = `CASE category ${CATEGORY_PRIORITY.map((category, index) => `WHEN '${category}' THEN ${index}`).join(" ")} ELSE ${MEMORY_CATEGORY_ORDER_UNKNOWN} END`;
|
|
165226
|
-
var CATEGORY_DEFAULT_TTL = {
|
|
165227
|
-
WORKFLOW_RULES: 90 * 24 * 60 * 60 * 1000,
|
|
165228
|
-
KNOWN_ISSUES: 30 * 24 * 60 * 60 * 1000
|
|
165229
|
-
};
|
|
165230
166387
|
// ../plugin/src/features/magic-context/memory/embedding-backfill.ts
|
|
165231
166388
|
init_logger();
|
|
165232
166389
|
async function ensureMemoryEmbeddings(args) {
|
|
@@ -165251,7 +166408,7 @@ async function ensureMemoryEmbeddings(args) {
|
|
|
165251
166408
|
continue;
|
|
165252
166409
|
}
|
|
165253
166410
|
saveEmbedding(args.db, memory2.id, embedding, result.modelId);
|
|
165254
|
-
staged.set(memory2.id, embedding);
|
|
166411
|
+
staged.set(memory2.id, { embedding, modelId: result.modelId });
|
|
165255
166412
|
}
|
|
165256
166413
|
})();
|
|
165257
166414
|
const currentSnapshot = getProjectEmbeddingSnapshot(args.projectIdentity);
|
|
@@ -165454,8 +166611,8 @@ function getMemoriesByProjectStatement(db, statuses) {
|
|
|
165454
166611
|
}
|
|
165455
166612
|
let stmt = statements.get(db);
|
|
165456
166613
|
if (!stmt) {
|
|
165457
|
-
const
|
|
165458
|
-
stmt = db.prepare(`SELECT ${getMemorySelectColumns(db)} FROM memories WHERE project_path = ? AND status IN (${
|
|
166614
|
+
const placeholders2 = statuses.map(() => "?").join(", ");
|
|
166615
|
+
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`);
|
|
165459
166616
|
statements.set(db, stmt);
|
|
165460
166617
|
}
|
|
165461
166618
|
return stmt;
|
|
@@ -165600,6 +166757,97 @@ function getMemoriesByProject(db, projectPath, statuses = ["active", "permanent"
|
|
|
165600
166757
|
const rows = getMemoriesByProjectStatement(db, statuses).all(projectPath, ...statuses, expiryCutoff).filter(isMemoryRow);
|
|
165601
166758
|
return rows.map(toMemory);
|
|
165602
166759
|
}
|
|
166760
|
+
function sqlPlaceholders(values) {
|
|
166761
|
+
return values.map(() => "?").join(", ");
|
|
166762
|
+
}
|
|
166763
|
+
function uniqueValues(values) {
|
|
166764
|
+
return [...new Set(values.filter((value) => value.length > 0))];
|
|
166765
|
+
}
|
|
166766
|
+
function buildWorkspaceMemorySqlFilter(args) {
|
|
166767
|
+
if (args.shareCategories === null || args.shareCategories === undefined) {
|
|
166768
|
+
return { clause: "", params: [], active: false };
|
|
166769
|
+
}
|
|
166770
|
+
const identities = uniqueValues(args.identities);
|
|
166771
|
+
const identitySet = new Set(identities);
|
|
166772
|
+
const ownSet = new Set(uniqueValues(args.ownIdentities ?? []).filter((identity) => identitySet.has(identity)));
|
|
166773
|
+
const foreignIdentities = identities.filter((identity) => !ownSet.has(identity));
|
|
166774
|
+
if (foreignIdentities.length === 0) {
|
|
166775
|
+
return { clause: "", params: [], active: false };
|
|
166776
|
+
}
|
|
166777
|
+
const ownIdentities = identities.filter((identity) => ownSet.has(identity));
|
|
166778
|
+
const shareCategories = uniqueValues([...args.shareCategories]);
|
|
166779
|
+
const qualifier = args.tableName ? `${args.tableName}.` : "";
|
|
166780
|
+
const predicates = [];
|
|
166781
|
+
const params = [];
|
|
166782
|
+
if (ownIdentities.length > 0) {
|
|
166783
|
+
predicates.push(`${qualifier}project_path IN (${sqlPlaceholders(ownIdentities)})`);
|
|
166784
|
+
params.push(...ownIdentities);
|
|
166785
|
+
}
|
|
166786
|
+
if (foreignIdentities.length > 0 && shareCategories.length > 0) {
|
|
166787
|
+
predicates.push(`(${qualifier}project_path IN (${sqlPlaceholders(foreignIdentities)}) AND ${qualifier}category IN (${sqlPlaceholders(shareCategories)}))`);
|
|
166788
|
+
params.push(...foreignIdentities, ...shareCategories);
|
|
166789
|
+
}
|
|
166790
|
+
if (predicates.length === 0) {
|
|
166791
|
+
return { clause: " AND 0 = 1", params: [], active: true };
|
|
166792
|
+
}
|
|
166793
|
+
return { clause: ` AND (${predicates.join(" OR ")})`, params, active: true };
|
|
166794
|
+
}
|
|
166795
|
+
function getMemoriesByProjects(db, projectPaths, statuses = ["active", "permanent"], expiryCutoff = Date.now(), ownIdentities, shareCategories) {
|
|
166796
|
+
const identities = uniqueValues(projectPaths);
|
|
166797
|
+
if (identities.length === 0 || statuses.length === 0)
|
|
166798
|
+
return [];
|
|
166799
|
+
const sharingFilter = buildWorkspaceMemorySqlFilter({
|
|
166800
|
+
identities,
|
|
166801
|
+
ownIdentities,
|
|
166802
|
+
shareCategories
|
|
166803
|
+
});
|
|
166804
|
+
if (identities.length === 1 && !sharingFilter.active) {
|
|
166805
|
+
return getMemoriesByProject(db, identities[0], statuses, expiryCutoff);
|
|
166806
|
+
}
|
|
166807
|
+
const rows = db.prepare(`SELECT ${getMemorySelectColumns(db)}
|
|
166808
|
+
FROM memories
|
|
166809
|
+
WHERE project_path IN (${sqlPlaceholders(identities)})
|
|
166810
|
+
AND status IN (${sqlPlaceholders(statuses)})
|
|
166811
|
+
AND (expires_at IS NULL OR expires_at > ?)${sharingFilter.clause}
|
|
166812
|
+
ORDER BY category ASC, updated_at DESC, id ASC`).all(...identities, ...statuses, expiryCutoff, ...sharingFilter.params).filter(isMemoryRow);
|
|
166813
|
+
return rows.map(toMemory);
|
|
166814
|
+
}
|
|
166815
|
+
function getMaxMemoryIdForProjects(db, projectPaths, ownIdentities, shareCategories) {
|
|
166816
|
+
const identities = uniqueValues(projectPaths);
|
|
166817
|
+
if (identities.length === 0)
|
|
166818
|
+
return 0;
|
|
166819
|
+
const sharingFilter = buildWorkspaceMemorySqlFilter({
|
|
166820
|
+
identities,
|
|
166821
|
+
ownIdentities,
|
|
166822
|
+
shareCategories
|
|
166823
|
+
});
|
|
166824
|
+
if (identities.length === 1 && !sharingFilter.active) {
|
|
166825
|
+
const row2 = db.prepare("SELECT COALESCE(MAX(id), 0) AS max_id FROM memories WHERE project_path = ?").get(identities[0]);
|
|
166826
|
+
return typeof row2?.max_id === "number" ? row2.max_id : 0;
|
|
166827
|
+
}
|
|
166828
|
+
const row = db.prepare(`SELECT COALESCE(MAX(id), 0) AS max_id
|
|
166829
|
+
FROM memories
|
|
166830
|
+
WHERE project_path IN (${sqlPlaceholders(identities)})${sharingFilter.clause}`).get(...identities, ...sharingFilter.params);
|
|
166831
|
+
return typeof row?.max_id === "number" ? row.max_id : 0;
|
|
166832
|
+
}
|
|
166833
|
+
function readNewMemoriesForM1Union(db, projectPaths, afterId, expiryCutoff, ownIdentities, shareCategories) {
|
|
166834
|
+
const identities = uniqueValues(projectPaths);
|
|
166835
|
+
if (identities.length === 0)
|
|
166836
|
+
return [];
|
|
166837
|
+
const sharingFilter = buildWorkspaceMemorySqlFilter({
|
|
166838
|
+
identities,
|
|
166839
|
+
ownIdentities,
|
|
166840
|
+
shareCategories
|
|
166841
|
+
});
|
|
166842
|
+
const rows = db.prepare(`SELECT ${getMemorySelectColumns(db)}
|
|
166843
|
+
FROM memories
|
|
166844
|
+
WHERE project_path IN (${sqlPlaceholders(identities)})
|
|
166845
|
+
AND id > ?
|
|
166846
|
+
AND status IN ('active', 'permanent')
|
|
166847
|
+
AND (expires_at IS NULL OR expires_at > ?)${sharingFilter.clause}
|
|
166848
|
+
ORDER BY ${MEMORY_CATEGORY_ORDER_SQL}, id ASC`).all(...identities, afterId, expiryCutoff, ...sharingFilter.params).filter(isMemoryRow);
|
|
166849
|
+
return rows.map(toMemory);
|
|
166850
|
+
}
|
|
165603
166851
|
function getAllActiveMemoriesForMigration(db, projectPath) {
|
|
165604
166852
|
const rows = getActiveMemoriesNoExpiryStatement(db).all(projectPath).filter(isMemoryRow);
|
|
165605
166853
|
return rows.map(toMemory);
|
|
@@ -165767,6 +167015,7 @@ async function embedAndStoreMemory(db, sessionId, projectPath, memoryId, content
|
|
|
165767
167015
|
// ../plugin/src/features/magic-context/memory/storage-memory-fts.ts
|
|
165768
167016
|
var DEFAULT_SEARCH_LIMIT = 10;
|
|
165769
167017
|
var searchStatements = new WeakMap;
|
|
167018
|
+
var unionSearchStatements = new Map;
|
|
165770
167019
|
function getSearchStatement(db) {
|
|
165771
167020
|
let stmt = searchStatements.get(db);
|
|
165772
167021
|
if (!stmt) {
|
|
@@ -165775,6 +167024,23 @@ function getSearchStatement(db) {
|
|
|
165775
167024
|
}
|
|
165776
167025
|
return stmt;
|
|
165777
167026
|
}
|
|
167027
|
+
function getUnionSearchStatement(db, arity) {
|
|
167028
|
+
let statements = unionSearchStatements.get(arity);
|
|
167029
|
+
if (!statements) {
|
|
167030
|
+
statements = new WeakMap;
|
|
167031
|
+
unionSearchStatements.set(arity, statements);
|
|
167032
|
+
}
|
|
167033
|
+
let stmt = statements.get(db);
|
|
167034
|
+
if (!stmt) {
|
|
167035
|
+
const placeholders2 = Array.from({ length: arity }, () => "?").join(", ");
|
|
167036
|
+
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 ?`);
|
|
167037
|
+
statements.set(db, stmt);
|
|
167038
|
+
}
|
|
167039
|
+
return stmt;
|
|
167040
|
+
}
|
|
167041
|
+
function uniqueProjectPaths(projectPaths) {
|
|
167042
|
+
return [...new Set(projectPaths.filter((path4) => path4.length > 0))];
|
|
167043
|
+
}
|
|
165778
167044
|
function sanitizeFtsQuery(query) {
|
|
165779
167045
|
const tokens = query.split(/\s+/).filter((token) => token.length > 0);
|
|
165780
167046
|
if (tokens.length === 0)
|
|
@@ -165793,6 +167059,28 @@ function searchMemoriesFTS(db, projectPath, query, limit = DEFAULT_SEARCH_LIMIT)
|
|
|
165793
167059
|
const rows = getSearchStatement(db).all(projectPath, Date.now(), sanitized, limit).filter(isMemoryRow);
|
|
165794
167060
|
return rows.map(toMemory);
|
|
165795
167061
|
}
|
|
167062
|
+
function searchMemoriesFTSUnion(db, projectPaths, query, limit = DEFAULT_SEARCH_LIMIT, ownIdentities, shareCategories) {
|
|
167063
|
+
const identities = uniqueProjectPaths(projectPaths);
|
|
167064
|
+
if (identities.length === 0)
|
|
167065
|
+
return [];
|
|
167066
|
+
const sharingFilter = buildWorkspaceMemorySqlFilter({
|
|
167067
|
+
identities,
|
|
167068
|
+
ownIdentities,
|
|
167069
|
+
shareCategories,
|
|
167070
|
+
tableName: "memories"
|
|
167071
|
+
});
|
|
167072
|
+
if (identities.length === 1 && !sharingFilter.active) {
|
|
167073
|
+
return searchMemoriesFTS(db, identities[0], query, limit);
|
|
167074
|
+
}
|
|
167075
|
+
const trimmedQuery = query.trim();
|
|
167076
|
+
if (trimmedQuery.length === 0 || limit <= 0)
|
|
167077
|
+
return [];
|
|
167078
|
+
const sanitized = sanitizeFtsQuery(trimmedQuery);
|
|
167079
|
+
if (sanitized.length === 0)
|
|
167080
|
+
return [];
|
|
167081
|
+
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);
|
|
167082
|
+
return rows.map(toMemory);
|
|
167083
|
+
}
|
|
165796
167084
|
// ../plugin/src/features/magic-context/message-index.ts
|
|
165797
167085
|
var lastIndexedStatements = new WeakMap;
|
|
165798
167086
|
var insertMessageStatements = new WeakMap;
|
|
@@ -165939,7 +167227,7 @@ function indexMessagesAfterOrdinal(db, sessionId, messages, lastIndexedOrdinal,
|
|
|
165939
167227
|
return inserted;
|
|
165940
167228
|
}
|
|
165941
167229
|
// ../plugin/src/features/magic-context/project-docs-hash.ts
|
|
165942
|
-
import { createHash as
|
|
167230
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
165943
167231
|
import { lstatSync, readFileSync as readFileSync4, statSync as statSync2 } from "node:fs";
|
|
165944
167232
|
import path4 from "node:path";
|
|
165945
167233
|
var PROJECT_DOC_FILES = ["ARCHITECTURE.md", "STRUCTURE.md"];
|
|
@@ -166037,7 +167325,7 @@ function hashCanonicalPieces(hashPieces) {
|
|
|
166037
167325
|
if (hashPieces.length === 0) {
|
|
166038
167326
|
return "";
|
|
166039
167327
|
}
|
|
166040
|
-
return
|
|
167328
|
+
return createHash8("sha256").update(hashPieces.join(PROJECT_DOCS_DELIMITER), "utf8").digest("hex");
|
|
166041
167329
|
}
|
|
166042
167330
|
function readProjectDocsCanonical(projectDirectory) {
|
|
166043
167331
|
const canonicalDirectory = path4.resolve(projectDirectory);
|
|
@@ -166143,18 +167431,13 @@ function getMemoryMutation(db, id) {
|
|
|
166143
167431
|
WHERE id = ?`).get(id);
|
|
166144
167432
|
return row ? toMemoryMutation(row) : null;
|
|
166145
167433
|
}
|
|
166146
|
-
function
|
|
166147
|
-
|
|
166148
|
-
|
|
166149
|
-
|
|
166150
|
-
|
|
166151
|
-
|
|
166152
|
-
|
|
166153
|
-
FROM memory_mutation_log
|
|
166154
|
-
WHERE project_path = ?
|
|
166155
|
-
AND id > ?
|
|
166156
|
-
AND target_memory_id IN (${placeholders})
|
|
166157
|
-
ORDER BY id ASC`).all(projectPath, afterId ?? 0, ...uniqueIds);
|
|
167434
|
+
function uniqueProjectPaths2(projectPaths) {
|
|
167435
|
+
return [...new Set(projectPaths.filter((path5) => path5.length > 0))];
|
|
167436
|
+
}
|
|
167437
|
+
function placeholders2(values) {
|
|
167438
|
+
return values.map(() => "?").join(", ");
|
|
167439
|
+
}
|
|
167440
|
+
function coalesceMutations(rows) {
|
|
166158
167441
|
const chosenByTarget = new Map;
|
|
166159
167442
|
for (const dbRow of rows) {
|
|
166160
167443
|
const candidate = toMemoryMutation(dbRow);
|
|
@@ -166175,10 +167458,54 @@ function getMemoryMutationsForRender(db, projectPath, afterId, renderedMemoryIds
|
|
|
166175
167458
|
}
|
|
166176
167459
|
return [...chosenByTarget.values()].sort((left, right) => left.id - right.id);
|
|
166177
167460
|
}
|
|
167461
|
+
function getMemoryMutationsForRender(db, projectPath, afterId, renderedMemoryIds) {
|
|
167462
|
+
if (renderedMemoryIds.length === 0)
|
|
167463
|
+
return [];
|
|
167464
|
+
const uniqueIds = [...new Set(renderedMemoryIds)].sort((left, right) => left - right);
|
|
167465
|
+
const placeholders3 = uniqueIds.map(() => "?").join(", ");
|
|
167466
|
+
const rows = db.prepare(`SELECT id, project_path, mutation_type, target_memory_id,
|
|
167467
|
+
superseded_by_id, category, new_content, queued_at
|
|
167468
|
+
FROM memory_mutation_log
|
|
167469
|
+
WHERE project_path = ?
|
|
167470
|
+
AND id > ?
|
|
167471
|
+
AND target_memory_id IN (${placeholders3})
|
|
167472
|
+
ORDER BY id ASC`).all(projectPath, afterId ?? 0, ...uniqueIds);
|
|
167473
|
+
return coalesceMutations(rows);
|
|
167474
|
+
}
|
|
167475
|
+
function getMemoryMutationsForRenderByProjects(db, projectPaths, afterId, renderedMemoryIds) {
|
|
167476
|
+
if (renderedMemoryIds.length === 0)
|
|
167477
|
+
return [];
|
|
167478
|
+
const identities = uniqueProjectPaths2(projectPaths);
|
|
167479
|
+
if (identities.length === 0)
|
|
167480
|
+
return [];
|
|
167481
|
+
if (identities.length === 1) {
|
|
167482
|
+
return getMemoryMutationsForRender(db, identities[0], afterId, renderedMemoryIds);
|
|
167483
|
+
}
|
|
167484
|
+
const uniqueIds = [...new Set(renderedMemoryIds)].sort((left, right) => left - right);
|
|
167485
|
+
const rows = db.prepare(`SELECT id, project_path, mutation_type, target_memory_id,
|
|
167486
|
+
superseded_by_id, category, new_content, queued_at
|
|
167487
|
+
FROM memory_mutation_log
|
|
167488
|
+
WHERE project_path IN (${placeholders2(identities)})
|
|
167489
|
+
AND id > ?
|
|
167490
|
+
AND target_memory_id IN (${placeholders2(uniqueIds)})
|
|
167491
|
+
ORDER BY id ASC`).all(...identities, afterId ?? 0, ...uniqueIds);
|
|
167492
|
+
return coalesceMutations(rows);
|
|
167493
|
+
}
|
|
166178
167494
|
function getMaxMemoryMutationId(db, projectPath) {
|
|
166179
167495
|
const row = db.prepare("SELECT MAX(id) AS max_id FROM memory_mutation_log WHERE project_path = ?").get(projectPath);
|
|
166180
167496
|
return row?.max_id ?? null;
|
|
166181
167497
|
}
|
|
167498
|
+
function getMaxMemoryMutationIdForProjects(db, projectPaths) {
|
|
167499
|
+
const identities = uniqueProjectPaths2(projectPaths);
|
|
167500
|
+
if (identities.length === 0)
|
|
167501
|
+
return null;
|
|
167502
|
+
if (identities.length === 1)
|
|
167503
|
+
return getMaxMemoryMutationId(db, identities[0]);
|
|
167504
|
+
const row = db.prepare(`SELECT MAX(id) AS max_id
|
|
167505
|
+
FROM memory_mutation_log
|
|
167506
|
+
WHERE project_path IN (${placeholders2(identities)})`).get(...identities);
|
|
167507
|
+
return row?.max_id ?? null;
|
|
167508
|
+
}
|
|
166182
167509
|
// ../plugin/src/features/magic-context/storage-meta-persisted.ts
|
|
166183
167510
|
init_logger();
|
|
166184
167511
|
var CAS_RETRY_LIMIT = 5;
|
|
@@ -167024,9 +168351,9 @@ function buildStatusClause(status) {
|
|
|
167024
168351
|
if (statuses.length === 0) {
|
|
167025
168352
|
return null;
|
|
167026
168353
|
}
|
|
167027
|
-
const
|
|
168354
|
+
const placeholders3 = statuses.map(() => "?").join(", ");
|
|
167028
168355
|
return {
|
|
167029
|
-
sql: `status IN (${
|
|
168356
|
+
sql: `status IN (${placeholders3})`,
|
|
167030
168357
|
params: statuses
|
|
167031
168358
|
};
|
|
167032
168359
|
}
|
|
@@ -167222,19 +168549,6 @@ function getProjectState(db, projectPath) {
|
|
|
167222
168549
|
WHERE project_path = ?`).get(projectPath);
|
|
167223
168550
|
return row ? toProjectState(row) : null;
|
|
167224
168551
|
}
|
|
167225
|
-
function bumpProjectMemoryEpoch(db, projectPath, now = Date.now()) {
|
|
167226
|
-
db.prepare(`INSERT INTO project_state
|
|
167227
|
-
(project_path, project_memory_epoch, project_user_profile_version, updated_at)
|
|
167228
|
-
VALUES (?, 1, 0, ?)
|
|
167229
|
-
ON CONFLICT(project_path) DO UPDATE SET
|
|
167230
|
-
project_memory_epoch = project_memory_epoch + 1,
|
|
167231
|
-
updated_at = excluded.updated_at`).run(projectPath, now);
|
|
167232
|
-
const state = getProjectState(db, projectPath);
|
|
167233
|
-
if (!state) {
|
|
167234
|
-
throw new Error(`Failed to bump project memory epoch for ${projectPath}`);
|
|
167235
|
-
}
|
|
167236
|
-
return state;
|
|
167237
|
-
}
|
|
167238
168552
|
function bumpProjectUserProfileVersion(db, projectPath = GLOBAL_USER_PROFILE_PROJECT_PATH, now = Date.now()) {
|
|
167239
168553
|
db.prepare(`INSERT INTO project_state
|
|
167240
168554
|
(project_path, project_memory_epoch, project_user_profile_version, updated_at)
|
|
@@ -167268,8 +168582,8 @@ function getSourceContents(db, sessionId, tagIds) {
|
|
|
167268
168582
|
if (tagIds.length === 0) {
|
|
167269
168583
|
return new Map;
|
|
167270
168584
|
}
|
|
167271
|
-
const
|
|
167272
|
-
const rows = db.prepare(`SELECT tag_id, content FROM source_contents WHERE session_id = ? AND tag_id IN (${
|
|
168585
|
+
const placeholders3 = tagIds.map(() => "?").join(", ");
|
|
168586
|
+
const rows = db.prepare(`SELECT tag_id, content FROM source_contents WHERE session_id = ? AND tag_id IN (${placeholders3})`).all(sessionId, ...tagIds).filter(isSourceContentRow);
|
|
167273
168587
|
const sources = new Map;
|
|
167274
168588
|
for (const row of rows) {
|
|
167275
168589
|
sources.set(row.tag_id, row.content);
|
|
@@ -167559,8 +168873,8 @@ function getTagsByNumbers(db, sessionId, tagNumbers) {
|
|
|
167559
168873
|
}
|
|
167560
168874
|
return all;
|
|
167561
168875
|
}
|
|
167562
|
-
const
|
|
167563
|
-
const rows = db.prepare(`SELECT ${TAG_SELECT_COLUMNS} FROM tags WHERE session_id = ? AND tag_number IN (${
|
|
168876
|
+
const placeholders3 = tagNumbers.map(() => "?").join(",");
|
|
168877
|
+
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);
|
|
167564
168878
|
return rows.map(toTagEntry);
|
|
167565
168879
|
}
|
|
167566
168880
|
var getToolTagNumberByOwnerStatements = new WeakMap;
|
|
@@ -167750,6 +169064,23 @@ function createCtxMemoryTool(deps) {
|
|
|
167750
169064
|
}
|
|
167751
169065
|
const projectIdentity = resolveProjectIdentity(ctx.cwd);
|
|
167752
169066
|
await deps.ensureProjectRegistered?.(ctx.cwd, deps.db);
|
|
169067
|
+
const workspaceIdentitySet = resolveWorkspaceIdentitySet(deps.db, projectIdentity);
|
|
169068
|
+
const expandedWorkspace = expandWorkspaceIdentitySetWithAliases(deps.db, workspaceIdentitySet.identities);
|
|
169069
|
+
const workspaceVisibleIdentities = workspaceIdentitySet.identities.length > 1 ? expandedWorkspace.expandedIdentities : workspaceIdentitySet.identities;
|
|
169070
|
+
const targetIdentityForStoredPath = (rawProjectPath) => workspaceIdentitySet.identities.length > 1 ? resolveStoredPathWorkspaceIdentity(rawProjectPath, workspaceIdentitySet.identities, expandedWorkspace.canonicalIdentityByStoredPath) ?? normalizeStoredProjectPath(rawProjectPath) : normalizeStoredProjectPath(rawProjectPath);
|
|
169071
|
+
const toolShareCategories = workspaceIdentitySet.identities.length > 1 ? resolveWorkspaceShareCategories(deps.db, projectIdentity) : null;
|
|
169072
|
+
const memoryVisibleToTool = (memory2) => {
|
|
169073
|
+
if (workspaceIdentitySet.identities.length <= 1) {
|
|
169074
|
+
return storedPathBelongsToIdentity(memory2.projectPath, projectIdentity);
|
|
169075
|
+
}
|
|
169076
|
+
if (!storedPathBelongsToWorkspace(memory2.projectPath, workspaceIdentitySet.identities, workspaceVisibleIdentities, expandedWorkspace.canonicalIdentityByStoredPath)) {
|
|
169077
|
+
return false;
|
|
169078
|
+
}
|
|
169079
|
+
const isOwn = targetIdentityForStoredPath(memory2.projectPath) === projectIdentity;
|
|
169080
|
+
if (isOwn)
|
|
169081
|
+
return true;
|
|
169082
|
+
return toolShareCategories === null || toolShareCategories.includes(memory2.category);
|
|
169083
|
+
};
|
|
167753
169084
|
const snapshot = getProjectEmbeddingSnapshot(projectIdentity);
|
|
167754
169085
|
if (snapshot ? !snapshot.features.memoryEnabled : deps.memoryEnabled === false) {
|
|
167755
169086
|
return err2("Cross-session memory is disabled for this project.");
|
|
@@ -167796,28 +169127,34 @@ function createCtxMemoryTool(deps) {
|
|
|
167796
169127
|
return err2("Error: 'content' is required when action is 'update'.");
|
|
167797
169128
|
}
|
|
167798
169129
|
const memory2 = getMemoryById(deps.db, updateId);
|
|
167799
|
-
if (!memory2 || !
|
|
169130
|
+
if (!memory2 || !memoryVisibleToTool(memory2)) {
|
|
167800
169131
|
return err2(`Error: Memory with ID ${updateId} was not found.`);
|
|
167801
169132
|
}
|
|
167802
169133
|
if (!dreamerAllowed && !isPrimaryMutableMemory(memory2)) {
|
|
167803
169134
|
return err2(inactiveMemoryError(updateId, "updating"));
|
|
167804
169135
|
}
|
|
167805
169136
|
const normalizedHash = computeNormalizedHash(content);
|
|
167806
|
-
const
|
|
169137
|
+
const targetIdentity = targetIdentityForStoredPath(memory2.projectPath);
|
|
169138
|
+
const duplicate = getMemoryByHash(deps.db, targetIdentity, memory2.category, normalizedHash);
|
|
167807
169139
|
if (duplicate && duplicate.id !== memory2.id) {
|
|
167808
169140
|
return err2(`Error: Memory content already exists as ID ${duplicate.id}; merge or archive duplicates instead.`);
|
|
167809
169141
|
}
|
|
167810
169142
|
deps.db.transaction(() => {
|
|
167811
169143
|
updateMemoryContent(deps.db, memory2.id, content, normalizedHash);
|
|
167812
169144
|
queueMemoryMutation(deps.db, {
|
|
167813
|
-
projectPath:
|
|
169145
|
+
projectPath: targetIdentity,
|
|
167814
169146
|
mutationType: "update",
|
|
167815
169147
|
targetMemoryId: memory2.id,
|
|
167816
169148
|
category: memory2.category,
|
|
167817
169149
|
newContent: content
|
|
167818
169150
|
});
|
|
167819
169151
|
})();
|
|
167820
|
-
queueEmbedding({
|
|
169152
|
+
queueEmbedding({
|
|
169153
|
+
deps,
|
|
169154
|
+
projectIdentity: targetIdentity,
|
|
169155
|
+
memoryId: memory2.id,
|
|
169156
|
+
content
|
|
169157
|
+
});
|
|
167821
169158
|
return ok2(`Updated memory [ID: ${memory2.id}] in ${memory2.category}.`);
|
|
167822
169159
|
}
|
|
167823
169160
|
if (params.action === "merge") {
|
|
@@ -167837,7 +169174,7 @@ function createCtxMemoryTool(deps) {
|
|
|
167837
169174
|
return err2("Error: One or more source memories were not found.");
|
|
167838
169175
|
}
|
|
167839
169176
|
if (!dreamerAllowed) {
|
|
167840
|
-
const foreign = sourceMemories.find((memory2) => !
|
|
169177
|
+
const foreign = sourceMemories.find((memory2) => !memoryVisibleToTool(memory2));
|
|
167841
169178
|
if (foreign) {
|
|
167842
169179
|
return err2(`Error: Memory with ID ${foreign.id} was not found.`);
|
|
167843
169180
|
}
|
|
@@ -167926,26 +169263,36 @@ function createCtxMemoryTool(deps) {
|
|
|
167926
169263
|
return ok2(`Merged memories [${ids.join(", ")}] into canonical memory [ID: ${canonicalMemory.id}] in ${category}; superseded [${supersededIds.join(", ")}].`);
|
|
167927
169264
|
}
|
|
167928
169265
|
if (params.action === "archive") {
|
|
167929
|
-
const
|
|
167930
|
-
if (!
|
|
169266
|
+
const rawArchiveIds = params.ids;
|
|
169267
|
+
if (!rawArchiveIds || rawArchiveIds.length === 0 || !rawArchiveIds.every(Number.isInteger)) {
|
|
167931
169268
|
return err2("Error: 'ids' must contain at least one integer memory ID when action is 'archive'.");
|
|
167932
169269
|
}
|
|
169270
|
+
const archiveIds = [...new Set(rawArchiveIds)];
|
|
167933
169271
|
for (const memoryId of archiveIds) {
|
|
167934
169272
|
const memory2 = getMemoryById(deps.db, memoryId);
|
|
167935
|
-
if (!memory2 || !
|
|
169273
|
+
if (!memory2 || !memoryVisibleToTool(memory2)) {
|
|
167936
169274
|
return err2(`Error: Memory with ID ${memoryId} was not found.`);
|
|
167937
169275
|
}
|
|
167938
169276
|
if (!dreamerAllowed && !isPrimaryMutableMemory(memory2)) {
|
|
167939
169277
|
return err2(inactiveMemoryError(memoryId, "archiving"));
|
|
167940
169278
|
}
|
|
167941
169279
|
}
|
|
169280
|
+
const targets = archiveIds.map((memoryId) => {
|
|
169281
|
+
const memory2 = getMemoryById(deps.db, memoryId);
|
|
169282
|
+
if (!memory2)
|
|
169283
|
+
throw new Error(`validated memory ${memoryId} disappeared`);
|
|
169284
|
+
return {
|
|
169285
|
+
memoryId,
|
|
169286
|
+
projectIdentity: targetIdentityForStoredPath(memory2.projectPath)
|
|
169287
|
+
};
|
|
169288
|
+
});
|
|
167942
169289
|
deps.db.transaction(() => {
|
|
167943
|
-
for (const
|
|
167944
|
-
archiveMemory(deps.db, memoryId, params.reason);
|
|
169290
|
+
for (const target2 of targets) {
|
|
169291
|
+
archiveMemory(deps.db, target2.memoryId, params.reason);
|
|
167945
169292
|
queueMemoryMutation(deps.db, {
|
|
167946
|
-
projectPath: projectIdentity,
|
|
169293
|
+
projectPath: target2.projectIdentity,
|
|
167947
169294
|
mutationType: "archive",
|
|
167948
|
-
targetMemoryId: memoryId
|
|
169295
|
+
targetMemoryId: target2.memoryId
|
|
167949
169296
|
});
|
|
167950
169297
|
}
|
|
167951
169298
|
})();
|
|
@@ -168917,6 +170264,37 @@ function previewText(text) {
|
|
|
168917
170264
|
}
|
|
168918
170265
|
return `${normalized.slice(0, RESULT_PREVIEW_LIMIT - 1).trimEnd()}…`;
|
|
168919
170266
|
}
|
|
170267
|
+
function resolveSearchWorkspaceContext(db, projectPath, identitySet) {
|
|
170268
|
+
const resolved = identitySet ?? resolveWorkspaceIdentitySet(db, projectPath);
|
|
170269
|
+
const isWorkspaced = resolved.identities.length > 1;
|
|
170270
|
+
const expanded = expandWorkspaceIdentitySetWithAliases(db, resolved.identities);
|
|
170271
|
+
const expandedIdentities = isWorkspaced ? expanded.expandedIdentities : resolved.identities;
|
|
170272
|
+
const canonicalIdentityByStoredPath = isWorkspaced ? expanded.canonicalIdentityByStoredPath : new Map(resolved.identities.map((identity) => [identity, identity]));
|
|
170273
|
+
const ownIdentities = expandedIdentities.filter((identity) => canonicalIdentityByStoredPath.get(identity) === projectPath);
|
|
170274
|
+
return {
|
|
170275
|
+
identities: resolved.identities,
|
|
170276
|
+
expandedIdentities,
|
|
170277
|
+
ownIdentities,
|
|
170278
|
+
shareCategories: isWorkspaced ? resolveWorkspaceShareCategories(db, projectPath) : null,
|
|
170279
|
+
namesByIdentity: resolved.namesByIdentity,
|
|
170280
|
+
canonicalIdentityByStoredPath,
|
|
170281
|
+
isWorkspaced
|
|
170282
|
+
};
|
|
170283
|
+
}
|
|
170284
|
+
function memoryWorkspaceIdentity(memory2, workspace) {
|
|
170285
|
+
return resolveStoredPathWorkspaceIdentity(memory2.projectPath, workspace.identities, workspace.canonicalIdentityByStoredPath);
|
|
170286
|
+
}
|
|
170287
|
+
function sourceNamesForSearchMemories(args) {
|
|
170288
|
+
if (!args.workspace.isWorkspaced)
|
|
170289
|
+
return;
|
|
170290
|
+
const sourceNames = new Map;
|
|
170291
|
+
for (const memory2 of args.memories) {
|
|
170292
|
+
const source = sourceNameForMemory(memory2.projectPath, args.projectPath, args.workspace.identities, args.workspace.namesByIdentity, args.workspace.canonicalIdentityByStoredPath);
|
|
170293
|
+
if (source)
|
|
170294
|
+
sourceNames.set(memory2.id, source);
|
|
170295
|
+
}
|
|
170296
|
+
return sourceNames.size > 0 ? sourceNames : undefined;
|
|
170297
|
+
}
|
|
168920
170298
|
function getMessageSearchStatement(db) {
|
|
168921
170299
|
let stmt = messageSearchStatements.get(db);
|
|
168922
170300
|
if (!stmt) {
|
|
@@ -168925,6 +170303,30 @@ function getMessageSearchStatement(db) {
|
|
|
168925
170303
|
}
|
|
168926
170304
|
return stmt;
|
|
168927
170305
|
}
|
|
170306
|
+
var ftsRowCountStatements = new WeakMap;
|
|
170307
|
+
var ftsMatchCountStatements = new WeakMap;
|
|
170308
|
+
function getSessionFtsRowCount(db, sessionId) {
|
|
170309
|
+
let stmt = ftsRowCountStatements.get(db);
|
|
170310
|
+
if (!stmt) {
|
|
170311
|
+
stmt = db.prepare("SELECT COUNT(*) AS n FROM message_history_fts WHERE session_id = ?");
|
|
170312
|
+
ftsRowCountStatements.set(db, stmt);
|
|
170313
|
+
}
|
|
170314
|
+
const row = stmt.get(sessionId);
|
|
170315
|
+
return typeof row?.n === "number" ? row.n : 0;
|
|
170316
|
+
}
|
|
170317
|
+
function countSessionFtsMatches(db, sessionId, ftsQuery) {
|
|
170318
|
+
let stmt = ftsMatchCountStatements.get(db);
|
|
170319
|
+
if (!stmt) {
|
|
170320
|
+
stmt = db.prepare("SELECT COUNT(*) AS n FROM message_history_fts WHERE session_id = ? AND message_history_fts MATCH ?");
|
|
170321
|
+
ftsMatchCountStatements.set(db, stmt);
|
|
170322
|
+
}
|
|
170323
|
+
try {
|
|
170324
|
+
const row = stmt.get(sessionId, ftsQuery);
|
|
170325
|
+
return typeof row?.n === "number" ? row.n : 0;
|
|
170326
|
+
} catch {
|
|
170327
|
+
return 0;
|
|
170328
|
+
}
|
|
170329
|
+
}
|
|
168928
170330
|
function getMessageOrdinal(value) {
|
|
168929
170331
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
168930
170332
|
return value;
|
|
@@ -168940,25 +170342,63 @@ async function getSemanticScores(args) {
|
|
|
168940
170342
|
if (!args.queryEmbedding || args.memories.length === 0) {
|
|
168941
170343
|
return semanticScores;
|
|
168942
170344
|
}
|
|
168943
|
-
|
|
168944
|
-
|
|
168945
|
-
|
|
168946
|
-
|
|
168947
|
-
|
|
168948
|
-
|
|
168949
|
-
|
|
170345
|
+
if (!args.workspace?.isWorkspaced) {
|
|
170346
|
+
const cachedEmbeddings = getProjectEmbeddings(args.db, args.projectPath);
|
|
170347
|
+
const embeddings = await ensureMemoryEmbeddings({
|
|
170348
|
+
db: args.db,
|
|
170349
|
+
projectIdentity: args.projectPath,
|
|
170350
|
+
memories: args.memories,
|
|
170351
|
+
existingEmbeddings: cachedEmbeddings
|
|
170352
|
+
});
|
|
170353
|
+
for (const memory2 of args.memories) {
|
|
170354
|
+
const memoryEmbedding = embeddings.get(memory2.id);
|
|
170355
|
+
if (!memoryEmbedding) {
|
|
170356
|
+
continue;
|
|
170357
|
+
}
|
|
170358
|
+
semanticScores.set(memory2.id, normalizeCosineScore(cosineSimilarity(args.queryEmbedding, memoryEmbedding.embedding)));
|
|
170359
|
+
}
|
|
170360
|
+
return semanticScores;
|
|
170361
|
+
}
|
|
170362
|
+
if (!args.queryModelId || args.queryModelId === "off") {
|
|
170363
|
+
return semanticScores;
|
|
170364
|
+
}
|
|
170365
|
+
const workspace = args.workspace;
|
|
170366
|
+
const memoriesByIdentity = new Map;
|
|
168950
170367
|
for (const memory2 of args.memories) {
|
|
168951
|
-
const
|
|
168952
|
-
if (!
|
|
170368
|
+
const identity = memoryWorkspaceIdentity(memory2, workspace);
|
|
170369
|
+
if (!identity)
|
|
170370
|
+
continue;
|
|
170371
|
+
const list = memoriesByIdentity.get(identity) ?? [];
|
|
170372
|
+
list.push(memory2);
|
|
170373
|
+
memoriesByIdentity.set(identity, list);
|
|
170374
|
+
}
|
|
170375
|
+
const ownMemories = memoriesByIdentity.get(args.projectPath) ?? [];
|
|
170376
|
+
if (ownMemories.length > 0) {
|
|
170377
|
+
const ownEmbeddings = getProjectEmbeddings(args.db, args.projectPath);
|
|
170378
|
+
await ensureMemoryEmbeddings({
|
|
170379
|
+
db: args.db,
|
|
170380
|
+
projectIdentity: args.projectPath,
|
|
170381
|
+
memories: ownMemories,
|
|
170382
|
+
existingEmbeddings: ownEmbeddings
|
|
170383
|
+
});
|
|
170384
|
+
}
|
|
170385
|
+
for (const identity of workspace.identities) {
|
|
170386
|
+
const memberMemories = memoriesByIdentity.get(identity) ?? [];
|
|
170387
|
+
if (memberMemories.length === 0)
|
|
168953
170388
|
continue;
|
|
170389
|
+
const cachedEmbeddings = getProjectEmbeddings(args.db, identity);
|
|
170390
|
+
for (const memory2 of memberMemories) {
|
|
170391
|
+
const memoryEmbedding = cachedEmbeddings.get(memory2.id);
|
|
170392
|
+
if (!memoryEmbedding || memoryEmbedding.modelId !== args.queryModelId)
|
|
170393
|
+
continue;
|
|
170394
|
+
semanticScores.set(memory2.id, normalizeCosineScore(cosineSimilarity(args.queryEmbedding, memoryEmbedding.embedding)));
|
|
168954
170395
|
}
|
|
168955
|
-
semanticScores.set(memory2.id, normalizeCosineScore(cosineSimilarity(args.queryEmbedding, memoryEmbedding)));
|
|
168956
170396
|
}
|
|
168957
170397
|
return semanticScores;
|
|
168958
170398
|
}
|
|
168959
170399
|
function getFtsMatches(args) {
|
|
168960
170400
|
try {
|
|
168961
|
-
return searchMemoriesFTS(args.db, args.projectPath, args.query, args.limit);
|
|
170401
|
+
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);
|
|
168962
170402
|
} catch (error51) {
|
|
168963
170403
|
log(`[search] FTS query failed for "${args.query}": ${error51 instanceof Error ? error51.message : String(error51)}`);
|
|
168964
170404
|
return [];
|
|
@@ -168972,8 +170412,11 @@ function selectSemanticCandidates(args) {
|
|
|
168972
170412
|
return args.memories;
|
|
168973
170413
|
}
|
|
168974
170414
|
const candidateIds = new Set(args.ftsMatches.map((memory2) => memory2.id));
|
|
168975
|
-
const
|
|
168976
|
-
|
|
170415
|
+
const embeddingProjects = args.workspace?.isWorkspaced ? args.workspace.identities : [args.projectPath];
|
|
170416
|
+
for (const projectPath of embeddingProjects) {
|
|
170417
|
+
const cachedEmbeddings = peekProjectEmbeddings(projectPath);
|
|
170418
|
+
if (!cachedEmbeddings)
|
|
170419
|
+
continue;
|
|
168977
170420
|
for (const memoryId of cachedEmbeddings.keys()) {
|
|
168978
170421
|
candidateIds.add(memoryId);
|
|
168979
170422
|
}
|
|
@@ -169015,7 +170458,8 @@ function mergeMemoryResults(args) {
|
|
|
169015
170458
|
score,
|
|
169016
170459
|
memoryId: memory2.id,
|
|
169017
170460
|
category: memory2.category,
|
|
169018
|
-
matchType
|
|
170461
|
+
matchType,
|
|
170462
|
+
sourceName: args.sourceNameByMemoryId?.get(memory2.id)
|
|
169019
170463
|
});
|
|
169020
170464
|
}
|
|
169021
170465
|
return results.sort((left, right) => {
|
|
@@ -169029,7 +170473,7 @@ async function searchMemories(args) {
|
|
|
169029
170473
|
if (!args.memoryEnabled) {
|
|
169030
170474
|
return [];
|
|
169031
170475
|
}
|
|
169032
|
-
const memories = getMemoriesByProject(args.db, args.projectPath);
|
|
170476
|
+
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);
|
|
169033
170477
|
if (memories.length === 0) {
|
|
169034
170478
|
return [];
|
|
169035
170479
|
}
|
|
@@ -169037,26 +170481,43 @@ async function searchMemories(args) {
|
|
|
169037
170481
|
db: args.db,
|
|
169038
170482
|
projectPath: args.projectPath,
|
|
169039
170483
|
query: args.query,
|
|
169040
|
-
limit: FTS_SEMANTIC_CANDIDATE_LIMIT
|
|
170484
|
+
limit: FTS_SEMANTIC_CANDIDATE_LIMIT,
|
|
170485
|
+
workspace: args.workspace
|
|
169041
170486
|
});
|
|
169042
170487
|
const ftsScores = getFtsScores(ftsMatches);
|
|
169043
170488
|
const semanticCandidates = selectSemanticCandidates({
|
|
169044
170489
|
memories,
|
|
169045
170490
|
projectPath: args.projectPath,
|
|
169046
|
-
ftsMatches
|
|
170491
|
+
ftsMatches,
|
|
170492
|
+
workspace: args.workspace
|
|
169047
170493
|
});
|
|
169048
170494
|
const semanticScores = await getSemanticScores({
|
|
169049
170495
|
db: args.db,
|
|
169050
170496
|
projectPath: args.projectPath,
|
|
169051
170497
|
memories: semanticCandidates,
|
|
169052
|
-
queryEmbedding: args.queryEmbedding
|
|
170498
|
+
queryEmbedding: args.queryEmbedding,
|
|
170499
|
+
queryModelId: args.queryModelId,
|
|
170500
|
+
workspace: args.workspace
|
|
169053
170501
|
});
|
|
169054
170502
|
return mergeMemoryResults({
|
|
169055
170503
|
memories,
|
|
169056
170504
|
semanticScores,
|
|
169057
170505
|
ftsScores,
|
|
169058
170506
|
limit: args.limit,
|
|
169059
|
-
visibleMemoryIds: args.visibleMemoryIds
|
|
170507
|
+
visibleMemoryIds: args.visibleMemoryIds,
|
|
170508
|
+
sourceNameByMemoryId: sourceNamesForSearchMemories({
|
|
170509
|
+
memories,
|
|
170510
|
+
projectPath: args.projectPath,
|
|
170511
|
+
workspace: args.workspace ?? {
|
|
170512
|
+
identities: [args.projectPath],
|
|
170513
|
+
expandedIdentities: [args.projectPath],
|
|
170514
|
+
namesByIdentity: new Map,
|
|
170515
|
+
canonicalIdentityByStoredPath: new Map([[args.projectPath, args.projectPath]]),
|
|
170516
|
+
ownIdentities: [args.projectPath],
|
|
170517
|
+
shareCategories: null,
|
|
170518
|
+
isWorkspaced: false
|
|
170519
|
+
}
|
|
170520
|
+
})
|
|
169060
170521
|
});
|
|
169061
170522
|
}
|
|
169062
170523
|
function linearDecayScore(rank, total) {
|
|
@@ -169087,7 +170548,13 @@ function runMessageFtsQuery(db, sessionId, ftsQuery, fetchLimit, cutoff) {
|
|
|
169087
170548
|
return result;
|
|
169088
170549
|
}
|
|
169089
170550
|
var RRF_K = 60;
|
|
169090
|
-
var
|
|
170551
|
+
var VERBATIM_RANK_BONUS = 1 / RRF_K;
|
|
170552
|
+
var IDF_FALLOFF = 100;
|
|
170553
|
+
function probeDiscriminationWeight(df, corpusSize) {
|
|
170554
|
+
if (corpusSize <= 0 || df <= 0)
|
|
170555
|
+
return 1;
|
|
170556
|
+
return 1 / (1 + IDF_FALLOFF * df / corpusSize);
|
|
170557
|
+
}
|
|
169091
170558
|
function searchMessages(args) {
|
|
169092
170559
|
const cutoff = args.maxOrdinal != null && args.maxOrdinal >= 0 ? args.maxOrdinal : null;
|
|
169093
170560
|
const fetchLimit = args.maxOrdinal != null && args.maxOrdinal >= 0 ? args.limit * 3 : args.limit;
|
|
@@ -169104,20 +170571,31 @@ function searchMessages(args) {
|
|
|
169104
170571
|
role: row.role
|
|
169105
170572
|
}));
|
|
169106
170573
|
}
|
|
170574
|
+
const corpusSize = getSessionFtsRowCount(args.db, args.sessionId);
|
|
169107
170575
|
const queryLists = [];
|
|
169108
170576
|
if (baseQuery.length > 0) {
|
|
169109
|
-
queryLists.push(
|
|
170577
|
+
queryLists.push({
|
|
170578
|
+
rows: runMessageFtsQuery(args.db, args.sessionId, baseQuery, fetchLimit, cutoff),
|
|
170579
|
+
weight: 1
|
|
170580
|
+
});
|
|
169110
170581
|
}
|
|
170582
|
+
const probeWeights = new Map;
|
|
169111
170583
|
for (const probe of probes) {
|
|
169112
170584
|
const probeQuery = sanitizeFtsQuery(probe);
|
|
169113
170585
|
if (probeQuery.length === 0)
|
|
169114
170586
|
continue;
|
|
169115
|
-
|
|
170587
|
+
const df = countSessionFtsMatches(args.db, args.sessionId, probeQuery);
|
|
170588
|
+
const weight = probeDiscriminationWeight(df, corpusSize);
|
|
170589
|
+
probeWeights.set(probe, weight);
|
|
170590
|
+
queryLists.push({
|
|
170591
|
+
rows: runMessageFtsQuery(args.db, args.sessionId, probeQuery, fetchLimit, cutoff),
|
|
170592
|
+
weight
|
|
170593
|
+
});
|
|
169116
170594
|
}
|
|
169117
170595
|
const fused = new Map;
|
|
169118
170596
|
for (const list of queryLists) {
|
|
169119
|
-
list.forEach((row, rank) => {
|
|
169120
|
-
const rrf =
|
|
170597
|
+
list.rows.forEach((row, rank) => {
|
|
170598
|
+
const rrf = list.weight / (RRF_K + rank);
|
|
169121
170599
|
const existing = fused.get(row.messageId);
|
|
169122
170600
|
if (existing) {
|
|
169123
170601
|
existing.score += rrf;
|
|
@@ -169127,26 +170605,107 @@ function searchMessages(args) {
|
|
|
169127
170605
|
});
|
|
169128
170606
|
}
|
|
169129
170607
|
for (const entry of fused.values()) {
|
|
169130
|
-
|
|
169131
|
-
|
|
170608
|
+
let best = 0;
|
|
170609
|
+
for (const probe of probes) {
|
|
170610
|
+
const weight = probeWeights.get(probe) ?? 0;
|
|
170611
|
+
if (weight > best && containsProbeVerbatim(entry.row.content, [probe])) {
|
|
170612
|
+
best = weight;
|
|
170613
|
+
}
|
|
170614
|
+
}
|
|
170615
|
+
if (best > 0) {
|
|
170616
|
+
entry.score += best * VERBATIM_RANK_BONUS;
|
|
169132
170617
|
}
|
|
169133
170618
|
}
|
|
169134
170619
|
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);
|
|
169135
|
-
|
|
169136
|
-
return ranked.map((entry) => ({
|
|
170620
|
+
return ranked.map((entry, rank) => ({
|
|
169137
170621
|
source: "message",
|
|
169138
170622
|
content: previewText(entry.row.content),
|
|
169139
|
-
score:
|
|
170623
|
+
score: linearDecayScore(rank, ranked.length),
|
|
169140
170624
|
messageOrdinal: entry.row.messageOrdinal,
|
|
169141
170625
|
messageId: entry.row.messageId,
|
|
169142
170626
|
role: entry.row.role
|
|
169143
170627
|
}));
|
|
169144
170628
|
}
|
|
170629
|
+
function searchCompartmentChunks(args) {
|
|
170630
|
+
if (!args.queryEmbedding || args.limit <= 0)
|
|
170631
|
+
return [];
|
|
170632
|
+
const cutoff = args.maxOrdinal != null && args.maxOrdinal >= 0 ? args.maxOrdinal : null;
|
|
170633
|
+
const rows = loadCompartmentChunkEmbeddingsForSearch(args.db, args.sessionId, args.projectPath, args.modelId);
|
|
170634
|
+
if (rows.length === 0)
|
|
170635
|
+
return [];
|
|
170636
|
+
const byCompartment = new Map;
|
|
170637
|
+
for (const row of rows) {
|
|
170638
|
+
if (cutoff !== null && row.endOrdinal > cutoff) {
|
|
170639
|
+
continue;
|
|
170640
|
+
}
|
|
170641
|
+
const score = normalizeCosineScore(cosineSimilarity(args.queryEmbedding, row.vector));
|
|
170642
|
+
if (score <= 0)
|
|
170643
|
+
continue;
|
|
170644
|
+
const existing = byCompartment.get(row.compartmentId);
|
|
170645
|
+
if (!existing || score > existing.score) {
|
|
170646
|
+
byCompartment.set(row.compartmentId, { row, score });
|
|
170647
|
+
}
|
|
170648
|
+
}
|
|
170649
|
+
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 }) => ({
|
|
170650
|
+
source: "compartment",
|
|
170651
|
+
content: previewText(row.title),
|
|
170652
|
+
score: score * SINGLE_SOURCE_PENALTY,
|
|
170653
|
+
compartmentId: row.compartmentId,
|
|
170654
|
+
sessionId: row.sessionId,
|
|
170655
|
+
title: row.title,
|
|
170656
|
+
startOrdinal: row.startOrdinal,
|
|
170657
|
+
endOrdinal: row.endOrdinal,
|
|
170658
|
+
matchType: "semantic"
|
|
170659
|
+
}));
|
|
170660
|
+
}
|
|
170661
|
+
function mergeMessageAndCompartmentResults(args) {
|
|
170662
|
+
if (args.compartments.length === 0)
|
|
170663
|
+
return args.messages;
|
|
170664
|
+
if (args.messages.length === 0)
|
|
170665
|
+
return args.compartments;
|
|
170666
|
+
const fused = new Map;
|
|
170667
|
+
const add = (key, result, score, tieOrdinal) => {
|
|
170668
|
+
const existing = fused.get(key);
|
|
170669
|
+
if (existing) {
|
|
170670
|
+
existing.score += score;
|
|
170671
|
+
return existing;
|
|
170672
|
+
}
|
|
170673
|
+
const entry = { result, score, tieOrdinal, snippetScore: -1 };
|
|
170674
|
+
fused.set(key, entry);
|
|
170675
|
+
return entry;
|
|
170676
|
+
};
|
|
170677
|
+
args.compartments.forEach((compartment, rank) => {
|
|
170678
|
+
add(`compartment:${compartment.compartmentId}`, compartment, 1 / (RRF_K + rank), compartment.startOrdinal);
|
|
170679
|
+
});
|
|
170680
|
+
for (const [rank, message] of args.messages.entries()) {
|
|
170681
|
+
const containing = args.compartments.find((compartment) => message.messageOrdinal >= compartment.startOrdinal && message.messageOrdinal <= compartment.endOrdinal);
|
|
170682
|
+
const contribution = 1 / (RRF_K + rank);
|
|
170683
|
+
if (!containing) {
|
|
170684
|
+
add(`message:${message.messageId}`, message, contribution, message.messageOrdinal);
|
|
170685
|
+
continue;
|
|
170686
|
+
}
|
|
170687
|
+
const entry = add(`compartment:${containing.compartmentId}`, containing, contribution, containing.startOrdinal);
|
|
170688
|
+
if (message.score > entry.snippetScore && entry.result.source === "compartment") {
|
|
170689
|
+
entry.snippetScore = message.score;
|
|
170690
|
+
entry.result = {
|
|
170691
|
+
...entry.result,
|
|
170692
|
+
matchType: "hybrid",
|
|
170693
|
+
snippet: message.content
|
|
170694
|
+
};
|
|
170695
|
+
}
|
|
170696
|
+
}
|
|
170697
|
+
const ranked = [...fused.values()].sort((left, right) => right.score !== left.score ? right.score - left.score : left.tieOrdinal - right.tieOrdinal).slice(0, args.limit);
|
|
170698
|
+
return ranked.map((entry, rank) => ({
|
|
170699
|
+
...entry.result,
|
|
170700
|
+
score: linearDecayScore(rank, ranked.length)
|
|
170701
|
+
}));
|
|
170702
|
+
}
|
|
169145
170703
|
function getSourceBoost(result) {
|
|
169146
170704
|
switch (result.source) {
|
|
169147
170705
|
case "memory":
|
|
169148
170706
|
return MEMORY_SOURCE_BOOST;
|
|
169149
170707
|
case "message":
|
|
170708
|
+
case "compartment":
|
|
169150
170709
|
return MESSAGE_SOURCE_BOOST;
|
|
169151
170710
|
case "git_commit":
|
|
169152
170711
|
return GIT_COMMIT_SOURCE_BOOST;
|
|
@@ -169164,6 +170723,9 @@ function compareUnifiedResults(left, right) {
|
|
|
169164
170723
|
if (left.source === "message" && right.source === "message") {
|
|
169165
170724
|
return left.messageOrdinal - right.messageOrdinal;
|
|
169166
170725
|
}
|
|
170726
|
+
if (left.source === "compartment" && right.source === "compartment") {
|
|
170727
|
+
return left.startOrdinal - right.startOrdinal;
|
|
170728
|
+
}
|
|
169167
170729
|
if (left.source === "git_commit" && right.source === "git_commit") {
|
|
169168
170730
|
return right.committedAtMs - left.committedAtMs;
|
|
169169
170731
|
}
|
|
@@ -169214,10 +170776,12 @@ async function unifiedSearch(db, sessionId, projectPath, query, options3 = {}) {
|
|
|
169214
170776
|
const isEmbeddingRuntimeEnabled = options3.isEmbeddingRuntimeEnabled ?? isEmbeddingEnabled;
|
|
169215
170777
|
const gitCommitsEnabled = options3.gitCommitsEnabled ?? false;
|
|
169216
170778
|
const activeSources = resolveSources(options3.sources);
|
|
169217
|
-
const
|
|
170779
|
+
const memoryFeatureEnabled = options3.memoryEnabled ?? true;
|
|
170780
|
+
const runMemory = activeSources.has("memory") && memoryFeatureEnabled;
|
|
169218
170781
|
const runMessages = activeSources.has("message");
|
|
169219
170782
|
const runGitCommits = activeSources.has("git_commit") && gitCommitsEnabled;
|
|
169220
|
-
const
|
|
170783
|
+
const runCompartmentChunks = runMessages && memoryFeatureEnabled && embeddingEnabled;
|
|
170784
|
+
const needsEmbedding = (runMemory || runGitCommits || runCompartmentChunks) && embeddingEnabled && isEmbeddingRuntimeEnabled();
|
|
169221
170785
|
const queryEmbeddingPromise = needsEmbedding ? embedQuery(trimmedQuery, options3.signal).catch((error51) => {
|
|
169222
170786
|
log(`[search] query embedding failed: ${error51 instanceof Error ? error51.message : String(error51)}`);
|
|
169223
170787
|
return null;
|
|
@@ -169233,6 +170797,24 @@ async function unifiedSearch(db, sessionId, projectPath, query, options3 = {}) {
|
|
|
169233
170797
|
probes: messageProbes
|
|
169234
170798
|
}) : [];
|
|
169235
170799
|
const queryEmbedding = await queryEmbeddingPromise;
|
|
170800
|
+
const workspace = resolveSearchWorkspaceContext(db, projectPath);
|
|
170801
|
+
const embeddingSnapshot = getProjectEmbeddingSnapshot(projectPath);
|
|
170802
|
+
const embeddingModelId = embeddingSnapshot?.modelId;
|
|
170803
|
+
const chunkModelId = embeddingSnapshot?.chunkModelId;
|
|
170804
|
+
const compartmentResults = runCompartmentChunks ? searchCompartmentChunks({
|
|
170805
|
+
db,
|
|
170806
|
+
sessionId,
|
|
170807
|
+
projectPath,
|
|
170808
|
+
queryEmbedding,
|
|
170809
|
+
limit: tierLimit,
|
|
170810
|
+
maxOrdinal: options3.maxMessageOrdinal,
|
|
170811
|
+
modelId: chunkModelId && chunkModelId !== "off" ? chunkModelId : null
|
|
170812
|
+
}) : [];
|
|
170813
|
+
const messageLikeResults = mergeMessageAndCompartmentResults({
|
|
170814
|
+
messages: messageResults,
|
|
170815
|
+
compartments: compartmentResults,
|
|
170816
|
+
limit: tierLimit
|
|
170817
|
+
});
|
|
169236
170818
|
const [memoryResults, gitCommitResults] = await Promise.all([
|
|
169237
170819
|
runMemory ? searchMemories({
|
|
169238
170820
|
db,
|
|
@@ -169241,6 +170823,8 @@ async function unifiedSearch(db, sessionId, projectPath, query, options3 = {}) {
|
|
|
169241
170823
|
limit: tierLimit,
|
|
169242
170824
|
memoryEnabled: true,
|
|
169243
170825
|
queryEmbedding,
|
|
170826
|
+
queryModelId: embeddingModelId && embeddingModelId !== "off" ? embeddingModelId : null,
|
|
170827
|
+
workspace,
|
|
169244
170828
|
visibleMemoryIds: options3.visibleMemoryIds
|
|
169245
170829
|
}) : Promise.resolve([]),
|
|
169246
170830
|
runGitCommits ? Promise.resolve(searchGitCommits({
|
|
@@ -169251,7 +170835,7 @@ async function unifiedSearch(db, sessionId, projectPath, query, options3 = {}) {
|
|
|
169251
170835
|
queryEmbedding
|
|
169252
170836
|
})) : Promise.resolve([])
|
|
169253
170837
|
]);
|
|
169254
|
-
const results = [...memoryResults, ...
|
|
170838
|
+
const results = [...memoryResults, ...messageLikeResults, ...gitCommitResults].sort(compareUnifiedResults).slice(0, limit);
|
|
169255
170839
|
const countRetrievals = options3.countRetrievals ?? true;
|
|
169256
170840
|
if (countRetrievals) {
|
|
169257
170841
|
const memoryIds = results.filter((result) => result.source === "memory").map((result) => result.memoryId);
|
|
@@ -169292,8 +170876,8 @@ function getUserMemoryCandidates(db) {
|
|
|
169292
170876
|
function deleteUserMemoryCandidates(db, ids) {
|
|
169293
170877
|
if (ids.length === 0)
|
|
169294
170878
|
return;
|
|
169295
|
-
const
|
|
169296
|
-
db.prepare(`DELETE FROM user_memory_candidates WHERE id IN (${
|
|
170879
|
+
const placeholders3 = ids.map(() => "?").join(",");
|
|
170880
|
+
db.prepare(`DELETE FROM user_memory_candidates WHERE id IN (${placeholders3})`).run(...ids);
|
|
169297
170881
|
}
|
|
169298
170882
|
function insertUserMemory(db, content, sourceCandidateIds) {
|
|
169299
170883
|
const now = Date.now();
|
|
@@ -169845,26 +171429,40 @@ ${sections.join(`
|
|
|
169845
171429
|
var DEFAULT_MEMORY_BUDGET_TOKENS = 8000;
|
|
169846
171430
|
var MEMORY_BLOCK_WRAPPER_TOKENS = 6;
|
|
169847
171431
|
var DEFAULT_USER_PROFILE_BUDGET_TOKENS = 4000;
|
|
171432
|
+
function memoryCanonicalIdentity(memory2, workspace) {
|
|
171433
|
+
return resolveStoredPathWorkspaceIdentity(memory2.projectPath, workspace.identities, workspace.canonicalIdentityByStoredPath);
|
|
171434
|
+
}
|
|
171435
|
+
function memorySelectionOrder(left, right) {
|
|
171436
|
+
if (left.status === "permanent" && right.status !== "permanent")
|
|
171437
|
+
return -1;
|
|
171438
|
+
if (right.status === "permanent" && left.status !== "permanent")
|
|
171439
|
+
return 1;
|
|
171440
|
+
const leftImportance = left.importance ?? Number.NEGATIVE_INFINITY;
|
|
171441
|
+
const rightImportance = right.importance ?? Number.NEGATIVE_INFINITY;
|
|
171442
|
+
const importanceDiff = rightImportance - leftImportance;
|
|
171443
|
+
if (importanceDiff !== 0)
|
|
171444
|
+
return importanceDiff;
|
|
171445
|
+
return left.id - right.id;
|
|
171446
|
+
}
|
|
171447
|
+
function memoryRenderOrder(left, right) {
|
|
171448
|
+
const aPriority = MEMORY_CATEGORY_ORDER_PRIORITY[left.category] ?? MEMORY_CATEGORY_ORDER_UNKNOWN;
|
|
171449
|
+
const bPriority = MEMORY_CATEGORY_ORDER_PRIORITY[right.category] ?? MEMORY_CATEGORY_ORDER_UNKNOWN;
|
|
171450
|
+
const categoryDiff = aPriority - bPriority;
|
|
171451
|
+
if (categoryDiff !== 0)
|
|
171452
|
+
return categoryDiff;
|
|
171453
|
+
return left.id - right.id;
|
|
171454
|
+
}
|
|
169848
171455
|
var maxCompartmentSeqStatements = new WeakMap;
|
|
169849
171456
|
var maxMemoryIdStatements = new WeakMap;
|
|
169850
171457
|
var legacyCompartmentCountStatements = new WeakMap;
|
|
169851
171458
|
var m0CompartmentStatements = new WeakMap;
|
|
169852
171459
|
var newCompartmentStatements = new WeakMap;
|
|
169853
|
-
function trimMemoriesToBudgetV2(sessionId, memories, budgetTokens) {
|
|
169854
|
-
const selectionOrder = [...memories].sort(
|
|
169855
|
-
if (a.status === "permanent" && b.status !== "permanent")
|
|
169856
|
-
return -1;
|
|
169857
|
-
if (b.status === "permanent" && a.status !== "permanent")
|
|
169858
|
-
return 1;
|
|
169859
|
-
const importanceDiff = (b.importance ?? 50) - (a.importance ?? 50);
|
|
169860
|
-
if (importanceDiff !== 0)
|
|
169861
|
-
return importanceDiff;
|
|
169862
|
-
return a.id - b.id;
|
|
169863
|
-
});
|
|
171460
|
+
function trimMemoriesToBudgetV2(sessionId, memories, budgetTokens, renderOptions = {}) {
|
|
171461
|
+
const selectionOrder = [...memories].sort(memorySelectionOrder);
|
|
169864
171462
|
const selected = [];
|
|
169865
171463
|
let usedTokens = MEMORY_BLOCK_WRAPPER_TOKENS;
|
|
169866
171464
|
for (const memory2 of selectionOrder) {
|
|
169867
|
-
const memoryTokens = estimateTokens(renderMemoryLineV2(memory2));
|
|
171465
|
+
const memoryTokens = estimateTokens(renderMemoryLineV2(memory2, renderOptions.sourceNameByMemoryId?.get(memory2.id)));
|
|
169868
171466
|
if (usedTokens + memoryTokens > budgetTokens)
|
|
169869
171467
|
continue;
|
|
169870
171468
|
selected.push(memory2);
|
|
@@ -169873,16 +171471,70 @@ function trimMemoriesToBudgetV2(sessionId, memories, budgetTokens) {
|
|
|
169873
171471
|
if (selected.length < memories.length) {
|
|
169874
171472
|
sessionLog(sessionId, `v2 trimmed memories from ${memories.length} to ${selected.length} to fit injection budget of ${budgetTokens} tokens`);
|
|
169875
171473
|
}
|
|
169876
|
-
const renderOrder = [...selected].sort(
|
|
169877
|
-
const aPriority = MEMORY_CATEGORY_ORDER_PRIORITY[a.category] ?? MEMORY_CATEGORY_ORDER_UNKNOWN;
|
|
169878
|
-
const bPriority = MEMORY_CATEGORY_ORDER_PRIORITY[b.category] ?? MEMORY_CATEGORY_ORDER_UNKNOWN;
|
|
169879
|
-
const categoryDiff = aPriority - bPriority;
|
|
169880
|
-
if (categoryDiff !== 0)
|
|
169881
|
-
return categoryDiff;
|
|
169882
|
-
return a.id - b.id;
|
|
169883
|
-
});
|
|
171474
|
+
const renderOrder = [...selected].sort(memoryRenderOrder);
|
|
169884
171475
|
return { selected, renderOrder };
|
|
169885
171476
|
}
|
|
171477
|
+
function trimWorkspaceMemoriesToBudgetV2(sessionId, memories, budgetTokens, workspace, renderOptions = {}) {
|
|
171478
|
+
if (!workspace.isWorkspaced) {
|
|
171479
|
+
return trimMemoriesToBudgetV2(sessionId, memories, budgetTokens, renderOptions);
|
|
171480
|
+
}
|
|
171481
|
+
const selected = [];
|
|
171482
|
+
const selectedIds = new Set;
|
|
171483
|
+
let usedTokens = MEMORY_BLOCK_WRAPPER_TOKENS;
|
|
171484
|
+
const tokenCost = (memory2) => estimateTokens(renderMemoryLineV2(memory2, renderOptions.sourceNameByMemoryId?.get(memory2.id)));
|
|
171485
|
+
const trySelect = (memory2) => {
|
|
171486
|
+
if (selectedIds.has(memory2.id))
|
|
171487
|
+
return false;
|
|
171488
|
+
const tokens = tokenCost(memory2);
|
|
171489
|
+
if (usedTokens + tokens > budgetTokens)
|
|
171490
|
+
return false;
|
|
171491
|
+
selected.push(memory2);
|
|
171492
|
+
selectedIds.add(memory2.id);
|
|
171493
|
+
usedTokens += tokens;
|
|
171494
|
+
return true;
|
|
171495
|
+
};
|
|
171496
|
+
for (const memory2 of memories.filter((candidate) => candidate.status === "permanent").sort(memorySelectionOrder)) {
|
|
171497
|
+
trySelect(memory2);
|
|
171498
|
+
}
|
|
171499
|
+
const remainingAfterPermanent = Math.max(0, budgetTokens - usedTokens);
|
|
171500
|
+
const floorTokens = remainingAfterPermanent / Math.max(1, workspace.identities.length);
|
|
171501
|
+
const byIdentity = new Map;
|
|
171502
|
+
for (const memory2 of memories) {
|
|
171503
|
+
if (memory2.status === "permanent")
|
|
171504
|
+
continue;
|
|
171505
|
+
const identity = memoryCanonicalIdentity(memory2, workspace);
|
|
171506
|
+
if (!identity)
|
|
171507
|
+
continue;
|
|
171508
|
+
const list = byIdentity.get(identity) ?? [];
|
|
171509
|
+
list.push(memory2);
|
|
171510
|
+
byIdentity.set(identity, list);
|
|
171511
|
+
}
|
|
171512
|
+
for (const identity of workspace.identities) {
|
|
171513
|
+
let memberTokens = 0;
|
|
171514
|
+
const candidates2 = (byIdentity.get(identity) ?? []).sort(memorySelectionOrder);
|
|
171515
|
+
for (const memory2 of candidates2) {
|
|
171516
|
+
if (selectedIds.has(memory2.id))
|
|
171517
|
+
continue;
|
|
171518
|
+
const tokens = tokenCost(memory2);
|
|
171519
|
+
if (memberTokens + tokens > floorTokens)
|
|
171520
|
+
continue;
|
|
171521
|
+
if (usedTokens + tokens > budgetTokens)
|
|
171522
|
+
continue;
|
|
171523
|
+
selected.push(memory2);
|
|
171524
|
+
selectedIds.add(memory2.id);
|
|
171525
|
+
usedTokens += tokens;
|
|
171526
|
+
memberTokens += tokens;
|
|
171527
|
+
}
|
|
171528
|
+
}
|
|
171529
|
+
const remaining = memories.filter((memory2) => !selectedIds.has(memory2.id)).sort(memorySelectionOrder);
|
|
171530
|
+
for (const memory2 of remaining) {
|
|
171531
|
+
trySelect(memory2);
|
|
171532
|
+
}
|
|
171533
|
+
if (selected.length < memories.length) {
|
|
171534
|
+
sessionLog(sessionId, `v2 trimmed memories from ${memories.length} to ${selected.length} to fit injection budget of ${budgetTokens} tokens`);
|
|
171535
|
+
}
|
|
171536
|
+
return { selected, renderOrder: [...selected].sort(memoryRenderOrder) };
|
|
171537
|
+
}
|
|
169886
171538
|
function trimUserMemoriesToBudget(memories, budgetTokens) {
|
|
169887
171539
|
const selected = [];
|
|
169888
171540
|
let usedTokens = 0;
|
|
@@ -169895,15 +171547,16 @@ function trimUserMemoriesToBudget(memories, budgetTokens) {
|
|
|
169895
171547
|
}
|
|
169896
171548
|
return selected;
|
|
169897
171549
|
}
|
|
169898
|
-
function renderMemoryLineV2(memory2) {
|
|
169899
|
-
|
|
171550
|
+
function renderMemoryLineV2(memory2, sourceName) {
|
|
171551
|
+
const sourceAttr = sourceName ? ` source="${escapeXmlAttr(sourceName)}"` : "";
|
|
171552
|
+
return ` <memory id="${memory2.id}" category="${escapeXmlAttr(memory2.category)}"${sourceAttr} importance="${memory2.importance ?? 50}">${escapeXmlContent(memory2.content)}</memory>`;
|
|
169900
171553
|
}
|
|
169901
|
-
function renderMemoryBlockV2(memories, wrapper = "project-memory") {
|
|
171554
|
+
function renderMemoryBlockV2(memories, wrapper = "project-memory", renderOptions = {}) {
|
|
169902
171555
|
if (memories.length === 0)
|
|
169903
171556
|
return "";
|
|
169904
171557
|
const lines = [`<${wrapper}>`];
|
|
169905
171558
|
for (const memory2 of memories) {
|
|
169906
|
-
lines.push(renderMemoryLineV2(memory2));
|
|
171559
|
+
lines.push(renderMemoryLineV2(memory2, renderOptions.sourceNameByMemoryId?.get(memory2.id)));
|
|
169907
171560
|
}
|
|
169908
171561
|
lines.push(`</${wrapper}>`);
|
|
169909
171562
|
return lines.join(`
|
|
@@ -169968,8 +171621,9 @@ function formatAge(committedAtMs) {
|
|
|
169968
171621
|
}
|
|
169969
171622
|
function formatResult(result, index) {
|
|
169970
171623
|
if (result.source === "memory") {
|
|
171624
|
+
const source = result.sourceName ? ` source=${result.sourceName}` : "";
|
|
169971
171625
|
return [
|
|
169972
|
-
`[${index}] [memory] score=${result.score.toFixed(2)} id=${result.memoryId} category=${result.category} match=${result.matchType}`,
|
|
171626
|
+
`[${index}] [memory] score=${result.score.toFixed(2)} id=${result.memoryId} category=${result.category}${source} match=${result.matchType}`,
|
|
169973
171627
|
result.content
|
|
169974
171628
|
].join(`
|
|
169975
171629
|
`);
|
|
@@ -169979,6 +171633,13 @@ function formatResult(result, index) {
|
|
|
169979
171633
|
`[${index}] [git_commit] score=${result.score.toFixed(2)} sha=${result.shortSha} ${formatAge(result.committedAtMs)} match=${result.matchType}`,
|
|
169980
171634
|
result.content
|
|
169981
171635
|
].join(`
|
|
171636
|
+
`);
|
|
171637
|
+
}
|
|
171638
|
+
if (result.source === "compartment") {
|
|
171639
|
+
return [
|
|
171640
|
+
`[${index}] [message] score=${result.score.toFixed(2)} compartment_id=${result.compartmentId} range=${result.startOrdinal}-${result.endOrdinal} match=${result.matchType} title=${result.title}`,
|
|
171641
|
+
result.snippet ? `Snippet: ${result.snippet}` : result.content
|
|
171642
|
+
].join(`
|
|
169982
171643
|
`);
|
|
169983
171644
|
}
|
|
169984
171645
|
const expandStart = Math.max(1, result.messageOrdinal - 3);
|
|
@@ -169994,7 +171655,7 @@ function formatSearchResults(query, results) {
|
|
|
169994
171655
|
return `No results found for "${query}" across memories, git commits, or message history.`;
|
|
169995
171656
|
}
|
|
169996
171657
|
const bodyParts = results.map((result, index) => formatResult(result, index + 1));
|
|
169997
|
-
if (results.some((result) => result.source === "message")) {
|
|
171658
|
+
if (results.some((result) => result.source === "message" || result.source === "compartment")) {
|
|
169998
171659
|
bodyParts.push("Use ctx_expand(start, end) with the range from any message result above to read the full conversation context.");
|
|
169999
171660
|
}
|
|
170000
171661
|
const body = bodyParts.join(`
|