@a9i5k4/dsh-auto-memory 2.5.3 → 3.0.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 +171 -1
- package/README.zh-CN.md +171 -1
- package/docs/CONTRIBUTORS.html +471 -0
- package/docs/HANDOFF-CRITERIA.md +92 -0
- package/docs/INTEGRATION-ANALYSIS.md +350 -348
- package/docs/USER-GUIDE.en.md +56 -1
- package/docs/USER-GUIDE.zh-CN.md +57 -2
- package/docs/internal/ACCEPT-35-LIVE.md +143 -0
- package/docs/internal/ACCEPTANCE-20260914.md +90 -0
- package/docs/internal/ARCH-REVIEW-BRIEF.md +411 -0
- package/docs/internal/ARCH-REVIEW-REQUEST.md +201 -0
- package/docs/internal/ARCH-REVIEW-ROUND2.md +169 -0
- package/docs/internal/ARCH-REVIEW-ROUND3.md +206 -0
- package/docs/internal/AUDIT-WB-GRAPH-FULL-20260916.md +314 -0
- package/docs/internal/CONCURRENCY-INVESTIGATION-20260917.md +192 -0
- package/docs/internal/CROSS-SESSION-SEARCH-PATH-DECISION.md +72 -0
- package/docs/internal/CROSS-SESSION-SEARCH-RESEARCH.md +131 -0
- package/docs/internal/DECISIONS-20260914-SESSION.md +269 -0
- package/docs/internal/DESIGN-P1-STATE-COMMIT-20260915.md +219 -0
- package/docs/internal/DIRECTION-CHECK-WB-GRAPH-20260916.md +132 -0
- package/docs/internal/FEEDBACK-TO-DSHAPI-RELAY.md +13 -0
- package/docs/internal/GH-DISCUSSION-5732-COMMENT.md +74 -0
- package/docs/internal/GPT-ACCEPTANCE-PROMPT-20260916.md +352 -0
- package/docs/internal/GPT-REVIEW-PROMPT.md +216 -0
- package/docs/internal/GROUP-WEBHOOK-SETUP.md +33 -0
- package/docs/internal/KICKOFF-P0.md +254 -0
- package/docs/internal/MASTER-PLAN-3.0.md +411 -0
- package/docs/internal/MEMORY-MUTATION-AND-INDEX-DESIGN.md +85 -0
- package/docs/internal/MERGE-CONFLICT-SCAN-20260914.md +222 -0
- package/docs/internal/PENDING-FIXES-20260916.md +289 -0
- package/docs/internal/RAG-KARPATHY-PROGRAM.md +229 -0
- package/docs/internal/REPORT-P0-NIGHTLY.md +212 -0
- package/docs/internal/REPORT-P5-ACCEPTANCE.md +31 -0
- package/docs/internal/REPORT-WB-GRAPH-NIGHTLY.md +153 -0
- package/docs/internal/REVIEW-WB-GRAPH-SELF.md +81 -0
- package/docs/internal/ROADMAP-20260917-WEEK.md +305 -0
- package/docs/internal/ROADMAP.md +106 -0
- package/docs/internal/RUN-P0-NIGHTLY.md +227 -0
- package/docs/internal/S10-CONSTRUCTION-HANDOFF-20260917.md +175 -0
- package/docs/internal/S10-GAPS-PLAIN-20260917.md +125 -0
- package/docs/internal/SEMANTIC-ARCHITECTURE-SPEC.md +360 -0
- package/docs/internal/SESSION-FILE-REPAIR-PROTOCOL.md +90 -0
- package/docs/internal/THREE-LAYER-CONTRACT.md +210 -0
- package/docs/internal/TODO-BACKLOG.md +263 -142
- package/docs/internal/TODO-GRAPH.html +715 -0
- package/docs/internal/TODO-GRAPH.html.bak-20260914-v2 +493 -0
- package/docs/internal/TODO-GRAPH.html.bak-20260915-alsfix +710 -0
- package/docs/internal/TODO-GRAPH.html.bak-20260915-p1 +710 -0
- package/docs/internal/TODO-GRAPH.html.bak-20260915-p6a-rev +703 -0
- package/docs/internal/TODO-GRAPH.html.bak-20260915-wshint +710 -0
- package/docs/internal/TODO-GRAPH.html.bak-20260916-batch +715 -0
- package/docs/internal/WB-FORMAT-CONVENTION.md +112 -0
- package/docs/internal/WB-GRAPH-DECISIONS-20260914.md +71 -0
- package/docs/internal/reviews/CLAIM-VERIFICATION-20260914.md +56 -0
- package/docs/internal/reviews/PLAN-gpt6astra-round2-20260914.md +787 -0
- package/docs/internal/reviews/REVIEW-gpt6astra-20260914.md +112 -0
- package/docs/internal/reviews/ROUND3-REVIEW-INTEGRATION-20260914.md +230 -0
- package/docs/prompts/M8-3-enable-verify.md +49 -49
- package/lib/acceptance.js +71 -0
- package/lib/activation-host.js +90 -9
- package/lib/activation-inbox.js +25 -7
- package/lib/board-mode.js +30 -0
- package/lib/client.js +878 -75
- package/lib/context-bridge.js +2 -2
- package/lib/context-host.js +70 -6
- package/lib/engine-identity.js +149 -0
- package/lib/engine-switch.js +247 -0
- package/lib/episodic-store.js +11 -10
- package/lib/evidence-store.js +2 -2
- package/lib/fact-store.js +1 -1
- package/lib/fs-retry.js +46 -0
- package/lib/index.js +1987 -153
- package/lib/intent-clean-safe.js +40 -0
- package/lib/intent-clean.js +12 -16
- package/lib/l0-extract.js +263 -149
- package/lib/l0-index-sync.js +195 -0
- package/lib/l0-index.js +349 -239
- package/lib/ledger-criteria.js +142 -0
- package/lib/m7-index-sync-host.js +65 -4
- package/lib/m7-wire.js +3 -3
- package/lib/memory-anchor.js +56 -1
- package/lib/memory-envelope.js +252 -0
- package/lib/memory-hub.js +14 -4
- package/lib/memory-mutation.js +246 -0
- package/lib/memory-writer.js +204 -24
- package/lib/procedure-observation.js +48 -0
- package/lib/procedure-store.js +34 -17
- package/lib/python-setup.js +1 -1
- package/lib/rerank-host.js +160 -0
- package/lib/rules-layer.js +261 -0
- package/lib/semantic-js.js +15 -0
- package/lib/shadow-retrieval.js +3 -3
- package/lib/state-commit.js +245 -0
- package/lib/subagent-gc.js +4 -8
- package/lib/tier-layer-inject.js +650 -0
- package/lib/tier0-catalog.js +693 -0
- package/lib/water-window.js +263 -186
- package/lib/wb-contract.js +495 -0
- package/lib/wb-sidecar.js +839 -0
- package/lib/ws-overview-rank.js +2 -2
- package/package.json +1 -1
- package/python/m7_embedding_v1.py +5 -5
- package/python/worker_semantic_v1.py +17 -6
- package/python/worker_v1.py +38 -4
package/lib/ws-overview-rank.js
CHANGED
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import path from 'node:path'
|
|
14
14
|
|
|
15
|
-
/**
|
|
16
|
-
const DATE_RE =
|
|
15
|
+
/** 日期词干须整串匹配,与 index.js 的 DATE_RE 格式对齐,避免临时/备份文件参与排序(issue #51)。 */
|
|
16
|
+
const DATE_RE = /^(\d{4})-(\d{2})-(\d{2})$/
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* 按记忆活跃度(最新日志 mtime)降序排列工作区。
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@a9i5k4/dsh-auto-memory",
|
|
3
3
|
"description": "Proactive associative memory for DSH: zero-prompt recall injected before the model speaks, three-layer auto-consolidation, skill crystallization, and Astra-style context management - handoff ledgers, PLAN whiteboard, water-level sensing. Local-first, model-agnostic, zero deps. 主动联想记忆+Astra 式上下文管理:自动唤回/自动沉淀/技能固化/交接账本与白板跨窗口续命/水位感知。",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "3.0.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"exports": {
|
|
@@ -13,10 +13,10 @@ Pure re-implementation of the frozen policy for the production sidecar path
|
|
|
13
13
|
-> configHash; mismatch = stale = full rebuild
|
|
14
14
|
|
|
15
15
|
Providers:
|
|
16
|
-
hash-
|
|
16
|
+
hash-v1 deterministic stdlib-only embedding (sha256-seeded bag of
|
|
17
17
|
token-trigram dims). Zero dependencies, zero network. Used
|
|
18
18
|
by CI and offline protocol tests. NOT a quality provider.
|
|
19
|
-
bge-m3-
|
|
19
|
+
bge-m3-v1 real model via transformers (lazy import; requires the
|
|
20
20
|
pinned local snapshot dir passed in the embedding config).
|
|
21
21
|
|
|
22
22
|
No DSH file reads; no writes except what the worker explicitly passes in.
|
|
@@ -25,9 +25,9 @@ import hashlib
|
|
|
25
25
|
import json
|
|
26
26
|
import re
|
|
27
27
|
|
|
28
|
-
PROVIDER_REAL = 'bge-m3-
|
|
28
|
+
PROVIDER_REAL = 'bge-m3-v1'
|
|
29
29
|
PROVIDER_REAL_INT8 = 'bge-m3-onnx-int8-v1'
|
|
30
|
-
PROVIDER_HASH = 'hash-
|
|
30
|
+
PROVIDER_HASH = 'hash-v1'
|
|
31
31
|
CHUNK_POLICY_VERSION = 'm7_chunk_v1'
|
|
32
32
|
CHUNK_MAX_TOKENS = 512
|
|
33
33
|
QUERY_MAX_TOKENS = 256
|
|
@@ -74,7 +74,7 @@ def config_hash(provider, model_revision, dimension):
|
|
|
74
74
|
|
|
75
75
|
def chunk_id_for(memory_id, record_digest, ordinal):
|
|
76
76
|
return 'chk_' + sha_hex(
|
|
77
|
-
('m7-chunk-
|
|
77
|
+
('m7-chunk-v1\u0000' + memory_id + '\u0000' + record_digest +
|
|
78
78
|
'\u0000' + str(ordinal)).encode('utf-8'))[:32]
|
|
79
79
|
|
|
80
80
|
|
|
@@ -19,7 +19,7 @@ index_sync rejection matrix, same atomic derived-corpus persistence. Adds:
|
|
|
19
19
|
|
|
20
20
|
Embedding backend is selected by an optional JSON config file passed via
|
|
21
21
|
the DSH_M7_EMBEDDING_CONFIG environment variable (no CLI change, no JS
|
|
22
|
-
change): {"provider":"bge-m3-
|
|
22
|
+
change): {"provider":"bge-m3-v1"|"hash-v1", "modelDir":...,
|
|
23
23
|
"modelRevision":..., "dimension":1024, "torchThreads":16}.
|
|
24
24
|
Without the env var the worker degrades to fake-worker behavior (protocol
|
|
25
25
|
alive, embedding not ready) - never crashes, never changes ack semantics.
|
|
@@ -64,7 +64,7 @@ def wsref_of(workspace_key):
|
|
|
64
64
|
artifact of differing miv values)."""
|
|
65
65
|
canon = canonical_workspace_key(workspace_key)
|
|
66
66
|
return 'wsr_' + hashlib.sha256(
|
|
67
|
-
('evidence-wsref-
|
|
67
|
+
('evidence-wsref-v1\u0000' + canon).encode('utf-8')).hexdigest()[:32]
|
|
68
68
|
|
|
69
69
|
|
|
70
70
|
def _tokenize(text, stopwords=frozenset()):
|
|
@@ -640,7 +640,7 @@ class SemanticWorker(base.Worker):
|
|
|
640
640
|
if not cands:
|
|
641
641
|
return None
|
|
642
642
|
activation_id = 'act_' + base.first32(
|
|
643
|
-
base.sha_str('m7-semantic-activation-
|
|
643
|
+
base.sha_str('m7-semantic-activation-v1\u0000' + obs))
|
|
644
644
|
created = req.get('sentAt', 0)
|
|
645
645
|
ttl = int(pol['ttlSteps'])
|
|
646
646
|
return {
|
|
@@ -690,12 +690,23 @@ class SemanticWorker(base.Worker):
|
|
|
690
690
|
if act is None:
|
|
691
691
|
return None
|
|
692
692
|
reasons = ','.join(str(x) for x in (out.get('reasonCodes') or []))
|
|
693
|
+
|
|
694
|
+
def _num(x):
|
|
695
|
+
try:
|
|
696
|
+
return float(x or 0.0)
|
|
697
|
+
except (TypeError, ValueError):
|
|
698
|
+
return 0.0
|
|
699
|
+
|
|
700
|
+
# 2026-09-14 P1-⑮:reason 串此前只有 lane/decision/reasonCodes,注入侧看不到
|
|
701
|
+
# 0-1 相似度(intent 概率 / 稠密分 / 融合 margin)。数值段放在 reasonCodes **之前**,
|
|
702
|
+
# 这样 160 字符截断只会砍掉代码列表,不会砍掉分值。
|
|
693
703
|
act['threshold'] = {
|
|
694
704
|
'policyVersion': featv2.ACTIVATION_POLICY_VERSION,
|
|
695
705
|
'score': round(score, 6),
|
|
696
706
|
'threshold': float(th.get('tauHi', 0.45)),
|
|
697
|
-
'reason': ('fv2 lane=%s %s %s' % (
|
|
698
|
-
feats.get('lane'), out.get('decision'),
|
|
707
|
+
'reason': ('fv2 lane=%s %s intent=%.2f dense=%.2f margin=%.2f %s' % (
|
|
708
|
+
feats.get('lane'), out.get('decision'), score,
|
|
709
|
+
_num(feats.get('denseTop')), _num(feats.get('margin')), reasons))[:160],
|
|
699
710
|
}
|
|
700
711
|
act['level'] = 'excerpt'
|
|
701
712
|
return act
|
|
@@ -1356,7 +1367,7 @@ def main():
|
|
|
1356
1367
|
args, _unknown = ap.parse_known_args()
|
|
1357
1368
|
if args.selftest:
|
|
1358
1369
|
base.run_selftest()
|
|
1359
|
-
w = SemanticWorker('ep', '', {'provider': 'hash-
|
|
1370
|
+
w = SemanticWorker('ep', '', {'provider': 'hash-v1',
|
|
1360
1371
|
'dimension': 64})
|
|
1361
1372
|
view = w.embedding_view()
|
|
1362
1373
|
assert view['enabled'] is True and view['ready'] is False
|
package/python/worker_v1.py
CHANGED
|
@@ -22,12 +22,23 @@ import os
|
|
|
22
22
|
import re
|
|
23
23
|
import sys
|
|
24
24
|
import tempfile
|
|
25
|
+
import time
|
|
25
26
|
|
|
26
27
|
PROTOCOL = 'm7_wire_v1'
|
|
27
28
|
NAMESPACE = 'dsh-auto-memory'
|
|
28
29
|
INDEX_POLICY = 'index_sync_v1'
|
|
29
30
|
MAX_LINE_BYTES = 256 * 1024
|
|
30
31
|
MAX_RECORDS_PER_PAGE = 64
|
|
32
|
+
# ★2026-09-17 修「索引永久不就绪 → 语义唤回全程降级」:JS 侧契约是「新 miv latest-wins;
|
|
33
|
+
# 旧 in-flight sync abort/cancel」,但 abort 只作用于 JS 的 fetch,Python 从不收到取消帧;
|
|
34
|
+
# 而 active_sync 原先只在 reject_sync / commit 两处清零 ⇒ 一次「begin 之后没收 page/commit」
|
|
35
|
+
# 的同步会把槽永久占住,此后每次 index_begin 都被回 sync-in-progress。
|
|
36
|
+
# 实测取证:engine-switch-state.json = failed:2527 / error:"sync-in-progress" / startedAt:0;
|
|
37
|
+
# 诊断日志 index-not-ready 累计 22945 行(sync-in-progress 占 664),自 2026-08-26 起持续;
|
|
38
|
+
# worker 于 00:49:50 重启后仍有 1593 行 ⇒ 重启只是暂时缓解,状态会重新形成。
|
|
39
|
+
# 本常量 = 「距最后一次活动多久仍无进展即视为持有者已死」的阈值;取 150s > JS 的 syncTimeoutMs
|
|
40
|
+
# (120s),保证 JS 先放弃、worker 后回收。
|
|
41
|
+
SYNC_TAKEOVER_IDLE_SECONDS = 150
|
|
31
42
|
STDERR_BUDGET = 64
|
|
32
43
|
|
|
33
44
|
JS_TYPES = frozenset(['health', 'context_push', 'index_sync_begin', 'index_sync_page',
|
|
@@ -136,7 +147,7 @@ class Worker:
|
|
|
136
147
|
self.active_sync = None # at most one in-flight index sync (JS serializes anyway)
|
|
137
148
|
self.derived = {} # (workspaceRef, scope) -> current committed entry ONLY (old versions discarded)
|
|
138
149
|
self.counts = {'frames': 0, 'errors': 0, 'acks': 0, 'activations': 0, 'commits': 0,
|
|
139
|
-
'cancels': 0, 'close_sessions': 0}
|
|
150
|
+
'cancels': 0, 'close_sessions': 0, 'sync_takeovers': 0}
|
|
140
151
|
|
|
141
152
|
# ---------- outbound ----------
|
|
142
153
|
|
|
@@ -246,7 +257,7 @@ class Worker:
|
|
|
246
257
|
continue
|
|
247
258
|
if not RE_HEX64.match(fdig) or not RE_HEX64.match(rdig):
|
|
248
259
|
continue
|
|
249
|
-
activation_id = 'act_' + first32(sha_str('m7-fake-activation-
|
|
260
|
+
activation_id = 'act_' + first32(sha_str('m7-fake-activation-v1\u0000' + obs))
|
|
250
261
|
cand = {
|
|
251
262
|
'candidateId': 'cand_' + first32(sha_str(activation_id + '\u0000' + mid + '\u0000' + str(i))),
|
|
252
263
|
'memoryId': mid,
|
|
@@ -262,7 +273,7 @@ class Worker:
|
|
|
262
273
|
candidates.append(cand)
|
|
263
274
|
if not candidates:
|
|
264
275
|
return None
|
|
265
|
-
activation_id = 'act_' + first32(sha_str('m7-fake-activation-
|
|
276
|
+
activation_id = 'act_' + first32(sha_str('m7-fake-activation-v1\u0000' + obs))
|
|
266
277
|
created = req.get('sentAt', 0)
|
|
267
278
|
ttl_steps = 2
|
|
268
279
|
activation = {
|
|
@@ -340,11 +351,33 @@ class Worker:
|
|
|
340
351
|
require((record_count == 0) == (page_count == 0), 'invalid-payload', 'count-consistency')
|
|
341
352
|
require(p.get('indexPolicyVersion') == INDEX_POLICY, 'invalid-payload', 'indexPolicyVersion')
|
|
342
353
|
if self.active_sync is not None:
|
|
343
|
-
|
|
354
|
+
prev = self.active_sync
|
|
355
|
+
prev_id = str(prev.get('syncId', ''))
|
|
356
|
+
try:
|
|
357
|
+
last_seen = float(prev.get('lastActivityAt') or prev.get('startedAt') or 0)
|
|
358
|
+
except Exception:
|
|
359
|
+
last_seen = 0.0
|
|
360
|
+
idle = (time.time() - last_seen) if last_seen else 0.0
|
|
361
|
+
same_key = (str(prev.get('workspaceRef', '')) == ws_ref
|
|
362
|
+
and prev.get('scope') == scope)
|
|
363
|
+
superseded = same_key and prev_id != sync_id
|
|
364
|
+
idle_dead = idle > SYNC_TAKEOVER_IDLE_SECONDS
|
|
365
|
+
if superseded or idle_dead:
|
|
366
|
+
# 只在「同 key 被新 syncId 取代」或「超过阈值无任何活动」时接管;
|
|
367
|
+
# 跨工作区并发、以及正在推进的长同步,仍按原样回 sync-in-progress。
|
|
368
|
+
self.counts['sync_takeovers'] = self.counts.get('sync_takeovers', 0) + 1
|
|
369
|
+
diag('index_sync takeover: %s -> %s (%s)' % (
|
|
370
|
+
prev_id or '(none)', sync_id,
|
|
371
|
+
'superseded' if superseded else 'idle:%.0fs' % idle))
|
|
372
|
+
self.active_sync = None
|
|
373
|
+
else:
|
|
374
|
+
return [self.index_ack(req, p, 'begin', False, reason='sync-in-progress')]
|
|
344
375
|
self.active_sync = {
|
|
345
376
|
'syncId': sync_id, 'workspaceRef': ws_ref, 'scope': scope, 'memoryIndexVersion': miv,
|
|
346
377
|
'recordCount': record_count, 'pageCount': page_count, 'tuples': tuples,
|
|
347
378
|
'pages': {}, 'next_page': 0, 'received': 0, 'page_digests': [],
|
|
379
|
+
'startedAt': time.time(),
|
|
380
|
+
'lastActivityAt': time.time(),
|
|
348
381
|
}
|
|
349
382
|
return [self.index_ack(req, p, 'begin', True)]
|
|
350
383
|
|
|
@@ -412,6 +445,7 @@ class Worker:
|
|
|
412
445
|
st['next_page'] = page_no + 1
|
|
413
446
|
st['received'] += len(records)
|
|
414
447
|
st['page_digests'].append(recomputed)
|
|
448
|
+
st['lastActivityAt'] = time.time()
|
|
415
449
|
return [self.index_ack(req, base, 'page', True, extra={
|
|
416
450
|
'pageNo': page_no, 'receivedPages': len(st['pages']), 'receivedRecords': st['received']})]
|
|
417
451
|
|