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.
Files changed (100) hide show
  1. package/AGENTS.md +1 -1
  2. package/README.md +19 -8
  3. package/README.zh.md +12 -6
  4. package/docs/contributing.md +16 -3
  5. package/docs/env-and-commands.md +8 -0
  6. package/docs/host-api.md +3 -1
  7. package/docs/memory-semantics.md +21 -2
  8. package/docs/troubleshooting.md +42 -3
  9. package/lib/activity-log.js +22 -5
  10. package/lib/backup.js +24 -2
  11. package/lib/binding-diagnostics.js +3 -3
  12. package/lib/client.js +1610 -117
  13. package/lib/consolidation-worker.js +68 -17
  14. package/lib/curator-evidence.js +131 -0
  15. package/lib/curator-policy.js +81 -0
  16. package/lib/curator.js +1299 -83
  17. package/lib/cycle.js +3 -0
  18. package/lib/embedding-download.js +39 -4
  19. package/lib/engine.js +6 -3
  20. package/lib/global-graph.js +282 -0
  21. package/lib/graph.js +190 -14
  22. package/lib/inbound.js +19 -0
  23. package/lib/index.js +33 -1
  24. package/lib/inject.js +277 -17
  25. package/lib/lexicon.js +1115 -0
  26. package/lib/local-vector-service.js +8 -6
  27. package/lib/managed-runtime.js +33 -2
  28. package/lib/memory.js +585 -76
  29. package/lib/memu.js +2 -2
  30. package/lib/migrate.js +83 -2
  31. package/lib/private-links.js +587 -0
  32. package/lib/private.js +128 -7
  33. package/lib/prompts/global-curator.md +76 -19
  34. package/lib/prompts/private-consolidation.coding.md +4 -1
  35. package/lib/prompts/private-consolidation.md +3 -1
  36. package/lib/prompts/private-consolidation.mixed.md +3 -1
  37. package/lib/prompts/private-consolidation.rp.md +3 -1
  38. package/lib/prompts.js +1 -1
  39. package/lib/rpc.js +622 -13
  40. package/lib/state.js +4 -1
  41. package/lib/text-retrieval.js +661 -0
  42. package/lib/text-similarity.js +124 -0
  43. package/lib/tools.js +230 -16
  44. package/lib/vector-migration.js +28 -3
  45. package/lib/vector-profile.js +24 -0
  46. package/lib/webui/standalone.css +17 -0
  47. package/lib/webui/standalone.js +2 -0
  48. package/package.json +3 -2
  49. package/scripts/acceptance-local.mjs +8 -5
  50. package/scripts/curator-pair-calibrate.mjs +259 -0
  51. package/scripts/lexicon-calibrate.mjs +230 -0
  52. package/scripts/lexicon-dedupe.mjs +318 -0
  53. package/scripts/replay.mjs +573 -0
  54. package/tests/absorb-review.test.mjs +353 -0
  55. package/tests/appendix-b.test.mjs +6 -2
  56. package/tests/appendix-b2.test.mjs +2 -2
  57. package/tests/client-export.test.mjs +520 -0
  58. package/tests/client-render.test.mjs +19 -3
  59. package/tests/consolidation-credential.test.mjs +16 -5
  60. package/tests/consolidation-feed.test.mjs +96 -0
  61. package/tests/consolidation-fork.test.mjs +144 -10
  62. package/tests/consolidation-snapshot.test.mjs +82 -4
  63. package/tests/curator-absorb.test.mjs +443 -0
  64. package/tests/curator-calibrate.test.mjs +173 -0
  65. package/tests/curator-clusters.test.mjs +281 -0
  66. package/tests/curator-commit.test.mjs +324 -0
  67. package/tests/curator-evidence.test.mjs +102 -0
  68. package/tests/curator-link.test.mjs +316 -0
  69. package/tests/curator-pairing.test.mjs +252 -0
  70. package/tests/curator-probe.test.mjs +15 -6
  71. package/tests/curator-review-mode.test.mjs +314 -0
  72. package/tests/disclosure.test.mjs +2 -1
  73. package/tests/fixtures/curator-pairs/labeled.v1.jsonl +90 -0
  74. package/tests/fixtures/lexicon-stub-hooks.mjs +15 -0
  75. package/tests/fixtures/lexicon-stub.mjs +284 -0
  76. package/tests/graph-cascade.test.mjs +223 -0
  77. package/tests/graph.test.mjs +113 -0
  78. package/tests/help-page.test.mjs +415 -0
  79. package/tests/hybrid-recall.test.mjs +32 -2
  80. package/tests/inbound.test.mjs +6 -3
  81. package/tests/inject-guide.test.mjs +28 -0
  82. package/tests/inject.test.mjs +157 -5
  83. package/tests/lexicon-calibrate.test.mjs +48 -0
  84. package/tests/lexicon-dedupe.test.mjs +171 -0
  85. package/tests/lexicon-frequency.test.mjs +179 -0
  86. package/tests/lexicon-inject.test.mjs +410 -0
  87. package/tests/lexicon-perf.test.mjs +436 -0
  88. package/tests/lexicon-ui.test.mjs +547 -0
  89. package/tests/lexicon.test.mjs +922 -0
  90. package/tests/managed-memory.test.mjs +55 -0
  91. package/tests/managed-runtime.test.mjs +32 -0
  92. package/tests/private-links.test.mjs +445 -0
  93. package/tests/prompts.test.mjs +35 -0
  94. package/tests/recall-dedup.test.mjs +446 -0
  95. package/tests/sidebar-beat.test.mjs +5 -1
  96. package/tests/tool-schema.test.mjs +1 -1
  97. package/tests/tools.test.mjs +112 -3
  98. package/tests/vector-migration.test.mjs +58 -1
  99. package/tests/webui-auth.test.mjs +348 -2
  100. package/tests/webui.test.mjs +191 -2
@@ -89,17 +89,19 @@ function profileFor(catalogId) {
89
89
  }
90
90
 
91
91
  function installationFor(id, sharedRuntime = null) {
92
- const status = getDownloader().getStatus(id, { deep: false })
92
+ const status = getDownloader().getStatus(id, { deep: true })
93
93
  // 所有可用模型 pin 的是同一个受管 runtime;每个模型各走一遍运行时目录树(几百个文件)
94
94
  // 会让每次 getState 多花几百毫秒。上层已经算过一次就复用。
95
95
  const runtime = sharedRuntime || localVectorRuntimeStatus({ modelId: id })
96
+ const degraded = status.state === 'installed' && status.verified === false
96
97
  return {
97
- status: status.state,
98
- installed: status.installed === true,
99
- modelDir: status.installed ? path.join(root, id) : null,
98
+ status: degraded ? 'degraded' : status.state,
99
+ installed: status.installed === true && !degraded,
100
+ verified: status.verified ?? null,
101
+ modelDir: status.installed && !degraded ? path.join(root, id) : null,
100
102
  runtimeDir: runtime.ready ? runtime.path : null,
101
103
  fingerprintMatches: status.fingerprintMatches ?? null,
102
- error: status.error || null,
104
+ error: degraded ? { code: 'EMBEDDING_INSTALL_TAMPERED', message: 'installed embedding files failed deep verification' } : (status.error || null),
103
105
  progress: status.progress || null,
104
106
  }
105
107
  }
@@ -164,7 +166,7 @@ export function getLocalVectorState() {
164
166
 
165
167
  export function selectLocalVectorModel(id) {
166
168
  const status = getDownloader().getStatus(String(id), { deep: true })
167
- if (status.state !== 'installed' || status.fingerprintMatches === false) {
169
+ if (status.state !== 'installed' || status.verified !== true || status.fingerprintMatches === false) {
168
170
  const error = new Error('install and verify the local embedding model before selecting it')
169
171
  error.code = 'LOCAL_VECTOR_MODEL_NOT_INSTALLED'
170
172
  throw error
@@ -20,6 +20,28 @@ export function verifyRuntimeArchive(data, digest) {
20
20
  if (crypto.createHash('sha256').update(data).digest('hex') !== digest) throw new Error('managed runtime checksum mismatch; refusing to execute')
21
21
  }
22
22
 
23
+ function executableFingerprint(file) {
24
+ try {
25
+ const stat = fs.lstatSync(file)
26
+ if (!stat.isFile() || stat.isSymbolicLink()) return null
27
+ const sha256 = crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex')
28
+ return { bytes: stat.size, sha256 }
29
+ } catch {
30
+ return null
31
+ }
32
+ }
33
+
34
+ function verifiedRuntimeMetadata(file, asset) {
35
+ try {
36
+ const metadata = JSON.parse(fs.readFileSync(file, 'utf8'))
37
+ return metadata && metadata.version === UV_VERSION && metadata.archive === asset[0] && metadata.archiveSha256 === asset[1] && Number.isInteger(metadata.executableBytes) && typeof metadata.executableSha256 === 'string'
38
+ ? metadata
39
+ : null
40
+ } catch {
41
+ return null
42
+ }
43
+ }
44
+
23
45
  export async function ensureManagedPython({ run, log = () => {}, fetcher = fetch } = {}) {
24
46
  const platform = `${process.platform}-${process.arch}`
25
47
  const asset = ASSETS[platform]
@@ -33,7 +55,14 @@ export async function ensureManagedPython({ run, log = () => {}, fetcher = fetch
33
55
  UV_PYTHON_BIN_DIR: ensureDir(path.join(root, 'bin')),
34
56
  UV_NO_PROGRESS: '1', UV_PYTHON_PREFERENCE: 'only-managed', UV_NO_MODIFY_PATH: '1', UV_NO_CONFIG: '1',
35
57
  }
36
- if (!fs.existsSync(executable)) {
58
+ const metadataFile = path.join(bin, 'verified.json')
59
+ const metadata = verifiedRuntimeMetadata(metadataFile, asset)
60
+ const fingerprint = executableFingerprint(executable)
61
+ const reusable = Boolean(metadata && fingerprint && metadata.executableBytes === fingerprint.bytes && metadata.executableSha256 === fingerprint.sha256)
62
+ if (!reusable) {
63
+ if (fingerprint || fs.existsSync(executable)) log(`managed uv ${UV_VERSION} is missing trusted metadata or was modified; replacing it`)
64
+ try { fs.rmSync(executable, { force: true }) } catch {}
65
+ try { fs.rmSync(metadataFile, { force: true }) } catch {}
37
66
  log(`no usable Python with pip; downloading pinned uv ${UV_VERSION}`)
38
67
  const response = await fetcher(`https://github.com/astral-sh/uv/releases/download/${UV_VERSION}/${asset[0]}`, { signal: AbortSignal.timeout(120_000) })
39
68
  if (!response.ok || Number(response.headers.get('content-length')) > 64 * 1024 * 1024) throw new Error('managed runtime download failed or is too large')
@@ -63,7 +92,9 @@ export async function ensureManagedPython({ run, log = () => {}, fetcher = fetch
63
92
  }
64
93
  if (fs.existsSync(temporary)) fs.renameSync(temporary, executable)
65
94
  if (process.platform !== 'win32') fs.chmodSync(executable, 0o700)
66
- writeJson(path.join(bin, 'verified.json'), { version: UV_VERSION, archive: asset[0], sha256: asset[1] })
95
+ const installedFingerprint = executableFingerprint(executable)
96
+ if (!installedFingerprint) throw new Error('managed runtime executable is not a regular file')
97
+ writeJson(metadataFile, { version: UV_VERSION, archive: asset[0], archiveSha256: asset[1], executableBytes: installedFingerprint.bytes, executableSha256: installedFingerprint.sha256 })
67
98
  }
68
99
  const version = await run(executable, ['--version'], { env, timeoutMs: 20_000 })
69
100
  if (version.code !== 0 || !version.stdout.startsWith(`uv ${UV_VERSION} `) && version.stdout.trim() !== `uv ${UV_VERSION}`) throw new Error('managed uv version mismatch')