dsh-prime-memory 0.26.17 → 0.26.18
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 +21 -0
- package/dist/clients/drsc.js +8 -2
- package/dist/room-review.d.ts +10 -1
- package/dist/room-review.js +19 -1
- package/dist/stats.js +2 -2
- package/dist/tools/index.js +5 -3
- package/dsh.plugin.json +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# 更新日志(Changelog)
|
|
2
2
|
|
|
3
|
+
## [未发布] — 待并入下一版
|
|
4
|
+
|
|
5
|
+
- **#20 加固(属性测试 + 形状一致)**:新增 `resolveDrscBaseUrl` 优先级的确定性属性测试(固定种子 mulberry32,400 + 300 组随机组合),钉住四条不变式:显式值恒为终值、有非空 trustedHosts 条目时 origin 不得抢位、origin 只替换那句 8080、前两档之外的来源一律不回显 Host 头。**该测试首遍即抓出一处真实形状不一致**:显式值/条目分支会给裸 authority 补 `http://` 前缀,而新增的 origin 分支原样透传不补——若调用方递的是 `127.0.0.1:<port>` 这类 authority(而非宿主 `getOrigin()` 返回的全 URL),就会拼出无 scheme 的基址。已抽单一 `withScheme()` 让三档共用同一规范化。
|
|
6
|
+
- **有效性已红绿验证**:临时把 origin 抬到 trustedHosts 之上(正是计划里判为回归的那个错误改法)后,5 例转红;恢复即全绿。全量 **1886 例**/188 文件。
|
|
7
|
+
|
|
8
|
+
## [0.26.18] — 2026-11-02
|
|
9
|
+
|
|
10
|
+
> **Room 词表真计数修复(issue #27):注册条目不再显示「现有 0 条」。本版会改变 LLM 归类分布,独立成版以给回滚位与观察窗。**
|
|
11
|
+
|
|
12
|
+
### 修复
|
|
13
|
+
|
|
14
|
+
- **注册条目的计数从 `listRooms()` 回查**:候选预标记(`annotateOrphanCandidates`)合并词表时,注册表条目被写死 `count: 0`,而未注册的自生长项带真计数。归类器被要求「优先从现有 Room 词表里选」,而计数是它唯一能看到的规模证据——证据方向与规则**恰好相反**:按推荐用法先 pre-register 一张词表,这张词表里每一项就立即变成空壳,历史上自生长出来的碎片 slug 反而看着像主仓。净效果正是 annotate 本来要防的那件事:词表碎片化。`?? 0` 保留原语义——只有「注册了但库里确实零绑定」的新 Room 才显示 0(那正是这个 0 本来想表达的意思)。
|
|
15
|
+
- **取数口径单点化**:同一句「注册表条目的真实计数」本仓写了四处,其中三处已是对的形态(`stats.ts` rooms-export、`tools` room-admin list、以及候选词表本该有的形态),只有候选词表这处写死成 0——有本地定义就有分叉位。抽 `roomCountsBySlug()` 供 annotate / room-admin list / rooms-export 三处共用,顺手消除 room-admin list 里对每个注册条目的线性 `find`(上限 200 条注册表 × N 个 Room)。`stats.ts:1044` 的 rooms-get 条目形态带 `source`、排序 grown 优先且计数本就没丢,与词表不同形,故未强并以免改到面板输出形状。
|
|
16
|
+
- **补 count 路径测试两例**:`tests/room-review.test.ts` 原先两个 annotate 用例都注入自己的 annotator 且忽略 catalog、`io()` 默认不带 registry,**count 路径零覆盖**——这是写死 0 能活过 5 个版本的直接原因。新增「注册条目回查真计数」(修复前必红,实测报 `expected +0 to be 1`,与报告人自述的红绿结果逐字一致)与「注册零绑定仍为 0」(锁住不误伤原语义),另加 `roomCountsBySlug` 口径一例。
|
|
17
|
+
|
|
18
|
+
### 行为边界(说清它不只是个计数)
|
|
19
|
+
|
|
20
|
+
- 本修复会改变发给归类器的 prompt 内容,因而**可能改变归类分布**:大 Room 拿到更强信号后可能更抑新 slug 生长(冷启动无处归)。这是修复目的而非纯 bug,故独立成版;若观察到新 Room 实在挂不上,revert 本版单个 commit 即可,不牵连 0.26.17 的三条声明面修复。
|
|
21
|
+
- **未一并做**(另立案,原因记在计划产物里):把注册表的 `label`/`description` 渲染进 prompt、调 `.slice(0, 40)` 窗口。annotate 是逐条一次 LLM 调用且无 prompt 前缀缓存(同一张词表作为 system 被重复发 40 次),把窗口抬到 120 并塞 label+description 会让每轮仅词表就吃掉 14-24 万 input token,需先有归类分布的可观测指标。
|
|
22
|
+
- 全量 **1883 例**/188 文件绿(本版在 0.26.17 的 1880 例之上加 3 例)。
|
|
23
|
+
|
|
3
24
|
## [0.26.17] — 2026-11-02
|
|
4
25
|
|
|
5
26
|
> **三条声明面修复:`memory:profile` 显式关插值(issue #24)、`memory_conflicts` 补两笔成熟度声明(issue #23)、DRSC 基址不再写死 8080(issue #20)**,并把「改视图忘改 schema」从注释纪律改成 CI 闸。无配置变更、无默认行为变更、`engines.dsh` 不动。
|
package/dist/clients/drsc.js
CHANGED
|
@@ -24,16 +24,22 @@ export function firstDrscTrustedHost(trustedHosts) {
|
|
|
24
24
|
export function resolveDrscBaseUrl(explicit, trustedHosts, fallbackOrigin) {
|
|
25
25
|
return resolveDrscBaseUrlDetail(explicit, trustedHosts, fallbackOrigin).url;
|
|
26
26
|
}
|
|
27
|
+
/** 条目→URL 的单一规范化:无 scheme 时补 `http://`。三档来源共用,不开两种形状。 */
|
|
28
|
+
function withScheme(authority) {
|
|
29
|
+
return /^https?:\/\//u.test(authority) ? authority : `http://${authority}`;
|
|
30
|
+
}
|
|
27
31
|
/** {@link resolveDrscBaseUrl} 的带来源形态,供客户端决定 Host 回显。 */
|
|
28
32
|
export function resolveDrscBaseUrlDetail(explicit, trustedHosts, fallbackOrigin) {
|
|
29
33
|
const trimmed = explicit?.trim() ?? '';
|
|
30
34
|
const pick = trimmed !== '' ? trimmed : firstDrscTrustedHost(trustedHosts);
|
|
31
35
|
if (pick !== undefined && pick !== '') {
|
|
32
|
-
return { url:
|
|
36
|
+
return { url: withScheme(pick), fromConfigured: true };
|
|
33
37
|
}
|
|
34
38
|
const origin = fallbackOrigin?.trim() ?? '';
|
|
39
|
+
// origin 也走同一规范化:宿主 getOrigin() 返的是全 URL(此处原样透传),但若调用方
|
|
40
|
+
// 递的是 `127.0.0.1:<port>` 这种 authority,不补 scheme 会让拼出的请求 URL 直接不行。
|
|
35
41
|
if (origin !== '')
|
|
36
|
-
return { url: origin, fromConfigured: false };
|
|
42
|
+
return { url: withScheme(origin), fromConfigured: false };
|
|
37
43
|
return { url: 'http://127.0.0.1:8080', fromConfigured: false };
|
|
38
44
|
}
|
|
39
45
|
/** kind → L0 角色词表(memory 只收 user/assistant 两类,其余(tool/* 等)不映射)。 */
|
package/dist/room-review.d.ts
CHANGED
|
@@ -22,10 +22,19 @@
|
|
|
22
22
|
*/
|
|
23
23
|
import type { Context } from '@deepseek-ai/cordis';
|
|
24
24
|
import type { MemoryConfig } from './config.js';
|
|
25
|
-
import type { MemoryLogger, MemoryRecord } from './types.js';
|
|
25
|
+
import type { MemoryLogger, MemoryRecord, RoomCount } from './types.js';
|
|
26
26
|
export declare const ROOM_CANDIDATES_KEY = "roomCandidates";
|
|
27
27
|
export declare const ROOM_REVIEW_KEY = "roomReview";
|
|
28
28
|
export type RoomReviewState = 'pending' | 'confirmed' | 'skipped';
|
|
29
|
+
/**
|
|
30
|
+
* `listRooms()` 结果里的 slug→计数索引(单点定义,供四个消费方共用)。
|
|
31
|
+
*
|
|
32
|
+
* 为何需要它(issue #27):同一句「注册表条目的真实计数」在本仓写了四处,其中三处
|
|
33
|
+
* 已经是对的形态(`stats.ts` 的 rooms-get / rooms-export、`tools` 的 room-admin list),
|
|
34
|
+
* 只有候选词表这一处把算好的计数**写死成 0**。计数一旦有本地定义就有分叉位,
|
|
35
|
+
* 所以取数口径收到这里,各调用点只剩「查不到时给多少」这一个本地决策。
|
|
36
|
+
*/
|
|
37
|
+
export declare function roomCountsBySlug(rooms: readonly RoomCount[]): Map<string, number>;
|
|
29
38
|
/** 复查与候选操作对 L1 的最小依赖面(便于测试注入)。 */
|
|
30
39
|
export interface RoomReviewStore {
|
|
31
40
|
list(opts: {
|
package/dist/room-review.js
CHANGED
|
@@ -2,6 +2,17 @@ import { isRoomSlug } from './metadata-validators.js';
|
|
|
2
2
|
import { callLLM, parseJsonLogged } from './llm.js';
|
|
3
3
|
export const ROOM_CANDIDATES_KEY = 'roomCandidates';
|
|
4
4
|
export const ROOM_REVIEW_KEY = 'roomReview';
|
|
5
|
+
/**
|
|
6
|
+
* `listRooms()` 结果里的 slug→计数索引(单点定义,供四个消费方共用)。
|
|
7
|
+
*
|
|
8
|
+
* 为何需要它(issue #27):同一句「注册表条目的真实计数」在本仓写了四处,其中三处
|
|
9
|
+
* 已经是对的形态(`stats.ts` 的 rooms-get / rooms-export、`tools` 的 room-admin list),
|
|
10
|
+
* 只有候选词表这一处把算好的计数**写死成 0**。计数一旦有本地定义就有分叉位,
|
|
11
|
+
* 所以取数口径收到这里,各调用点只剩「查不到时给多少」这一个本地决策。
|
|
12
|
+
*/
|
|
13
|
+
export function roomCountsBySlug(rooms) {
|
|
14
|
+
return new Map(rooms.map((r) => [r.room, r.count]));
|
|
15
|
+
}
|
|
5
16
|
/** 解析标注器输出的逐条候选(容错:id 配对失败丢弃;isRoomSlug 校验(两级制);目录内优先,≤3)。 */
|
|
6
17
|
export function parseRoomCandidateItems(parsed, ids, roomCatalog) {
|
|
7
18
|
const idSet = new Set(ids);
|
|
@@ -107,8 +118,15 @@ export async function annotateOrphanCandidates(ctx, cfg, io, limit = 40, overrid
|
|
|
107
118
|
const grown = io.l1.listRooms();
|
|
108
119
|
const regActive = io.registry?.listActive() ?? [];
|
|
109
120
|
const regSet = new Set(regActive.map((e) => e.slug));
|
|
121
|
+
// issue #27:注册条目从 grown 回查真计数,而不是写死 0。`listRooms()` 的 SQL 是按
|
|
122
|
+
// metadata.tags 展开全部记录聚合的,注册过的 slug 只认真挂了记录就必然出现在这里
|
|
123
|
+
// 且带正确计数——所以那个 0 不是「信息不可得」的降级,是把已算好的数据丢掉再补个假值。
|
|
124
|
+
// 后果是方向性的:词表里只有未注册项显示真数,归类器收到的唯一规模证据恰好与
|
|
125
|
+
// 「优先挂靠现有词表」这条规则反向——按推荐用法先 pre-register 的词表反而全变空壳。
|
|
126
|
+
// `?? 0` 保留原语义:只有「注册了但库里确实零绑定」的新 Room 才显示 0。
|
|
127
|
+
const grownCounts = roomCountsBySlug(grown);
|
|
110
128
|
const merged = [
|
|
111
|
-
...regActive.map((e) => ({ room: e.slug, count: 0, label: e.label, description: e.description })),
|
|
129
|
+
...regActive.map((e) => ({ room: e.slug, count: grownCounts.get(e.slug) ?? 0, label: e.label, description: e.description })),
|
|
112
130
|
...grown.filter((r) => !regSet.has(r.room)).map((r) => ({ room: r.room, count: r.count })),
|
|
113
131
|
].slice(0, 120);
|
|
114
132
|
const annotate = overrides.annotator ??
|
package/dist/stats.js
CHANGED
|
@@ -19,7 +19,7 @@ import { typeCatalogEntries } from './memory-types.js';
|
|
|
19
19
|
import { effectiveCfg } from './pipeline/runner.js';
|
|
20
20
|
import { emptyRecallStats } from './hooks/recall.js';
|
|
21
21
|
import { buildRouteChain, decideSendableEffort, LAYER_DEFAULT_BUDGETS, layerChainOrNull, resolveModelContextWindow, resolveModelEfforts, resolveModelRoute } from './llm.js';
|
|
22
|
-
import { mergeRoom, renameRoom } from './room-review.js';
|
|
22
|
+
import { mergeRoom, renameRoom, roomCountsBySlug } from './room-review.js';
|
|
23
23
|
import { scanSources } from './memport/scan.js';
|
|
24
24
|
import { projectDistillChain, validateDistillChain } from './settings.js';
|
|
25
25
|
import { RECEIPTS_QUERY_LIMIT_MAX, dimensionOf, toReceiptView } from './store/receipts.js';
|
|
@@ -1149,7 +1149,7 @@ export async function handleEndpoint(endpoint, payload, deps) {
|
|
|
1149
1149
|
};
|
|
1150
1150
|
if (kind === 'rooms') {
|
|
1151
1151
|
const registry = roomRegistry?.list() ?? [];
|
|
1152
|
-
const counts =
|
|
1152
|
+
const counts = roomCountsBySlug(stores.l1.listRooms());
|
|
1153
1153
|
const lines = ['slug,count,source,label,description,status,aliases'];
|
|
1154
1154
|
let n = 0;
|
|
1155
1155
|
const seen = new Set();
|
package/dist/tools/index.js
CHANGED
|
@@ -23,7 +23,7 @@ import { isRetired, readSupersedeMarker, stripSupersedeMarker } from '../store/s
|
|
|
23
23
|
import { listConflictPairs, renderConflictResolution, renderConflicts, resolveConflictPair } from '../conflict-service.js';
|
|
24
24
|
import { WING_CATALOG, WING_FALLBACK, normPersistence, normScope, resolveRecordScope } from '../types.js';
|
|
25
25
|
import { scopeFilterOf, workspaceIdOf } from '../workspace.js';
|
|
26
|
-
import { annotateOrphanCandidates, confirmReview, mergeRoom, nextReview, pendingReviewCount, renameRoom, skipReview, } from '../room-review.js';
|
|
26
|
+
import { annotateOrphanCandidates, confirmReview, mergeRoom, nextReview, pendingReviewCount, roomCountsBySlug, renameRoom, skipReview, } from '../room-review.js';
|
|
27
27
|
import { applyGovernanceWeights } from '../store/governance.js';
|
|
28
28
|
import { resolveRepoScope } from '../repo-scope.js';
|
|
29
29
|
import { GRAPH_STATUS_LABELS } from '../prompts/graph-projection.js';
|
|
@@ -386,8 +386,10 @@ graphWrite) {
|
|
|
386
386
|
return { notice: 'Room 注册表未装配(需要插件重载以初始化)。' };
|
|
387
387
|
const registry = roomRegistry.list();
|
|
388
388
|
const counts = stores.l1.listRooms();
|
|
389
|
-
|
|
390
|
-
|
|
389
|
+
// 取数口径单点(`roomCountsBySlug` 注释说明了为何收它):本行原先对每个注册条目
|
|
390
|
+
// 线性 find,上限 200 条注册表 × N 个 Room——语义相同、形状不同就是分叉位。
|
|
391
|
+
const countOf = roomCountsBySlug(counts);
|
|
392
|
+
const regLines = registry.map((e) => `- ${e.slug}${e.label ? `(${e.label})` : ''} [${e.source}/${e.status}] 现有 ${countOf.get(e.slug) ?? 0} 条${e.description ? ` — ${e.description}` : ''}`);
|
|
391
393
|
const grownOnly = counts.filter((r) => !registry.some((e) => e.slug === r.room));
|
|
392
394
|
return {
|
|
393
395
|
notice: `注册表 ${registry.length} 条(上限 200),自生长未注册 ${grownOnly.length} 个。\n` +
|
package/dsh.plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "dsh-prime-memory",
|
|
3
3
|
"name": "dsh-prime-memory",
|
|
4
|
-
"version": "0.26.
|
|
4
|
+
"version": "0.26.18",
|
|
5
5
|
"description": {
|
|
6
6
|
"en": "Layered distilled memory plugin (L0 capture → L1 atomic memory → L2 scenes → L3 persona) with automatic recall injection before each model step.",
|
|
7
7
|
"zh": "L0~L3 分层蒸馏记忆插件:自动捕获对话、抽取原子记忆、整合场景块、蒸馏核心画像,并在模型步骤前自动召回注入。"
|