@a9i5k4/dsh-auto-memory 2.2.6 → 2.3.0
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/README.md +20 -10
- package/README.zh-CN.md +22 -10
- package/docs/CONTINUITY-FLOW.md +222 -0
- package/docs/HANDBOOK.md +354 -0
- package/docs/INTEGRATION-ANALYSIS.md +348 -0
- package/docs/M-CM7-HANDOFF-LAYERED-RETRIEVAL.md +311 -0
- package/docs/M8-MEMORY-HUB.md +1 -1
- package/docs/PROMPT-PACK-LAYERED-RECALL.md +474 -0
- package/docs/PROMPT-SET-STRICT.md +389 -0
- package/docs/RELEASE-GO-NOGO.md +82 -0
- package/docs/ROADMAP.md +162 -0
- package/docs/STATUS-BOARD.md +147 -0
- package/docs/USER-GUIDE.en.md +382 -0
- package/docs/USER-GUIDE.zh-CN.md +382 -289
- package/docs/prompts/EXEC-ORDER.md +77 -0
- package/docs/prompts/FEEDING-SCRIPT.md +174 -0
- package/docs/prompts/FEEDING-SEQUENCE.md +61 -0
- package/docs/prompts/FIX-AGENT-M8-2b.md +119 -0
- package/docs/prompts/FIX-AGENT-P11.md +97 -0
- package/docs/prompts/FIX-AGENT-P12-FULL-REGRESSION.md +135 -0
- package/docs/prompts/FIX-AGENT-P12.md +113 -0
- package/docs/prompts/FIX-AGENT-P13-PYTHON-RANK.md +100 -0
- package/docs/prompts/FIX-AGENT-P8.md +120 -0
- package/docs/prompts/FIX-AGENT-P9.md +110 -0
- package/docs/prompts/FIX-AGENT-P9a.md +94 -0
- package/docs/prompts/FIX-AGENT-P9d.md +114 -0
- package/docs/prompts/FIX-AGENT-TEMPORAL-ARM.md +148 -0
- package/docs/prompts/LIVE-VERIFY-ZCODE.md +105 -0
- package/docs/prompts/M8-1-fact-metadata.md +45 -0
- package/docs/prompts/M8-2-ADJUDICATION.md +98 -0
- package/docs/prompts/M8-2-importance-wiring.md +42 -0
- package/docs/prompts/M8-2b-evidence-pipeline.md +52 -0
- package/docs/prompts/M8-3-enable-verify.md +49 -0
- package/docs/prompts/M8-R-REPORT.md +156 -0
- package/docs/prompts/M8-R-research.md +67 -0
- package/docs/prompts/P1-l0-index.md +30 -0
- package/docs/prompts/P10-importance-calibration.md +45 -0
- package/docs/prompts/P11-silent-catch-observability.md +43 -0
- package/docs/prompts/P2-semantic-recall.md +30 -0
- package/docs/prompts/P3-fusion.md +28 -0
- package/docs/prompts/P4-l0-response.md +28 -0
- package/docs/prompts/P5-handoff-anchor.md +28 -0
- package/docs/prompts/P6-ledger-weight.md +27 -0
- package/docs/prompts/P7-write-fix.md +26 -0
- package/docs/prompts/P8-rrf-wiring.md +47 -0
- package/docs/prompts/P9-REVIEW-DECISION.md +95 -0
- package/docs/prompts/P9-evidence-write-coverage.md +113 -0
- package/docs/prompts/README.md +105 -0
- package/docs/prompts/ZCODE-DROPIN.md +229 -0
- package/docs/prompts/_COMMON.md +88 -0
- package/lib/client.js +36 -2
- package/lib/context-host.js +77 -2
- package/lib/evidence-agg.js +81 -0
- package/lib/fact-store.js +32 -0
- package/lib/handoff-anchor.js +114 -0
- package/lib/index.js +402 -43
- package/lib/l0-extract.js +149 -0
- package/lib/l0-index.js +239 -0
- package/lib/m7-wire.js +4 -3
- package/lib/memory-importance.js +70 -0
- package/lib/python-setup.js +16 -4
- package/lib/recall-fusion.js +99 -0
- package/lib/shadow-retrieval.js +2 -2
- package/lib/storage-manage.js +17 -0
- package/lib/subagent-gc.js +8 -1
- package/lib/temporal-parse.js +159 -0
- package/package.json +1 -1
- package/python/worker_semantic_v1.py +28 -1
package/lib/fact-store.js
CHANGED
|
@@ -37,6 +37,12 @@ export const FACT_ID_RE = /^fact_[0-9a-f]{32}$/
|
|
|
37
37
|
/** scope 枚举: 与 M5/M6 的 scope 对齐(User=跨工作区, Workspace=当前工作区)。 */
|
|
38
38
|
export const FACT_SCOPES_V1 = Object.freeze(['User', 'Workspace'])
|
|
39
39
|
|
|
40
|
+
/** M8-1 认识论状态枚举: fact=证据/不可推导, observation=推断/可重算, directive=行为指令。 */
|
|
41
|
+
export const FACT_EPISTEMIC_STATUSES_V1 = Object.freeze(['fact', 'observation', 'directive'])
|
|
42
|
+
|
|
43
|
+
/** M8-1 趋势枚举: 相对上次确认的生命周期走向。 */
|
|
44
|
+
export const FACT_TRENDS_V1 = Object.freeze(['new', 'strengthening', 'stable', 'weakening', 'stale'])
|
|
45
|
+
|
|
40
46
|
/** sourceClass 枚举: 与 m4-corpus-pre 的 sourceClass 对齐。 */
|
|
41
47
|
export const FACT_SOURCE_CLASSES_V1 = Object.freeze([
|
|
42
48
|
'user-memory', 'workspace-notes', 'workspace-log', 'semantic-candidate', 'profile-candidate',
|
|
@@ -69,6 +75,12 @@ export function validateFactCandidatePre(cand) {
|
|
|
69
75
|
if (cand.provenance !== undefined && (!Array.isArray(cand.provenance) || cand.provenance.some((s) => typeof s !== 'string'))) p.push('provenance')
|
|
70
76
|
if (cand.confidence !== undefined && (typeof cand.confidence !== 'number' || !Number.isFinite(cand.confidence) || cand.confidence < 0 || cand.confidence > 1)) p.push('confidence')
|
|
71
77
|
if (cand.ttl !== undefined && (typeof cand.ttl !== 'number' || !Number.isFinite(cand.ttl) || cand.ttl < 0)) p.push('ttl')
|
|
78
|
+
// M8-1 时间三价 + 认识论状态 + 趋势(全部可选;缺省放行 → 旧结构/旧调用方不受影响)
|
|
79
|
+
for (const k of ['occurredAt', 'mentionedAt', 'ingestedAt']) {
|
|
80
|
+
if (cand[k] !== undefined && (typeof cand[k] !== 'number' || !Number.isFinite(cand[k]) || cand[k] < 0)) p.push(k)
|
|
81
|
+
}
|
|
82
|
+
if (cand.epistemicStatus !== undefined && !FACT_EPISTEMIC_STATUSES_V1.includes(cand.epistemicStatus)) p.push('epistemicStatus')
|
|
83
|
+
if (cand.trend !== undefined && !FACT_TRENDS_V1.includes(cand.trend)) p.push('trend')
|
|
72
84
|
if (p.length) return { ok: false, reason: 'invalid:' + p.join(',') }
|
|
73
85
|
return { ok: true, candidate: cand }
|
|
74
86
|
}
|
|
@@ -89,6 +101,12 @@ export function validateFactPre(fact) {
|
|
|
89
101
|
if (typeof fact.confirmedAt !== 'number' || !Number.isFinite(fact.confirmedAt)) p.push('confirmedAt')
|
|
90
102
|
if (fact.ttl !== undefined && (typeof fact.ttl !== 'number' || !Number.isFinite(fact.ttl))) p.push('ttl')
|
|
91
103
|
if (fact.revoked !== undefined && typeof fact.revoked !== 'boolean') p.push('revoked')
|
|
104
|
+
// M8-1 新字段(全部可选;缺字段=旧数据,放行 → 既有 facts.json 可读,不报错不丢弃)
|
|
105
|
+
for (const k of ['occurredAt', 'mentionedAt', 'ingestedAt']) {
|
|
106
|
+
if (fact[k] !== undefined && (typeof fact[k] !== 'number' || !Number.isFinite(fact[k]))) p.push(k)
|
|
107
|
+
}
|
|
108
|
+
if (fact.epistemicStatus !== undefined && !FACT_EPISTEMIC_STATUSES_V1.includes(fact.epistemicStatus)) p.push('epistemicStatus')
|
|
109
|
+
if (fact.trend !== undefined && !FACT_TRENDS_V1.includes(fact.trend)) p.push('trend')
|
|
92
110
|
if (p.length) return { ok: false, reason: 'invalid:' + p.join(',') }
|
|
93
111
|
return { ok: true, fact }
|
|
94
112
|
}
|
|
@@ -190,6 +208,12 @@ export function createFactStorePre(opts = {}) {
|
|
|
190
208
|
confidence: c.confidence !== undefined ? c.confidence : null,
|
|
191
209
|
confirmedAt: now, ttl: c.ttl !== undefined ? c.ttl : FACT_TTL_DEFAULT_V1,
|
|
192
210
|
revoked: false,
|
|
211
|
+
// M8-1 时间三价:入库时间必填(=本次确认);发生/陈述时间候选提供则透传,缺省 undefined(落盘时省略)
|
|
212
|
+
ingestedAt: now,
|
|
213
|
+
occurredAt: c.occurredAt,
|
|
214
|
+
mentionedAt: c.mentionedAt,
|
|
215
|
+
...(c.epistemicStatus !== undefined ? { epistemicStatus: c.epistemicStatus } : {}),
|
|
216
|
+
...(c.trend !== undefined ? { trend: c.trend } : {}),
|
|
193
217
|
}
|
|
194
218
|
const fv = validateFactPre(fact)
|
|
195
219
|
if (!fv.ok) return { ok: false, reason: 'fact-invalid:' + fv.reason, outcome: 'invalid' }
|
|
@@ -210,7 +234,15 @@ export function createFactStorePre(opts = {}) {
|
|
|
210
234
|
for (const s of c.provenance) if (!seen.has(s)) prev.provenance.push(s)
|
|
211
235
|
}
|
|
212
236
|
if (c.confidence !== undefined && c.confidence !== null) prev.confidence = c.confidence
|
|
237
|
+
// M8-1 向后兼容回填(须在 confirmedAt 更新前执行):旧记录(无 ingestedAt)首次合并时补齐,
|
|
238
|
+
// 取其原始确认时间(=首次入库);不改变其余合并语义
|
|
239
|
+
if (prev.ingestedAt === undefined && Number.isFinite(prev.confirmedAt)) prev.ingestedAt = prev.confirmedAt
|
|
213
240
|
prev.confirmedAt = now // 合并视为重新确认
|
|
241
|
+
// M8-1 可选元数据透传:重新陈述时更新发生/陈述时间与认识论状态/趋势(候选提供才写,加法性)
|
|
242
|
+
if (c.occurredAt !== undefined) prev.occurredAt = c.occurredAt
|
|
243
|
+
if (c.mentionedAt !== undefined) prev.mentionedAt = c.mentionedAt
|
|
244
|
+
if (c.epistemicStatus !== undefined) prev.epistemicStatus = c.epistemicStatus
|
|
245
|
+
if (c.trend !== undefined) prev.trend = c.trend
|
|
214
246
|
if (c.ttl !== undefined) prev.ttl = c.ttl
|
|
215
247
|
stats.merged++
|
|
216
248
|
void persist()
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* P6(2026-09-09) 账本权重化截断纯核心(handoff_ledger_weight_v1)。
|
|
3
|
+
*
|
|
4
|
+
* 背景:buildContinueCarry 的 `ledger.slice(0, 8000)` 是位置截断,而账本段内异质——
|
|
5
|
+
* 位置截可能把高价值段整体截掉(M-CM7 §C/§G4)。本模块按账本自身四段标题赋权:
|
|
6
|
+
* 已试方案与失败原因 .35 > 进度与下一步 .30 > 目标 .20 > 任务状态 .15
|
|
7
|
+
* 预算不足时从最低权重段开始截(保留段标题行),预算充足时逐字节原样返回。
|
|
8
|
+
*
|
|
9
|
+
* 契约:
|
|
10
|
+
* - 标题字符串来自账本文件本身(写入侧骨架逐字一致),不做内容语义标注——禁止把"成功解法"误标为"失败原因"。
|
|
11
|
+
* - 纯函数、零 IO、零依赖;同输入逐字节确定;非法输入 fail closed(返回 null,调用方回落位置截断)。
|
|
12
|
+
* - 只影响注入,不动文件原文。
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
export const HANDOFF_LEDGER_WEIGHT_VERSION = 'handoff_ledger_weight_v1'
|
|
16
|
+
|
|
17
|
+
/** 四段标题 → 权重(降序)。标题为账本中的原样标题行(半角 '## '+单空格,不含行尾 \r)。 */
|
|
18
|
+
export const HANDOFF_LEDGER_SECTION_WEIGHTS_V1 = Object.freeze([
|
|
19
|
+
Object.freeze({ title: '已试方案与失败原因', weight: 0.35 }),
|
|
20
|
+
Object.freeze({ title: '进度与下一步', weight: 0.30 }),
|
|
21
|
+
Object.freeze({ title: '目标', weight: 0.20 }),
|
|
22
|
+
Object.freeze({ title: '任务状态', weight: 0.15 }),
|
|
23
|
+
])
|
|
24
|
+
|
|
25
|
+
const TITLE_WEIGHT = new Map(HANDOFF_LEDGER_SECTION_WEIGHTS_V1.map((x) => [x.title, x.weight]))
|
|
26
|
+
/** 未知 '## ' 段:权重最低(最先被截),但仍保留标题与内容直到轮到它。 */
|
|
27
|
+
const UNKNOWN_WEIGHT = 0.05
|
|
28
|
+
const TRIM_MARK = '…(已按预算截断,全文见 handoff/ 最新账本)'
|
|
29
|
+
|
|
30
|
+
function weightOf(titleText) {
|
|
31
|
+
const name = String(titleText || '').replace(/^##\s*/, '').replace(/\s+$/, '')
|
|
32
|
+
return TITLE_WEIGHT.has(name) ? TITLE_WEIGHT.get(name) : UNKNOWN_WEIGHT
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* 解析账本四段。行级切分:遇到 `## ` 开头行即开新段;首个标题行之前的内容为 preamble(如 `# 交接账本 · …` 大标题)。
|
|
37
|
+
* title 保留原样行(含 '## ' 前缀,容忍尾随 \r);body 为两标题行之间的原始行(不做 trim,保证原样回装)。
|
|
38
|
+
* @returns {{preamble: string[], sections: Array<{title: string, body: string[], weight: number}>} | null}
|
|
39
|
+
* 无任何 '## ' 段或输入非法 → null(fail closed)。
|
|
40
|
+
*/
|
|
41
|
+
export function parseHandoffLedgerPre(text) {
|
|
42
|
+
const src = typeof text === 'string' ? text : ''
|
|
43
|
+
if (!src.trim()) return null
|
|
44
|
+
const lines = src.split('\n')
|
|
45
|
+
const preamble = []
|
|
46
|
+
const sections = []
|
|
47
|
+
let cur = null
|
|
48
|
+
for (const line of lines) {
|
|
49
|
+
if (/^## (.+)$/.test(line.replace(/\r$/, ''))) {
|
|
50
|
+
if (cur) sections.push(cur)
|
|
51
|
+
cur = { title: line, body: [], weight: weightOf(line) }
|
|
52
|
+
} else if (cur) cur.body.push(line)
|
|
53
|
+
else preamble.push(line)
|
|
54
|
+
}
|
|
55
|
+
if (cur) sections.push(cur)
|
|
56
|
+
if (!sections.length) return null
|
|
57
|
+
return { preamble, sections }
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** 回装:preamble + 各段(title 行 + body 行)以 \n 连接;对无 \r 输入逐字节还原原文本。 */
|
|
61
|
+
function assemble(preamble, sections) {
|
|
62
|
+
const parts = []
|
|
63
|
+
if (preamble.length) parts.push(preamble.join('\n'))
|
|
64
|
+
for (const s of sections) parts.push(s.body.length ? s.title + '\n' + s.body.join('\n') : s.title)
|
|
65
|
+
return parts.join('\n')
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** 段内按行收缩:从 body 尾部丢行(保头部,与 slice(0,N) 语义一致),返回新 body(≤keepChars 字符)。 */
|
|
69
|
+
function shrinkBody(body, keepChars) {
|
|
70
|
+
if (keepChars <= 0) return []
|
|
71
|
+
const out = []
|
|
72
|
+
let used = 0
|
|
73
|
+
for (let i = 0; i < body.length; i++) {
|
|
74
|
+
const cost = body[i].length + (i > 0 ? 1 : 0)
|
|
75
|
+
if (used + cost > keepChars) break
|
|
76
|
+
out.push(body[i])
|
|
77
|
+
used += cost
|
|
78
|
+
}
|
|
79
|
+
return out
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* 权重化截断。预算充足(原文 ≤budget)→ 返回与原文逐字节相同的字符串;
|
|
84
|
+
* 不足 → 从最低权重段开始截 body(段标题保留,截点带 TRIM_MARK),直到进入预算;
|
|
85
|
+
* 全部段收缩后仍超预算(如 preamble/标题行本身超限)→ 最后回落 `slice(0, budget)`。
|
|
86
|
+
* 解析失败(非法输入/无已知段结构)→ 返回 null,调用方自行回落。
|
|
87
|
+
* @param {string} text 账本全文
|
|
88
|
+
* @param {number} budget 注入预算(字符)
|
|
89
|
+
* @returns {string | null}
|
|
90
|
+
*/
|
|
91
|
+
export function weightedTrimHandoffLedgerPre(text, budget) {
|
|
92
|
+
const b = Number(budget)
|
|
93
|
+
if (!Number.isFinite(b) || b <= 0) return null
|
|
94
|
+
const parsed = parseHandoffLedgerPre(text)
|
|
95
|
+
if (!parsed) return null
|
|
96
|
+
const original = assemble(parsed.preamble, parsed.sections)
|
|
97
|
+
if (original.length <= b) return original
|
|
98
|
+
|
|
99
|
+
// 截断顺序:权重升序(稳定:同权重保持文内出现顺序)
|
|
100
|
+
const order = parsed.sections.map((s, i) => ({ s, i })).sort((x, y) => x.s.weight - y.s.weight || x.i - y.i)
|
|
101
|
+
const markCost = TRIM_MARK.length + 1 // 截点换行 + marker 本身,预留避免逐段收敛震荡
|
|
102
|
+
for (const { s } of order) {
|
|
103
|
+
const current = assemble(parsed.preamble, parsed.sections)
|
|
104
|
+
if (current.length <= b) break
|
|
105
|
+
const bodyLen = s.body.join('\n').length
|
|
106
|
+
if (bodyLen <= 0) continue
|
|
107
|
+
const excess = current.length - b
|
|
108
|
+
const keep = Math.max(0, bodyLen - excess - markCost)
|
|
109
|
+
s.body = shrinkBody(s.body, keep)
|
|
110
|
+
s.body = s.body.length ? s.body.concat([TRIM_MARK]) : [TRIM_MARK]
|
|
111
|
+
}
|
|
112
|
+
const out = assemble(parsed.preamble, parsed.sections)
|
|
113
|
+
return out.length <= b ? out : out.slice(0, b)
|
|
114
|
+
}
|