dsh-memu-memory 1.2.0 → 1.2.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/AGENTS.md +1 -1
- package/README.md +19 -8
- package/README.zh.md +12 -6
- package/docs/contributing.md +16 -3
- package/docs/env-and-commands.md +8 -0
- package/docs/host-api.md +3 -1
- package/docs/memory-semantics.md +21 -2
- package/docs/troubleshooting.md +42 -3
- package/lib/activity-log.js +22 -5
- package/lib/backup.js +24 -2
- package/lib/binding-diagnostics.js +3 -3
- package/lib/client.js +1610 -117
- package/lib/consolidation-worker.js +68 -17
- package/lib/curator-evidence.js +131 -0
- package/lib/curator-policy.js +81 -0
- package/lib/curator.js +1299 -83
- package/lib/cycle.js +3 -0
- package/lib/embedding-download.js +39 -4
- package/lib/engine.js +6 -3
- package/lib/global-graph.js +282 -0
- package/lib/graph.js +190 -14
- package/lib/inbound.js +19 -0
- package/lib/index.js +33 -1
- package/lib/inject.js +277 -17
- package/lib/lexicon.js +1115 -0
- package/lib/local-vector-service.js +8 -6
- package/lib/managed-runtime.js +33 -2
- package/lib/memory.js +585 -76
- package/lib/memu.js +2 -2
- package/lib/migrate.js +83 -2
- package/lib/private-links.js +587 -0
- package/lib/private.js +128 -7
- package/lib/prompts/global-curator.md +76 -19
- package/lib/prompts/private-consolidation.coding.md +4 -1
- package/lib/prompts/private-consolidation.md +3 -1
- package/lib/prompts/private-consolidation.mixed.md +3 -1
- package/lib/prompts/private-consolidation.rp.md +3 -1
- package/lib/prompts.js +1 -1
- package/lib/rpc.js +622 -13
- package/lib/state.js +4 -1
- package/lib/text-retrieval.js +661 -0
- package/lib/text-similarity.js +124 -0
- package/lib/tools.js +230 -16
- package/lib/vector-migration.js +28 -3
- package/lib/vector-profile.js +24 -0
- package/lib/webui/standalone.css +17 -0
- package/lib/webui/standalone.js +2 -0
- package/package.json +3 -2
- package/scripts/acceptance-local.mjs +8 -5
- package/scripts/curator-pair-calibrate.mjs +259 -0
- package/scripts/lexicon-calibrate.mjs +230 -0
- package/scripts/lexicon-dedupe.mjs +318 -0
- package/scripts/replay.mjs +573 -0
- package/tests/absorb-review.test.mjs +353 -0
- package/tests/appendix-b.test.mjs +6 -2
- package/tests/appendix-b2.test.mjs +2 -2
- package/tests/client-export.test.mjs +520 -0
- package/tests/client-render.test.mjs +19 -3
- package/tests/consolidation-credential.test.mjs +16 -5
- package/tests/consolidation-feed.test.mjs +96 -0
- package/tests/consolidation-fork.test.mjs +144 -10
- package/tests/consolidation-snapshot.test.mjs +82 -4
- package/tests/curator-absorb.test.mjs +443 -0
- package/tests/curator-calibrate.test.mjs +173 -0
- package/tests/curator-clusters.test.mjs +281 -0
- package/tests/curator-commit.test.mjs +324 -0
- package/tests/curator-evidence.test.mjs +102 -0
- package/tests/curator-link.test.mjs +316 -0
- package/tests/curator-pairing.test.mjs +252 -0
- package/tests/curator-probe.test.mjs +15 -6
- package/tests/curator-review-mode.test.mjs +314 -0
- package/tests/disclosure.test.mjs +2 -1
- package/tests/fixtures/curator-pairs/labeled.v1.jsonl +90 -0
- package/tests/fixtures/lexicon-stub-hooks.mjs +15 -0
- package/tests/fixtures/lexicon-stub.mjs +284 -0
- package/tests/graph-cascade.test.mjs +223 -0
- package/tests/graph.test.mjs +113 -0
- package/tests/help-page.test.mjs +415 -0
- package/tests/hybrid-recall.test.mjs +32 -2
- package/tests/inbound.test.mjs +6 -3
- package/tests/inject-guide.test.mjs +28 -0
- package/tests/inject.test.mjs +157 -5
- package/tests/lexicon-calibrate.test.mjs +48 -0
- package/tests/lexicon-dedupe.test.mjs +171 -0
- package/tests/lexicon-frequency.test.mjs +179 -0
- package/tests/lexicon-inject.test.mjs +410 -0
- package/tests/lexicon-perf.test.mjs +436 -0
- package/tests/lexicon-ui.test.mjs +547 -0
- package/tests/lexicon.test.mjs +922 -0
- package/tests/managed-memory.test.mjs +55 -0
- package/tests/managed-runtime.test.mjs +32 -0
- package/tests/private-links.test.mjs +445 -0
- package/tests/prompts.test.mjs +35 -0
- package/tests/recall-dedup.test.mjs +446 -0
- package/tests/sidebar-beat.test.mjs +5 -1
- package/tests/tool-schema.test.mjs +1 -1
- package/tests/tools.test.mjs +112 -3
- package/tests/vector-migration.test.mjs +58 -1
- package/tests/webui-auth.test.mjs +348 -2
- package/tests/webui.test.mjs +191 -2
package/lib/memu.js
CHANGED
|
@@ -2,7 +2,7 @@ import fs from 'node:fs'
|
|
|
2
2
|
import path from 'node:path'
|
|
3
3
|
import { spawn } from 'node:child_process'
|
|
4
4
|
import { dataDir, ensureDir, redactSecrets, windowsSystemEnv } from './paths.js'
|
|
5
|
-
import { readCurrent, verifyEngine } from './engine.js'
|
|
5
|
+
import { ENGINE_VERSION, readCurrent, verifyEngine } from './engine.js'
|
|
6
6
|
import { getSettings } from './state.js'
|
|
7
7
|
import { activeProfile, captureVectorGeneration, assertVectorGeneration, assertProfileVerified, assertMigrationProfile } from './vector-profile.js'
|
|
8
8
|
import { embeddingWorkerManager } from './local-embedding-manager.js'
|
|
@@ -219,6 +219,6 @@ export async function forget({ db, name, timeoutMs = 60_000 } = {}) {
|
|
|
219
219
|
|
|
220
220
|
export async function probe() {
|
|
221
221
|
const current = currentEngine()
|
|
222
|
-
const verified = await verifyEngine(current.python)
|
|
222
|
+
const verified = await verifyEngine(current.python, { expectedVersion: current.version || ENGINE_VERSION })
|
|
223
223
|
return { ...verified, current }
|
|
224
224
|
}
|
package/lib/migrate.js
CHANGED
|
@@ -7,11 +7,18 @@
|
|
|
7
7
|
// the pass exists to rewrite the files: a library nobody writes to again would otherwise
|
|
8
8
|
// keep the dead `sessionId` fields on disk forever, and an auditor reading the JSON would
|
|
9
9
|
// have to know about a level the code no longer has.
|
|
10
|
+
//
|
|
11
|
+
// The lexicon half (§2.1 of the construction spec) is the same shape: older builds
|
|
12
|
+
// wrote `pending` proposals and revision drafts; the current lifecycle has only active/archived. The rewrite rule
|
|
13
|
+
// lives in `lib/lexicon.js` (`migrateLegacyLexiconStatuses`); this file only finds the
|
|
14
|
+
// files and runs it, one library at a time, so a single corrupt lexicon cannot take the
|
|
15
|
+
// sweep — or startup — down with it.
|
|
10
16
|
import fs from 'node:fs'
|
|
11
17
|
import path from 'node:path'
|
|
12
|
-
import { indexDir, readJson, stamp } from './paths.js'
|
|
18
|
+
import { dataDir, indexDir, isSafeId, readJson, readJsonStrict, stamp } from './paths.js'
|
|
13
19
|
import { listLibrariesAll, updatePrivateIndex } from './state.js'
|
|
14
20
|
import { countLegacyInRaw, migrateEntryVisibility } from './visibility.js'
|
|
21
|
+
import { migrateLegacyLexiconStatuses } from './lexicon.js'
|
|
15
22
|
|
|
16
23
|
const DEFAULT_MIN_INTERVAL_MS = 60_000
|
|
17
24
|
const lastRun = { at: 0 }
|
|
@@ -109,11 +116,85 @@ export function maybeMigrateLibraries({ minIntervalMs = DEFAULT_MIN_INTERVAL_MS,
|
|
|
109
116
|
return migrateLibraries({ now })
|
|
110
117
|
}
|
|
111
118
|
|
|
119
|
+
// ── lexicon lifecycle (§2.1): pending → active/archived ─────────────────────────
|
|
120
|
+
// The directory holds exactly `<libraryId>.json`; a name that does not pass `isSafeId`
|
|
121
|
+
// is skipped rather than guessed at, the same path boundary `lib/lexicon.js` enforces
|
|
122
|
+
// before it builds a filename.
|
|
123
|
+
|
|
124
|
+
function lexiconDir() {
|
|
125
|
+
return path.join(dataDir(), 'lexicon')
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Library ids that own a lexicon file. Sorted, safe names only. */
|
|
129
|
+
export function scanLexiconLibraries() {
|
|
130
|
+
let names = []
|
|
131
|
+
try { names = fs.readdirSync(lexiconDir()) } catch { return [] }
|
|
132
|
+
return names
|
|
133
|
+
.filter((name) => name.endsWith('.json'))
|
|
134
|
+
.map((name) => name.slice(0, -'.json'.length))
|
|
135
|
+
.filter((id) => isSafeId(id))
|
|
136
|
+
.sort()
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Counted on the raw file, before any load-time interpretation, so a sweep can tell a
|
|
140
|
+
// library that still needs the rewrite from one that is already migrated. `readJsonStrict`
|
|
141
|
+
// (not `readJson`) on purpose: a scan must not move files, and `readJson` quarantines a
|
|
142
|
+
// corrupt one as a side effect. A file that does not parse counts 0 — it cannot hold
|
|
143
|
+
// statuses to migrate, and `lib/lexicon.js` already blocks writes to it until restored.
|
|
144
|
+
function legacyLexiconCount(libraryId) {
|
|
145
|
+
const result = readJsonStrict(path.join(lexiconDir(), `${libraryId}.json`))
|
|
146
|
+
if (!result.ok) return 0
|
|
147
|
+
const terms = result.value?.terms
|
|
148
|
+
if (!terms || typeof terms !== 'object') return 0
|
|
149
|
+
return Object.values(terms).filter((entry) => entry && entry.status === 'pending').length
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** Lexicon files that still hold legacy `pending` entries. */
|
|
153
|
+
export function scanLegacyLexiconLibraries() {
|
|
154
|
+
return scanLexiconLibraries()
|
|
155
|
+
.map((libraryId) => ({ libraryId, legacy: legacyLexiconCount(libraryId) }))
|
|
156
|
+
.filter((report) => report.legacy > 0)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Rewrite every lexicon that still carries `pending` entries.
|
|
161
|
+
*
|
|
162
|
+
* Per-library try/catch is the point: a single corrupt or write-protected lexicon must
|
|
163
|
+
* not stop the others, and must never abort startup (the pass runs from the cycle tick).
|
|
164
|
+
* A library with nothing to do is not written at all — `migrateLegacyLexiconStatuses`
|
|
165
|
+
* skips the write (and the revision bump) when no entry changed.
|
|
166
|
+
*/
|
|
167
|
+
export function migrateLexiconLibraries() {
|
|
168
|
+
const reports = []
|
|
169
|
+
for (const { libraryId, legacy } of scanLegacyLexiconLibraries()) {
|
|
170
|
+
try {
|
|
171
|
+
const result = migrateLegacyLexiconStatuses(libraryId)
|
|
172
|
+
reports.push({
|
|
173
|
+
libraryId,
|
|
174
|
+
before: legacy,
|
|
175
|
+
migrated: result.migrated,
|
|
176
|
+
...(result.details ? { details: result.details } : {}),
|
|
177
|
+
})
|
|
178
|
+
} catch (error) {
|
|
179
|
+
reports.push({ libraryId, before: legacy, migrated: 0, error: String(error?.message || error) })
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return reports
|
|
183
|
+
}
|
|
184
|
+
|
|
112
185
|
export function runVisibilityMigrationPass({ now = stamp(), log = () => {} } = {}) {
|
|
113
186
|
const reports = migrateLibraries({ now })
|
|
114
187
|
for (const report of reports) {
|
|
115
188
|
if (report.error) log(`visibility migration failed for ${report.libraryId}: ${report.error}`)
|
|
116
189
|
else if (report.migrated > 0) log(`migrated ${report.migrated} legacy memory/memories to shared in ${report.libraryId}`)
|
|
117
190
|
}
|
|
118
|
-
|
|
191
|
+
// §2.1: legacy lexicons may still carry pending proposals/revision drafts. The rewrite is
|
|
192
|
+
// idempotent and only writes files that change, so running it on every cycle tick
|
|
193
|
+
// costs a directory read plus one JSON parse per file that still has pending entries.
|
|
194
|
+
const lexicon = migrateLexiconLibraries()
|
|
195
|
+
for (const report of lexicon) {
|
|
196
|
+
if (report.error) log(`lexicon lifecycle migration failed for ${report.libraryId}: ${report.error}`)
|
|
197
|
+
else if (report.migrated > 0) log(`migrated ${report.migrated} legacy lexicon term(s) to the active/archived lifecycle in ${report.libraryId}`)
|
|
198
|
+
}
|
|
199
|
+
return { reports, lexicon }
|
|
119
200
|
}
|