solopup-memory 0.21.0-beta.2 → 0.22.0-beta.3

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 (99) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/COMPATIBILITY.md +8 -5
  3. package/DETECTION.md +139 -0
  4. package/DREAMING-DESIGN.md +167 -0
  5. package/README.en.md +35 -6
  6. package/README.md +44 -7
  7. package/RELEASE.md +36 -0
  8. package/SECURITY.md +11 -0
  9. package/context-history.mjs +20 -3
  10. package/detect.mjs +441 -0
  11. package/eval/bench-detect.mjs +107 -0
  12. package/eval/semantic/cases.json +387 -0
  13. package/eval/semantic-suite.mjs +156 -0
  14. package/index.mjs +117 -27
  15. package/managed-policy.mjs +3 -1
  16. package/package.json +91 -68
  17. package/privacy.mjs +25 -0
  18. package/scripts/demo-source-recall.mjs +123 -0
  19. package/scripts/detect.mjs +69 -0
  20. package/scripts/dream.mjs +205 -0
  21. package/scripts/probe-history.mjs +92 -0
  22. package/scripts/probe-load.mjs +1 -1
  23. package/scripts/probe-package.mjs +6 -2
  24. package/scripts/security-check.mjs +173 -0
  25. package/scripts/soak.mjs +219 -0
  26. package/scripts/telemetry.mjs +187 -0
  27. package/scripts/verify.mjs +3 -1
  28. package/{test-adversarial.mjs → test/test-adversarial.mjs} +5 -5
  29. package/{test-belief-revision.mjs → test/test-belief-revision.mjs} +7 -7
  30. package/{test-candidates.mjs → test/test-candidates.mjs} +4 -4
  31. package/{test-capture-diagnostics.mjs → test/test-capture-diagnostics.mjs} +6 -6
  32. package/{test-capture-field-lineage.mjs → test/test-capture-field-lineage.mjs} +4 -4
  33. package/{test-capture-inputs.mjs → test/test-capture-inputs.mjs} +8 -8
  34. package/{test-capture-jobs.mjs → test/test-capture-jobs.mjs} +5 -5
  35. package/{test-capture-projections.mjs → test/test-capture-projections.mjs} +12 -12
  36. package/{test-capture-revision.mjs → test/test-capture-revision.mjs} +6 -6
  37. package/{test-capture-submission.mjs → test/test-capture-submission.mjs} +3 -3
  38. package/{test-capture-validation-flow.mjs → test/test-capture-validation-flow.mjs} +5 -5
  39. package/{test-capture-validation-job.mjs → test/test-capture-validation-job.mjs} +5 -5
  40. package/{test-capture-validation-lineage.mjs → test/test-capture-validation-lineage.mjs} +2 -2
  41. package/{test-capture-validation.mjs → test/test-capture-validation.mjs} +2 -2
  42. package/{test-capture.mjs → test/test-capture.mjs} +20 -20
  43. package/{test-compaction-compat.mjs → test/test-compaction-compat.mjs} +5 -5
  44. package/{test-compat.mjs → test/test-compat.mjs} +3 -3
  45. package/{test-concurrency.mjs → test/test-concurrency.mjs} +3 -3
  46. package/{test-conflict.mjs → test/test-conflict.mjs} +2 -2
  47. package/{test-context-history.mjs → test/test-context-history.mjs} +1 -1
  48. package/{test-control-schema.mjs → test/test-control-schema.mjs} +1 -1
  49. package/test/test-detect.mjs +125 -0
  50. package/test/test-dream-plan.mjs +72 -0
  51. package/test/test-dsh-registration.mjs +111 -0
  52. package/{test-episodes.mjs → test/test-episodes.mjs} +5 -5
  53. package/{test-explicit-memory.mjs → test/test-explicit-memory.mjs} +4 -4
  54. package/{test-field-lineage.mjs → test/test-field-lineage.mjs} +4 -4
  55. package/{test-history.mjs → test/test-history.mjs} +4 -4
  56. package/{test-inline-summary.mjs → test/test-inline-summary.mjs} +5 -5
  57. package/{test-legacy-source-identity.mjs → test/test-legacy-source-identity.mjs} +2 -2
  58. package/{test-links.mjs → test/test-links.mjs} +4 -4
  59. package/{test-managed-access.mjs → test/test-managed-access.mjs} +29 -5
  60. package/{test-managed-activation.mjs → test/test-managed-activation.mjs} +5 -5
  61. package/{test-meaning.mjs → test/test-meaning.mjs} +6 -6
  62. package/{test-native-service-lifecycle.mjs → test/test-native-service-lifecycle.mjs} +1 -1
  63. package/{test-native-write-validation.mjs → test/test-native-write-validation.mjs} +10 -4
  64. package/{test-packaging.mjs → test/test-packaging.mjs} +32 -5
  65. package/{test-paired-analysis.mjs → test/test-paired-analysis.mjs} +1 -1
  66. package/{test-patch.mjs → test/test-patch.mjs} +11 -11
  67. package/{test-plugin-contract.mjs → test/test-plugin-contract.mjs} +4 -4
  68. package/test/test-privacy.mjs +65 -0
  69. package/{test-project-stores.mjs → test/test-project-stores.mjs} +5 -5
  70. package/{test-publication.mjs → test/test-publication.mjs} +4 -4
  71. package/{test-restore.mjs → test/test-restore.mjs} +11 -11
  72. package/{test-retrieval.mjs → test/test-retrieval.mjs} +2 -2
  73. package/{test-revision-admission.mjs → test/test-revision-admission.mjs} +3 -3
  74. package/{test-rule-admission.mjs → test/test-rule-admission.mjs} +8 -8
  75. package/{test-runtime-startup.mjs → test/test-runtime-startup.mjs} +2 -2
  76. package/{test-schema.mjs → test/test-schema.mjs} +2 -2
  77. package/{test-security.mjs → test/test-security.mjs} +6 -6
  78. package/test/test-semantic-suite.mjs +20 -0
  79. package/{test-service-readiness.mjs → test/test-service-readiness.mjs} +1 -1
  80. package/{test-smell.mjs → test/test-smell.mjs} +4 -4
  81. package/{test-snapshots.mjs → test/test-snapshots.mjs} +3 -3
  82. package/{test-source-completeness.mjs → test/test-source-completeness.mjs} +6 -6
  83. package/{test-source-key.mjs → test/test-source-key.mjs} +2 -2
  84. package/{test-source-retirement.mjs → test/test-source-retirement.mjs} +4 -4
  85. package/{test-status-both.mjs → test/test-status-both.mjs} +12 -4
  86. package/{test-supersede.mjs → test/test-supersede.mjs} +3 -3
  87. package/test/test-telemetry.mjs +60 -0
  88. package/{test-text-admission.mjs → test/test-text-admission.mjs} +6 -6
  89. package/{test-tool-inputs.mjs → test/test-tool-inputs.mjs} +1 -1
  90. package/{test-tool-observations.mjs → test/test-tool-observations.mjs} +6 -6
  91. package/{test-tool-write-guard.mjs → test/test-tool-write-guard.mjs} +3 -3
  92. package/{test-tool-write-lifecycle.mjs → test/test-tool-write-lifecycle.mjs} +3 -3
  93. package/{test-usability.mjs → test/test-usability.mjs} +7 -7
  94. package/{test-validation-source-refs.mjs → test/test-validation-source-refs.mjs} +2 -2
  95. package/{test-validation-transition-context.mjs → test/test-validation-transition-context.mjs} +1 -1
  96. package/{test-withdrawal-archives.mjs → test/test-withdrawal-archives.mjs} +6 -6
  97. package/{test-withdrawal.mjs → test/test-withdrawal.mjs} +2 -2
  98. package/{test.mjs → test/test.mjs} +4 -4
  99. package/version.mjs +1 -1
package/index.mjs CHANGED
@@ -5,6 +5,7 @@ export {initializeManagedMemory,managedMemoryStatus} from './managed-access.mjs'
5
5
  import {createObservationTracker,observationRoots,OBSERVATION_REFERENCE_SCHEMA,OBSERVATION_LIST_SCHEMA} from './tool-observations.mjs'
6
6
  import {createToolInputTracker} from './tool-inputs.mjs'
7
7
  import {memoryProtocol} from './memory-protocol.mjs'
8
+ import {privateMemory,publicMemoryRecords,publicMemoryIndex,PRIVATE_MEMORY_NOTICE} from './privacy.mjs'
8
9
  import {nativeOwnerEvidence,validateNativeWrite} from './native-write-validation.mjs'
9
10
  import {toolProposal} from './source-record.mjs'
10
11
  import {revisionMode,prepareValueRevision,inheritedRoots} from './field-lineage.mjs'
@@ -291,8 +292,9 @@ function renderScopeLocal(scope, dir) {
291
292
  let flagged = 0
292
293
  const kept = []
293
294
  const policy = withdrawalPolicy(dir)
294
- const typed=activeMeanings(candidateSnapshot(dir,scope).records),assessmentRecords=effectiveSourceRecords(dir,scope)
295
+ const typed=activeMeanings(candidateSnapshot(dir,scope).records),assessmentRecords=publicMemoryRecords(effectiveSourceRecords(dir,scope))
295
296
  for (const line of filterWithdrawn(index.text, policy, true).split('\n')) {
297
+ if (privateMemory(meaningForLine(typed,line),line)) continue
296
298
  if (line.trim().startsWith('- [')) {
297
299
  if (isExpired(line, now)) {
298
300
  hidden += 1
@@ -888,7 +890,7 @@ function writeMemoryLocal({ type, dir, line, detail, summary, expires, supersede
888
890
  }
889
891
 
890
892
  // 相似度闸:改写同一件事却再叠一条,会让新旧两条同时被注入、给出矛盾信息。
891
- // 0.95 以上直接拒绝并指出候选;0.85–0.95 放行但带警告(避免误伤「相似但不同」的条目)。
893
+ // 0.9 以上直接拒绝并指出候选;0.8–0.9 放行但带警告(避免误伤「相似但不同」的条目)。
892
894
  let similarWarning
893
895
  // 完全重复走 duplicate 分支(幂等返回),相似度闸只管「像但不是同一句」
894
896
  const alias = hasTopicDetail(cleanDetail) ? undefined : routeAlias(cleanLine, type)
@@ -1048,16 +1050,19 @@ function writeMemoryLocal({ type, dir, line, detail, summary, expires, supersede
1048
1050
  evidence: provenance?.evidence,
1049
1051
  basis: provenance?.basis,
1050
1052
  })
1053
+ const privateLines=new Set(liveSourceRecords(dir,scope).filter(privateMemory).map(record=>record.indexLine).filter(Boolean))
1054
+ const hiddenLine=line=>privateLines.has(line)||privateMemory(line)
1055
+ const visibleWarnings=warnings.filter(w=>!privateMemory(w)&&![...privateLines].some(line=>w.includes(summaryOf(line))))
1051
1056
  return {
1052
1057
  path: indexFile,
1053
1058
  indexLine: cleanLine,
1054
1059
  duplicate: false,
1055
1060
  lineCount: next.split(/\r?\n/).length,
1056
1061
  ...(detailPath === undefined ? {} : { detailPath }),
1057
- ...(supersededLine === undefined ? {} : { superseded: supersededLine }),
1062
+ ...(supersededLine === undefined||hiddenLine(supersededLine) ? {} : { superseded: supersededLine }),
1058
1063
  id: newId,
1059
- related,
1060
- ...(warnings.length === 0 ? {} : { warning: warnings.join(';') }),
1064
+ related:related.filter(edge=>!hiddenLine(edge.line)),
1065
+ ...(visibleWarnings.length === 0 ? {} : { warning: visibleWarnings.join(';') }),
1061
1066
  }
1062
1067
  }
1063
1068
  return lockToken === REVIEW_LOCK ? execute() : withLock(dir, execute)
@@ -1219,8 +1224,32 @@ function statusOfLocal(dir) {
1219
1224
  }
1220
1225
 
1221
1226
  /**
1222
- * 在索引与主题文件里做子串检索。
1223
- *
1227
+ * L0 检测摘要:把 detect.mjs 的 findings 压成计数,避免状态输出膨胀。
1228
+ * 动态导入是为了不形成 index ↔ detect 的静态循环(detect 依赖本文件的纯函数)。
1229
+ */
1230
+ async function lintStatus(dir, scope) {
1231
+ try {
1232
+ const { detectStore } = await import('./detect.mjs')
1233
+ const report = detectStore(dir, { scope })
1234
+ const counts = new Map()
1235
+ for (const finding of report.findings) {
1236
+ if (finding.severity === 'info') continue
1237
+ const row = counts.get(finding.id) ?? { id: finding.id, severity: finding.severity, count: 0 }
1238
+ row.count += 1
1239
+ counts.set(finding.id, row)
1240
+ }
1241
+ return {
1242
+ errors: report.summary.errors,
1243
+ warnings: report.summary.warnings,
1244
+ checks: [...counts.values()].sort((left, right) => (left.id < right.id ? -1 : left.id > right.id ? 1 : 0)),
1245
+ }
1246
+ } catch {
1247
+ // 检测器本身坏了不能伪装成“库没问题”。
1248
+ return { errors: 0, warnings: 1, checks: [{ id: 'lint-unavailable', severity: 'warning', count: 1 }] }
1249
+ }
1250
+ }
1251
+
1252
+ /**
1224
1253
  * 存在的原因:索引超上限时,被省略的条目对模型完全不可见(`lookupLinks` 只查索引)。
1225
1254
  * 检索是确定性的:归一化后做子串匹配,不做正则、不做模糊。
1226
1255
  *
@@ -1245,7 +1274,7 @@ function effectiveSourceRecords(dir,scope,indexText,includeUntyped=false){
1245
1274
  }
1246
1275
 
1247
1276
  /** Current index-only legacy entries are visible as unclassified data, never inherited sources. */
1248
- function validationCurrentRecords(dir,scope,records){
1277
+ function validationCurrentRecords(dir,scope,records,privateTarget){
1249
1278
  const policy=withdrawalPolicy(dir),index=filterWithdrawn(readTextSafe(join(dir,'MEMORY.md'))??'',policy,true)
1250
1279
  const known=new Set(records.map(r=>r.indexLine??indexLine(r.type,r.summary,r.expires,r.detail!==undefined))),rows=[...records]
1251
1280
  for(const line of new Set(index.split(/\r?\n/).map(line=>line.trim()))){
@@ -1253,7 +1282,7 @@ function validationCurrentRecords(dir,scope,records){
1253
1282
  const type=/^- \[(user|feedback|project|reference)\] /.exec(line)?.[1];if(!type)continue
1254
1283
  rows.push({id:createHash('sha256').update('memory-unclassified-current-index:'+scope+':'+line).digest('hex'),status:'recorded',scope,type,summary:summaryOf(line),indexLine:line,legacyIndexOnly:true,expires:/ \(exp:(\d{4}-\d{2}-\d{2})\)$/.exec(line)?.[1]})
1255
1284
  }
1256
- return rows
1285
+ return rows.filter(row=>!privateMemory(row)||typeof privateTarget==='string'&&privateTarget.length>0&&row.indexLine?.includes(privateTarget))
1257
1286
  }
1258
1287
 
1259
1288
  function publishIndexChange(dir,before,after,reason){
@@ -1269,6 +1298,7 @@ function searchHistoricalStores(stores,needle,options,context,task,limit,mode){
1269
1298
  const policy=withdrawalPolicy(dir),index=filterWithdrawn(readTextSafe(join(dir,'MEMORY.md'))??'',policy,true)
1270
1299
  const result=historyRows(dir,{scope,index,revoked:policy.revoked,atSequence:options.atSequence,asOf:options.asOf,expiredAt:isExpired});coverage.push(result.coverage)
1271
1300
  for(const {row,line,history}of result.rows){
1301
+ if(options.includePrivate!==true&&privateMemory(row.source,row.line))continue
1272
1302
  const interpretation=row.source.meaning?meaningView(row.source,context,task):undefined
1273
1303
  if(interpretation?.applicability==='not-applicable')continue
1274
1304
  if(interpretation)interpretation.advice={action:'continue',reason:'historical-record-is-not-a-current-instruction',authority:'none'}
@@ -1298,6 +1328,7 @@ function searchScopedStoresLocal(stores, query, options = {}) {
1298
1328
  if (needle.length > 200) throw new Error('memory_search: query 太长(上限 200 字)')
1299
1329
  const limit = Math.max(1, Math.min(20, options.limit ?? 8))
1300
1330
  const mode = options.mode ?? 'ranked'
1331
+ if(options.includePrivate!==undefined&&typeof options.includePrivate!=='boolean')throw Error('memory-search-invalid-private-option')
1301
1332
  if (mode !== 'exact' && mode !== 'ranked') throw new Error('memory_search: mode 必须是 exact 或 ranked')
1302
1333
  const context=normalizeContext(options.context),task=normalizeTask(options.task)
1303
1334
  if(options.view!==undefined&&!['current','history'].includes(options.view))throw new Error('memory-search-invalid-view')
@@ -1306,7 +1337,7 @@ function searchScopedStoresLocal(stores, query, options = {}) {
1306
1337
 
1307
1338
  const files = stores.flatMap(({ dir, scope }) => {
1308
1339
  const policy = withdrawalPolicy(dir)
1309
- const owned=liveSourceRecords(dir,scope).filter(record=>['recorded','adopted'].includes(record.status)),typed=activeMeanings(owned),assessmentRecords=effectiveSourceRecords(dir,scope)
1340
+ const owned=liveSourceRecords(dir,scope).filter(record=>['recorded','adopted'].includes(record.status)),typed=activeMeanings(owned),assessmentRecords=options.includePrivate===true?effectiveSourceRecords(dir,scope):publicMemoryRecords(effectiveSourceRecords(dir,scope))
1310
1341
  return ['MEMORY.md', ...TYPES.map(type => `${type}.md`)].map(name => ({
1311
1342
  name, path: join(dir, name), policy, scope, typed, owned, assessmentRecords, key: scope === undefined ? name : `${scope}/${name}`,
1312
1343
  }))
@@ -1319,12 +1350,16 @@ function searchScopedStoresLocal(stores, query, options = {}) {
1319
1350
  for (const file of files) {
1320
1351
  const text = readTextSafe(file.path)
1321
1352
  if (text === undefined) continue
1353
+ // Legacy topic paragraphs may span lines without record markers. Never
1354
+ // return the unlabelled continuation of a recognized private paragraph.
1355
+ if(file.name!=='MEMORY.md'&&options.includePrivate!==true&&privateMemory(text))continue
1322
1356
  let lineNumber = 0
1323
1357
  const filtered=filterWithdrawn(text,file.policy,file.name==='MEMORY.md'),details=file.name==='MEMORY.md'?undefined:detailedMeanings(file.owned,filtered)
1324
1358
  for (const raw of filtered.split('\n')) {
1325
1359
  lineNumber += 1
1326
1360
  if(file.name==='MEMORY.md'&&isExpired(raw))continue
1327
1361
  const record=file.name==='MEMORY.md'?meaningForLine(file.typed,raw):details.get(lineNumber)
1362
+ if(options.includePrivate!==true&&privateMemory(record,raw))continue
1328
1363
  if(record?.expires&&isExpired(`placeholder (exp:${record.expires})`))continue
1329
1364
  const interpretation=record?.meaning?meaningView(record,context,task,file.assessmentRecords):undefined
1330
1365
  if(interpretation?.applicability==='not-applicable')continue
@@ -1599,6 +1634,8 @@ function readLog(dir) {
1599
1634
  * @param query - `{ id }` 或 `{ match }`(索引行子串,必须唯一)。
1600
1635
  */
1601
1636
  function lookupLinksLocal(dir, query) {
1637
+ const hiddenLines=new Set(liveSourceRecords(dir).filter(privateMemory).map(record=>record.indexLine).filter(Boolean))
1638
+ const hidden=line=>hiddenLines.has(line)||privateMemory(line)
1602
1639
  assertPublicationClear(dir)
1603
1640
  const policy = withdrawalPolicy(dir)
1604
1641
  const events = readLog(dir).filter((e) => !policy.memoryIds.includes(e.id) && !policy.lineHashes.includes(normalizedLineHash(String(e.line ?? ''))))
@@ -1607,7 +1644,7 @@ function lookupLinksLocal(dir, query) {
1607
1644
  const live = new Map()
1608
1645
  for (const line of current.split('\n')) {
1609
1646
  const trimmed = line.trim()
1610
- if (trimmed.startsWith('- [')) live.set(entryId(trimmed), trimmed)
1647
+ if (trimmed.startsWith('- [')&&!hidden(trimmed)) live.set(entryId(trimmed), trimmed)
1611
1648
  }
1612
1649
 
1613
1650
  let key = query.id
@@ -1628,8 +1665,9 @@ function lookupLinksLocal(dir, query) {
1628
1665
  const inserted = events.filter((event) => event.op === 'insert' && event.id === key)
1629
1666
  const line = live.get(key) ?? inserted.at(-1)?.line ?? events.find((event) => event.id === key)?.line
1630
1667
  if (line === undefined) return { found: false, related: [], backlinks: [] }
1668
+ if(hidden(line))return{found:false,related:[],backlinks:[]}
1631
1669
 
1632
- const related = inserted.at(-1)?.related ?? []
1670
+ const related = (inserted.at(-1)?.related ?? []).filter(item=>!hidden(item.line))
1633
1671
 
1634
1672
  // 入边要顺着取代链找:指向被取代旧条目的边,等同指向新条目。
1635
1673
  // 否则「改了一条记忆」就等于把它从图上拔掉。
@@ -1640,6 +1678,7 @@ function lookupLinksLocal(dir, query) {
1640
1678
  const backlinks = []
1641
1679
  for (const event of events) {
1642
1680
  if (event.op !== 'insert' || lineage.has(event.id)) continue
1681
+ if(hidden(event.line))continue
1643
1682
  if (!(event.related ?? []).some((item) => lineage.has(item.id))) continue
1644
1683
  if (backlinks.some((item) => item.id === event.id)) continue
1645
1684
  backlinks.push({ id: event.id, line: event.line })
@@ -1661,7 +1700,7 @@ function lookupLinksLocal(dir, query) {
1661
1700
  backlinks: backlinks.slice(0, RELATION_LIMIT),
1662
1701
  backlinksTotal: backlinks.length,
1663
1702
  ...(replaced?.supersededById === undefined ? {} : { supersededById: replaced.supersededById }),
1664
- ...(supersedesLine === undefined ? {} : { supersedesLine }),
1703
+ ...(supersedesLine === undefined||hidden(supersedesLine) ? {} : { supersedesLine }),
1665
1704
  }
1666
1705
  }
1667
1706
 
@@ -1777,7 +1816,7 @@ async function admitNativeWrite(ctx,sourceInputs,config,args,exec,target){
1777
1816
  const revision=readOwned(()=>{
1778
1817
  const value=storeRevision(target.dir)
1779
1818
  const sourceRecords=effectiveSourceRecords(target.dir,target.scope,undefined,true)
1780
- currentRecords=validationCurrentRecords(target.dir,target.scope,sourceRecords)
1819
+ currentRecords=validationCurrentRecords(target.dir,target.scope,sourceRecords,target.supersedes)
1781
1820
  if(target.supersedes!==undefined){
1782
1821
  const hits=(readTextSafe(join(target.dir,'MEMORY.md'))??'').split(/\r?\n/).filter(line=>line.trim().startsWith('- [')&&line.includes(target.supersedes));if(hits.length!==1)throw Error('memory-native-supersedes-not-unique')
1783
1822
  const compiled=prepareValueRevision(target,sourceRecords,hits[0].trim())
@@ -1946,6 +1985,7 @@ function defineSearchTool(ctx) {
1946
1985
  name: 'memory_search',
1947
1986
  description:
1948
1987
  '在记忆索引与主题文件里检索。默认 ranked 支持中英多词问题与有限英文词形归并;exact 用于精确子串定位;' +
1988
+ '默认隐藏已识别的个人敏感内容。用户询问相关资料时才设includePrivate=true;返回内容会进入当前会话日志和模型请求,不能声称只在本地读取。' +
1949
1989
  '默认同时查当前项目和全局偏好,不查其他项目;可指定单库。view=history显式查询带来源的旧值、取代与移出记录,返回历史身份及覆盖范围,不能当当前指令。',
1950
1990
  parameters: {
1951
1991
  type: 'object',
@@ -1953,6 +1993,7 @@ function defineSearchTool(ctx) {
1953
1993
  required: ['query'],
1954
1994
  properties: {
1955
1995
  query: { type: 'string', description: '检索词,尽量具体(≤200 字);也可给返回的完整64位recordId精确定位来源条目,不能用8位索引id' },
1996
+ includePrivate:{type:'boolean',description:'仅在用户询问相关个人资料时明确设true,允许读取被自动降载的个人内容。默认false;不是文件访问权限或完整隐私识别器。'},
1956
1997
  mode: { type: 'string', enum: ['exact', 'ranked'], description: 'ranked 默认多词排序;exact 精确定位。不是语义向量检索,不支持完整同义词或英文词形分析' },
1957
1998
  view:{type:'string',enum:['current','history'],description:'current默认只查当前视图;history查受管历史档案(已撤回来源仍不可见)。旧日志不会被伪造成完整历史。'},
1958
1999
  atSequence:{type:'integer',description:'仅history:按选定单库的记录序号查询当时有效的条目,必须指定scope=project或global;不是跨库统一版本。'},
@@ -2000,10 +2041,10 @@ function defineSearchTool(ctx) {
2000
2041
  return searchScopedStores([
2001
2042
  { dir: resolveStoreDir(ctx, exec, 'memory_search', 'project'), scope: 'project' },
2002
2043
  { dir: resolveStoreDir(ctx, exec, 'memory_search', 'global'), scope: 'global' },
2003
- ], args.query, { limit: args.limit, mode: args.mode, context:args.context,task:args.task,view:args.view,atSequence:args.atSequence,asOf:args.asOf })
2044
+ ], args.query, { includePrivate:args.includePrivate,limit: args.limit, mode: args.mode, context:args.context,task:args.task,view:args.view,atSequence:args.atSequence,asOf:args.asOf })
2004
2045
  }
2005
2046
  const dir = resolveStoreDir(ctx, exec, 'memory_search', args.scope)
2006
- return searchStore(dir, args.query, { limit: args.limit, mode: args.mode, context:args.context,task:args.task,view:args.view,atSequence:args.atSequence,asOf:args.asOf })
2047
+ return searchStore(dir, args.query, { includePrivate:args.includePrivate,limit: args.limit, mode: args.mode, context:args.context,task:args.task,view:args.view,atSequence:args.atSequence,asOf:args.asOf })
2007
2048
  },
2008
2049
  }
2009
2050
  }
@@ -2134,6 +2175,29 @@ function defineStatusTool(ctx, sourceInputs) {
2134
2175
  expired: { type: 'integer' },
2135
2176
  duplicateGroups: { type: 'integer' },
2136
2177
  storeBytes: { type: 'integer' },
2178
+ // L0 检测摘要:只给计数与检查项;明细走 `node scripts/detect.mjs --json`。
2179
+ lint: {
2180
+ type: 'object',
2181
+ additionalProperties: false,
2182
+ required: ['errors', 'warnings', 'checks'],
2183
+ properties: {
2184
+ errors: { type: 'integer' },
2185
+ warnings: { type: 'integer' },
2186
+ checks: {
2187
+ type: 'array',
2188
+ items: {
2189
+ type: 'object',
2190
+ additionalProperties: false,
2191
+ required: ['id', 'severity', 'count'],
2192
+ properties: {
2193
+ id: { type: 'string' },
2194
+ severity: { type: 'string', enum: ['error', 'warning'] },
2195
+ count: { type: 'integer' },
2196
+ },
2197
+ },
2198
+ },
2199
+ },
2200
+ },
2137
2201
  sourceLedger: { type: 'object', additionalProperties: false,
2138
2202
  required: ['available', 'fullLimit', 'pendingLimit', 'retiredLimit', 'byteLimit'],
2139
2203
  properties: { available: { type: 'boolean' }, fullRecords: { type: 'integer' }, retiredRecords: { type: 'integer' }, pending: { type: 'integer' }, bytes: { type: 'integer' }, fullLimit: { type: 'integer' }, pendingLimit: { type: 'integer' }, retiredLimit: { type: 'integer' }, byteLimit: { type: 'integer' }, reason: { type: 'string', enum: ['source-ledger-unavailable'] } } },
@@ -2160,7 +2224,8 @@ function defineStatusTool(ctx, sourceInputs) {
2160
2224
  text:
2161
2225
  `索引 ${value.index.lines}/${value.index.limitLines} 行、${value.index.bytes}/${value.index.limitBytes} 字节;` +
2162
2226
  `类型:${types};过期 ${value.expired} 条;重复组 ${value.duplicateGroups};` +
2163
- `主题文件:${topics || '无'};库占用 ${(value.storeBytes / 1024).toFixed(1)}K。\n` +
2227
+ `主题文件:${topics || '无'};库占用 ${(value.storeBytes / 1024).toFixed(1)}K。` +
2228
+ (value.lint ? `\n检测(L0):error ${value.lint.errors}、warning ${value.lint.warnings}` + (value.lint.checks.length ? ';' + value.lint.checks.map((check) => `${check.id}×${check.count}`).join('、') : '') + '(明细:node scripts/detect.mjs)' : '') + '\n' +
2164
2229
  (value.sourceLedger?.available ? `来源记录 ${value.sourceLedger.fullRecords}/${value.sourceLedger.fullLimit};历史来源凭据 ${value.sourceLedger.retiredRecords}/${value.sourceLedger.retiredLimit};待审 ${value.sourceLedger.pending}/${value.sourceLedger.pendingLimit};来源占用 ${(value.sourceLedger.bytes / 1024).toFixed(1)}/${value.sourceLedger.byteLimit / 1024}K。\n` : '来源账本待修复,容量状态未知。\n') + value.dir+(value.capture?'\n捕获诊断(全局保留作业):'+(!value.capture.available?'账本不可用,未知':value.capture.latest?.diagnostic?value.capture.latest.diagnostic.classification+';解析拒绝'+value.capture.latest.diagnostic.parserRejected+',截断'+value.capture.latest.diagnostic.deferred+',准入拒绝'+value.capture.latest.diagnostic.admissionRejected+',写入失败'+value.capture.latest.diagnostic.writeFailures+';不证明没有漏记':value.capture.latest?'旧作业无细分诊断或仍未完成,不能推断空结果':'尚无捕获作业记录;不能推断没有长期信息'):'')+(value.capture?.blockedInput?'\n自动整理未执行:用户来源不完整;没有从截断的前半段建立记忆。':'')+(value.executionObservations?'\n可引用执行结果(复制callId及原文):\n'+value.executionObservations.items.map(item=>JSON.stringify(item)).join('\n')+'\n'+value.executionObservations.note+(value.executionObservations.truncated?'(列表有省略,优先核对最近结果)':''):'') ,
2165
2230
  },
2166
2231
  ]
@@ -2169,6 +2234,7 @@ function defineStatusTool(ctx, sourceInputs) {
2169
2234
  async execute(args, exec) {
2170
2235
  const dir = resolveStoreDir(ctx, exec, 'memory_status', args.scope)
2171
2236
  const value={...statusOf(dir),capture:managedRead(storeRoot(),()=>captureJobStatus(storeRoot()))}
2237
+ value.lint = await lintStatus(dir, dir === storeRoot() ? 'global' : 'project')
2172
2238
  if(args.includeObservations!==undefined&&typeof args.includeObservations!=='boolean')throw Error('memory-observation-invalid-list-option')
2173
2239
  if(args.includeObservations===true){if(!captureSessionEligible(exec?.agent?.session))throw Error('memory-observation-source-unavailable');return{...value,executionObservations:sourceInputs.availableForExecution(exec)}}
2174
2240
  return value
@@ -2196,7 +2262,7 @@ function defineStatusTool(ctx, sourceInputs) {
2196
2262
  if(args.scope!=='both')return singleExecute(args,exec)
2197
2263
  if(args.includeObservations!==undefined&&typeof args.includeObservations!=='boolean')throw Error('memory-observation-invalid-list-option')
2198
2264
  const root=storeRoot()
2199
- return managedRead(root,()=>{
2265
+ const result=managedRead(root,()=>{
2200
2266
  const stores=[{scope:'global',available:true,status:statusOf(root)}];let project
2201
2267
  if(!exec?.agent||typeof ctx.workingDirectory?.get!=='function')project={scope:'project',available:false,reason:'no-current-project'}
2202
2268
  else{
@@ -2212,10 +2278,14 @@ function defineStatusTool(ctx, sourceInputs) {
2212
2278
  }catch{project={scope:'project',available:false,reason:'project-status-unavailable'}}
2213
2279
  }
2214
2280
  stores.push(project)
2215
- const value={scope:'both',stores,capture:captureJobStatus(root)}
2216
- if(args.includeObservations===true){if(!captureSessionEligible(exec?.agent?.session))throw Error('memory-observation-source-unavailable');value.executionObservations=sourceInputs.availableForExecution(exec)}
2217
- return value
2281
+ return {scope:'both',stores,capture:captureJobStatus(root)}
2218
2282
  })
2283
+ for(const row of result.stores){
2284
+ if(!row.available)continue
2285
+ row.status.lint=await lintStatus(row.status.dir,row.scope)
2286
+ }
2287
+ if(args.includeObservations===true){if(!captureSessionEligible(exec?.agent?.session))throw Error('memory-observation-source-unavailable');result.executionObservations=sourceInputs.availableForExecution(exec)}
2288
+ return result
2219
2289
  }
2220
2290
  return definition
2221
2291
  }
@@ -2255,7 +2325,7 @@ function defineForgetTool(ctx) {
2255
2325
  text:
2256
2326
  value.removed === 0
2257
2327
  ? `没有匹配到任何索引行:${value.path}`
2258
- : `已删除 ${value.removed} 条(${value.path})`,
2328
+ : `已停止使用 ${value.removed} 条索引记忆(${value.path})。原话、主题正文、会话日志、备份及已发送内容未由此清除;这不是历史数据擦除。`,
2259
2329
  },
2260
2330
  ],
2261
2331
  },
@@ -2430,13 +2500,14 @@ function setupAutoCapture(ctx, config) {
2430
2500
  const captureDirs=[storeRoot(),projectDir].filter(Boolean)
2431
2501
  const memorySnapshot=managedReadMany(captureDirs,()=>{
2432
2502
  const stores=captureDirs.map(dir=>{const scope=dir===storeRoot()?'global':'project',policy=withdrawalPolicy(dir);return{dir,scope,policy,records:effectiveSourceRecords(dir,scope,undefined,true),indexLines:filterWithdrawn(readTextSafe(join(dir,'MEMORY.md'))??'',policy,true).split('\n').map(line=>line.trim()).filter(line=>line.startsWith('- [')).slice(-60)}})
2433
- const projections=stores.flatMap(store=>store.records.flatMap(record=>{
2503
+ for(const store of stores)store.indexLines=publicMemoryIndex(store.indexLines,store.records)
2504
+ const projections=stores.flatMap(store=>publicMemoryRecords(store.records).flatMap(record=>{
2434
2505
  const line=record.indexLine??indexLine(record.type,record.summary,record.expires,record.detail!==undefined)
2435
2506
  if(!store.indexLines.includes(line))return[]
2436
2507
  const topic=filterWithdrawn(readTextSafe(join(store.dir,record.type+'.md'))??'',store.policy)
2437
2508
  return[captureProjection(record,store.scope,`[${store.scope}] ${line}`,topic)]
2438
2509
  })).slice(-60)
2439
- return{validationRecords:stores.flatMap(store=>validationCurrentRecords(store.dir,store.scope,store.records)),sourceRecords:stores.flatMap(store=>store.records),structuredRecords:projections,relationTargets:stores.flatMap(store=>store.records.filter(record=>['claim','belief'].includes(record.meaning?.kind)&&!record.meaning.relations?.length).map(record=>({scope:store.scope,recordId:record.id,kind:record.meaning.kind,summary:record.summary}))).slice(-60),revisions:new Map(captureDirs.map(dir=>[dir,{value:storeRevision(dir)}])),indexLines:stores.flatMap(store=>store.indexLines.map(line=>`[${store.scope}] ${line}`))}
2510
+ return{validationRecords:stores.flatMap(store=>validationCurrentRecords(store.dir,store.scope,store.records)),sourceRecords:stores.flatMap(store=>store.records),structuredRecords:projections,relationTargets:stores.flatMap(store=>publicMemoryRecords(store.records).filter(record=>['claim','belief'].includes(record.meaning?.kind)&&!record.meaning.relations?.length).map(record=>({scope:store.scope,recordId:record.id,kind:record.meaning.kind,summary:record.summary}))).slice(-60),revisions:new Map(captureDirs.map(dir=>[dir,{value:storeRevision(dir)}])),indexLines:stores.flatMap(store=>store.indexLines.map(line=>`[${store.scope}] ${line}`))}
2440
2511
  })
2441
2512
  const revisions=memorySnapshot.revisions
2442
2513
  // 按类型分库:跨项目的偏好/纠正进全局库,项目决定进项目库。
@@ -2722,7 +2793,19 @@ function setupAutoCapture(ctx, config) {
2722
2793
  rememberSession(session)
2723
2794
  scheduleRestore(session)
2724
2795
  })
2725
- ctx.on('session/disposed', session => { observedSessions.delete(session.id); sessions.delete(session.id) })
2796
+ ctx.on('session/disposed', session => {
2797
+ // 短会话在结构上永远到不了 everyTurns;把“没整理”记成可观测事件,而不是静默丢弃。
2798
+ const state = sessions.get(session?.id)
2799
+ if (state && state.turns > 0 && state.turns < options.everyTurns && !state.running) {
2800
+ try {
2801
+ appendAudit(storeRoot(), { op: 'capture', outcome: 'skipped', reason: 'short-session-below-threshold', sessionId: session.id, turns: state.turns, everyTurns: options.everyTurns })
2802
+ } catch {
2803
+ /* 状态卸载路径上的审计是尽力而为,不能阻断 */
2804
+ }
2805
+ }
2806
+ observedSessions.delete(session.id)
2807
+ sessions.delete(session.id)
2808
+ })
2726
2809
 
2727
2810
  queryService = watchOptionalService(ctx, 'sessionQuery', state => {
2728
2811
  const available = typeof state.value?.observeSession === 'function'
@@ -2798,12 +2881,18 @@ export function apply(ctx, config = {}) {
2798
2881
  }
2799
2882
  }
2800
2883
 
2884
+ const history = setupMemoryHistory(ctx, renderForRuntime)
2885
+ // The host waits for apply(): no contexts, tools or success marker are
2886
+ // published until an asynchronous projection registration has succeeded.
2887
+ return typeof history?.then === 'function' ? history.then(activate) : activate(history)
2888
+
2889
+ function activate(memoryHistory) {
2890
+ if (ctx.fiber?.uid === null) return
2801
2891
  let calls = 0
2802
- const memoryHistory = setupMemoryHistory(ctx, renderForRuntime)
2803
2892
 
2804
2893
  ctx.systemPrompt.context({
2805
2894
  name: 'memory:protocol', order: CONTEXT_ORDER - 1, required: true,
2806
- interpolate: false, text: context => memoryProtocol(ctx, context),
2895
+ interpolate: false, text: context => memoryProtocol(ctx, context)+'\n'+PRIVATE_MEMORY_NOTICE,
2807
2896
  })
2808
2897
 
2809
2898
  ctx.systemPrompt.context({
@@ -2904,6 +2993,7 @@ export function apply(ctx, config = {}) {
2904
2993
  } catch {
2905
2994
  /* 写不出来就算了:注入不依赖这两个文件 */
2906
2995
  }
2996
+ }
2907
2997
  }
2908
2998
 
2909
2999
  installManagedMemoryInternals({indexLine,clearCache:()=>cache.clear(),storeRevision:storeRevisionLocal,withdrawManagedSourceKey:withdrawManagedSourceKeyLocal,recoverMemoryPublication:recoverMemoryPublicationLocal})
@@ -68,7 +68,9 @@ function openDb(paths,{write=false,requireExisting=false}={}){
68
68
  }
69
69
  const before=lstatSync(path);let db
70
70
  try{
71
- db=new DatabaseSync(path,{readOnly:!write});db.exec('PRAGMA trusted_schema=OFF; PRAGMA busy_timeout=0;')
71
+ // Managed reads share the writer lock. Let brief contention clear without
72
+ // releasing transaction ownership or waiting indefinitely.
73
+ db=new DatabaseSync(path,{readOnly:!write});db.exec('PRAGMA trusted_schema=OFF; PRAGMA busy_timeout=750;')
72
74
  const now=lstatSync(path);if(now.isSymbolicLink()||now.ino!==before.ino||now.dev!==before.dev)fail('changed-database-file')
73
75
  if(created){db.exec('BEGIN IMMEDIATE; '+SCHEMA+'; PRAGMA application_id='+APPLICATION_ID+'; PRAGMA user_version=1; COMMIT;')}
74
76
  schema(db)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "solopup-memory",
3
- "version": "0.21.0-beta.2",
3
+ "version": "0.22.0-beta.3",
4
4
  "description": "SoloPup Memory — source-linked, layered text memory for DeepSeek Harness. Public beta.",
5
5
  "type": "module",
6
6
  "main": "index.mjs",
@@ -25,129 +25,145 @@
25
25
  "CHANGELOG.md",
26
26
  "scripts/",
27
27
  "COMPATIBILITY.md",
28
- "test.mjs",
29
- "test-security.mjs",
30
- "test-adversarial.mjs",
31
- "test-concurrency.mjs",
32
- "test-supersede.mjs",
33
- "test-history.mjs",
34
- "test-smell.mjs",
35
- "test-links.mjs",
36
- "test-packaging.mjs",
37
- "test-patch.mjs",
38
- "test-compat.mjs",
28
+ "test/test.mjs",
29
+ "test/test-security.mjs",
30
+ "test/test-adversarial.mjs",
31
+ "test/test-concurrency.mjs",
32
+ "test/test-supersede.mjs",
33
+ "test/test-history.mjs",
34
+ "test/test-smell.mjs",
35
+ "test/test-links.mjs",
36
+ "test/test-packaging.mjs",
37
+ "test/test-patch.mjs",
38
+ "test/test-compat.mjs",
39
39
  "accept.mjs",
40
40
  "worker-write.mjs",
41
- "test-conflict.mjs",
42
- "test-usability.mjs",
43
- "test-compaction-compat.mjs",
44
- "test-schema.mjs",
41
+ "test/test-conflict.mjs",
42
+ "test/test-usability.mjs",
43
+ "test/test-compaction-compat.mjs",
44
+ "test/test-schema.mjs",
45
45
  "scripts/probe-load.mjs",
46
46
  "capture.mjs",
47
47
  "capture-llm.mjs",
48
48
  "capture-evidence.mjs",
49
49
  "capture-revision.mjs",
50
- "test-capture-revision.mjs",
51
- "test-capture.mjs",
52
- "test-paired-analysis.mjs",
50
+ "test/test-capture-revision.mjs",
51
+ "test/test-capture.mjs",
52
+ "test/test-paired-analysis.mjs",
53
53
  "retrieval.mjs",
54
- "test-retrieval.mjs",
54
+ "test/test-retrieval.mjs",
55
55
  "candidates.mjs",
56
- "test-candidates.mjs",
56
+ "test/test-candidates.mjs",
57
57
  "publication.mjs",
58
- "test-publication.mjs",
58
+ "test/test-publication.mjs",
59
59
  "withdrawal.mjs",
60
- "test-withdrawal.mjs",
60
+ "test/test-withdrawal.mjs",
61
61
  "capture-jobs.mjs",
62
- "test-capture-jobs.mjs",
62
+ "test/test-capture-jobs.mjs",
63
63
  "context-history.mjs",
64
- "test-context-history.mjs",
64
+ "test/test-context-history.mjs",
65
65
  "capture-inputs.mjs",
66
- "test-capture-inputs.mjs",
67
- "test-inline-summary.mjs",
66
+ "test/test-capture-inputs.mjs",
67
+ "test/test-inline-summary.mjs",
68
68
  "managed-logs.mjs",
69
- "test-withdrawal-archives.mjs",
69
+ "test/test-withdrawal-archives.mjs",
70
70
  "cordis.patch.yml",
71
71
  "plugin-config.mjs",
72
72
  "host-services.mjs",
73
- "test-plugin-contract.mjs",
73
+ "test/test-plugin-contract.mjs",
74
74
  "project-stores.mjs",
75
- "test-project-stores.mjs",
75
+ "test/test-project-stores.mjs",
76
76
  "service-readiness.mjs",
77
- "test-service-readiness.mjs",
77
+ "test/test-service-readiness.mjs",
78
78
  "tool-inputs.mjs",
79
79
  "fixture-host.mjs",
80
- "test-source-key.mjs",
81
- "test-tool-inputs.mjs",
82
- "test-tool-write-lifecycle.mjs",
83
- "test-tool-write-guard.mjs",
80
+ "test/test-source-key.mjs",
81
+ "test/test-tool-inputs.mjs",
82
+ "test/test-tool-write-lifecycle.mjs",
83
+ "test/test-tool-write-guard.mjs",
84
84
  "source-retirement.mjs",
85
- "test-source-retirement.mjs",
85
+ "test/test-source-retirement.mjs",
86
86
  "managed-policy.mjs",
87
87
  "managed-hooks.mjs",
88
88
  "managed-access.mjs",
89
89
  "store-lock-guard.mjs",
90
- "test-managed-access.mjs",
90
+ "test/test-managed-access.mjs",
91
91
  "managed-activation.mjs",
92
- "test-managed-activation.mjs",
92
+ "test/test-managed-activation.mjs",
93
93
  "runtime-startup.mjs",
94
- "test-runtime-startup.mjs",
94
+ "test/test-runtime-startup.mjs",
95
95
  "meaning.mjs",
96
- "test-meaning.mjs",
96
+ "test/test-meaning.mjs",
97
97
  "episodes.mjs",
98
98
  "source-record.mjs",
99
- "test-episodes.mjs",
99
+ "test/test-episodes.mjs",
100
100
  "belief-revision.mjs",
101
- "test-belief-revision.mjs",
101
+ "test/test-belief-revision.mjs",
102
102
  "tool-observations.mjs",
103
- "test-tool-observations.mjs",
103
+ "test/test-tool-observations.mjs",
104
104
  "capture-diagnostics.mjs",
105
- "test-capture-diagnostics.mjs",
105
+ "test/test-capture-diagnostics.mjs",
106
106
  "snapshots.mjs",
107
- "test-snapshots.mjs",
107
+ "test/test-snapshots.mjs",
108
108
  "restore.mjs",
109
109
  "restoration-lock.mjs",
110
- "test-restore.mjs",
111
- "test-status-both.mjs",
112
- "test-text-admission.mjs",
110
+ "test/test-restore.mjs",
111
+ "test/test-status-both.mjs",
112
+ "test/test-text-admission.mjs",
113
113
  "memory-policy.mjs",
114
114
  "capture-submission.mjs",
115
- "test-capture-submission.mjs",
115
+ "test/test-capture-submission.mjs",
116
116
  "capture-projections.mjs",
117
- "test-capture-projections.mjs",
117
+ "test/test-capture-projections.mjs",
118
118
  "capture-validation-contract.mjs",
119
119
  "capture-validation.mjs",
120
- "test-capture-validation.mjs",
120
+ "test/test-capture-validation.mjs",
121
121
  "capture-validation-job.mjs",
122
- "test-capture-validation-job.mjs",
123
- "test-capture-validation-flow.mjs",
124
- "test-capture-validation-lineage.mjs",
122
+ "test/test-capture-validation-job.mjs",
123
+ "test/test-capture-validation-flow.mjs",
124
+ "test/test-capture-validation-lineage.mjs",
125
125
  "native-write-validation.mjs",
126
- "test-native-write-validation.mjs",
127
- "test-native-service-lifecycle.mjs",
126
+ "test/test-native-write-validation.mjs",
127
+ "test/test-native-service-lifecycle.mjs",
128
128
  "field-lineage.mjs",
129
- "test-field-lineage.mjs",
130
- "test-capture-field-lineage.mjs",
129
+ "test/test-field-lineage.mjs",
130
+ "test/test-capture-field-lineage.mjs",
131
131
  "validation-transition-context.mjs",
132
- "test-validation-transition-context.mjs",
132
+ "test/test-validation-transition-context.mjs",
133
133
  "memory-scope-policy.mjs",
134
134
  "control-schema.mjs",
135
- "test-control-schema.mjs",
135
+ "test/test-control-schema.mjs",
136
136
  "revision-admission.mjs",
137
- "test-revision-admission.mjs",
137
+ "test/test-revision-admission.mjs",
138
138
  "memory-rule-policy.mjs",
139
- "test-rule-admission.mjs",
140
- "test-validation-source-refs.mjs",
141
- "test-source-completeness.mjs",
139
+ "test/test-rule-admission.mjs",
140
+ "test/test-validation-source-refs.mjs",
141
+ "test/test-source-completeness.mjs",
142
142
  "source-identity.mjs",
143
- "test-legacy-source-identity.mjs",
143
+ "test/test-legacy-source-identity.mjs",
144
144
  "icon.svg",
145
145
  "locale/",
146
146
  "README.en.md",
147
147
  "RELEASE.md",
148
148
  "memory-protocol.mjs",
149
149
  "capture-source-aliases.mjs",
150
- "test-explicit-memory.mjs"
150
+ "test/test-explicit-memory.mjs",
151
+ "privacy.mjs",
152
+ "test/test-privacy.mjs",
153
+ "detect.mjs",
154
+ "scripts/detect.mjs",
155
+ "scripts/telemetry.mjs",
156
+ "scripts/dream.mjs",
157
+ "eval/semantic-suite.mjs",
158
+ "eval/semantic/cases.json",
159
+ "eval/bench-detect.mjs",
160
+ "test/test-detect.mjs",
161
+ "test/test-telemetry.mjs",
162
+ "test/test-semantic-suite.mjs",
163
+ "test/test-dream-plan.mjs",
164
+ "DETECTION.md",
165
+ "DREAMING-DESIGN.md",
166
+ "test/test-dsh-registration.mjs"
151
167
  ],
152
168
  "engines": {
153
169
  "node": ">=24.9",
@@ -160,7 +176,7 @@
160
176
  },
161
177
  "license": "MIT",
162
178
  "scripts": {
163
- "test": "node --test --test-concurrency=4 test.mjs test-security.mjs test-adversarial.mjs test-concurrency.mjs test-supersede.mjs test-history.mjs test-smell.mjs test-links.mjs test-packaging.mjs test-patch.mjs test-compat.mjs test-conflict.mjs test-usability.mjs test-compaction-compat.mjs test-schema.mjs test-capture.mjs test-paired-analysis.mjs test-retrieval.mjs test-candidates.mjs test-publication.mjs test-withdrawal.mjs test-capture-jobs.mjs test-context-history.mjs test-capture-inputs.mjs test-inline-summary.mjs test-withdrawal-archives.mjs test-plugin-contract.mjs test-project-stores.mjs test-service-readiness.mjs test-source-key.mjs test-tool-inputs.mjs test-tool-write-lifecycle.mjs test-tool-write-guard.mjs test-source-retirement.mjs test-managed-access.mjs test-managed-activation.mjs test-runtime-startup.mjs test-capture-revision.mjs test-meaning.mjs test-episodes.mjs test-belief-revision.mjs test-tool-observations.mjs test-capture-diagnostics.mjs test-snapshots.mjs test-restore.mjs test-status-both.mjs test-text-admission.mjs test-capture-submission.mjs test-capture-projections.mjs test-capture-validation.mjs test-capture-validation-job.mjs test-capture-validation-flow.mjs test-capture-validation-lineage.mjs test-native-write-validation.mjs test-native-service-lifecycle.mjs test-field-lineage.mjs test-capture-field-lineage.mjs test-validation-transition-context.mjs test-control-schema.mjs test-revision-admission.mjs test-rule-admission.mjs test-validation-source-refs.mjs test-source-completeness.mjs test-legacy-source-identity.mjs test-explicit-memory.mjs",
179
+ "test": "node --test --test-concurrency=4 test/test.mjs test/test-security.mjs test/test-adversarial.mjs test/test-concurrency.mjs test/test-supersede.mjs test/test-history.mjs test/test-smell.mjs test/test-links.mjs test/test-packaging.mjs test/test-patch.mjs test/test-compat.mjs test/test-conflict.mjs test/test-usability.mjs test/test-compaction-compat.mjs test/test-schema.mjs test/test-capture.mjs test/test-paired-analysis.mjs test/test-retrieval.mjs test/test-candidates.mjs test/test-publication.mjs test/test-withdrawal.mjs test/test-capture-jobs.mjs test/test-context-history.mjs test/test-capture-inputs.mjs test/test-inline-summary.mjs test/test-withdrawal-archives.mjs test/test-plugin-contract.mjs test/test-project-stores.mjs test/test-service-readiness.mjs test/test-source-key.mjs test/test-tool-inputs.mjs test/test-tool-write-lifecycle.mjs test/test-tool-write-guard.mjs test/test-source-retirement.mjs test/test-managed-access.mjs test/test-managed-activation.mjs test/test-runtime-startup.mjs test/test-capture-revision.mjs test/test-meaning.mjs test/test-episodes.mjs test/test-belief-revision.mjs test/test-tool-observations.mjs test/test-capture-diagnostics.mjs test/test-snapshots.mjs test/test-restore.mjs test/test-status-both.mjs test/test-text-admission.mjs test/test-capture-submission.mjs test/test-capture-projections.mjs test/test-capture-validation.mjs test/test-capture-validation-job.mjs test/test-capture-validation-flow.mjs test/test-capture-validation-lineage.mjs test/test-native-write-validation.mjs test/test-native-service-lifecycle.mjs test/test-field-lineage.mjs test/test-capture-field-lineage.mjs test/test-validation-transition-context.mjs test/test-control-schema.mjs test/test-revision-admission.mjs test/test-rule-admission.mjs test/test-validation-source-refs.mjs test/test-source-completeness.mjs test/test-legacy-source-identity.mjs test/test-explicit-memory.mjs test/test-privacy.mjs test/test-detect.mjs test/test-telemetry.mjs test/test-semantic-suite.mjs test/test-dream-plan.mjs test/test-dsh-registration.mjs",
164
180
  "eval": "node eval/run-eval.mjs",
165
181
  "gate": "node eval/run-eval.mjs --check",
166
182
  "verify": "node scripts/verify.mjs",
@@ -170,7 +186,14 @@
170
186
  "probe:package": "node scripts/probe-package.mjs",
171
187
  "projects": "node scripts/projects.mjs",
172
188
  "control": "node scripts/control.mjs",
173
- "backup": "node scripts/backup.mjs"
189
+ "backup": "node scripts/backup.mjs",
190
+ "detect": "node scripts/detect.mjs",
191
+ "telemetry": "node scripts/telemetry.mjs",
192
+ "dream": "node scripts/dream.mjs",
193
+ "semantic": "node eval/semantic-suite.mjs --verbose",
194
+ "security": "node scripts/security-check.mjs",
195
+ "soak": "node scripts/soak.mjs",
196
+ "probe:history": "node scripts/probe-history.mjs"
174
197
  },
175
198
  "dshMemoryRestoreProtocol": 1,
176
199
  "dshMemoryDataSchemas": {