memorix 1.9.6 → 1.9.8
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 +18 -0
- package/dist/cli/index.js +254 -87
- package/dist/index.js +254 -87
- 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 +18 -0
- package/dist/sdk.js +253 -86
- package/dist/vector-backfill-runner.js +96 -84
- package/docs/DEVELOPMENT.md +1 -1
- package/docs/INTEGRATIONS.md +1 -1
- package/docs/KNOWN_ISSUES_AND_ROADMAP.md +1 -1
- package/docs/README.md +1 -1
- package/docs/SETUP.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/hooks/installers/index.ts +160 -3
- package/src/store/orama-store.ts +16 -1
- package/src/store/sqlite-fts.ts +38 -16
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.8" : "1.0.1"
|
|
160096
160108
|
};
|
|
160097
160109
|
}
|
|
160098
160110
|
function createMcpDiscoverResult(capabilities, serverInfo) {
|
|
@@ -170081,6 +170093,161 @@ export const MemorixPlugin = async ({ project, client, $, directory, worktree })
|
|
|
170081
170093
|
},
|
|
170082
170094
|
};
|
|
170083
170095
|
};
|
|
170096
|
+
|
|
170097
|
+
/**
|
|
170098
|
+
* OpenCode v2 compatibility layer. Keep this beside the v1 export so one
|
|
170099
|
+
* generated file can be loaded by both host generations during migration.
|
|
170100
|
+
*/
|
|
170101
|
+
const v2HookCommand = ${hookCommand};
|
|
170102
|
+
|
|
170103
|
+
function v2ReportHookFailure(eventName, detail) {
|
|
170104
|
+
if (process.env.MEMORIX_HOOK_DEBUG !== '1') return;
|
|
170105
|
+
console.error('[memorix-plugin] v2 hook delivery failed:', eventName, detail);
|
|
170106
|
+
}
|
|
170107
|
+
|
|
170108
|
+
function v2EventData(event) {
|
|
170109
|
+
return event && typeof event.data === 'object' && event.data !== null ? event.data : {};
|
|
170110
|
+
}
|
|
170111
|
+
|
|
170112
|
+
function v2SessionId(event) {
|
|
170113
|
+
const data = v2EventData(event);
|
|
170114
|
+
const value = data.sessionID ?? data.sessionId ?? data.session_id
|
|
170115
|
+
?? event?.sessionID ?? event?.sessionId;
|
|
170116
|
+
return typeof value === 'string' ? value : '';
|
|
170117
|
+
}
|
|
170118
|
+
|
|
170119
|
+
function v2RunHook(payload, directory, sessionId) {
|
|
170120
|
+
const nextPayload = { agent: 'opencode', cwd: directory, ...payload };
|
|
170121
|
+
if (sessionId) nextPayload.session_id = sessionId;
|
|
170122
|
+
const eventName = nextPayload.hook_event_name || 'unknown';
|
|
170123
|
+
try {
|
|
170124
|
+
const result = spawnSync(v2HookCommand, ['hook'], {
|
|
170125
|
+
input: JSON.stringify(nextPayload),
|
|
170126
|
+
timeout: 10_000,
|
|
170127
|
+
encoding: 'utf-8',
|
|
170128
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
170129
|
+
shell: process.platform === 'win32',
|
|
170130
|
+
windowsHide: true,
|
|
170131
|
+
});
|
|
170132
|
+
if (result.status !== 0) {
|
|
170133
|
+
v2ReportHookFailure(eventName, {
|
|
170134
|
+
exit: result.status,
|
|
170135
|
+
stderr: (result.stderr || '').slice(0, 200),
|
|
170136
|
+
error: result.error?.message,
|
|
170137
|
+
});
|
|
170138
|
+
}
|
|
170139
|
+
} catch (error) {
|
|
170140
|
+
v2ReportHookFailure(eventName, error?.message ?? error);
|
|
170141
|
+
}
|
|
170142
|
+
}
|
|
170143
|
+
|
|
170144
|
+
function v2SessionState(states, sessionId) {
|
|
170145
|
+
const key = sessionId || '__unknown__';
|
|
170146
|
+
let state = states.get(key);
|
|
170147
|
+
if (!state) {
|
|
170148
|
+
state = { pendingText: '', lastDeliveredKey: '' };
|
|
170149
|
+
states.set(key, state);
|
|
170150
|
+
}
|
|
170151
|
+
return state;
|
|
170152
|
+
}
|
|
170153
|
+
|
|
170154
|
+
function v2FlushAssistant(state, directory, sessionId) {
|
|
170155
|
+
const text = state.pendingText.trim();
|
|
170156
|
+
if (!text || text === state.lastDeliveredKey) return;
|
|
170157
|
+
v2RunHook({ hook_event_name: 'message.updated', ai_response: text }, directory, sessionId);
|
|
170158
|
+
state.lastDeliveredKey = text;
|
|
170159
|
+
state.pendingText = '';
|
|
170160
|
+
}
|
|
170161
|
+
|
|
170162
|
+
function v2FilePath(file) {
|
|
170163
|
+
if (typeof file === 'string') return file;
|
|
170164
|
+
if (!file || typeof file !== 'object') return '';
|
|
170165
|
+
return file.path ?? file.file ?? file.name ?? '';
|
|
170166
|
+
}
|
|
170167
|
+
|
|
170168
|
+
function v2HandleEvent(event, directory, states) {
|
|
170169
|
+
if (!event || typeof event !== 'object') return;
|
|
170170
|
+
const type = typeof event.type === 'string' ? event.type : '';
|
|
170171
|
+
const data = v2EventData(event);
|
|
170172
|
+
const sessionId = v2SessionId(event);
|
|
170173
|
+
const state = v2SessionState(states, sessionId);
|
|
170174
|
+
const run = (payload) => v2RunHook(payload, directory, sessionId);
|
|
170175
|
+
|
|
170176
|
+
switch (type) {
|
|
170177
|
+
case 'session.created':
|
|
170178
|
+
run({ hook_event_name: 'session.created' });
|
|
170179
|
+
break;
|
|
170180
|
+
case 'session.text.delta':
|
|
170181
|
+
state.pendingText += typeof data.delta === 'string' ? data.delta : (typeof data.text === 'string' ? data.text : '');
|
|
170182
|
+
break;
|
|
170183
|
+
case 'session.message.updated':
|
|
170184
|
+
case 'message.updated':
|
|
170185
|
+
if (data.role === 'assistant' && typeof data.text === 'string') state.pendingText = data.text;
|
|
170186
|
+
break;
|
|
170187
|
+
case 'session.step.ended': {
|
|
170188
|
+
const files = Array.isArray(data.files) ? data.files : [];
|
|
170189
|
+
for (const file of files) {
|
|
170190
|
+
const filePath = v2FilePath(file);
|
|
170191
|
+
if (filePath) run({ hook_event_name: 'file.edited', file_path: filePath });
|
|
170192
|
+
}
|
|
170193
|
+
if (typeof data.text === 'string') state.pendingText += data.text;
|
|
170194
|
+
break;
|
|
170195
|
+
}
|
|
170196
|
+
case 'session.idle':
|
|
170197
|
+
v2FlushAssistant(state, directory, sessionId);
|
|
170198
|
+
run({ hook_event_name: 'session.idle' });
|
|
170199
|
+
break;
|
|
170200
|
+
case 'session.compacted':
|
|
170201
|
+
run({ hook_event_name: 'session.compacted' });
|
|
170202
|
+
break;
|
|
170203
|
+
default:
|
|
170204
|
+
break;
|
|
170205
|
+
}
|
|
170206
|
+
}
|
|
170207
|
+
|
|
170208
|
+
function setup(ctx) {
|
|
170209
|
+
const directory = ctx?.location?.directory || ctx?.location?.worktree || process.cwd();
|
|
170210
|
+
const controller = new AbortController();
|
|
170211
|
+
const states = new Map();
|
|
170212
|
+
|
|
170213
|
+
if (ctx?.tool?.hook) {
|
|
170214
|
+
try {
|
|
170215
|
+
Promise.resolve(ctx.tool.hook('execute.after', async (event) => {
|
|
170216
|
+
const sessionId = v2SessionId(event);
|
|
170217
|
+
const data = event && typeof event.input === 'object' ? event.input : {};
|
|
170218
|
+
const tool = event?.tool ?? event?.name ?? '';
|
|
170219
|
+
v2RunHook({
|
|
170220
|
+
hook_event_name: 'tool.execute.after',
|
|
170221
|
+
tool_name: tool,
|
|
170222
|
+
tool_input: data,
|
|
170223
|
+
}, directory, sessionId);
|
|
170224
|
+
if (tool === 'shell') {
|
|
170225
|
+
v2RunHook({
|
|
170226
|
+
hook_event_name: 'command.executed',
|
|
170227
|
+
command: data.command ?? data.cmd ?? '',
|
|
170228
|
+
}, directory, sessionId);
|
|
170229
|
+
}
|
|
170230
|
+
})).catch((error) => v2ReportHookFailure('execute.after', error?.message ?? error));
|
|
170231
|
+
} catch (error) {
|
|
170232
|
+
v2ReportHookFailure('execute.after', error?.message ?? error);
|
|
170233
|
+
}
|
|
170234
|
+
}
|
|
170235
|
+
|
|
170236
|
+
if (ctx?.event?.subscribe) {
|
|
170237
|
+
void (async () => {
|
|
170238
|
+
try {
|
|
170239
|
+
const stream = ctx.event.subscribe({ signal: controller.signal });
|
|
170240
|
+
for await (const event of stream) v2HandleEvent(event, directory, states);
|
|
170241
|
+
} catch (error) {
|
|
170242
|
+
if (!controller.signal.aborted) v2ReportHookFailure('event.subscribe', error?.message ?? error);
|
|
170243
|
+
}
|
|
170244
|
+
})();
|
|
170245
|
+
}
|
|
170246
|
+
|
|
170247
|
+
return () => controller.abort();
|
|
170248
|
+
}
|
|
170249
|
+
|
|
170250
|
+
export default { id: 'memorix', setup };
|
|
170084
170251
|
`;
|
|
170085
170252
|
}
|
|
170086
170253
|
async function installOpenCodeSkills(projectRoot, global2 = false) {
|
|
@@ -170906,7 +171073,7 @@ var init_installers = __esm({
|
|
|
170906
171073
|
"use strict";
|
|
170907
171074
|
init_esm_shims();
|
|
170908
171075
|
init_official_skills();
|
|
170909
|
-
OPENCODE_PLUGIN_VERSION =
|
|
171076
|
+
OPENCODE_PLUGIN_VERSION = 8;
|
|
170910
171077
|
AGENT_SKILL_DIRS = {
|
|
170911
171078
|
cursor: { project: path34.join(".cursor", "skills"), global: path34.join(".cursor", "skills") },
|
|
170912
171079
|
windsurf: { project: path34.join(".windsurf", "skills"), global: path34.join(".windsurf", "skills") },
|
|
@@ -176430,7 +176597,7 @@ var init_serve_shared = __esm({
|
|
|
176430
176597
|
|
|
176431
176598
|
// src/cli/version.ts
|
|
176432
176599
|
function getCliVersion() {
|
|
176433
|
-
return true ? "1.9.
|
|
176600
|
+
return true ? "1.9.8" : pkg.version;
|
|
176434
176601
|
}
|
|
176435
176602
|
var init_version3 = __esm({
|
|
176436
176603
|
"src/cli/version.ts"() {
|
|
@@ -222,6 +222,74 @@ var init_launch_root = __esm({
|
|
|
222
222
|
}
|
|
223
223
|
});
|
|
224
224
|
|
|
225
|
+
// src/store/sqlite-reliability.ts
|
|
226
|
+
function isTransientSqliteError(error) {
|
|
227
|
+
const code = error?.code;
|
|
228
|
+
if (typeof code === "string") {
|
|
229
|
+
return /^SQLITE_BUSY(?:_|$)/i.test(code);
|
|
230
|
+
}
|
|
231
|
+
return error instanceof Error && /\bdatabase is locked\b|\bdatabase is busy\b/i.test(error.message);
|
|
232
|
+
}
|
|
233
|
+
function isSqliteUnavailableError(error) {
|
|
234
|
+
return error instanceof SqliteUnavailableError || error instanceof Error && /SQLite is not available|better-sqlite3.*failed|node:sqlite.*failed|bun:sqlite.*failed/i.test(error.message);
|
|
235
|
+
}
|
|
236
|
+
function degradedReadError(storeName) {
|
|
237
|
+
return new Error(`[memorix] Cannot read ${storeName}: SQLite backend unavailable (memory is not loaded)`);
|
|
238
|
+
}
|
|
239
|
+
function degradedWriteError(storeName) {
|
|
240
|
+
return new Error(`[memorix] Cannot write ${storeName}: SQLite backend unavailable (degraded mode; read-only)`);
|
|
241
|
+
}
|
|
242
|
+
function formatSqliteFailure(error) {
|
|
243
|
+
return error instanceof Error && error.message ? error.message : String(error);
|
|
244
|
+
}
|
|
245
|
+
function delay(ms) {
|
|
246
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
247
|
+
}
|
|
248
|
+
async function retrySqliteBusy(operation, label) {
|
|
249
|
+
for (let attempt = 0; ; attempt++) {
|
|
250
|
+
try {
|
|
251
|
+
return await operation();
|
|
252
|
+
} catch (error) {
|
|
253
|
+
if (!isTransientSqliteError(error) || attempt >= SQLITE_BUSY_RETRY_DELAYS_MS.length) throw error;
|
|
254
|
+
const waitMs = SQLITE_BUSY_RETRY_DELAYS_MS[attempt];
|
|
255
|
+
console.error(
|
|
256
|
+
`[memorix] ${label} SQLite busy (attempt ${attempt + 1}/${SQLITE_BUSY_RETRY_DELAYS_MS.length + 1}), retrying in ${waitMs}ms`
|
|
257
|
+
);
|
|
258
|
+
await delay(waitMs);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
async function initializeSqliteStore(factory, dataDir, label) {
|
|
263
|
+
try {
|
|
264
|
+
loadBetterSqlite3();
|
|
265
|
+
} catch (error) {
|
|
266
|
+
throw new SqliteUnavailableError(error);
|
|
267
|
+
}
|
|
268
|
+
return retrySqliteBusy(async () => {
|
|
269
|
+
const store = factory();
|
|
270
|
+
await store.init(dataDir);
|
|
271
|
+
return store;
|
|
272
|
+
}, label);
|
|
273
|
+
}
|
|
274
|
+
var SQLITE_BUSY_RETRY_DELAYS_MS, SqliteUnavailableError;
|
|
275
|
+
var init_sqlite_reliability = __esm({
|
|
276
|
+
"src/store/sqlite-reliability.ts"() {
|
|
277
|
+
"use strict";
|
|
278
|
+
init_esm_shims();
|
|
279
|
+
init_sqlite_db();
|
|
280
|
+
SQLITE_BUSY_RETRY_DELAYS_MS = [50, 150, 400, 1e3, 2500];
|
|
281
|
+
SqliteUnavailableError = class extends Error {
|
|
282
|
+
code = "MEMORIX_SQLITE_UNAVAILABLE";
|
|
283
|
+
constructor(cause) {
|
|
284
|
+
super(
|
|
285
|
+
cause instanceof Error && cause.message ? cause.message : "[memorix] SQLite is unavailable (install a supported SQLite runtime)"
|
|
286
|
+
);
|
|
287
|
+
this.name = "SqliteUnavailableError";
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
|
|
225
293
|
// src/store/sqlite-fts.ts
|
|
226
294
|
function normalizeFtsToken(token) {
|
|
227
295
|
return token.replace(/"/g, '""');
|
|
@@ -235,22 +303,27 @@ function buildFtsQuery(query) {
|
|
|
235
303
|
function initializeObservationLexicalIndex(db2) {
|
|
236
304
|
const known = ftsAvailability.get(db2);
|
|
237
305
|
if (known !== void 0) return known;
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
db2.
|
|
306
|
+
let lastError;
|
|
307
|
+
for (let attempt = 1; attempt <= FTS_INIT_MAX_ATTEMPTS; attempt++) {
|
|
308
|
+
try {
|
|
309
|
+
db2.exec(CREATE_OBSERVATION_FTS);
|
|
310
|
+
const observationCount = Number(db2.prepare("SELECT COUNT(*) AS count FROM observations").get()?.count ?? 0);
|
|
311
|
+
const indexedCount = Number(db2.prepare(`SELECT COUNT(*) AS count FROM ${FTS_TABLE}`).get()?.count ?? 0);
|
|
312
|
+
if (observationCount !== indexedCount) {
|
|
313
|
+
db2.exec(`INSERT INTO ${FTS_TABLE}(${FTS_TABLE}) VALUES ('rebuild')`);
|
|
314
|
+
}
|
|
315
|
+
ftsAvailability.set(db2, true);
|
|
316
|
+
return true;
|
|
317
|
+
} catch (error) {
|
|
318
|
+
lastError = error;
|
|
319
|
+
if (!isTransientSqliteError(error) || attempt === FTS_INIT_MAX_ATTEMPTS) break;
|
|
244
320
|
}
|
|
245
|
-
ftsAvailability.set(db2, true);
|
|
246
|
-
return true;
|
|
247
|
-
} catch (error) {
|
|
248
|
-
ftsAvailability.set(db2, false);
|
|
249
|
-
console.warn(
|
|
250
|
-
`[memorix] SQLite FTS5 unavailable; using the existing search fallback: ${error instanceof Error ? error.message : String(error)}`
|
|
251
|
-
);
|
|
252
|
-
return false;
|
|
253
321
|
}
|
|
322
|
+
ftsAvailability.set(db2, false);
|
|
323
|
+
console.warn(
|
|
324
|
+
`[memorix] SQLite FTS5 unavailable; using the existing search fallback: ${lastError instanceof Error ? lastError.message : String(lastError)}`
|
|
325
|
+
);
|
|
326
|
+
return false;
|
|
254
327
|
}
|
|
255
328
|
function isObservationLexicalIndexEnabled(db2) {
|
|
256
329
|
return ftsAvailability.get(db2) === true;
|
|
@@ -304,11 +377,12 @@ function searchObservationLexically(db2, options) {
|
|
|
304
377
|
score: Math.max(0, Number(row.lexical_score ?? 0))
|
|
305
378
|
}));
|
|
306
379
|
}
|
|
307
|
-
var ftsAvailability, FTS_TABLE, CREATE_OBSERVATION_FTS;
|
|
380
|
+
var ftsAvailability, FTS_TABLE, CREATE_OBSERVATION_FTS, FTS_INIT_MAX_ATTEMPTS;
|
|
308
381
|
var init_sqlite_fts = __esm({
|
|
309
382
|
"src/store/sqlite-fts.ts"() {
|
|
310
383
|
"use strict";
|
|
311
384
|
init_esm_shims();
|
|
385
|
+
init_sqlite_reliability();
|
|
312
386
|
ftsAvailability = /* @__PURE__ */ new WeakMap();
|
|
313
387
|
FTS_TABLE = "observations_fts";
|
|
314
388
|
CREATE_OBSERVATION_FTS = `
|
|
@@ -342,6 +416,7 @@ CREATE TRIGGER IF NOT EXISTS observations_fts_au AFTER UPDATE ON observations BE
|
|
|
342
416
|
VALUES (new.id, new.entityName, new.title, new.narrative, new.facts, new.filesModified, new.concepts, new.attachments);
|
|
343
417
|
END;
|
|
344
418
|
`;
|
|
419
|
+
FTS_INIT_MAX_ATTEMPTS = 3;
|
|
345
420
|
}
|
|
346
421
|
});
|
|
347
422
|
|
|
@@ -3061,74 +3136,6 @@ var init_provider = __esm({
|
|
|
3061
3136
|
}
|
|
3062
3137
|
});
|
|
3063
3138
|
|
|
3064
|
-
// src/store/sqlite-reliability.ts
|
|
3065
|
-
function isTransientSqliteError(error) {
|
|
3066
|
-
const code = error?.code;
|
|
3067
|
-
if (typeof code === "string") {
|
|
3068
|
-
return /^SQLITE_BUSY(?:_|$)/i.test(code);
|
|
3069
|
-
}
|
|
3070
|
-
return error instanceof Error && /\bdatabase is locked\b|\bdatabase is busy\b/i.test(error.message);
|
|
3071
|
-
}
|
|
3072
|
-
function isSqliteUnavailableError(error) {
|
|
3073
|
-
return error instanceof SqliteUnavailableError || error instanceof Error && /SQLite is not available|better-sqlite3.*failed|node:sqlite.*failed|bun:sqlite.*failed/i.test(error.message);
|
|
3074
|
-
}
|
|
3075
|
-
function degradedReadError(storeName) {
|
|
3076
|
-
return new Error(`[memorix] Cannot read ${storeName}: SQLite backend unavailable (memory is not loaded)`);
|
|
3077
|
-
}
|
|
3078
|
-
function degradedWriteError(storeName) {
|
|
3079
|
-
return new Error(`[memorix] Cannot write ${storeName}: SQLite backend unavailable (degraded mode; read-only)`);
|
|
3080
|
-
}
|
|
3081
|
-
function formatSqliteFailure(error) {
|
|
3082
|
-
return error instanceof Error && error.message ? error.message : String(error);
|
|
3083
|
-
}
|
|
3084
|
-
function delay(ms) {
|
|
3085
|
-
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
3086
|
-
}
|
|
3087
|
-
async function retrySqliteBusy(operation, label) {
|
|
3088
|
-
for (let attempt = 0; ; attempt++) {
|
|
3089
|
-
try {
|
|
3090
|
-
return await operation();
|
|
3091
|
-
} catch (error) {
|
|
3092
|
-
if (!isTransientSqliteError(error) || attempt >= SQLITE_BUSY_RETRY_DELAYS_MS.length) throw error;
|
|
3093
|
-
const waitMs = SQLITE_BUSY_RETRY_DELAYS_MS[attempt];
|
|
3094
|
-
console.error(
|
|
3095
|
-
`[memorix] ${label} SQLite busy (attempt ${attempt + 1}/${SQLITE_BUSY_RETRY_DELAYS_MS.length + 1}), retrying in ${waitMs}ms`
|
|
3096
|
-
);
|
|
3097
|
-
await delay(waitMs);
|
|
3098
|
-
}
|
|
3099
|
-
}
|
|
3100
|
-
}
|
|
3101
|
-
async function initializeSqliteStore(factory, dataDir, label) {
|
|
3102
|
-
try {
|
|
3103
|
-
loadBetterSqlite3();
|
|
3104
|
-
} catch (error) {
|
|
3105
|
-
throw new SqliteUnavailableError(error);
|
|
3106
|
-
}
|
|
3107
|
-
return retrySqliteBusy(async () => {
|
|
3108
|
-
const store = factory();
|
|
3109
|
-
await store.init(dataDir);
|
|
3110
|
-
return store;
|
|
3111
|
-
}, label);
|
|
3112
|
-
}
|
|
3113
|
-
var SQLITE_BUSY_RETRY_DELAYS_MS, SqliteUnavailableError;
|
|
3114
|
-
var init_sqlite_reliability = __esm({
|
|
3115
|
-
"src/store/sqlite-reliability.ts"() {
|
|
3116
|
-
"use strict";
|
|
3117
|
-
init_esm_shims();
|
|
3118
|
-
init_sqlite_db();
|
|
3119
|
-
SQLITE_BUSY_RETRY_DELAYS_MS = [50, 150, 400, 1e3, 2500];
|
|
3120
|
-
SqliteUnavailableError = class extends Error {
|
|
3121
|
-
code = "MEMORIX_SQLITE_UNAVAILABLE";
|
|
3122
|
-
constructor(cause) {
|
|
3123
|
-
super(
|
|
3124
|
-
cause instanceof Error && cause.message ? cause.message : "[memorix] SQLite is unavailable (install a supported SQLite runtime)"
|
|
3125
|
-
);
|
|
3126
|
-
this.name = "SqliteUnavailableError";
|
|
3127
|
-
}
|
|
3128
|
-
};
|
|
3129
|
-
}
|
|
3130
|
-
});
|
|
3131
|
-
|
|
3132
3139
|
// src/store/file-lock.ts
|
|
3133
3140
|
import { promises as fs2 } from "fs";
|
|
3134
3141
|
import { randomUUID } from "crypto";
|
|
@@ -7686,6 +7693,7 @@ async function searchObservations(options) {
|
|
|
7686
7693
|
};
|
|
7687
7694
|
let queryVector = null;
|
|
7688
7695
|
let embeddingProviderForSearch = null;
|
|
7696
|
+
let embeddingUnavailable = false;
|
|
7689
7697
|
if (quality !== "fast" && embeddingEnabled && hasQuery && tier !== "fast") {
|
|
7690
7698
|
try {
|
|
7691
7699
|
const provider2 = await getEmbeddingProvider();
|
|
@@ -7693,6 +7701,7 @@ async function searchObservations(options) {
|
|
|
7693
7701
|
embeddingProviderForSearch = provider2;
|
|
7694
7702
|
const activeVectorDimensions = getVectorDimensions();
|
|
7695
7703
|
if (activeVectorDimensions !== null && provider2.dimensions !== activeVectorDimensions) {
|
|
7704
|
+
embeddingUnavailable = true;
|
|
7696
7705
|
rememberSearchMode(
|
|
7697
7706
|
modeKey,
|
|
7698
7707
|
`fulltext (embedding dimension mismatch: provider ${provider2.dimensions}d vs index ${activeVectorDimensions}d)`
|
|
@@ -7732,8 +7741,11 @@ async function searchObservations(options) {
|
|
|
7732
7741
|
hybridWeights: isCJKHeavy ? { text: 0.2, vector: 0.8 } : { text: 0.6, vector: 0.4 }
|
|
7733
7742
|
};
|
|
7734
7743
|
}
|
|
7744
|
+
} else {
|
|
7745
|
+
embeddingUnavailable = true;
|
|
7735
7746
|
}
|
|
7736
7747
|
} catch (error) {
|
|
7748
|
+
embeddingUnavailable = true;
|
|
7737
7749
|
rememberSearchMode(modeKey, "fulltext (embedding unavailable)");
|
|
7738
7750
|
console.error("[memorix] Embedding failed or timed out, falling back to fulltext search");
|
|
7739
7751
|
}
|
|
@@ -7741,7 +7753,7 @@ async function searchObservations(options) {
|
|
|
7741
7753
|
mark("preSearch");
|
|
7742
7754
|
let results;
|
|
7743
7755
|
const persistentCorpusEligible = await shouldUsePersistentSearch(projectIds, options.projectId);
|
|
7744
|
-
const canUsePersistentLexical = persistentCorpusEligible && Boolean(hasQuery) && !queryVector && (quality === "fast" || tier === "fast" || !embeddingEnabled);
|
|
7756
|
+
const canUsePersistentLexical = persistentCorpusEligible && Boolean(hasQuery) && !queryVector && (quality === "fast" || tier === "fast" || !embeddingEnabled || embeddingUnavailable);
|
|
7745
7757
|
let persistent = persistentCorpusEligible && queryVector && embeddingProviderForSearch ? await searchPersistentSemantically(options, projectIds, requestLimit, queryVector, embeddingProviderForSearch) : canUsePersistentLexical ? await searchPersistentLexically(options, projectIds, requestLimit) : null;
|
|
7746
7758
|
const durableStore = getOptionalObservationStore();
|
|
7747
7759
|
if (!persistent && queryVector && durableStore?.hasLexicalIndex?.() && count(database) === 0) {
|