memorix 1.9.6 → 1.9.7
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/CHANGELOG.md +11 -0
- package/dist/cli/index.js +98 -86
- package/dist/index.js +98 -86
- package/dist/maintenance-runner.js +96 -84
- package/dist/media-audio-runner.js +96 -84
- package/dist/media-video-runner.js +96 -84
- package/dist/memcode-runtime/CHANGELOG.md +11 -0
- package/dist/sdk.js +97 -85
- package/dist/vector-backfill-runner.js +96 -84
- package/docs/DEVELOPMENT.md +1 -1
- package/docs/KNOWN_ISSUES_AND_ROADMAP.md +1 -1
- package/docs/README.md +1 -1
- package/package.json +1 -1
- package/plugins/antigravity/memorix/plugin.json +1 -1
- package/plugins/claude/memorix/.claude-plugin/plugin.json +1 -1
- package/plugins/codebuddy/memorix/.codebuddy-plugin/plugin.json +1 -1
- package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
- package/plugins/copilot/memorix/plugin.json +1 -1
- package/plugins/gemini/memorix/gemini-extension.json +1 -1
- package/plugins/hermes/memorix/plugin.yaml +1 -1
- package/plugins/omp/memorix/package.json +1 -1
- package/plugins/openclaw/memorix/.codex-plugin/plugin.json +1 -1
- package/plugins/pi/memorix/package.json +1 -1
- package/server.json +2 -2
- package/src/store/orama-store.ts +16 -1
- package/src/store/sqlite-fts.ts +38 -16
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,17 @@ All notable changes to this project will be documented in this file.
|
|
|
6
6
|
|
|
7
7
|
No unreleased changes.
|
|
8
8
|
|
|
9
|
+
## [1.9.7] - 2026-09-28
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
- **SQLite FTS5 startup resilience** -- transient `SQLITE_BUSY` contention during
|
|
13
|
+
lexical-index initialization is retried before FTS5 is marked unavailable.
|
|
14
|
+
- **Large-store search fallback** -- runtime embedding failure, provider
|
|
15
|
+
disappearance, and vector-dimension mismatch now all fall back to the durable
|
|
16
|
+
SQLite lexical index instead of returning a silent empty result.
|
|
17
|
+
- **Release metadata** -- synchronized the npm, MCP Registry, and packaged
|
|
18
|
+
integration metadata for the 1.9.7 patch release.
|
|
19
|
+
|
|
9
20
|
## [1.9.6] - 2026-09-22
|
|
10
21
|
|
|
11
22
|
### Added
|
package/dist/cli/index.js
CHANGED
|
@@ -3232,7 +3232,7 @@ ${import_picocolors.default.gray(m3)} ${s4}
|
|
|
3232
3232
|
|
|
3233
3233
|
// src/cli/version.ts
|
|
3234
3234
|
function getCliVersion() {
|
|
3235
|
-
return true ? "1.9.
|
|
3235
|
+
return true ? "1.9.7" : pkg.version;
|
|
3236
3236
|
}
|
|
3237
3237
|
var init_version = __esm({
|
|
3238
3238
|
"src/cli/version.ts"() {
|
|
@@ -9349,6 +9349,74 @@ var init_launch_root = __esm({
|
|
|
9349
9349
|
}
|
|
9350
9350
|
});
|
|
9351
9351
|
|
|
9352
|
+
// src/store/sqlite-reliability.ts
|
|
9353
|
+
function isTransientSqliteError(error40) {
|
|
9354
|
+
const code = error40?.code;
|
|
9355
|
+
if (typeof code === "string") {
|
|
9356
|
+
return /^SQLITE_BUSY(?:_|$)/i.test(code);
|
|
9357
|
+
}
|
|
9358
|
+
return error40 instanceof Error && /\bdatabase is locked\b|\bdatabase is busy\b/i.test(error40.message);
|
|
9359
|
+
}
|
|
9360
|
+
function isSqliteUnavailableError(error40) {
|
|
9361
|
+
return error40 instanceof SqliteUnavailableError || error40 instanceof Error && /SQLite is not available|better-sqlite3.*failed|node:sqlite.*failed|bun:sqlite.*failed/i.test(error40.message);
|
|
9362
|
+
}
|
|
9363
|
+
function degradedReadError(storeName) {
|
|
9364
|
+
return new Error(`[memorix] Cannot read ${storeName}: SQLite backend unavailable (memory is not loaded)`);
|
|
9365
|
+
}
|
|
9366
|
+
function degradedWriteError(storeName) {
|
|
9367
|
+
return new Error(`[memorix] Cannot write ${storeName}: SQLite backend unavailable (degraded mode; read-only)`);
|
|
9368
|
+
}
|
|
9369
|
+
function formatSqliteFailure(error40) {
|
|
9370
|
+
return error40 instanceof Error && error40.message ? error40.message : String(error40);
|
|
9371
|
+
}
|
|
9372
|
+
function delay(ms2) {
|
|
9373
|
+
return new Promise((resolve9) => setTimeout(resolve9, ms2));
|
|
9374
|
+
}
|
|
9375
|
+
async function retrySqliteBusy(operation, label) {
|
|
9376
|
+
for (let attempt = 0; ; attempt++) {
|
|
9377
|
+
try {
|
|
9378
|
+
return await operation();
|
|
9379
|
+
} catch (error40) {
|
|
9380
|
+
if (!isTransientSqliteError(error40) || attempt >= SQLITE_BUSY_RETRY_DELAYS_MS.length) throw error40;
|
|
9381
|
+
const waitMs = SQLITE_BUSY_RETRY_DELAYS_MS[attempt];
|
|
9382
|
+
console.error(
|
|
9383
|
+
`[memorix] ${label} SQLite busy (attempt ${attempt + 1}/${SQLITE_BUSY_RETRY_DELAYS_MS.length + 1}), retrying in ${waitMs}ms`
|
|
9384
|
+
);
|
|
9385
|
+
await delay(waitMs);
|
|
9386
|
+
}
|
|
9387
|
+
}
|
|
9388
|
+
}
|
|
9389
|
+
async function initializeSqliteStore(factory, dataDir, label) {
|
|
9390
|
+
try {
|
|
9391
|
+
loadBetterSqlite3();
|
|
9392
|
+
} catch (error40) {
|
|
9393
|
+
throw new SqliteUnavailableError(error40);
|
|
9394
|
+
}
|
|
9395
|
+
return retrySqliteBusy(async () => {
|
|
9396
|
+
const store2 = factory();
|
|
9397
|
+
await store2.init(dataDir);
|
|
9398
|
+
return store2;
|
|
9399
|
+
}, label);
|
|
9400
|
+
}
|
|
9401
|
+
var SQLITE_BUSY_RETRY_DELAYS_MS, SqliteUnavailableError;
|
|
9402
|
+
var init_sqlite_reliability = __esm({
|
|
9403
|
+
"src/store/sqlite-reliability.ts"() {
|
|
9404
|
+
"use strict";
|
|
9405
|
+
init_esm_shims();
|
|
9406
|
+
init_sqlite_db();
|
|
9407
|
+
SQLITE_BUSY_RETRY_DELAYS_MS = [50, 150, 400, 1e3, 2500];
|
|
9408
|
+
SqliteUnavailableError = class extends Error {
|
|
9409
|
+
code = "MEMORIX_SQLITE_UNAVAILABLE";
|
|
9410
|
+
constructor(cause) {
|
|
9411
|
+
super(
|
|
9412
|
+
cause instanceof Error && cause.message ? cause.message : "[memorix] SQLite is unavailable (install a supported SQLite runtime)"
|
|
9413
|
+
);
|
|
9414
|
+
this.name = "SqliteUnavailableError";
|
|
9415
|
+
}
|
|
9416
|
+
};
|
|
9417
|
+
}
|
|
9418
|
+
});
|
|
9419
|
+
|
|
9352
9420
|
// src/store/sqlite-fts.ts
|
|
9353
9421
|
function normalizeFtsToken(token) {
|
|
9354
9422
|
return token.replace(/"/g, '""');
|
|
@@ -9362,22 +9430,27 @@ function buildFtsQuery(query) {
|
|
|
9362
9430
|
function initializeObservationLexicalIndex(db2) {
|
|
9363
9431
|
const known = ftsAvailability.get(db2);
|
|
9364
9432
|
if (known !== void 0) return known;
|
|
9365
|
-
|
|
9366
|
-
|
|
9367
|
-
|
|
9368
|
-
|
|
9369
|
-
|
|
9370
|
-
db2.
|
|
9433
|
+
let lastError;
|
|
9434
|
+
for (let attempt = 1; attempt <= FTS_INIT_MAX_ATTEMPTS; attempt++) {
|
|
9435
|
+
try {
|
|
9436
|
+
db2.exec(CREATE_OBSERVATION_FTS);
|
|
9437
|
+
const observationCount = Number(db2.prepare("SELECT COUNT(*) AS count FROM observations").get()?.count ?? 0);
|
|
9438
|
+
const indexedCount = Number(db2.prepare(`SELECT COUNT(*) AS count FROM ${FTS_TABLE}`).get()?.count ?? 0);
|
|
9439
|
+
if (observationCount !== indexedCount) {
|
|
9440
|
+
db2.exec(`INSERT INTO ${FTS_TABLE}(${FTS_TABLE}) VALUES ('rebuild')`);
|
|
9441
|
+
}
|
|
9442
|
+
ftsAvailability.set(db2, true);
|
|
9443
|
+
return true;
|
|
9444
|
+
} catch (error40) {
|
|
9445
|
+
lastError = error40;
|
|
9446
|
+
if (!isTransientSqliteError(error40) || attempt === FTS_INIT_MAX_ATTEMPTS) break;
|
|
9371
9447
|
}
|
|
9372
|
-
ftsAvailability.set(db2, true);
|
|
9373
|
-
return true;
|
|
9374
|
-
} catch (error40) {
|
|
9375
|
-
ftsAvailability.set(db2, false);
|
|
9376
|
-
console.warn(
|
|
9377
|
-
`[memorix] SQLite FTS5 unavailable; using the existing search fallback: ${error40 instanceof Error ? error40.message : String(error40)}`
|
|
9378
|
-
);
|
|
9379
|
-
return false;
|
|
9380
9448
|
}
|
|
9449
|
+
ftsAvailability.set(db2, false);
|
|
9450
|
+
console.warn(
|
|
9451
|
+
`[memorix] SQLite FTS5 unavailable; using the existing search fallback: ${lastError instanceof Error ? lastError.message : String(lastError)}`
|
|
9452
|
+
);
|
|
9453
|
+
return false;
|
|
9381
9454
|
}
|
|
9382
9455
|
function isObservationLexicalIndexEnabled(db2) {
|
|
9383
9456
|
return ftsAvailability.get(db2) === true;
|
|
@@ -9431,11 +9504,12 @@ function searchObservationLexically(db2, options) {
|
|
|
9431
9504
|
score: Math.max(0, Number(row.lexical_score ?? 0))
|
|
9432
9505
|
}));
|
|
9433
9506
|
}
|
|
9434
|
-
var ftsAvailability, FTS_TABLE, CREATE_OBSERVATION_FTS;
|
|
9507
|
+
var ftsAvailability, FTS_TABLE, CREATE_OBSERVATION_FTS, FTS_INIT_MAX_ATTEMPTS;
|
|
9435
9508
|
var init_sqlite_fts = __esm({
|
|
9436
9509
|
"src/store/sqlite-fts.ts"() {
|
|
9437
9510
|
"use strict";
|
|
9438
9511
|
init_esm_shims();
|
|
9512
|
+
init_sqlite_reliability();
|
|
9439
9513
|
ftsAvailability = /* @__PURE__ */ new WeakMap();
|
|
9440
9514
|
FTS_TABLE = "observations_fts";
|
|
9441
9515
|
CREATE_OBSERVATION_FTS = `
|
|
@@ -9469,6 +9543,7 @@ CREATE TRIGGER IF NOT EXISTS observations_fts_au AFTER UPDATE ON observations BE
|
|
|
9469
9543
|
VALUES (new.id, new.entityName, new.title, new.narrative, new.facts, new.filesModified, new.concepts, new.attachments);
|
|
9470
9544
|
END;
|
|
9471
9545
|
`;
|
|
9546
|
+
FTS_INIT_MAX_ATTEMPTS = 3;
|
|
9472
9547
|
}
|
|
9473
9548
|
});
|
|
9474
9549
|
|
|
@@ -10724,74 +10799,6 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
|
|
|
10724
10799
|
}
|
|
10725
10800
|
});
|
|
10726
10801
|
|
|
10727
|
-
// src/store/sqlite-reliability.ts
|
|
10728
|
-
function isTransientSqliteError(error40) {
|
|
10729
|
-
const code = error40?.code;
|
|
10730
|
-
if (typeof code === "string") {
|
|
10731
|
-
return /^SQLITE_BUSY(?:_|$)/i.test(code);
|
|
10732
|
-
}
|
|
10733
|
-
return error40 instanceof Error && /\bdatabase is locked\b|\bdatabase is busy\b/i.test(error40.message);
|
|
10734
|
-
}
|
|
10735
|
-
function isSqliteUnavailableError(error40) {
|
|
10736
|
-
return error40 instanceof SqliteUnavailableError || error40 instanceof Error && /SQLite is not available|better-sqlite3.*failed|node:sqlite.*failed|bun:sqlite.*failed/i.test(error40.message);
|
|
10737
|
-
}
|
|
10738
|
-
function degradedReadError(storeName) {
|
|
10739
|
-
return new Error(`[memorix] Cannot read ${storeName}: SQLite backend unavailable (memory is not loaded)`);
|
|
10740
|
-
}
|
|
10741
|
-
function degradedWriteError(storeName) {
|
|
10742
|
-
return new Error(`[memorix] Cannot write ${storeName}: SQLite backend unavailable (degraded mode; read-only)`);
|
|
10743
|
-
}
|
|
10744
|
-
function formatSqliteFailure(error40) {
|
|
10745
|
-
return error40 instanceof Error && error40.message ? error40.message : String(error40);
|
|
10746
|
-
}
|
|
10747
|
-
function delay(ms2) {
|
|
10748
|
-
return new Promise((resolve9) => setTimeout(resolve9, ms2));
|
|
10749
|
-
}
|
|
10750
|
-
async function retrySqliteBusy(operation, label) {
|
|
10751
|
-
for (let attempt = 0; ; attempt++) {
|
|
10752
|
-
try {
|
|
10753
|
-
return await operation();
|
|
10754
|
-
} catch (error40) {
|
|
10755
|
-
if (!isTransientSqliteError(error40) || attempt >= SQLITE_BUSY_RETRY_DELAYS_MS.length) throw error40;
|
|
10756
|
-
const waitMs = SQLITE_BUSY_RETRY_DELAYS_MS[attempt];
|
|
10757
|
-
console.error(
|
|
10758
|
-
`[memorix] ${label} SQLite busy (attempt ${attempt + 1}/${SQLITE_BUSY_RETRY_DELAYS_MS.length + 1}), retrying in ${waitMs}ms`
|
|
10759
|
-
);
|
|
10760
|
-
await delay(waitMs);
|
|
10761
|
-
}
|
|
10762
|
-
}
|
|
10763
|
-
}
|
|
10764
|
-
async function initializeSqliteStore(factory, dataDir, label) {
|
|
10765
|
-
try {
|
|
10766
|
-
loadBetterSqlite3();
|
|
10767
|
-
} catch (error40) {
|
|
10768
|
-
throw new SqliteUnavailableError(error40);
|
|
10769
|
-
}
|
|
10770
|
-
return retrySqliteBusy(async () => {
|
|
10771
|
-
const store2 = factory();
|
|
10772
|
-
await store2.init(dataDir);
|
|
10773
|
-
return store2;
|
|
10774
|
-
}, label);
|
|
10775
|
-
}
|
|
10776
|
-
var SQLITE_BUSY_RETRY_DELAYS_MS, SqliteUnavailableError;
|
|
10777
|
-
var init_sqlite_reliability = __esm({
|
|
10778
|
-
"src/store/sqlite-reliability.ts"() {
|
|
10779
|
-
"use strict";
|
|
10780
|
-
init_esm_shims();
|
|
10781
|
-
init_sqlite_db();
|
|
10782
|
-
SQLITE_BUSY_RETRY_DELAYS_MS = [50, 150, 400, 1e3, 2500];
|
|
10783
|
-
SqliteUnavailableError = class extends Error {
|
|
10784
|
-
code = "MEMORIX_SQLITE_UNAVAILABLE";
|
|
10785
|
-
constructor(cause) {
|
|
10786
|
-
super(
|
|
10787
|
-
cause instanceof Error && cause.message ? cause.message : "[memorix] SQLite is unavailable (install a supported SQLite runtime)"
|
|
10788
|
-
);
|
|
10789
|
-
this.name = "SqliteUnavailableError";
|
|
10790
|
-
}
|
|
10791
|
-
};
|
|
10792
|
-
}
|
|
10793
|
-
});
|
|
10794
|
-
|
|
10795
10802
|
// src/store/file-lock.ts
|
|
10796
10803
|
import { promises as fs2 } from "fs";
|
|
10797
10804
|
import { randomUUID } from "crypto";
|
|
@@ -188224,6 +188231,7 @@ async function searchObservations(options) {
|
|
|
188224
188231
|
};
|
|
188225
188232
|
let queryVector = null;
|
|
188226
188233
|
let embeddingProviderForSearch = null;
|
|
188234
|
+
let embeddingUnavailable = false;
|
|
188227
188235
|
if (quality !== "fast" && embeddingEnabled && hasQuery && tier !== "fast") {
|
|
188228
188236
|
try {
|
|
188229
188237
|
const provider2 = await getEmbeddingProvider();
|
|
@@ -188231,6 +188239,7 @@ async function searchObservations(options) {
|
|
|
188231
188239
|
embeddingProviderForSearch = provider2;
|
|
188232
188240
|
const activeVectorDimensions = getVectorDimensions();
|
|
188233
188241
|
if (activeVectorDimensions !== null && provider2.dimensions !== activeVectorDimensions) {
|
|
188242
|
+
embeddingUnavailable = true;
|
|
188234
188243
|
rememberSearchMode(
|
|
188235
188244
|
modeKey,
|
|
188236
188245
|
`fulltext (embedding dimension mismatch: provider ${provider2.dimensions}d vs index ${activeVectorDimensions}d)`
|
|
@@ -188270,8 +188279,11 @@ async function searchObservations(options) {
|
|
|
188270
188279
|
hybridWeights: isCJKHeavy ? { text: 0.2, vector: 0.8 } : { text: 0.6, vector: 0.4 }
|
|
188271
188280
|
};
|
|
188272
188281
|
}
|
|
188282
|
+
} else {
|
|
188283
|
+
embeddingUnavailable = true;
|
|
188273
188284
|
}
|
|
188274
188285
|
} catch (error40) {
|
|
188286
|
+
embeddingUnavailable = true;
|
|
188275
188287
|
rememberSearchMode(modeKey, "fulltext (embedding unavailable)");
|
|
188276
188288
|
console.error("[memorix] Embedding failed or timed out, falling back to fulltext search");
|
|
188277
188289
|
}
|
|
@@ -188279,7 +188291,7 @@ async function searchObservations(options) {
|
|
|
188279
188291
|
mark("preSearch");
|
|
188280
188292
|
let results;
|
|
188281
188293
|
const persistentCorpusEligible = await shouldUsePersistentSearch(projectIds, options.projectId);
|
|
188282
|
-
const canUsePersistentLexical = persistentCorpusEligible && Boolean(hasQuery) && !queryVector && (quality === "fast" || tier === "fast" || !embeddingEnabled);
|
|
188294
|
+
const canUsePersistentLexical = persistentCorpusEligible && Boolean(hasQuery) && !queryVector && (quality === "fast" || tier === "fast" || !embeddingEnabled || embeddingUnavailable);
|
|
188283
188295
|
let persistent = persistentCorpusEligible && queryVector && embeddingProviderForSearch ? await searchPersistentSemantically(options, projectIds, requestLimit, queryVector, embeddingProviderForSearch) : canUsePersistentLexical ? await searchPersistentLexically(options, projectIds, requestLimit) : null;
|
|
188284
188296
|
const durableStore = getOptionalObservationStore();
|
|
188285
188297
|
if (!persistent && queryVector && durableStore?.hasLexicalIndex?.() && count2(database) === 0) {
|
|
@@ -209007,7 +209019,7 @@ var init_tool_profile = __esm({
|
|
|
209007
209019
|
function getMcpServerInfo() {
|
|
209008
209020
|
return {
|
|
209009
209021
|
name: "memorix",
|
|
209010
|
-
version: true ? "1.9.
|
|
209022
|
+
version: true ? "1.9.7" : "1.0.1"
|
|
209011
209023
|
};
|
|
209012
209024
|
}
|
|
209013
209025
|
function createMcpDiscoverResult(capabilities, serverInfo) {
|
package/dist/index.js
CHANGED
|
@@ -286,6 +286,74 @@ var init_launch_root = __esm({
|
|
|
286
286
|
}
|
|
287
287
|
});
|
|
288
288
|
|
|
289
|
+
// src/store/sqlite-reliability.ts
|
|
290
|
+
function isTransientSqliteError(error) {
|
|
291
|
+
const code = error?.code;
|
|
292
|
+
if (typeof code === "string") {
|
|
293
|
+
return /^SQLITE_BUSY(?:_|$)/i.test(code);
|
|
294
|
+
}
|
|
295
|
+
return error instanceof Error && /\bdatabase is locked\b|\bdatabase is busy\b/i.test(error.message);
|
|
296
|
+
}
|
|
297
|
+
function isSqliteUnavailableError(error) {
|
|
298
|
+
return error instanceof SqliteUnavailableError || error instanceof Error && /SQLite is not available|better-sqlite3.*failed|node:sqlite.*failed|bun:sqlite.*failed/i.test(error.message);
|
|
299
|
+
}
|
|
300
|
+
function degradedReadError(storeName) {
|
|
301
|
+
return new Error(`[memorix] Cannot read ${storeName}: SQLite backend unavailable (memory is not loaded)`);
|
|
302
|
+
}
|
|
303
|
+
function degradedWriteError(storeName) {
|
|
304
|
+
return new Error(`[memorix] Cannot write ${storeName}: SQLite backend unavailable (degraded mode; read-only)`);
|
|
305
|
+
}
|
|
306
|
+
function formatSqliteFailure(error) {
|
|
307
|
+
return error instanceof Error && error.message ? error.message : String(error);
|
|
308
|
+
}
|
|
309
|
+
function delay(ms) {
|
|
310
|
+
return new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
311
|
+
}
|
|
312
|
+
async function retrySqliteBusy(operation, label) {
|
|
313
|
+
for (let attempt = 0; ; attempt++) {
|
|
314
|
+
try {
|
|
315
|
+
return await operation();
|
|
316
|
+
} catch (error) {
|
|
317
|
+
if (!isTransientSqliteError(error) || attempt >= SQLITE_BUSY_RETRY_DELAYS_MS.length) throw error;
|
|
318
|
+
const waitMs = SQLITE_BUSY_RETRY_DELAYS_MS[attempt];
|
|
319
|
+
console.error(
|
|
320
|
+
`[memorix] ${label} SQLite busy (attempt ${attempt + 1}/${SQLITE_BUSY_RETRY_DELAYS_MS.length + 1}), retrying in ${waitMs}ms`
|
|
321
|
+
);
|
|
322
|
+
await delay(waitMs);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
async function initializeSqliteStore(factory, dataDir, label) {
|
|
327
|
+
try {
|
|
328
|
+
loadBetterSqlite3();
|
|
329
|
+
} catch (error) {
|
|
330
|
+
throw new SqliteUnavailableError(error);
|
|
331
|
+
}
|
|
332
|
+
return retrySqliteBusy(async () => {
|
|
333
|
+
const store = factory();
|
|
334
|
+
await store.init(dataDir);
|
|
335
|
+
return store;
|
|
336
|
+
}, label);
|
|
337
|
+
}
|
|
338
|
+
var SQLITE_BUSY_RETRY_DELAYS_MS, SqliteUnavailableError;
|
|
339
|
+
var init_sqlite_reliability = __esm({
|
|
340
|
+
"src/store/sqlite-reliability.ts"() {
|
|
341
|
+
"use strict";
|
|
342
|
+
init_esm_shims();
|
|
343
|
+
init_sqlite_db();
|
|
344
|
+
SQLITE_BUSY_RETRY_DELAYS_MS = [50, 150, 400, 1e3, 2500];
|
|
345
|
+
SqliteUnavailableError = class extends Error {
|
|
346
|
+
code = "MEMORIX_SQLITE_UNAVAILABLE";
|
|
347
|
+
constructor(cause) {
|
|
348
|
+
super(
|
|
349
|
+
cause instanceof Error && cause.message ? cause.message : "[memorix] SQLite is unavailable (install a supported SQLite runtime)"
|
|
350
|
+
);
|
|
351
|
+
this.name = "SqliteUnavailableError";
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
|
|
289
357
|
// src/store/sqlite-fts.ts
|
|
290
358
|
function normalizeFtsToken(token) {
|
|
291
359
|
return token.replace(/"/g, '""');
|
|
@@ -299,22 +367,27 @@ function buildFtsQuery(query) {
|
|
|
299
367
|
function initializeObservationLexicalIndex(db2) {
|
|
300
368
|
const known = ftsAvailability.get(db2);
|
|
301
369
|
if (known !== void 0) return known;
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
db2.
|
|
370
|
+
let lastError;
|
|
371
|
+
for (let attempt = 1; attempt <= FTS_INIT_MAX_ATTEMPTS; attempt++) {
|
|
372
|
+
try {
|
|
373
|
+
db2.exec(CREATE_OBSERVATION_FTS);
|
|
374
|
+
const observationCount = Number(db2.prepare("SELECT COUNT(*) AS count FROM observations").get()?.count ?? 0);
|
|
375
|
+
const indexedCount = Number(db2.prepare(`SELECT COUNT(*) AS count FROM ${FTS_TABLE}`).get()?.count ?? 0);
|
|
376
|
+
if (observationCount !== indexedCount) {
|
|
377
|
+
db2.exec(`INSERT INTO ${FTS_TABLE}(${FTS_TABLE}) VALUES ('rebuild')`);
|
|
378
|
+
}
|
|
379
|
+
ftsAvailability.set(db2, true);
|
|
380
|
+
return true;
|
|
381
|
+
} catch (error) {
|
|
382
|
+
lastError = error;
|
|
383
|
+
if (!isTransientSqliteError(error) || attempt === FTS_INIT_MAX_ATTEMPTS) break;
|
|
308
384
|
}
|
|
309
|
-
ftsAvailability.set(db2, true);
|
|
310
|
-
return true;
|
|
311
|
-
} catch (error) {
|
|
312
|
-
ftsAvailability.set(db2, false);
|
|
313
|
-
console.warn(
|
|
314
|
-
`[memorix] SQLite FTS5 unavailable; using the existing search fallback: ${error instanceof Error ? error.message : String(error)}`
|
|
315
|
-
);
|
|
316
|
-
return false;
|
|
317
385
|
}
|
|
386
|
+
ftsAvailability.set(db2, false);
|
|
387
|
+
console.warn(
|
|
388
|
+
`[memorix] SQLite FTS5 unavailable; using the existing search fallback: ${lastError instanceof Error ? lastError.message : String(lastError)}`
|
|
389
|
+
);
|
|
390
|
+
return false;
|
|
318
391
|
}
|
|
319
392
|
function isObservationLexicalIndexEnabled(db2) {
|
|
320
393
|
return ftsAvailability.get(db2) === true;
|
|
@@ -368,11 +441,12 @@ function searchObservationLexically(db2, options) {
|
|
|
368
441
|
score: Math.max(0, Number(row.lexical_score ?? 0))
|
|
369
442
|
}));
|
|
370
443
|
}
|
|
371
|
-
var ftsAvailability, FTS_TABLE, CREATE_OBSERVATION_FTS;
|
|
444
|
+
var ftsAvailability, FTS_TABLE, CREATE_OBSERVATION_FTS, FTS_INIT_MAX_ATTEMPTS;
|
|
372
445
|
var init_sqlite_fts = __esm({
|
|
373
446
|
"src/store/sqlite-fts.ts"() {
|
|
374
447
|
"use strict";
|
|
375
448
|
init_esm_shims();
|
|
449
|
+
init_sqlite_reliability();
|
|
376
450
|
ftsAvailability = /* @__PURE__ */ new WeakMap();
|
|
377
451
|
FTS_TABLE = "observations_fts";
|
|
378
452
|
CREATE_OBSERVATION_FTS = `
|
|
@@ -406,6 +480,7 @@ CREATE TRIGGER IF NOT EXISTS observations_fts_au AFTER UPDATE ON observations BE
|
|
|
406
480
|
VALUES (new.id, new.entityName, new.title, new.narrative, new.facts, new.filesModified, new.concepts, new.attachments);
|
|
407
481
|
END;
|
|
408
482
|
`;
|
|
483
|
+
FTS_INIT_MAX_ATTEMPTS = 3;
|
|
409
484
|
}
|
|
410
485
|
});
|
|
411
486
|
|
|
@@ -2557,74 +2632,6 @@ var init_types = __esm({
|
|
|
2557
2632
|
}
|
|
2558
2633
|
});
|
|
2559
2634
|
|
|
2560
|
-
// src/store/sqlite-reliability.ts
|
|
2561
|
-
function isTransientSqliteError(error) {
|
|
2562
|
-
const code = error?.code;
|
|
2563
|
-
if (typeof code === "string") {
|
|
2564
|
-
return /^SQLITE_BUSY(?:_|$)/i.test(code);
|
|
2565
|
-
}
|
|
2566
|
-
return error instanceof Error && /\bdatabase is locked\b|\bdatabase is busy\b/i.test(error.message);
|
|
2567
|
-
}
|
|
2568
|
-
function isSqliteUnavailableError(error) {
|
|
2569
|
-
return error instanceof SqliteUnavailableError || error instanceof Error && /SQLite is not available|better-sqlite3.*failed|node:sqlite.*failed|bun:sqlite.*failed/i.test(error.message);
|
|
2570
|
-
}
|
|
2571
|
-
function degradedReadError(storeName) {
|
|
2572
|
-
return new Error(`[memorix] Cannot read ${storeName}: SQLite backend unavailable (memory is not loaded)`);
|
|
2573
|
-
}
|
|
2574
|
-
function degradedWriteError(storeName) {
|
|
2575
|
-
return new Error(`[memorix] Cannot write ${storeName}: SQLite backend unavailable (degraded mode; read-only)`);
|
|
2576
|
-
}
|
|
2577
|
-
function formatSqliteFailure(error) {
|
|
2578
|
-
return error instanceof Error && error.message ? error.message : String(error);
|
|
2579
|
-
}
|
|
2580
|
-
function delay(ms) {
|
|
2581
|
-
return new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
2582
|
-
}
|
|
2583
|
-
async function retrySqliteBusy(operation, label) {
|
|
2584
|
-
for (let attempt = 0; ; attempt++) {
|
|
2585
|
-
try {
|
|
2586
|
-
return await operation();
|
|
2587
|
-
} catch (error) {
|
|
2588
|
-
if (!isTransientSqliteError(error) || attempt >= SQLITE_BUSY_RETRY_DELAYS_MS.length) throw error;
|
|
2589
|
-
const waitMs = SQLITE_BUSY_RETRY_DELAYS_MS[attempt];
|
|
2590
|
-
console.error(
|
|
2591
|
-
`[memorix] ${label} SQLite busy (attempt ${attempt + 1}/${SQLITE_BUSY_RETRY_DELAYS_MS.length + 1}), retrying in ${waitMs}ms`
|
|
2592
|
-
);
|
|
2593
|
-
await delay(waitMs);
|
|
2594
|
-
}
|
|
2595
|
-
}
|
|
2596
|
-
}
|
|
2597
|
-
async function initializeSqliteStore(factory, dataDir, label) {
|
|
2598
|
-
try {
|
|
2599
|
-
loadBetterSqlite3();
|
|
2600
|
-
} catch (error) {
|
|
2601
|
-
throw new SqliteUnavailableError(error);
|
|
2602
|
-
}
|
|
2603
|
-
return retrySqliteBusy(async () => {
|
|
2604
|
-
const store = factory();
|
|
2605
|
-
await store.init(dataDir);
|
|
2606
|
-
return store;
|
|
2607
|
-
}, label);
|
|
2608
|
-
}
|
|
2609
|
-
var SQLITE_BUSY_RETRY_DELAYS_MS, SqliteUnavailableError;
|
|
2610
|
-
var init_sqlite_reliability = __esm({
|
|
2611
|
-
"src/store/sqlite-reliability.ts"() {
|
|
2612
|
-
"use strict";
|
|
2613
|
-
init_esm_shims();
|
|
2614
|
-
init_sqlite_db();
|
|
2615
|
-
SQLITE_BUSY_RETRY_DELAYS_MS = [50, 150, 400, 1e3, 2500];
|
|
2616
|
-
SqliteUnavailableError = class extends Error {
|
|
2617
|
-
code = "MEMORIX_SQLITE_UNAVAILABLE";
|
|
2618
|
-
constructor(cause) {
|
|
2619
|
-
super(
|
|
2620
|
-
cause instanceof Error && cause.message ? cause.message : "[memorix] SQLite is unavailable (install a supported SQLite runtime)"
|
|
2621
|
-
);
|
|
2622
|
-
this.name = "SqliteUnavailableError";
|
|
2623
|
-
}
|
|
2624
|
-
};
|
|
2625
|
-
}
|
|
2626
|
-
});
|
|
2627
|
-
|
|
2628
2635
|
// src/store/mini-skill-store.ts
|
|
2629
2636
|
var mini_skill_store_exports = {};
|
|
2630
2637
|
__export(mini_skill_store_exports, {
|
|
@@ -9120,6 +9127,7 @@ async function searchObservations(options) {
|
|
|
9120
9127
|
};
|
|
9121
9128
|
let queryVector = null;
|
|
9122
9129
|
let embeddingProviderForSearch = null;
|
|
9130
|
+
let embeddingUnavailable = false;
|
|
9123
9131
|
if (quality !== "fast" && embeddingEnabled && hasQuery && tier !== "fast") {
|
|
9124
9132
|
try {
|
|
9125
9133
|
const provider2 = await getEmbeddingProvider();
|
|
@@ -9127,6 +9135,7 @@ async function searchObservations(options) {
|
|
|
9127
9135
|
embeddingProviderForSearch = provider2;
|
|
9128
9136
|
const activeVectorDimensions = getVectorDimensions();
|
|
9129
9137
|
if (activeVectorDimensions !== null && provider2.dimensions !== activeVectorDimensions) {
|
|
9138
|
+
embeddingUnavailable = true;
|
|
9130
9139
|
rememberSearchMode(
|
|
9131
9140
|
modeKey,
|
|
9132
9141
|
`fulltext (embedding dimension mismatch: provider ${provider2.dimensions}d vs index ${activeVectorDimensions}d)`
|
|
@@ -9166,8 +9175,11 @@ async function searchObservations(options) {
|
|
|
9166
9175
|
hybridWeights: isCJKHeavy ? { text: 0.2, vector: 0.8 } : { text: 0.6, vector: 0.4 }
|
|
9167
9176
|
};
|
|
9168
9177
|
}
|
|
9178
|
+
} else {
|
|
9179
|
+
embeddingUnavailable = true;
|
|
9169
9180
|
}
|
|
9170
9181
|
} catch (error) {
|
|
9182
|
+
embeddingUnavailable = true;
|
|
9171
9183
|
rememberSearchMode(modeKey, "fulltext (embedding unavailable)");
|
|
9172
9184
|
console.error("[memorix] Embedding failed or timed out, falling back to fulltext search");
|
|
9173
9185
|
}
|
|
@@ -9175,7 +9187,7 @@ async function searchObservations(options) {
|
|
|
9175
9187
|
mark("preSearch");
|
|
9176
9188
|
let results;
|
|
9177
9189
|
const persistentCorpusEligible = await shouldUsePersistentSearch(projectIds, options.projectId);
|
|
9178
|
-
const canUsePersistentLexical = persistentCorpusEligible && Boolean(hasQuery) && !queryVector && (quality === "fast" || tier === "fast" || !embeddingEnabled);
|
|
9190
|
+
const canUsePersistentLexical = persistentCorpusEligible && Boolean(hasQuery) && !queryVector && (quality === "fast" || tier === "fast" || !embeddingEnabled || embeddingUnavailable);
|
|
9179
9191
|
let persistent = persistentCorpusEligible && queryVector && embeddingProviderForSearch ? await searchPersistentSemantically(options, projectIds, requestLimit, queryVector, embeddingProviderForSearch) : canUsePersistentLexical ? await searchPersistentLexically(options, projectIds, requestLimit) : null;
|
|
9180
9192
|
const durableStore = getOptionalObservationStore();
|
|
9181
9193
|
if (!persistent && queryVector && durableStore?.hasLexicalIndex?.() && count(database) === 0) {
|
|
@@ -160092,7 +160104,7 @@ var init_request_context = __esm({
|
|
|
160092
160104
|
function getMcpServerInfo() {
|
|
160093
160105
|
return {
|
|
160094
160106
|
name: "memorix",
|
|
160095
|
-
version: true ? "1.9.
|
|
160107
|
+
version: true ? "1.9.7" : "1.0.1"
|
|
160096
160108
|
};
|
|
160097
160109
|
}
|
|
160098
160110
|
function createMcpDiscoverResult(capabilities, serverInfo) {
|
|
@@ -176430,7 +176442,7 @@ var init_serve_shared = __esm({
|
|
|
176430
176442
|
|
|
176431
176443
|
// src/cli/version.ts
|
|
176432
176444
|
function getCliVersion() {
|
|
176433
|
-
return true ? "1.9.
|
|
176445
|
+
return true ? "1.9.7" : pkg.version;
|
|
176434
176446
|
}
|
|
176435
176447
|
var init_version3 = __esm({
|
|
176436
176448
|
"src/cli/version.ts"() {
|