memorix 1.7.1 → 1.7.2
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 +16 -0
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/cli/index.js +29 -12
- package/dist/index.js +30 -13
- package/dist/maintenance-runner.js +28 -11
- package/dist/media-audio-runner.js +28 -11
- package/dist/media-video-runner.js +28 -11
- package/dist/memcode-runtime/CHANGELOG.md +16 -0
- package/dist/sdk.js +29 -12
- package/dist/vector-backfill-runner.js +28 -11
- package/docs/DEVELOPMENT.md +6 -2
- package/docs/PERFORMANCE.md +10 -3
- package/docs/README.md +3 -1
- package/package.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/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 +32 -12
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,22 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [1.7.2] - 2026-08-19
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
- **Faster large-store cold retrieval** -- startup hydration now inserts
|
|
11
|
+
persisted observations into Orama in bounded batches. This avoids rebuilding
|
|
12
|
+
index balance once per row while continuing to yield to HTTP health traffic.
|
|
13
|
+
- **Fail-closed large-store evidence** -- the reproducible 40,000-record gate
|
|
14
|
+
now exercises and times HTTP readiness, MCP initialize/bind/context/search/
|
|
15
|
+
store, hook capture, and SDK reopen behavior with hard request and release
|
|
16
|
+
budgets instead of checking persistence alone.
|
|
17
|
+
- **Isolated index tests** -- startup hydration tests explicitly disable the
|
|
18
|
+
embedding lane and restore the caller environment instead of reading an
|
|
19
|
+
operator's provider configuration or local vector cache.
|
|
20
|
+
- **Current public guidance** -- README and canonical development/status docs
|
|
21
|
+
now describe preview-first Dashboard maintenance and the 1.7 product line.
|
|
22
|
+
|
|
7
23
|
## [1.7.1] - 2026-08-19
|
|
8
24
|
|
|
9
25
|
### Fixed
|
package/README.md
CHANGED
|
@@ -80,7 +80,7 @@ Memorix is more than a memory store. It also installs agent integrations, keeps
|
|
|
80
80
|
| Agent setup | One setup path for MCP, rules, hooks, skills, plugins, bundles, or extensions depending on the agent | `memorix setup --agent <agent>` |
|
|
81
81
|
| Agent doctor | Checks whether agent MCP config and guidance are current, then repairs Memorix-owned entries when needed | `memorix doctor agents`, `memorix repair agents` |
|
|
82
82
|
| Hooks and skills | Optional capture from supported agents, plus reusable project skills promoted from durable knowledge | `memorix hooks`, `memorix skills` |
|
|
83
|
-
| Dashboard and HTTP | A local web UI and shared MCP endpoint for browsing memory, project state, teams, and
|
|
83
|
+
| Dashboard and HTTP | A local web UI and shared MCP endpoint for browsing memory, project state, teams, diagnostics, and preview-first cleanup, consolidation, deduplication, and retention actions | `memorix dashboard`, `memorix background start` |
|
|
84
84
|
| Orchestration and team work | Task planning, worker handoffs, file locks, messages, verification gates, and review loops | `memorix orchestrate`, `memorix team`, `memorix lock` |
|
|
85
85
|
| memcode | A bundled terminal coding agent that already reads and writes the same project memory | `memorix`, `memcode` |
|
|
86
86
|
| CLI and SDK | Scriptable access for automation, imports/exports, diagnostics, and custom integrations | `memorix ...`, `createMemoryClient()` |
|
package/README.zh-CN.md
CHANGED
|
@@ -79,7 +79,7 @@ Memorix 不只是一个记忆库。它还负责安装 Agent 接入、保留有
|
|
|
79
79
|
| Agent setup | 按目标 Agent 写入 MCP、rules、hooks、skills、plugin、bundle 或 extension | `memorix setup --agent <agent>` |
|
|
80
80
|
| Agent doctor | 检查 Agent 的 MCP 配置和规则是否是当前版本,并修复 Memorix 自己管理的条目 | `memorix doctor agents`、`memorix repair agents` |
|
|
81
81
|
| Hooks 和 skills | 在支持的 Agent 中可选捕获工作事件,并把稳定知识提升成可复用项目技能 | `memorix hooks`、`memorix skills` |
|
|
82
|
-
| Dashboard 和 HTTP | 本地 Web UI 与共享 MCP endpoint
|
|
82
|
+
| Dashboard 和 HTTP | 本地 Web UI 与共享 MCP endpoint,用于浏览记忆、项目状态、团队和诊断,并在预览确认后执行清理、合并、去重和保留归档 | `memorix dashboard`、`memorix background start` |
|
|
83
83
|
| Orchestration 和团队协作 | 任务规划、worker 交接、文件锁、消息、验证门和 review loop | `memorix orchestrate`、`memorix team`、`memorix lock` |
|
|
84
84
|
| memcode | 内置终端 Coding Agent,默认读写同一套项目记忆 | `memorix`、`memcode` |
|
|
85
85
|
| CLI 和 SDK | 给自动化、导入导出、诊断和自定义集成使用的本地接口 | `memorix ...`、`createMemoryClient()` |
|
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.7.
|
|
3235
|
+
return true ? "1.7.2" : pkg.version;
|
|
3236
3236
|
}
|
|
3237
3237
|
var init_version = __esm({
|
|
3238
3238
|
"src/cli/version.ts"() {
|
|
@@ -185335,7 +185335,7 @@ async function hydrateIndex(observations2, options = {}) {
|
|
|
185335
185335
|
}
|
|
185336
185336
|
const deferCachedVectors = options.deferCachedVectors === true && Boolean(indexEmbeddingProvider?.getCachedEmbeddings);
|
|
185337
185337
|
const cachedVectors = deferCachedVectors ? candidates.map(() => null) : await getCachedEmbeddings(candidates.map(({ observation }) => observationEmbeddingText(observation)));
|
|
185338
|
-
|
|
185338
|
+
const documents = [];
|
|
185339
185339
|
for (let index = 0; index < candidates.length; index++) {
|
|
185340
185340
|
const { observation: obs, id } = candidates[index];
|
|
185341
185341
|
try {
|
|
@@ -185369,17 +185369,34 @@ async function hydrateIndex(observations2, options = {}) {
|
|
|
185369
185369
|
knowledgeLayer: resolveKnowledgeLayer("observation", obs.sourceDetail, obs.source),
|
|
185370
185370
|
...compatibleVector ? { embedding: compatibleVector } : {}
|
|
185371
185371
|
};
|
|
185372
|
-
|
|
185373
|
-
|
|
185374
|
-
|
|
185375
|
-
|
|
185376
|
-
|
|
185377
|
-
|
|
185378
|
-
|
|
185379
|
-
|
|
185380
|
-
|
|
185372
|
+
documents.push(doc);
|
|
185373
|
+
} catch {
|
|
185374
|
+
}
|
|
185375
|
+
}
|
|
185376
|
+
let inserted = 0;
|
|
185377
|
+
const hydrationBatchSize = 200;
|
|
185378
|
+
for (let start = 0; start < documents.length; start += hydrationBatchSize) {
|
|
185379
|
+
const batch = documents.slice(start, start + hydrationBatchSize);
|
|
185380
|
+
try {
|
|
185381
|
+
await insertMultiple(database, batch, batch.length);
|
|
185382
|
+
for (const doc of batch) rememberObservationDoc(doc);
|
|
185383
|
+
inserted += batch.length;
|
|
185381
185384
|
} catch {
|
|
185385
|
+
for (const doc of batch) {
|
|
185386
|
+
if (getByID(database, doc.id)) {
|
|
185387
|
+
rememberObservationDoc(doc);
|
|
185388
|
+
inserted++;
|
|
185389
|
+
continue;
|
|
185390
|
+
}
|
|
185391
|
+
try {
|
|
185392
|
+
await insert3(database, doc);
|
|
185393
|
+
rememberObservationDoc(doc);
|
|
185394
|
+
inserted++;
|
|
185395
|
+
} catch {
|
|
185396
|
+
}
|
|
185397
|
+
}
|
|
185382
185398
|
}
|
|
185399
|
+
await new Promise((resolve6) => setImmediate(resolve6));
|
|
185383
185400
|
}
|
|
185384
185401
|
deferredCachedVectorHydration = options.skipCachedVectors ? null : deferCachedVectors ? attachCachedVectors(database, candidates).catch(() => {
|
|
185385
185402
|
}) : null;
|
|
@@ -260203,7 +260220,7 @@ The path should point to a directory containing a .git folder.`
|
|
|
260203
260220
|
};
|
|
260204
260221
|
const server = existingServer ?? new McpServer({
|
|
260205
260222
|
name: "memorix",
|
|
260206
|
-
version: true ? "1.7.
|
|
260223
|
+
version: true ? "1.7.2" : "1.0.1"
|
|
260207
260224
|
});
|
|
260208
260225
|
const originalRegisterTool = server.registerTool.bind(server);
|
|
260209
260226
|
server.registerTool = ((name, ...args) => {
|
package/dist/index.js
CHANGED
|
@@ -6888,7 +6888,7 @@ __export(orama_store_exports, {
|
|
|
6888
6888
|
searchObservations: () => searchObservations,
|
|
6889
6889
|
updateObservationMetadata: () => updateObservationMetadata
|
|
6890
6890
|
});
|
|
6891
|
-
import { create, insert as insert2, search, remove as remove2, update, count, getByID } from "@orama/orama";
|
|
6891
|
+
import { create, insert as insert2, insertMultiple, search, remove as remove2, update, count, getByID } from "@orama/orama";
|
|
6892
6892
|
function getLastSearchMode(projectId) {
|
|
6893
6893
|
return lastSearchModeByProject.get(projectId ?? SEARCH_MODE_DEFAULT_KEY) ?? "fulltext";
|
|
6894
6894
|
}
|
|
@@ -7100,7 +7100,7 @@ async function hydrateIndex(observations2, options = {}) {
|
|
|
7100
7100
|
}
|
|
7101
7101
|
const deferCachedVectors = options.deferCachedVectors === true && Boolean(indexEmbeddingProvider?.getCachedEmbeddings);
|
|
7102
7102
|
const cachedVectors = deferCachedVectors ? candidates.map(() => null) : await getCachedEmbeddings(candidates.map(({ observation }) => observationEmbeddingText(observation)));
|
|
7103
|
-
|
|
7103
|
+
const documents = [];
|
|
7104
7104
|
for (let index = 0; index < candidates.length; index++) {
|
|
7105
7105
|
const { observation: obs, id } = candidates[index];
|
|
7106
7106
|
try {
|
|
@@ -7134,17 +7134,34 @@ async function hydrateIndex(observations2, options = {}) {
|
|
|
7134
7134
|
knowledgeLayer: resolveKnowledgeLayer("observation", obs.sourceDetail, obs.source),
|
|
7135
7135
|
...compatibleVector ? { embedding: compatibleVector } : {}
|
|
7136
7136
|
};
|
|
7137
|
-
|
|
7138
|
-
|
|
7139
|
-
|
|
7140
|
-
|
|
7141
|
-
|
|
7142
|
-
|
|
7143
|
-
|
|
7144
|
-
|
|
7145
|
-
|
|
7137
|
+
documents.push(doc);
|
|
7138
|
+
} catch {
|
|
7139
|
+
}
|
|
7140
|
+
}
|
|
7141
|
+
let inserted = 0;
|
|
7142
|
+
const hydrationBatchSize = 200;
|
|
7143
|
+
for (let start = 0; start < documents.length; start += hydrationBatchSize) {
|
|
7144
|
+
const batch = documents.slice(start, start + hydrationBatchSize);
|
|
7145
|
+
try {
|
|
7146
|
+
await insertMultiple(database, batch, batch.length);
|
|
7147
|
+
for (const doc of batch) rememberObservationDoc(doc);
|
|
7148
|
+
inserted += batch.length;
|
|
7146
7149
|
} catch {
|
|
7150
|
+
for (const doc of batch) {
|
|
7151
|
+
if (getByID(database, doc.id)) {
|
|
7152
|
+
rememberObservationDoc(doc);
|
|
7153
|
+
inserted++;
|
|
7154
|
+
continue;
|
|
7155
|
+
}
|
|
7156
|
+
try {
|
|
7157
|
+
await insert2(database, doc);
|
|
7158
|
+
rememberObservationDoc(doc);
|
|
7159
|
+
inserted++;
|
|
7160
|
+
} catch {
|
|
7161
|
+
}
|
|
7162
|
+
}
|
|
7147
7163
|
}
|
|
7164
|
+
await new Promise((resolve2) => setImmediate(resolve2));
|
|
7148
7165
|
}
|
|
7149
7166
|
deferredCachedVectorHydration = options.skipCachedVectors ? null : deferCachedVectors ? attachCachedVectors(database, candidates).catch(() => {
|
|
7150
7167
|
}) : null;
|
|
@@ -167285,7 +167302,7 @@ The path should point to a directory containing a .git folder.`
|
|
|
167285
167302
|
};
|
|
167286
167303
|
const server = existingServer ?? new McpServer({
|
|
167287
167304
|
name: "memorix",
|
|
167288
|
-
version: true ? "1.7.
|
|
167305
|
+
version: true ? "1.7.2" : "1.0.1"
|
|
167289
167306
|
});
|
|
167290
167307
|
const originalRegisterTool = server.registerTool.bind(server);
|
|
167291
167308
|
server.registerTool = ((name, ...args) => {
|
|
@@ -171725,7 +171742,7 @@ var init_serve_shared = __esm({
|
|
|
171725
171742
|
|
|
171726
171743
|
// src/cli/version.ts
|
|
171727
171744
|
function getCliVersion() {
|
|
171728
|
-
return true ? "1.7.
|
|
171745
|
+
return true ? "1.7.2" : pkg.version;
|
|
171729
171746
|
}
|
|
171730
171747
|
var init_version3 = __esm({
|
|
171731
171748
|
"src/cli/version.ts"() {
|
|
@@ -6094,7 +6094,7 @@ __export(orama_store_exports, {
|
|
|
6094
6094
|
searchObservations: () => searchObservations,
|
|
6095
6095
|
updateObservationMetadata: () => updateObservationMetadata
|
|
6096
6096
|
});
|
|
6097
|
-
import { create, insert as insert2, search, remove as remove2, update, count, getByID } from "@orama/orama";
|
|
6097
|
+
import { create, insert as insert2, insertMultiple, search, remove as remove2, update, count, getByID } from "@orama/orama";
|
|
6098
6098
|
function getLastSearchMode(projectId) {
|
|
6099
6099
|
return lastSearchModeByProject.get(projectId ?? SEARCH_MODE_DEFAULT_KEY) ?? "fulltext";
|
|
6100
6100
|
}
|
|
@@ -6306,7 +6306,7 @@ async function hydrateIndex(observations2, options = {}) {
|
|
|
6306
6306
|
}
|
|
6307
6307
|
const deferCachedVectors = options.deferCachedVectors === true && Boolean(indexEmbeddingProvider?.getCachedEmbeddings);
|
|
6308
6308
|
const cachedVectors = deferCachedVectors ? candidates.map(() => null) : await getCachedEmbeddings(candidates.map(({ observation }) => observationEmbeddingText(observation)));
|
|
6309
|
-
|
|
6309
|
+
const documents = [];
|
|
6310
6310
|
for (let index = 0; index < candidates.length; index++) {
|
|
6311
6311
|
const { observation: obs, id } = candidates[index];
|
|
6312
6312
|
try {
|
|
@@ -6340,17 +6340,34 @@ async function hydrateIndex(observations2, options = {}) {
|
|
|
6340
6340
|
knowledgeLayer: resolveKnowledgeLayer("observation", obs.sourceDetail, obs.source),
|
|
6341
6341
|
...compatibleVector ? { embedding: compatibleVector } : {}
|
|
6342
6342
|
};
|
|
6343
|
-
|
|
6344
|
-
|
|
6345
|
-
|
|
6346
|
-
|
|
6347
|
-
|
|
6348
|
-
|
|
6349
|
-
|
|
6350
|
-
|
|
6351
|
-
|
|
6343
|
+
documents.push(doc);
|
|
6344
|
+
} catch {
|
|
6345
|
+
}
|
|
6346
|
+
}
|
|
6347
|
+
let inserted = 0;
|
|
6348
|
+
const hydrationBatchSize = 200;
|
|
6349
|
+
for (let start = 0; start < documents.length; start += hydrationBatchSize) {
|
|
6350
|
+
const batch = documents.slice(start, start + hydrationBatchSize);
|
|
6351
|
+
try {
|
|
6352
|
+
await insertMultiple(database, batch, batch.length);
|
|
6353
|
+
for (const doc of batch) rememberObservationDoc(doc);
|
|
6354
|
+
inserted += batch.length;
|
|
6352
6355
|
} catch {
|
|
6356
|
+
for (const doc of batch) {
|
|
6357
|
+
if (getByID(database, doc.id)) {
|
|
6358
|
+
rememberObservationDoc(doc);
|
|
6359
|
+
inserted++;
|
|
6360
|
+
continue;
|
|
6361
|
+
}
|
|
6362
|
+
try {
|
|
6363
|
+
await insert2(database, doc);
|
|
6364
|
+
rememberObservationDoc(doc);
|
|
6365
|
+
inserted++;
|
|
6366
|
+
} catch {
|
|
6367
|
+
}
|
|
6368
|
+
}
|
|
6353
6369
|
}
|
|
6370
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
6354
6371
|
}
|
|
6355
6372
|
deferredCachedVectorHydration = options.skipCachedVectors ? null : deferCachedVectors ? attachCachedVectors(database, candidates).catch(() => {
|
|
6356
6373
|
}) : null;
|
|
@@ -6580,7 +6580,7 @@ __export(orama_store_exports, {
|
|
|
6580
6580
|
searchObservations: () => searchObservations,
|
|
6581
6581
|
updateObservationMetadata: () => updateObservationMetadata
|
|
6582
6582
|
});
|
|
6583
|
-
import { create, insert as insert2, search, remove as remove2, update, count, getByID } from "@orama/orama";
|
|
6583
|
+
import { create, insert as insert2, insertMultiple, search, remove as remove2, update, count, getByID } from "@orama/orama";
|
|
6584
6584
|
function getLastSearchMode(projectId) {
|
|
6585
6585
|
return lastSearchModeByProject.get(projectId ?? SEARCH_MODE_DEFAULT_KEY) ?? "fulltext";
|
|
6586
6586
|
}
|
|
@@ -6792,7 +6792,7 @@ async function hydrateIndex(observations2, options = {}) {
|
|
|
6792
6792
|
}
|
|
6793
6793
|
const deferCachedVectors = options.deferCachedVectors === true && Boolean(indexEmbeddingProvider?.getCachedEmbeddings);
|
|
6794
6794
|
const cachedVectors = deferCachedVectors ? candidates.map(() => null) : await getCachedEmbeddings(candidates.map(({ observation }) => observationEmbeddingText(observation)));
|
|
6795
|
-
|
|
6795
|
+
const documents = [];
|
|
6796
6796
|
for (let index = 0; index < candidates.length; index++) {
|
|
6797
6797
|
const { observation: obs, id } = candidates[index];
|
|
6798
6798
|
try {
|
|
@@ -6826,17 +6826,34 @@ async function hydrateIndex(observations2, options = {}) {
|
|
|
6826
6826
|
knowledgeLayer: resolveKnowledgeLayer("observation", obs.sourceDetail, obs.source),
|
|
6827
6827
|
...compatibleVector ? { embedding: compatibleVector } : {}
|
|
6828
6828
|
};
|
|
6829
|
-
|
|
6830
|
-
|
|
6831
|
-
|
|
6832
|
-
|
|
6833
|
-
|
|
6834
|
-
|
|
6835
|
-
|
|
6836
|
-
|
|
6837
|
-
|
|
6829
|
+
documents.push(doc);
|
|
6830
|
+
} catch {
|
|
6831
|
+
}
|
|
6832
|
+
}
|
|
6833
|
+
let inserted = 0;
|
|
6834
|
+
const hydrationBatchSize = 200;
|
|
6835
|
+
for (let start = 0; start < documents.length; start += hydrationBatchSize) {
|
|
6836
|
+
const batch = documents.slice(start, start + hydrationBatchSize);
|
|
6837
|
+
try {
|
|
6838
|
+
await insertMultiple(database, batch, batch.length);
|
|
6839
|
+
for (const doc of batch) rememberObservationDoc(doc);
|
|
6840
|
+
inserted += batch.length;
|
|
6838
6841
|
} catch {
|
|
6842
|
+
for (const doc of batch) {
|
|
6843
|
+
if (getByID(database, doc.id)) {
|
|
6844
|
+
rememberObservationDoc(doc);
|
|
6845
|
+
inserted++;
|
|
6846
|
+
continue;
|
|
6847
|
+
}
|
|
6848
|
+
try {
|
|
6849
|
+
await insert2(database, doc);
|
|
6850
|
+
rememberObservationDoc(doc);
|
|
6851
|
+
inserted++;
|
|
6852
|
+
} catch {
|
|
6853
|
+
}
|
|
6854
|
+
}
|
|
6839
6855
|
}
|
|
6856
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
6840
6857
|
}
|
|
6841
6858
|
deferredCachedVectorHydration = options.skipCachedVectors ? null : deferCachedVectors ? attachCachedVectors(database, candidates).catch(() => {
|
|
6842
6859
|
}) : null;
|
|
@@ -5597,7 +5597,7 @@ __export(orama_store_exports, {
|
|
|
5597
5597
|
searchObservations: () => searchObservations,
|
|
5598
5598
|
updateObservationMetadata: () => updateObservationMetadata
|
|
5599
5599
|
});
|
|
5600
|
-
import { create, insert as insert2, search, remove as remove2, update, count, getByID } from "@orama/orama";
|
|
5600
|
+
import { create, insert as insert2, insertMultiple, search, remove as remove2, update, count, getByID } from "@orama/orama";
|
|
5601
5601
|
function getLastSearchMode(projectId) {
|
|
5602
5602
|
return lastSearchModeByProject.get(projectId ?? SEARCH_MODE_DEFAULT_KEY) ?? "fulltext";
|
|
5603
5603
|
}
|
|
@@ -5809,7 +5809,7 @@ async function hydrateIndex(observations2, options = {}) {
|
|
|
5809
5809
|
}
|
|
5810
5810
|
const deferCachedVectors = options.deferCachedVectors === true && Boolean(indexEmbeddingProvider?.getCachedEmbeddings);
|
|
5811
5811
|
const cachedVectors = deferCachedVectors ? candidates.map(() => null) : await getCachedEmbeddings(candidates.map(({ observation }) => observationEmbeddingText(observation)));
|
|
5812
|
-
|
|
5812
|
+
const documents = [];
|
|
5813
5813
|
for (let index = 0; index < candidates.length; index++) {
|
|
5814
5814
|
const { observation: obs, id } = candidates[index];
|
|
5815
5815
|
try {
|
|
@@ -5843,17 +5843,34 @@ async function hydrateIndex(observations2, options = {}) {
|
|
|
5843
5843
|
knowledgeLayer: resolveKnowledgeLayer("observation", obs.sourceDetail, obs.source),
|
|
5844
5844
|
...compatibleVector ? { embedding: compatibleVector } : {}
|
|
5845
5845
|
};
|
|
5846
|
-
|
|
5847
|
-
|
|
5848
|
-
|
|
5849
|
-
|
|
5850
|
-
|
|
5851
|
-
|
|
5852
|
-
|
|
5853
|
-
|
|
5854
|
-
|
|
5846
|
+
documents.push(doc);
|
|
5847
|
+
} catch {
|
|
5848
|
+
}
|
|
5849
|
+
}
|
|
5850
|
+
let inserted = 0;
|
|
5851
|
+
const hydrationBatchSize = 200;
|
|
5852
|
+
for (let start = 0; start < documents.length; start += hydrationBatchSize) {
|
|
5853
|
+
const batch = documents.slice(start, start + hydrationBatchSize);
|
|
5854
|
+
try {
|
|
5855
|
+
await insertMultiple(database, batch, batch.length);
|
|
5856
|
+
for (const doc of batch) rememberObservationDoc(doc);
|
|
5857
|
+
inserted += batch.length;
|
|
5855
5858
|
} catch {
|
|
5859
|
+
for (const doc of batch) {
|
|
5860
|
+
if (getByID(database, doc.id)) {
|
|
5861
|
+
rememberObservationDoc(doc);
|
|
5862
|
+
inserted++;
|
|
5863
|
+
continue;
|
|
5864
|
+
}
|
|
5865
|
+
try {
|
|
5866
|
+
await insert2(database, doc);
|
|
5867
|
+
rememberObservationDoc(doc);
|
|
5868
|
+
inserted++;
|
|
5869
|
+
} catch {
|
|
5870
|
+
}
|
|
5871
|
+
}
|
|
5856
5872
|
}
|
|
5873
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
5857
5874
|
}
|
|
5858
5875
|
deferredCachedVectorHydration = options.skipCachedVectors ? null : deferCachedVectors ? attachCachedVectors(database, candidates).catch(() => {
|
|
5859
5876
|
}) : null;
|
|
@@ -4,6 +4,22 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [1.7.2] - 2026-08-19
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
- **Faster large-store cold retrieval** -- startup hydration now inserts
|
|
11
|
+
persisted observations into Orama in bounded batches. This avoids rebuilding
|
|
12
|
+
index balance once per row while continuing to yield to HTTP health traffic.
|
|
13
|
+
- **Fail-closed large-store evidence** -- the reproducible 40,000-record gate
|
|
14
|
+
now exercises and times HTTP readiness, MCP initialize/bind/context/search/
|
|
15
|
+
store, hook capture, and SDK reopen behavior with hard request and release
|
|
16
|
+
budgets instead of checking persistence alone.
|
|
17
|
+
- **Isolated index tests** -- startup hydration tests explicitly disable the
|
|
18
|
+
embedding lane and restore the caller environment instead of reading an
|
|
19
|
+
operator's provider configuration or local vector cache.
|
|
20
|
+
- **Current public guidance** -- README and canonical development/status docs
|
|
21
|
+
now describe preview-first Dashboard maintenance and the 1.7 product line.
|
|
22
|
+
|
|
7
23
|
## [1.7.1] - 2026-08-19
|
|
8
24
|
|
|
9
25
|
### Fixed
|
package/dist/sdk.js
CHANGED
|
@@ -6876,7 +6876,7 @@ __export(orama_store_exports, {
|
|
|
6876
6876
|
searchObservations: () => searchObservations,
|
|
6877
6877
|
updateObservationMetadata: () => updateObservationMetadata
|
|
6878
6878
|
});
|
|
6879
|
-
import { create, insert as insert2, search, remove as remove2, update, count, getByID } from "@orama/orama";
|
|
6879
|
+
import { create, insert as insert2, insertMultiple, search, remove as remove2, update, count, getByID } from "@orama/orama";
|
|
6880
6880
|
function getLastSearchMode(projectId) {
|
|
6881
6881
|
return lastSearchModeByProject.get(projectId ?? SEARCH_MODE_DEFAULT_KEY) ?? "fulltext";
|
|
6882
6882
|
}
|
|
@@ -7088,7 +7088,7 @@ async function hydrateIndex(observations2, options = {}) {
|
|
|
7088
7088
|
}
|
|
7089
7089
|
const deferCachedVectors = options.deferCachedVectors === true && Boolean(indexEmbeddingProvider?.getCachedEmbeddings);
|
|
7090
7090
|
const cachedVectors = deferCachedVectors ? candidates.map(() => null) : await getCachedEmbeddings(candidates.map(({ observation }) => observationEmbeddingText(observation)));
|
|
7091
|
-
|
|
7091
|
+
const documents = [];
|
|
7092
7092
|
for (let index = 0; index < candidates.length; index++) {
|
|
7093
7093
|
const { observation: obs, id } = candidates[index];
|
|
7094
7094
|
try {
|
|
@@ -7122,17 +7122,34 @@ async function hydrateIndex(observations2, options = {}) {
|
|
|
7122
7122
|
knowledgeLayer: resolveKnowledgeLayer("observation", obs.sourceDetail, obs.source),
|
|
7123
7123
|
...compatibleVector ? { embedding: compatibleVector } : {}
|
|
7124
7124
|
};
|
|
7125
|
-
|
|
7126
|
-
|
|
7127
|
-
|
|
7128
|
-
|
|
7129
|
-
|
|
7130
|
-
|
|
7131
|
-
|
|
7132
|
-
|
|
7133
|
-
|
|
7125
|
+
documents.push(doc);
|
|
7126
|
+
} catch {
|
|
7127
|
+
}
|
|
7128
|
+
}
|
|
7129
|
+
let inserted = 0;
|
|
7130
|
+
const hydrationBatchSize = 200;
|
|
7131
|
+
for (let start = 0; start < documents.length; start += hydrationBatchSize) {
|
|
7132
|
+
const batch = documents.slice(start, start + hydrationBatchSize);
|
|
7133
|
+
try {
|
|
7134
|
+
await insertMultiple(database, batch, batch.length);
|
|
7135
|
+
for (const doc of batch) rememberObservationDoc(doc);
|
|
7136
|
+
inserted += batch.length;
|
|
7134
7137
|
} catch {
|
|
7138
|
+
for (const doc of batch) {
|
|
7139
|
+
if (getByID(database, doc.id)) {
|
|
7140
|
+
rememberObservationDoc(doc);
|
|
7141
|
+
inserted++;
|
|
7142
|
+
continue;
|
|
7143
|
+
}
|
|
7144
|
+
try {
|
|
7145
|
+
await insert2(database, doc);
|
|
7146
|
+
rememberObservationDoc(doc);
|
|
7147
|
+
inserted++;
|
|
7148
|
+
} catch {
|
|
7149
|
+
}
|
|
7150
|
+
}
|
|
7135
7151
|
}
|
|
7152
|
+
await new Promise((resolve2) => setImmediate(resolve2));
|
|
7136
7153
|
}
|
|
7137
7154
|
deferredCachedVectorHydration = options.skipCachedVectors ? null : deferCachedVectors ? attachCachedVectors(database, candidates).catch(() => {
|
|
7138
7155
|
}) : null;
|
|
@@ -167080,7 +167097,7 @@ The path should point to a directory containing a .git folder.`
|
|
|
167080
167097
|
};
|
|
167081
167098
|
const server = existingServer ?? new McpServer({
|
|
167082
167099
|
name: "memorix",
|
|
167083
|
-
version: true ? "1.7.
|
|
167100
|
+
version: true ? "1.7.2" : "1.0.1"
|
|
167084
167101
|
});
|
|
167085
167102
|
const originalRegisterTool = server.registerTool.bind(server);
|
|
167086
167103
|
server.registerTool = ((name, ...args) => {
|
|
@@ -5597,7 +5597,7 @@ __export(orama_store_exports, {
|
|
|
5597
5597
|
searchObservations: () => searchObservations,
|
|
5598
5598
|
updateObservationMetadata: () => updateObservationMetadata
|
|
5599
5599
|
});
|
|
5600
|
-
import { create, insert as insert2, search, remove as remove2, update, count, getByID } from "@orama/orama";
|
|
5600
|
+
import { create, insert as insert2, insertMultiple, search, remove as remove2, update, count, getByID } from "@orama/orama";
|
|
5601
5601
|
function getLastSearchMode(projectId) {
|
|
5602
5602
|
return lastSearchModeByProject.get(projectId ?? SEARCH_MODE_DEFAULT_KEY) ?? "fulltext";
|
|
5603
5603
|
}
|
|
@@ -5809,7 +5809,7 @@ async function hydrateIndex(observations2, options = {}) {
|
|
|
5809
5809
|
}
|
|
5810
5810
|
const deferCachedVectors = options.deferCachedVectors === true && Boolean(indexEmbeddingProvider?.getCachedEmbeddings);
|
|
5811
5811
|
const cachedVectors = deferCachedVectors ? candidates.map(() => null) : await getCachedEmbeddings(candidates.map(({ observation }) => observationEmbeddingText(observation)));
|
|
5812
|
-
|
|
5812
|
+
const documents = [];
|
|
5813
5813
|
for (let index = 0; index < candidates.length; index++) {
|
|
5814
5814
|
const { observation: obs, id } = candidates[index];
|
|
5815
5815
|
try {
|
|
@@ -5843,17 +5843,34 @@ async function hydrateIndex(observations2, options = {}) {
|
|
|
5843
5843
|
knowledgeLayer: resolveKnowledgeLayer("observation", obs.sourceDetail, obs.source),
|
|
5844
5844
|
...compatibleVector ? { embedding: compatibleVector } : {}
|
|
5845
5845
|
};
|
|
5846
|
-
|
|
5847
|
-
|
|
5848
|
-
|
|
5849
|
-
|
|
5850
|
-
|
|
5851
|
-
|
|
5852
|
-
|
|
5853
|
-
|
|
5854
|
-
|
|
5846
|
+
documents.push(doc);
|
|
5847
|
+
} catch {
|
|
5848
|
+
}
|
|
5849
|
+
}
|
|
5850
|
+
let inserted = 0;
|
|
5851
|
+
const hydrationBatchSize = 200;
|
|
5852
|
+
for (let start = 0; start < documents.length; start += hydrationBatchSize) {
|
|
5853
|
+
const batch = documents.slice(start, start + hydrationBatchSize);
|
|
5854
|
+
try {
|
|
5855
|
+
await insertMultiple(database, batch, batch.length);
|
|
5856
|
+
for (const doc of batch) rememberObservationDoc(doc);
|
|
5857
|
+
inserted += batch.length;
|
|
5855
5858
|
} catch {
|
|
5859
|
+
for (const doc of batch) {
|
|
5860
|
+
if (getByID(database, doc.id)) {
|
|
5861
|
+
rememberObservationDoc(doc);
|
|
5862
|
+
inserted++;
|
|
5863
|
+
continue;
|
|
5864
|
+
}
|
|
5865
|
+
try {
|
|
5866
|
+
await insert2(database, doc);
|
|
5867
|
+
rememberObservationDoc(doc);
|
|
5868
|
+
inserted++;
|
|
5869
|
+
} catch {
|
|
5870
|
+
}
|
|
5871
|
+
}
|
|
5856
5872
|
}
|
|
5873
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
5857
5874
|
}
|
|
5858
5875
|
deferredCachedVectorHydration = options.skipCachedVectors ? null : deferCachedVectors ? attachCachedVectors(database, candidates).catch(() => {
|
|
5859
5876
|
}) : null;
|
package/docs/DEVELOPMENT.md
CHANGED
|
@@ -13,9 +13,10 @@ Memorix is a TypeScript project built around:
|
|
|
13
13
|
|
|
14
14
|
## Current Development Baseline
|
|
15
15
|
|
|
16
|
-
The current release work targets the **1.
|
|
16
|
+
The current release work targets the **1.7 line** while package metadata may
|
|
17
|
+
still show the last published version until the release commit is cut.
|
|
17
18
|
|
|
18
|
-
Contributors should assume the following areas are part of the
|
|
19
|
+
Contributors should assume the following areas are part of the current release line:
|
|
19
20
|
|
|
20
21
|
- shared memory across MCP clients, CLI, SDK, dashboard, hooks, and memcode
|
|
21
22
|
- memcode uses Memorix project memory, hooks, `/memory` commands, resumable sessions, and model switching as the bundled terminal agent
|
|
@@ -27,6 +28,9 @@ Contributors should assume the following areas are part of the 1.4 release line:
|
|
|
27
28
|
- dashboard config loading aligned with CLI/TUI status surfaces
|
|
28
29
|
- the existing layered retrieval, retention, attribution, and compact output model
|
|
29
30
|
- source-backed long-term memory with candidate, qualification, approval, archival, supersession, and explicit portable user scope
|
|
31
|
+
- preview-first project maintenance shared across Dashboard, CLI, and MCP
|
|
32
|
+
- the reproducible large-store gate for SDK, HTTP MCP, hooks, persistence, and
|
|
33
|
+
cold-start retrieval budgets
|
|
30
34
|
|
|
31
35
|
---
|
|
32
36
|
|
package/docs/PERFORMANCE.md
CHANGED
|
@@ -103,9 +103,16 @@ On the release development machine used for this check, the healthy HTTP service
|
|
|
103
103
|
temporary Git project with an isolated data directory. It runs with embeddings
|
|
104
104
|
disabled, so it does not use provider credentials or make paid network calls.
|
|
105
105
|
The gate measures steady-state writes, first lexical search, process memory,
|
|
106
|
-
HTTP MCP
|
|
107
|
-
|
|
108
|
-
|
|
106
|
+
HTTP readiness, MCP initialize/bind/context/search/store, hook capture, and an
|
|
107
|
+
SDK close/reopen cycle. Every network request has a hard timeout, and the
|
|
108
|
+
report fails when an interactive path exceeds its release budget. It also
|
|
109
|
+
verifies that the personal hook candidate remains durable on disk without
|
|
110
|
+
becoming visible to an unbound SDK reader.
|
|
111
|
+
|
|
112
|
+
Startup hydration uses bounded Orama batches. This keeps HTTP health responsive
|
|
113
|
+
while avoiding a full index rebalance for every persisted observation. The MCP
|
|
114
|
+
search number is intentionally a cold control-plane measurement: it includes
|
|
115
|
+
any lexical hydration the fresh service still owes before its first search.
|
|
109
116
|
|
|
110
117
|
Results are machine-specific and should be compared on the same OS and Node
|
|
111
118
|
version. The release gate is an integrity and regression check, not a public
|
package/docs/README.md
CHANGED
|
@@ -119,11 +119,13 @@ Historical/deep-reference documents may describe older designs. If they conflict
|
|
|
119
119
|
|
|
120
120
|
## Current Product Line
|
|
121
121
|
|
|
122
|
-
The released product is on the **1.
|
|
122
|
+
The released product is on the **1.7 line**. The current baseline has:
|
|
123
123
|
|
|
124
124
|
- `memorix setup --agent <agent> --global` is the default agent integration command
|
|
125
125
|
- `memorix serve` is the manual stdio MCP server for external agents
|
|
126
126
|
- `memorix background start` runs the shared HTTP MCP service and dashboard
|
|
127
|
+
- Dashboard maintenance is preview-first, project-scoped, and uses the same
|
|
128
|
+
cleanup and deduplication planners as CLI and MCP
|
|
127
129
|
- context and CodeGraph commands return bounded task receipts before detailed diagnostics
|
|
128
130
|
- HTTP MCP sessions have a bounded, explicit lifecycle and fail with a reinitialize hint after expiry
|
|
129
131
|
- `memorix integrate --agent <agent>` and `memorix hooks install --agent <agent>` remain manual/fallback generation commands
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "memorix",
|
|
3
3
|
"mcpName": "io.github.AVIDS2/memorix",
|
|
4
|
-
"version": "1.7.
|
|
4
|
+
"version": "1.7.2",
|
|
5
5
|
"description": "Local-first shared memory layer for AI coding agents across MCP clients, Git history, reasoning context, and project sessions.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"sideEffects": false,
|
package/server.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"url": "https://github.com/AVIDS2/memorix",
|
|
8
8
|
"source": "github"
|
|
9
9
|
},
|
|
10
|
-
"version": "1.7.
|
|
10
|
+
"version": "1.7.2",
|
|
11
11
|
"websiteUrl": "https://github.com/AVIDS2/memorix#readme",
|
|
12
12
|
"icons": [
|
|
13
13
|
{
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
{
|
|
23
23
|
"registryType": "npm",
|
|
24
24
|
"identifier": "memorix",
|
|
25
|
-
"version": "1.7.
|
|
25
|
+
"version": "1.7.2",
|
|
26
26
|
"runtimeHint": "npx",
|
|
27
27
|
"packageArguments": [
|
|
28
28
|
{
|
package/src/store/orama-store.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* Vector search (embeddings) will be added in P1 phase.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import { create, insert, search, remove, update, count, getByID, type AnyOrama } from '@orama/orama';
|
|
11
|
+
import { create, insert, insertMultiple, search, remove, update, count, getByID, type AnyOrama } from '@orama/orama';
|
|
12
12
|
import type { MemorixDocument, SearchOptions, IndexEntry, KnowledgeLayer, ObservationReader } from '../types.js';
|
|
13
13
|
import { OBSERVATION_ICONS, type ObservationType } from '../types.js';
|
|
14
14
|
import { resolveKnowledgeLayer } from '../skills/mini-skills.js';
|
|
@@ -405,7 +405,7 @@ export async function hydrateIndex(
|
|
|
405
405
|
? candidates.map(() => null)
|
|
406
406
|
: await getCachedEmbeddings(candidates.map(({ observation }) => observationEmbeddingText(observation)));
|
|
407
407
|
|
|
408
|
-
|
|
408
|
+
const documents: MemorixDocument[] = [];
|
|
409
409
|
for (let index = 0; index < candidates.length; index++) {
|
|
410
410
|
const { observation: obs, id } = candidates[index];
|
|
411
411
|
try {
|
|
@@ -439,19 +439,39 @@ export async function hydrateIndex(
|
|
|
439
439
|
knowledgeLayer: resolveKnowledgeLayer('observation', obs.sourceDetail, obs.source),
|
|
440
440
|
...(compatibleVector ? { embedding: compatibleVector } : {}),
|
|
441
441
|
};
|
|
442
|
-
|
|
443
|
-
rememberObservationDoc(doc);
|
|
444
|
-
inserted++;
|
|
445
|
-
// Keep the HTTP event loop alive on large corpora so /health still answers.
|
|
446
|
-
if (inserted % 50 === 0) {
|
|
447
|
-
await new Promise<void>((resolve) => setImmediate(resolve));
|
|
448
|
-
}
|
|
449
|
-
if (inserted % 200 === 0) {
|
|
450
|
-
await new Promise<void>((resolve) => setTimeout(resolve, 0));
|
|
451
|
-
}
|
|
442
|
+
documents.push(doc);
|
|
452
443
|
} catch { /* skip malformed entries */ }
|
|
453
444
|
}
|
|
454
445
|
|
|
446
|
+
let inserted = 0;
|
|
447
|
+
const hydrationBatchSize = 200;
|
|
448
|
+
for (let start = 0; start < documents.length; start += hydrationBatchSize) {
|
|
449
|
+
const batch = documents.slice(start, start + hydrationBatchSize);
|
|
450
|
+
try {
|
|
451
|
+
// Orama can rebalance its indexes once per batch instead of once per
|
|
452
|
+
// document. Keep batches bounded so HTTP health remains responsive.
|
|
453
|
+
await insertMultiple(database, batch, batch.length);
|
|
454
|
+
for (const doc of batch) rememberObservationDoc(doc);
|
|
455
|
+
inserted += batch.length;
|
|
456
|
+
} catch {
|
|
457
|
+
// A concurrent write or malformed document can partially complete a
|
|
458
|
+
// batch. Preserve the old best-effort behavior without double-inserting.
|
|
459
|
+
for (const doc of batch) {
|
|
460
|
+
if (getByID(database, doc.id)) {
|
|
461
|
+
rememberObservationDoc(doc);
|
|
462
|
+
inserted++;
|
|
463
|
+
continue;
|
|
464
|
+
}
|
|
465
|
+
try {
|
|
466
|
+
await insert(database, doc);
|
|
467
|
+
rememberObservationDoc(doc);
|
|
468
|
+
inserted++;
|
|
469
|
+
} catch { /* skip malformed or concurrently inserted entries */ }
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
await new Promise<void>((resolve) => setImmediate(resolve));
|
|
473
|
+
}
|
|
474
|
+
|
|
455
475
|
deferredCachedVectorHydration = options.skipCachedVectors
|
|
456
476
|
? null
|
|
457
477
|
: deferCachedVectors
|