solopup-memory 0.21.0-beta.3 → 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.
- package/CHANGELOG.md +30 -0
- package/COMPATIBILITY.md +8 -5
- package/DETECTION.md +139 -0
- package/DREAMING-DESIGN.md +167 -0
- package/README.en.md +33 -6
- package/README.md +38 -7
- package/RELEASE.md +26 -0
- package/SECURITY.md +7 -0
- package/context-history.mjs +20 -3
- package/detect.mjs +441 -0
- package/eval/bench-detect.mjs +107 -0
- package/eval/semantic/cases.json +387 -0
- package/eval/semantic-suite.mjs +156 -0
- package/index.mjs +82 -10
- package/managed-policy.mjs +3 -1
- package/package.json +90 -69
- package/scripts/demo-source-recall.mjs +123 -0
- package/scripts/detect.mjs +69 -0
- package/scripts/dream.mjs +205 -0
- package/scripts/probe-history.mjs +92 -0
- package/scripts/probe-load.mjs +1 -1
- package/scripts/probe-package.mjs +6 -2
- package/scripts/security-check.mjs +173 -0
- package/scripts/soak.mjs +219 -0
- package/scripts/telemetry.mjs +187 -0
- package/scripts/verify.mjs +3 -1
- package/{test-adversarial.mjs → test/test-adversarial.mjs} +5 -5
- package/{test-belief-revision.mjs → test/test-belief-revision.mjs} +7 -7
- package/{test-candidates.mjs → test/test-candidates.mjs} +4 -4
- package/{test-capture-diagnostics.mjs → test/test-capture-diagnostics.mjs} +6 -6
- package/{test-capture-field-lineage.mjs → test/test-capture-field-lineage.mjs} +4 -4
- package/{test-capture-inputs.mjs → test/test-capture-inputs.mjs} +8 -8
- package/{test-capture-jobs.mjs → test/test-capture-jobs.mjs} +5 -5
- package/{test-capture-projections.mjs → test/test-capture-projections.mjs} +12 -12
- package/{test-capture-revision.mjs → test/test-capture-revision.mjs} +6 -6
- package/{test-capture-submission.mjs → test/test-capture-submission.mjs} +3 -3
- package/{test-capture-validation-flow.mjs → test/test-capture-validation-flow.mjs} +5 -5
- package/{test-capture-validation-job.mjs → test/test-capture-validation-job.mjs} +5 -5
- package/{test-capture-validation-lineage.mjs → test/test-capture-validation-lineage.mjs} +2 -2
- package/{test-capture-validation.mjs → test/test-capture-validation.mjs} +2 -2
- package/{test-capture.mjs → test/test-capture.mjs} +20 -20
- package/{test-compaction-compat.mjs → test/test-compaction-compat.mjs} +5 -5
- package/{test-compat.mjs → test/test-compat.mjs} +3 -3
- package/{test-concurrency.mjs → test/test-concurrency.mjs} +3 -3
- package/{test-conflict.mjs → test/test-conflict.mjs} +2 -2
- package/{test-context-history.mjs → test/test-context-history.mjs} +1 -1
- package/{test-control-schema.mjs → test/test-control-schema.mjs} +1 -1
- package/test/test-detect.mjs +125 -0
- package/test/test-dream-plan.mjs +72 -0
- package/test/test-dsh-registration.mjs +111 -0
- package/{test-episodes.mjs → test/test-episodes.mjs} +5 -5
- package/{test-explicit-memory.mjs → test/test-explicit-memory.mjs} +4 -4
- package/{test-field-lineage.mjs → test/test-field-lineage.mjs} +4 -4
- package/{test-history.mjs → test/test-history.mjs} +4 -4
- package/{test-inline-summary.mjs → test/test-inline-summary.mjs} +5 -5
- package/{test-legacy-source-identity.mjs → test/test-legacy-source-identity.mjs} +2 -2
- package/{test-links.mjs → test/test-links.mjs} +4 -4
- package/{test-managed-access.mjs → test/test-managed-access.mjs} +29 -5
- package/{test-managed-activation.mjs → test/test-managed-activation.mjs} +5 -5
- package/{test-meaning.mjs → test/test-meaning.mjs} +6 -6
- package/{test-native-service-lifecycle.mjs → test/test-native-service-lifecycle.mjs} +1 -1
- package/{test-native-write-validation.mjs → test/test-native-write-validation.mjs} +4 -4
- package/{test-packaging.mjs → test/test-packaging.mjs} +32 -5
- package/{test-paired-analysis.mjs → test/test-paired-analysis.mjs} +1 -1
- package/{test-patch.mjs → test/test-patch.mjs} +11 -11
- package/{test-plugin-contract.mjs → test/test-plugin-contract.mjs} +4 -4
- package/{test-privacy.mjs → test/test-privacy.mjs} +3 -3
- package/{test-project-stores.mjs → test/test-project-stores.mjs} +5 -5
- package/{test-publication.mjs → test/test-publication.mjs} +4 -4
- package/{test-restore.mjs → test/test-restore.mjs} +11 -11
- package/{test-retrieval.mjs → test/test-retrieval.mjs} +2 -2
- package/{test-revision-admission.mjs → test/test-revision-admission.mjs} +3 -3
- package/{test-rule-admission.mjs → test/test-rule-admission.mjs} +8 -8
- package/{test-runtime-startup.mjs → test/test-runtime-startup.mjs} +2 -2
- package/{test-schema.mjs → test/test-schema.mjs} +2 -2
- package/{test-security.mjs → test/test-security.mjs} +6 -6
- package/test/test-semantic-suite.mjs +20 -0
- package/{test-service-readiness.mjs → test/test-service-readiness.mjs} +1 -1
- package/{test-smell.mjs → test/test-smell.mjs} +4 -4
- package/{test-snapshots.mjs → test/test-snapshots.mjs} +3 -3
- package/{test-source-completeness.mjs → test/test-source-completeness.mjs} +6 -6
- package/{test-source-key.mjs → test/test-source-key.mjs} +2 -2
- package/{test-source-retirement.mjs → test/test-source-retirement.mjs} +4 -4
- package/{test-status-both.mjs → test/test-status-both.mjs} +12 -4
- package/{test-supersede.mjs → test/test-supersede.mjs} +3 -3
- package/test/test-telemetry.mjs +60 -0
- package/{test-text-admission.mjs → test/test-text-admission.mjs} +6 -6
- package/{test-tool-inputs.mjs → test/test-tool-inputs.mjs} +1 -1
- package/{test-tool-observations.mjs → test/test-tool-observations.mjs} +6 -6
- package/{test-tool-write-guard.mjs → test/test-tool-write-guard.mjs} +3 -3
- package/{test-tool-write-lifecycle.mjs → test/test-tool-write-lifecycle.mjs} +3 -3
- package/{test-usability.mjs → test/test-usability.mjs} +7 -7
- package/{test-validation-source-refs.mjs → test/test-validation-source-refs.mjs} +2 -2
- package/{test-validation-transition-context.mjs → test/test-validation-transition-context.mjs} +1 -1
- package/{test-withdrawal-archives.mjs → test/test-withdrawal-archives.mjs} +6 -6
- package/{test-withdrawal.mjs → test/test-withdrawal.mjs} +2 -2
- package/{test.mjs → test/test.mjs} +4 -4
- package/version.mjs +1 -1
package/index.mjs
CHANGED
|
@@ -890,7 +890,7 @@ function writeMemoryLocal({ type, dir, line, detail, summary, expires, supersede
|
|
|
890
890
|
}
|
|
891
891
|
|
|
892
892
|
// 相似度闸:改写同一件事却再叠一条,会让新旧两条同时被注入、给出矛盾信息。
|
|
893
|
-
// 0.
|
|
893
|
+
// 0.9 以上直接拒绝并指出候选;0.8–0.9 放行但带警告(避免误伤「相似但不同」的条目)。
|
|
894
894
|
let similarWarning
|
|
895
895
|
// 完全重复走 duplicate 分支(幂等返回),相似度闸只管「像但不是同一句」
|
|
896
896
|
const alias = hasTopicDetail(cleanDetail) ? undefined : routeAlias(cleanLine, type)
|
|
@@ -1224,8 +1224,32 @@ function statusOfLocal(dir) {
|
|
|
1224
1224
|
}
|
|
1225
1225
|
|
|
1226
1226
|
/**
|
|
1227
|
-
*
|
|
1228
|
-
*
|
|
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
|
+
/**
|
|
1229
1253
|
* 存在的原因:索引超上限时,被省略的条目对模型完全不可见(`lookupLinks` 只查索引)。
|
|
1230
1254
|
* 检索是确定性的:归一化后做子串匹配,不做正则、不做模糊。
|
|
1231
1255
|
*
|
|
@@ -2151,6 +2175,29 @@ function defineStatusTool(ctx, sourceInputs) {
|
|
|
2151
2175
|
expired: { type: 'integer' },
|
|
2152
2176
|
duplicateGroups: { type: 'integer' },
|
|
2153
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
|
+
},
|
|
2154
2201
|
sourceLedger: { type: 'object', additionalProperties: false,
|
|
2155
2202
|
required: ['available', 'fullLimit', 'pendingLimit', 'retiredLimit', 'byteLimit'],
|
|
2156
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'] } } },
|
|
@@ -2177,7 +2224,8 @@ function defineStatusTool(ctx, sourceInputs) {
|
|
|
2177
2224
|
text:
|
|
2178
2225
|
`索引 ${value.index.lines}/${value.index.limitLines} 行、${value.index.bytes}/${value.index.limitBytes} 字节;` +
|
|
2179
2226
|
`类型:${types};过期 ${value.expired} 条;重复组 ${value.duplicateGroups};` +
|
|
2180
|
-
`主题文件:${topics || '无'};库占用 ${(value.storeBytes / 1024).toFixed(1)}K
|
|
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' +
|
|
2181
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?'(列表有省略,优先核对最近结果)':''):'') ,
|
|
2182
2230
|
},
|
|
2183
2231
|
]
|
|
@@ -2186,6 +2234,7 @@ function defineStatusTool(ctx, sourceInputs) {
|
|
|
2186
2234
|
async execute(args, exec) {
|
|
2187
2235
|
const dir = resolveStoreDir(ctx, exec, 'memory_status', args.scope)
|
|
2188
2236
|
const value={...statusOf(dir),capture:managedRead(storeRoot(),()=>captureJobStatus(storeRoot()))}
|
|
2237
|
+
value.lint = await lintStatus(dir, dir === storeRoot() ? 'global' : 'project')
|
|
2189
2238
|
if(args.includeObservations!==undefined&&typeof args.includeObservations!=='boolean')throw Error('memory-observation-invalid-list-option')
|
|
2190
2239
|
if(args.includeObservations===true){if(!captureSessionEligible(exec?.agent?.session))throw Error('memory-observation-source-unavailable');return{...value,executionObservations:sourceInputs.availableForExecution(exec)}}
|
|
2191
2240
|
return value
|
|
@@ -2213,7 +2262,7 @@ function defineStatusTool(ctx, sourceInputs) {
|
|
|
2213
2262
|
if(args.scope!=='both')return singleExecute(args,exec)
|
|
2214
2263
|
if(args.includeObservations!==undefined&&typeof args.includeObservations!=='boolean')throw Error('memory-observation-invalid-list-option')
|
|
2215
2264
|
const root=storeRoot()
|
|
2216
|
-
|
|
2265
|
+
const result=managedRead(root,()=>{
|
|
2217
2266
|
const stores=[{scope:'global',available:true,status:statusOf(root)}];let project
|
|
2218
2267
|
if(!exec?.agent||typeof ctx.workingDirectory?.get!=='function')project={scope:'project',available:false,reason:'no-current-project'}
|
|
2219
2268
|
else{
|
|
@@ -2229,10 +2278,14 @@ function defineStatusTool(ctx, sourceInputs) {
|
|
|
2229
2278
|
}catch{project={scope:'project',available:false,reason:'project-status-unavailable'}}
|
|
2230
2279
|
}
|
|
2231
2280
|
stores.push(project)
|
|
2232
|
-
|
|
2233
|
-
if(args.includeObservations===true){if(!captureSessionEligible(exec?.agent?.session))throw Error('memory-observation-source-unavailable');value.executionObservations=sourceInputs.availableForExecution(exec)}
|
|
2234
|
-
return value
|
|
2281
|
+
return {scope:'both',stores,capture:captureJobStatus(root)}
|
|
2235
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
|
|
2236
2289
|
}
|
|
2237
2290
|
return definition
|
|
2238
2291
|
}
|
|
@@ -2740,7 +2793,19 @@ function setupAutoCapture(ctx, config) {
|
|
|
2740
2793
|
rememberSession(session)
|
|
2741
2794
|
scheduleRestore(session)
|
|
2742
2795
|
})
|
|
2743
|
-
ctx.on('session/disposed', session => {
|
|
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
|
+
})
|
|
2744
2809
|
|
|
2745
2810
|
queryService = watchOptionalService(ctx, 'sessionQuery', state => {
|
|
2746
2811
|
const available = typeof state.value?.observeSession === 'function'
|
|
@@ -2816,8 +2881,14 @@ export function apply(ctx, config = {}) {
|
|
|
2816
2881
|
}
|
|
2817
2882
|
}
|
|
2818
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
|
|
2819
2891
|
let calls = 0
|
|
2820
|
-
const memoryHistory = setupMemoryHistory(ctx, renderForRuntime)
|
|
2821
2892
|
|
|
2822
2893
|
ctx.systemPrompt.context({
|
|
2823
2894
|
name: 'memory:protocol', order: CONTEXT_ORDER - 1, required: true,
|
|
@@ -2922,6 +2993,7 @@ export function apply(ctx, config = {}) {
|
|
|
2922
2993
|
} catch {
|
|
2923
2994
|
/* 写不出来就算了:注入不依赖这两个文件 */
|
|
2924
2995
|
}
|
|
2996
|
+
}
|
|
2925
2997
|
}
|
|
2926
2998
|
|
|
2927
2999
|
installManagedMemoryInternals({indexLine,clearCache:()=>cache.clear(),storeRevision:storeRevisionLocal,withdrawManagedSourceKey:withdrawManagedSourceKeyLocal,recoverMemoryPublication:recoverMemoryPublicationLocal})
|
package/managed-policy.mjs
CHANGED
|
@@ -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
|
-
|
|
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.
|
|
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,131 +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
151
|
"privacy.mjs",
|
|
152
|
-
"test-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"
|
|
153
167
|
],
|
|
154
168
|
"engines": {
|
|
155
169
|
"node": ">=24.9",
|
|
@@ -162,7 +176,7 @@
|
|
|
162
176
|
},
|
|
163
177
|
"license": "MIT",
|
|
164
178
|
"scripts": {
|
|
165
|
-
"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 test-privacy.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",
|
|
166
180
|
"eval": "node eval/run-eval.mjs",
|
|
167
181
|
"gate": "node eval/run-eval.mjs --check",
|
|
168
182
|
"verify": "node scripts/verify.mjs",
|
|
@@ -172,7 +186,14 @@
|
|
|
172
186
|
"probe:package": "node scripts/probe-package.mjs",
|
|
173
187
|
"projects": "node scripts/projects.mjs",
|
|
174
188
|
"control": "node scripts/control.mjs",
|
|
175
|
-
"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"
|
|
176
197
|
},
|
|
177
198
|
"dshMemoryRestoreProtocol": 1,
|
|
178
199
|
"dshMemoryDataSchemas": {
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Offline, synthetic mechanics demonstration. Production tool handlers run in a
|
|
4
|
+
* scripted host, with a scripted validation response, NOT DSH or a real model.
|
|
5
|
+
* Each phase uses a separate Node process; no old conversation is replayed.
|
|
6
|
+
* Run without arguments for six output lines, or --json for the full receipt.
|
|
7
|
+
*/
|
|
8
|
+
import assert from 'node:assert/strict'
|
|
9
|
+
import { spawnSync } from 'node:child_process'
|
|
10
|
+
import { createHash } from 'node:crypto'
|
|
11
|
+
import { mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync, writeFileSync } from 'node:fs'
|
|
12
|
+
import { tmpdir } from 'node:os'
|
|
13
|
+
import { basename, join } from 'node:path'
|
|
14
|
+
import { fileURLToPath } from 'node:url'
|
|
15
|
+
import { apply } from '../index.mjs'
|
|
16
|
+
import { scriptedNativeValidationLlm } from '../fixture-host.mjs'
|
|
17
|
+
import { VERSION } from '../version.mjs'
|
|
18
|
+
import { probeEnvironment } from './probe-env.mjs'
|
|
19
|
+
|
|
20
|
+
const script = fileURLToPath(import.meta.url)
|
|
21
|
+
const quote = '记住:本项目的交付说明统一用中文。'
|
|
22
|
+
const summary = '本项目的交付说明统一用中文'
|
|
23
|
+
const query = '交付说明'
|
|
24
|
+
const sourceSession = 'synthetic-demo-write'
|
|
25
|
+
|
|
26
|
+
async function phase(name, root) {
|
|
27
|
+
// The internal phase runner refuses a non-demo home, including a user's home.
|
|
28
|
+
assert.equal(realpathSync(tmpdir()), join(realpathSync(root), 'tmp'))
|
|
29
|
+
assert.match(basename(root), /^solopup-readme-demo-/)
|
|
30
|
+
assert.equal(process.env.DSH_HOME, join(root, 'home'))
|
|
31
|
+
assert.ok(['write', 'recall', 'other-project'].includes(name))
|
|
32
|
+
const handlers = [], tools = [], disposers = []
|
|
33
|
+
const session = { id: name === 'write' ? sourceSession : `synthetic-demo-${name}` }
|
|
34
|
+
const agent = { session, options: { provider: 'scripted-demo', model: 'scripted-demo' } }
|
|
35
|
+
let validationCalls = 0
|
|
36
|
+
const scripted = scriptedNativeValidationLlm()
|
|
37
|
+
const ctx = {
|
|
38
|
+
llm: { async prepareCall(config) { validationCalls++; return scripted.prepareCall(config) } },
|
|
39
|
+
systemPrompt: { context() {} },
|
|
40
|
+
workingDirectory: { get: () => join(root, name === 'other-project' ? 'project-b' : 'project-a') },
|
|
41
|
+
tools: { register: tool => tools.push(tool) },
|
|
42
|
+
on: (event, handler) => handlers.push([event, handler]),
|
|
43
|
+
effect: fn => { const dispose = fn(); if (typeof dispose === 'function') disposers.push(dispose) },
|
|
44
|
+
}
|
|
45
|
+
const emit = (event, ...args) => {
|
|
46
|
+
for (const [registered, handler] of handlers) if (registered === event) handler(...args)
|
|
47
|
+
}
|
|
48
|
+
apply(ctx, { autoCapture: { enabled: false, timeoutMs: 2000 } })
|
|
49
|
+
try {
|
|
50
|
+
if (name === 'write') {
|
|
51
|
+
const message = { id: 'synthetic-owner-message', role: 'user', source: { kind: 'user' }, content: [{ type: 'text', text: quote }] }
|
|
52
|
+
emit('agent/pre-step', { agent, turn: 1, step: 1, messages: [message] }, () => {})
|
|
53
|
+
emit('session/event', session, { type: 'user/message', seq: 1, data: message })
|
|
54
|
+
const args = { type: 'project', scope: 'project', summary, meaning: { kind: 'decision' } }
|
|
55
|
+
const callId = 'synthetic-demo-write-call'
|
|
56
|
+
emit('session/event', session, { type: 'tool/call', seq: 2, data: { callId, name: 'memory_write', arguments: JSON.stringify(args), turn: 1, step: 1 } })
|
|
57
|
+
const result = await tools.find(t => t.name === 'memory_write').execute(args, { agent, callId })
|
|
58
|
+
assert.equal(result.status, 'recorded')
|
|
59
|
+
assert.equal(validationCalls, 1)
|
|
60
|
+
return { sessionId: session.id, ownerMessage: quote, args, result, scriptedValidationCalls: validationCalls }
|
|
61
|
+
}
|
|
62
|
+
// No user-message events or old history are provided to these new processes.
|
|
63
|
+
const search = tools.find(t => t.name === 'memory_search')
|
|
64
|
+
const args = { query, mode: 'exact' }
|
|
65
|
+
const current = await search.execute(args, { agent })
|
|
66
|
+
const history = await search.execute({ ...args, view: 'history' }, { agent })
|
|
67
|
+
assert.equal(validationCalls, 0)
|
|
68
|
+
if (name === 'recall') {
|
|
69
|
+
assert.equal(current.hits.length, 1)
|
|
70
|
+
assert.equal(current.hits[0].scope, 'project')
|
|
71
|
+
assert.ok(current.hits[0].text.includes(summary))
|
|
72
|
+
assert.equal(history.hits.length, 1)
|
|
73
|
+
assert.equal(history.hits[0].history.sourceSessionId, sourceSession)
|
|
74
|
+
assert.deepEqual(history.hits[0].history.sourceMessageIds, [`${sourceSession}:1`])
|
|
75
|
+
assert.ok(history.hits[0].text.includes(quote))
|
|
76
|
+
} else {
|
|
77
|
+
assert.equal(current.hits.length, 0)
|
|
78
|
+
assert.equal(history.hits.length, 0)
|
|
79
|
+
}
|
|
80
|
+
return { sessionId: session.id, inheritedMessages: 0, args, current, history, scriptedValidationCalls: validationCalls }
|
|
81
|
+
} finally {
|
|
82
|
+
for (const dispose of disposers) dispose()
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (process.argv[2] === '--phase' && process.argv.length === 5) {
|
|
87
|
+
console.log(JSON.stringify(await phase(process.argv[3], process.argv[4])))
|
|
88
|
+
} else {
|
|
89
|
+
assert.ok(process.argv.length === 2 || process.argv.length === 3 && process.argv[2] === '--json', 'usage: node scripts/demo-source-recall.mjs [--json]')
|
|
90
|
+
const root = realpathSync(mkdtempSync(join(tmpdir(), 'solopup-readme-demo-')))
|
|
91
|
+
const env = probeEnvironment(root)
|
|
92
|
+
for (const dir of [env.HOME, env.TMPDIR, join(root, 'project-a'), join(root, 'project-b')]) mkdirSync(dir)
|
|
93
|
+
for (const project of ['project-a', 'project-b']) writeFileSync(join(root, project, '.dsh-root'), '')
|
|
94
|
+
try {
|
|
95
|
+
const phases = {}
|
|
96
|
+
for (const name of ['write', 'recall', 'other-project']) {
|
|
97
|
+
const child = spawnSync(process.execPath, [script, '--phase', name, root], { env, encoding: 'utf8', timeout: 30000 })
|
|
98
|
+
assert.equal(child.status, 0, `demo ${name} failed: ${child.stderr}`)
|
|
99
|
+
phases[name] = JSON.parse(child.stdout)
|
|
100
|
+
}
|
|
101
|
+
const lines = [
|
|
102
|
+
`synthetic user: ${quote}`,
|
|
103
|
+
`memory_write: ${phases.write.result.status} (scope=project, meaning=decision)`,
|
|
104
|
+
`fresh process / memory_search("${query}"): ${phases.recall.current.hits.length} hit (scope=${phases.recall.current.hits[0].scope})`,
|
|
105
|
+
`current text: ${phases.recall.current.hits[0].text}`,
|
|
106
|
+
`history source: ${phases.recall.history.hits[0].history.sourceMessageIds.join(', ')}; quote: ${quote}`,
|
|
107
|
+
`other project / current + history: ${phases['other-project'].current.hits.length} + ${phases['other-project'].history.hits.length} hits`,
|
|
108
|
+
]
|
|
109
|
+
const report = {
|
|
110
|
+
kind: 'synthetic-scripted-host-mechanics',
|
|
111
|
+
at: new Date().toISOString(), version: VERSION,
|
|
112
|
+
scriptSha256: createHash('sha256').update(readFileSync(script)).digest('hex'),
|
|
113
|
+
environment: { platform: process.platform, arch: process.arch, node: process.version },
|
|
114
|
+
realDshHost: false, realModel: false, networkRequests: 0,
|
|
115
|
+
isolatedTemporaryHome: true, separateProcesses: 3,
|
|
116
|
+
limitations: 'Scripted validator approves this synthetic candidate. This verifies persistence, source return and project filtering only, not model judgment, real DSH schema registration, or natural fresh-chat recall.',
|
|
117
|
+
lines, phases,
|
|
118
|
+
}
|
|
119
|
+
console.log(process.argv[2] === '--json' ? JSON.stringify(report, null, 2) : lines.join('\n'))
|
|
120
|
+
} finally {
|
|
121
|
+
rmSync(root, { recursive: true, force: true })
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* 记忆层检测 CLI(L0 不变量层)。只读,不调用模型。
|
|
4
|
+
*
|
|
5
|
+
* node scripts/detect.mjs # 检查 ~/.dsh/memory
|
|
6
|
+
* node scripts/detect.mjs --root DIR # 指定记忆根
|
|
7
|
+
* node scripts/detect.mjs --json # 机器可读
|
|
8
|
+
* node scripts/detect.mjs --check # 有 error 则退出码 1(CI 门禁)
|
|
9
|
+
* node scripts/detect.mjs --verbose # 连 info 一起打印
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { homedir } from 'node:os'
|
|
13
|
+
import { existsSync } from 'node:fs'
|
|
14
|
+
import { join, resolve } from 'node:path'
|
|
15
|
+
|
|
16
|
+
import { detectRoot } from '../detect.mjs'
|
|
17
|
+
|
|
18
|
+
const argv = process.argv.slice(2)
|
|
19
|
+
const flag = (name) => argv.includes(name)
|
|
20
|
+
const option = (name) => {
|
|
21
|
+
const at = argv.indexOf(name)
|
|
22
|
+
return at >= 0 ? argv[at + 1] : undefined
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const root = resolve(option('--root') ?? join(process.env.DSH_HOME ?? join(homedir(), '.dsh'), 'memory'))
|
|
26
|
+
if (!existsSync(root)) {
|
|
27
|
+
console.error(`[detect] 记忆根不存在:${root}`)
|
|
28
|
+
process.exit(2)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const report = detectRoot(root)
|
|
32
|
+
if (flag('--json')) {
|
|
33
|
+
process.stdout.write(JSON.stringify(report, null, 2) + '\n')
|
|
34
|
+
} else {
|
|
35
|
+
const lines = []
|
|
36
|
+
lines.push(`SoloPup 记忆检测 v${report.version} · ${report.generatedAt}`)
|
|
37
|
+
lines.push(`根目录:${report.root}`)
|
|
38
|
+
lines.push(`总计:error ${report.summary.errors} · warning ${report.summary.warnings} · info ${report.summary.infos}`)
|
|
39
|
+
for (const store of report.stores) {
|
|
40
|
+
const label = store.scope === 'global' ? 'global' : 'project'
|
|
41
|
+
const metrics = store.metrics
|
|
42
|
+
lines.push('')
|
|
43
|
+
lines.push(`== ${label}:${store.dir}`)
|
|
44
|
+
if (metrics) {
|
|
45
|
+
lines.push(
|
|
46
|
+
` 索引 ${metrics.indexLines}/${metrics.limitLines} 行、${metrics.indexBytes}/${metrics.limitBytes} 字节;` +
|
|
47
|
+
`有效记录 ${metrics.activeRecords}、待审 ${metrics.pendingRecords}、过期行 ${metrics.expiredIndexLines};` +
|
|
48
|
+
`库占用 ${(metrics.storeBytes / 1024).toFixed(1)}K/${metrics.storeLimitBytes / 1024}K`,
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
if (store.findings.length === 0) {
|
|
52
|
+
lines.push(' [ok] 未发现问题(“未发现”不等于“没有问题”,语义质量见 L2)')
|
|
53
|
+
continue
|
|
54
|
+
}
|
|
55
|
+
for (const finding of store.findings) {
|
|
56
|
+
if (finding.severity === 'info' && !flag('--verbose')) continue
|
|
57
|
+
const refs = (finding.refs ?? []).map((item) => (item.line === undefined ? item.file : `${item.file}:${item.line}`)).join(' ')
|
|
58
|
+
lines.push(` [${finding.severity}] ${finding.id} ${finding.message}${refs ? ` (${refs})` : ''}`)
|
|
59
|
+
if (finding.fixHint && flag('--verbose')) lines.push(` 修复:${finding.fixHint}`)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
for (const finding of report.crossFindings) {
|
|
63
|
+
if (finding.severity === 'info' && !flag('--verbose')) continue
|
|
64
|
+
lines.push(` [${finding.severity}] ${finding.id} ${finding.message}`)
|
|
65
|
+
}
|
|
66
|
+
process.stdout.write(lines.join('\n') + '\n')
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (flag('--check') && report.summary.errors > 0) process.exit(1)
|