@max-null/dsh-memory 0.19.1 → 0.20.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 +12 -0
- package/dist/engine.d.ts +11 -0
- package/dist/engine.js +6 -2
- package/dist/index.js +8 -0
- package/dist/schema.d.ts +1 -0
- package/dist/schema.js +8 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -76,6 +76,18 @@ npm install @max-null/dsh-memory
|
|
|
76
76
|
|
|
77
77
|
两个根都可用 config 覆盖(`globalRoot` / `projectRoot`)。`memory_list` / `memory_search` 不带 `namespace` 过滤时会同时查两层。旧版双重前缀文件名(`memory_project_memory_project_<hash>.json`)在打开时自动迁移为规范名。
|
|
78
78
|
|
|
79
|
+
## 主题(topic,0.20.0)
|
|
80
|
+
|
|
81
|
+
`namespace` 管「在哪生效」,`topic` 管「这条讲什么」,两者正交:
|
|
82
|
+
|
|
83
|
+
| 字段 | 回答的问题 | 取值 |
|
|
84
|
+
|---|---|---|
|
|
85
|
+
| `namespace` | 换个项目还成立吗 | `global` / `project` |
|
|
86
|
+
| `kind` | 是记忆还是提示词模板 | `fact` / `prompt` |
|
|
87
|
+
| `topic` | 这条记忆讲什么 | 自由字符串:`见闻` / `约定` / `坑` / `偏好` / `事实` / `判据` … |
|
|
88
|
+
|
|
89
|
+
`memory_save` 传 `topic` 落库,`memory_list` / `memory_search` 传 `topic` 切片。留空或省略即「未分类」。用它把「自己出去逛到的」(`见闻`)和「一起干活时记下的」分开翻,不必再往正文里手写前缀。
|
|
90
|
+
|
|
79
91
|
## 跨工作区可见性(0.10.0 / 0.11.0)
|
|
80
92
|
|
|
81
93
|
project 记忆按**会话工作区**分文件存放,所以「工作区」就是可见性的边界。0.10.0 起补了三个方向——针对的是同一个盲区:**不知道存在**(检索是有意图的动作,搜不出自己不知道存在的东西)。
|
package/dist/engine.d.ts
CHANGED
|
@@ -81,6 +81,13 @@ export interface MemoryRecord {
|
|
|
81
81
|
lastUsedAt?: number;
|
|
82
82
|
/** 记录类别(缺省 'fact')。 */
|
|
83
83
|
kind?: MemoryKind;
|
|
84
|
+
/**
|
|
85
|
+
* 记忆主题(0.20.0):见闻 / 约定 / 坑 / 偏好 / 事实 / 判据 …
|
|
86
|
+
*
|
|
87
|
+
* 与 `kind` 正交:`kind` 分「记忆还是提示词模板」,`topic` 分「这条记忆讲什么」。
|
|
88
|
+
* 存自由字符串,方便「只翻我出去逛到的那些」这类切片。
|
|
89
|
+
*/
|
|
90
|
+
topic?: string;
|
|
84
91
|
/** prompt 记录元数据(仅 kind==='prompt')。 */
|
|
85
92
|
meta?: PromptMetaIndex;
|
|
86
93
|
/**
|
|
@@ -93,6 +100,8 @@ export interface MemoryWrite {
|
|
|
93
100
|
content: string;
|
|
94
101
|
namespace?: MemoryNamespace;
|
|
95
102
|
keywords?: string[];
|
|
103
|
+
/** 记忆主题(0.20.0):见闻 / 约定 / 坑 / 偏好 / 事实 / 判据 …;省略表示未分类。 */
|
|
104
|
+
topic?: string;
|
|
96
105
|
/** 写入来源(2026-09-15);缺省 'agent'(主模型写入)。面板等人工路径传 'human'。 */
|
|
97
106
|
source?: MemorySource;
|
|
98
107
|
/**
|
|
@@ -115,6 +124,8 @@ export interface MemoryFilter {
|
|
|
115
124
|
quarantined?: boolean;
|
|
116
125
|
/** 记录类别过滤(0.6.0);缺省不过滤(工具层显式传 kind='fact' 保持旧行为)。 */
|
|
117
126
|
kind?: MemoryKind;
|
|
127
|
+
/** 主题过滤(0.20.0):只取这个 topic 的记录;缺省不过滤。 */
|
|
128
|
+
topic?: string;
|
|
118
129
|
/**
|
|
119
130
|
* 更新时间下界 / 上界(0.12.0;epoch ms,含端点)。
|
|
120
131
|
*
|
package/dist/engine.js
CHANGED
|
@@ -145,10 +145,11 @@ function toRecord(id, block) {
|
|
|
145
145
|
...block.stale === undefined ? {} : { stale: block.stale },
|
|
146
146
|
...block.staleReason === undefined ? {} : { staleReason: block.staleReason },
|
|
147
147
|
};
|
|
148
|
-
// 0.6.0 模板库字段(缺省 fact;prompt
|
|
148
|
+
// 0.6.0 模板库字段(缺省 fact;prompt 元数据透传);0.20.0 起带主题
|
|
149
149
|
return {
|
|
150
150
|
...base,
|
|
151
151
|
...block.kind === undefined ? {} : { kind: block.kind },
|
|
152
|
+
...block.topic === undefined ? {} : { topic: block.topic },
|
|
152
153
|
...block.meta === undefined ? {} : { meta: block.meta },
|
|
153
154
|
};
|
|
154
155
|
}
|
|
@@ -758,6 +759,8 @@ export class MemoryEngine extends Service {
|
|
|
758
759
|
...input.injected === undefined ? {} : { injectedAuto: false },
|
|
759
760
|
// 有效性锚点(可选):值变了这条记忆会被标 stale,不再被当成仍然正确
|
|
760
761
|
...input.anchor === undefined ? {} : { anchor: input.anchor },
|
|
762
|
+
// 主题(0.20.0):省略即未分类,与 `kind`(fact/prompt)正交
|
|
763
|
+
...input.topic === undefined || input.topic.trim() === '' ? {} : { topic: input.topic.trim() },
|
|
761
764
|
...verdict.quarantined
|
|
762
765
|
? { quarantined: true, ...verdict.reason === undefined ? {} : { quarantineReason: verdict.reason } }
|
|
763
766
|
: {},
|
|
@@ -778,7 +781,8 @@ export class MemoryEngine extends Service {
|
|
|
778
781
|
// 0.12.0:按更新时间筛——与注入排序、注入行的日期标记同一个依据,不制造第二个「新」的定义
|
|
779
782
|
&& (filter?.after === undefined || record.updatedAt >= filter.after)
|
|
780
783
|
&& (filter?.before === undefined || record.updatedAt <= filter.before)
|
|
781
|
-
&& (filter?.kind === undefined || (record.kind ?? 'fact') === filter.kind)
|
|
784
|
+
&& (filter?.kind === undefined || (record.kind ?? 'fact') === filter.kind)
|
|
785
|
+
&& (filter?.topic === undefined || record.topic === filter.topic));
|
|
782
786
|
}
|
|
783
787
|
/**
|
|
784
788
|
* 关键词检索。每次成功返回的检索都记一条查询日志(⑥ 2026-09-15)——命中与否都记:
|
package/dist/index.js
CHANGED
|
@@ -125,6 +125,7 @@ const RECORD_SCHEMA = {
|
|
|
125
125
|
injected: { type: 'boolean', required: true },
|
|
126
126
|
content: { type: 'string', required: true },
|
|
127
127
|
keywords: { type: 'array', required: true, items: { type: 'string' } },
|
|
128
|
+
topic: { type: 'string', description: '主题(0.20.0):见闻 / 约定 / 坑 / 偏好 / 事实 / 判据 …;缺省表示未分类。' },
|
|
128
129
|
createdAt: { type: 'number', required: true },
|
|
129
130
|
updatedAt: { type: 'number', required: true },
|
|
130
131
|
lastUsedAt: { type: 'number' },
|
|
@@ -296,6 +297,7 @@ function recordValue(record) {
|
|
|
296
297
|
injected: record.injected,
|
|
297
298
|
content: record.content,
|
|
298
299
|
keywords: record.keywords,
|
|
300
|
+
...record.topic === undefined ? {} : { topic: record.topic },
|
|
299
301
|
createdAt: record.createdAt,
|
|
300
302
|
updatedAt: record.updatedAt,
|
|
301
303
|
...record.lastUsedAt === undefined ? {} : { lastUsedAt: record.lastUsedAt },
|
|
@@ -587,6 +589,7 @@ export async function apply(ctx, config) {
|
|
|
587
589
|
content: { type: 'string', required: true, description: 'Plaintext memory content.' },
|
|
588
590
|
namespace: { type: 'string', enum: ['global', 'project'], description: 'Where it applies; defaults to global.' },
|
|
589
591
|
keywords: { type: 'array', items: { type: 'string' }, description: 'Explicit searchable anchors for memory_search. Give several angles (synonyms, abbreviations, zh/en) or it will not be found later. 多角度关键词(同义词、缩写、中英),否则将来检索不到它。' },
|
|
592
|
+
topic: { type: 'string', description: 'What this memory is about, one short word: 见闻 (things noticed while wandering) / 约定 (standing agreement) / 坑 (a pitfall hit) / 偏好 (a preference) / 事实 / 判据. Orthogonal to namespace. 主题:见闻 / 约定 / 坑 / 偏好 / 事实 / 判据;与 namespace 正交,缺省表示未分类。' },
|
|
590
593
|
injected: { type: 'boolean', description: 'Set true to make this memory resident in every turn\'s context, as a deliberate decision that no longer auto-expires. Reserve it for rules, standing agreements and judgement criteria — the kind that must apply even when nobody thinks to search for them. Facts, references and case notes stay search-only (omit it). 设为 true 让这条记忆每轮常驻上下文,且视为有意决定、不再被自动淘汰。只用于规则、长期约定与判据——那些「没人想起来搜也必须生效」的记忆;事实、参考与案例留作按需检索(省略即可)。' },
|
|
591
594
|
anchor: {
|
|
592
595
|
type: 'object',
|
|
@@ -609,6 +612,7 @@ export async function apply(ctx, config) {
|
|
|
609
612
|
content: args.content,
|
|
610
613
|
...args.namespace === undefined ? {} : { namespace: args.namespace },
|
|
611
614
|
...args.keywords === undefined ? {} : { keywords: args.keywords },
|
|
615
|
+
...args.topic === undefined ? {} : { topic: args.topic },
|
|
612
616
|
...args.injected === undefined ? {} : { injected: args.injected },
|
|
613
617
|
// 参数已由 tool schema 校验,这里的窄化只为过类型
|
|
614
618
|
...args.anchor === undefined
|
|
@@ -632,6 +636,7 @@ export async function apply(ctx, config) {
|
|
|
632
636
|
namespace: { type: 'string', enum: ['global', 'project'], description: 'Restrict to one namespace. 限定单个命名空间.' },
|
|
633
637
|
status: { type: 'string', enum: ['suggested', 'approved'], description: 'Restrict to one review status. 限定审核状态(suggested=待审核 / approved=已审核).' },
|
|
634
638
|
injected: { type: 'boolean', description: 'Restrict by the persistent-injection switch. 按常驻注入开关过滤.' },
|
|
639
|
+
topic: { type: 'string', description: 'Restrict to one topic (见闻 / 约定 / 坑 / 偏好 / 事实 / 判据 …). 按主题过滤(0.20.0).' },
|
|
635
640
|
after: { type: 'string', description: 'Only memories updated at/after this time: `YYYY-MM-DD`, or a relative form like `7d` / `30d`. 只返回此后更新过的记忆(`YYYY-MM-DD`,或相对形式 `7d` / `30d`).' },
|
|
636
641
|
before: { type: 'string', description: 'Only memories updated at/before this time (same forms as `after`). 只返回此前更新过的记忆(写法同 after).' },
|
|
637
642
|
},
|
|
@@ -647,6 +652,7 @@ export async function apply(ctx, config) {
|
|
|
647
652
|
...args.namespace === undefined ? {} : { namespace: args.namespace },
|
|
648
653
|
...args.status === undefined ? {} : { status: args.status },
|
|
649
654
|
...args.injected === undefined ? {} : { injected: args.injected },
|
|
655
|
+
...args.topic === undefined ? {} : { topic: args.topic },
|
|
650
656
|
...after === undefined ? {} : { after },
|
|
651
657
|
...before === undefined ? {} : { before },
|
|
652
658
|
}, execProjectCwd(exec)).then(records => records.map(recordValue));
|
|
@@ -661,6 +667,7 @@ export async function apply(ctx, config) {
|
|
|
661
667
|
namespace: { type: 'string', enum: ['global', 'project'], description: 'Restrict to one namespace. 限定单个命名空间.' },
|
|
662
668
|
status: { type: 'string', enum: ['suggested', 'approved'], description: 'Restrict to one review status. 限定审核状态.' },
|
|
663
669
|
kind: { type: 'string', enum: ['fact', 'prompt'], description: 'Restrict by record kind — fact (memories, default) or prompt (templates; use prompt_search instead). 按记录类别过滤:fact=记忆(默认)/ prompt=模板(模板请用 prompt_search).' },
|
|
670
|
+
topic: { type: 'string', description: 'Restrict to one topic (见闻 / 约定 / 坑 / 偏好 / 事实 / 判据 …). 按主题过滤(0.20.0).' },
|
|
664
671
|
limit: { type: 'integer', default: DEFAULT_SEARCH_LIMIT, description: 'Maximum hits to return (default 20; 0 = no limit; negative falls back to 20). 最多返回条数(默认 20;0 = 不限;负数按 20).' },
|
|
665
672
|
},
|
|
666
673
|
output: {
|
|
@@ -676,6 +683,7 @@ export async function apply(ctx, config) {
|
|
|
676
683
|
...args.namespace === undefined ? {} : { namespace: args.namespace },
|
|
677
684
|
...args.status === undefined ? {} : { status: args.status },
|
|
678
685
|
...args.kind === undefined ? {} : { kind: args.kind },
|
|
686
|
+
...args.topic === undefined ? {} : { topic: args.topic },
|
|
679
687
|
}, execProjectCwd(exec)).then(hits => (limit === 0 ? hits : hits.slice(0, limit)).map(hitValue));
|
|
680
688
|
},
|
|
681
689
|
presentCall: args => present('Search memory', 'read', args.query),
|
package/dist/schema.d.ts
CHANGED
package/dist/schema.js
CHANGED
|
@@ -45,6 +45,14 @@ export const blockSchema = z.object({
|
|
|
45
45
|
lastUsedAt: z.number().optional(),
|
|
46
46
|
vector: z.array(z.number()).optional(),
|
|
47
47
|
kind: z.enum(['fact', 'prompt']).optional(),
|
|
48
|
+
/**
|
|
49
|
+
* 记忆主题(0.20.0):见闻 / 约定 / 坑 / 偏好 / 事实 / 判据 … 自由字符串。
|
|
50
|
+
*
|
|
51
|
+
* 与 `kind` 正交——`kind` 分「记忆还是提示词模板」(记录类别),`topic` 分「这条记忆讲什么」
|
|
52
|
+
* (内容主题)。用自由字符串而不是枚举:新增主题不该要求改 schema,而枚举一旦写死,
|
|
53
|
+
* 存量文件里出现未登记的值会被门三判成不合规、进而整块读不出来。
|
|
54
|
+
*/
|
|
55
|
+
topic: z.string().optional(),
|
|
48
56
|
meta: z.object({
|
|
49
57
|
seq: z.number().optional(),
|
|
50
58
|
name: z.string(),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@max-null/dsh-memory",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.0",
|
|
4
4
|
"description": "Cross-session plaintext memory plugin for the DeepSeek Harness — deterministic BM25 recall, writes take effect immediately, signal-driven injection, no embeddings",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"publishConfig": {
|