sage-mem 0.7.2 → 0.9.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/LICENSE +202 -202
- package/NOTICE +8 -8
- package/README.md +312 -284
- package/cordis.patch.yml +4 -4
- package/lib/autodream/config.js +83 -0
- package/lib/autodream/engine.js +524 -0
- package/lib/autodream/gate.js +78 -0
- package/lib/autodream/llm-loop.js +248 -0
- package/lib/autodream/report.js +285 -0
- package/lib/autodream/rollback.js +221 -0
- package/lib/autodream/settings.js +289 -0
- package/lib/autodream/snapshot.js +142 -0
- package/lib/autodream/util.js +112 -0
- package/lib/autodream-audit.js +270 -229
- package/lib/autodream-prompt.js +129 -129
- package/lib/autodream-tools.js +497 -498
- package/lib/autodream.js +8 -1722
- package/lib/client.js +4277 -2854
- package/lib/index.js +893 -951
- package/lib/memory/access.js +220 -0
- package/lib/memory/archive.js +91 -0
- package/lib/memory/config.js +151 -0
- package/lib/memory/frontmatter.js +117 -0
- package/lib/memory/naming.js +54 -0
- package/lib/memory/retrieval.js +86 -0
- package/lib/memory/scan.js +204 -0
- package/lib/memory/util.js +37 -0
- package/lib/typert.host.js +747 -461
- package/package.json +7 -2
package/lib/autodream-prompt.js
CHANGED
|
@@ -1,129 +1,129 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* sage-mem autodream — 提示词。
|
|
3
|
-
*
|
|
4
|
-
* 四阶段结构:定位 → 收集 → 整合 → 修剪与索引。两个非显然的取舍:
|
|
5
|
-
*
|
|
6
|
-
* 1. **写记忆的规范必须随 prompt 自带。** autodream 是一次独立调用,带不上外部的
|
|
7
|
-
* 技能文档。所以四类前缀、`type` 一致性、YAML 两个静默陷阱、受保护文件名这些
|
|
8
|
-
* **必须**出现在 prompt 里,否则模型会照着「看起来像」的方式写坏。
|
|
9
|
-
* 2. **按模式分叉。** 「只出报告」不是靠一句「请不要写」约束的——那一趟根本
|
|
10
|
-
* 没有写工具(见 autodream-tools.js)。所以 prompt 只说它有什么、该产出什么形状。
|
|
11
|
-
*
|
|
12
|
-
* ⚠️ 这里**不许出现任何具体的记忆文件名**(除了使用者通过 `reserved.json` 自己声明的)。
|
|
13
|
-
* 那段文字会发给每一个使用者的模型;把作者的记忆主题编进去就是泄漏
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
/** 让 prompt 里的日期对人可读:模型算相对日期时不会写错。 */
|
|
17
|
-
function today(ms) {
|
|
18
|
-
const d = new Date(ms)
|
|
19
|
-
const p = (n) => String(n).padStart(2, '0')
|
|
20
|
-
return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}`
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* 系统提示:身份 + 记忆系统的约定 + 这一趟的权限边界。
|
|
25
|
-
* @param {{apply:boolean, withSessions:boolean, reservedFiles?:string[]}} opts
|
|
26
|
-
* `reservedFiles` 是**使用者自己声明**的受保护文件名(来自 `<状态根>/reserved.json`)。
|
|
27
|
-
* 由运行时注入而不是写死在这里——见文件头那条警告。
|
|
28
|
-
* @returns {string}
|
|
29
|
-
*/
|
|
30
|
-
export function buildAutodreamSystemPrompt(opts) {
|
|
31
|
-
const { apply, withSessions } = opts
|
|
32
|
-
const reserved = Array.isArray(opts.reservedFiles) ? opts.reservedFiles.filter(Boolean) : []
|
|
33
|
-
const reservedNote = reserved.length
|
|
34
|
-
? `- **不许改动的文件**(使用者指定的保护名单):${reserved.map((f) => `\`${f}\``).join('、')} —— 这些多半是手写单副本、人工维护的重要记忆,一个字都不要动`
|
|
35
|
-
: '- 有些记忆是使用者手写单副本、人工维护的;本次没有额外指定保护名单,遇到明显「成篇手写、结构不像流水」的记忆请保守处理'
|
|
36
|
-
const modeLine = apply
|
|
37
|
-
? '本次是**改写模式**:你有写工具,可以直接改记忆文件。运行前已经把整个 memory 目录快照了一份(这就是回滚点),改坏能整体退回去——但这只保证「可恢复」,不保证「改得对」。宁可少改,不要乱动。\n\n**每一次写入都请写清 `reason`。** 这一趟是无人值守跑的,人只能靠这份缘由判断你改得对不对。写清「为什么改这条、改成了什么」,不要写「整理优化」这种没有信息量的话。缺了**不会被拒绝**——改动照样生效——但整理声明里会把那条标成「未自述」,用宿主能追溯到的信息顶替。'
|
|
38
|
-
: '本次是**只读模式**:你没有任何写工具。把所有发现写成建议清单,写进你最后的回复里——要具体到「哪个文件、改成什么」,不要泛泛而谈。'
|
|
39
|
-
|
|
40
|
-
return `你是 sage-mem(一个文件式跨会话记忆系统)的整理进程。你的记忆不是数据库,是一个装着 markdown 文件的目录,靠 frontmatter 分类、靠双字组匹配检索。你要做的是一次反思性通读,把最近的经历沉淀成耐久、有条理的记忆。
|
|
41
|
-
|
|
42
|
-
${modeLine}
|
|
43
|
-
|
|
44
|
-
## 记忆的约定(必须遵守,违反了会静默损坏记忆)
|
|
45
|
-
|
|
46
|
-
**分四类,靠文件名前缀 + frontmatter 的 \`type\` 双重标记:**
|
|
47
|
-
|
|
48
|
-
| 前缀 | type | 装什么 |
|
|
49
|
-
|---|---|---|
|
|
50
|
-
| \`user_\` | \`user\` | 用户是谁:角色、背景、偏好、习惯 |
|
|
51
|
-
| \`feedback_\` | \`feedback\` | 用户给的工作方式指导。**正文必须带 \`**Why:**\` 与 \`**How to apply:**\` 两段** |
|
|
52
|
-
| \`project_\` | \`project\` | 进行中的工作与状态。**相对日期一律转成绝对日期** |
|
|
53
|
-
| \`reference_\` | \`reference\` | 外部信息在哪找:服务器、路径、端点、命令 |
|
|
54
|
-
|
|
55
|
-
**分类只看 \`type\`,不看前缀** —— 两者不一致时以 \`type\` 为准,而那种不一致本身就是该修的毛病。\`type\` 缺失会被兜底成 \`reference\`(静默归错类)。
|
|
56
|
-
|
|
57
|
-
**\`description\` 是检索的唯一入口。** 检索机制是把提问和「\`description\` + 文件名」都切成双字组比覆盖率,命中靠词面重合、不靠语义。所以 description 要写「将来我会怎么问它」时会出现的词,而不是一句抽象的概括。
|
|
58
|
-
|
|
59
|
-
**frontmatter 是 YAML,两个元字符会静默出事:**
|
|
60
|
-
- 未加引号的值里有 \`: \`(半角冒号+空格)→ 解析失败,整条描述与类型全空
|
|
61
|
-
- 未加引号的值里有 \` #\`(空格+井号)→ **解析成功但后半句被当注释吞掉**,页面与日志都没有任何提示
|
|
62
|
-
|
|
63
|
-
修法:短的值直接加单引号,长文本避开这两个形态。
|
|
64
|
-
|
|
65
|
-
**几个特殊文件:**
|
|
66
|
-
- \`MEMORY.md\` —— 索引。它是一份索引用,每条一行 \`- [标题](文件.md) — 一句话\`,**不要把记忆内容写进去**
|
|
67
|
-
- \`session-log.md\` —— 追加型流水账,**不是一条记忆**,不进检索、不进列表、不要当记忆改
|
|
68
|
-
${reservedNote}
|
|
69
|
-
- \`archive/\` —— 归档区,不在检索范围内。归档 = 把文件移进去,**可人工捞回**;这里没有真删除
|
|
70
|
-
|
|
71
|
-
**硬标准:靠读代码 / git / 系统提示能推出来的东西不要存。** 存的是推不出来的上下文。写之前先问一句「下一个我会真的用得上这条吗」。${withSessions ? '' : '\n\n**本次输入源不含会话记录**,所以你没有 search_sessions 工具。手上的材料就是记忆目录本身。'}`
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* 首条用户消息:四阶段流程 + 这一趟的实时上下文。
|
|
76
|
-
*
|
|
77
|
-
* 阶段名与顺序:定位(Orient)→ 收集(Gather)→ 整合(Consolidate)→
|
|
78
|
-
* 修剪与索引(Prune and index)。
|
|
79
|
-
*
|
|
80
|
-
* @param {{memoryDir:string, apply:boolean, hoursSince:number, sessionCount:number, now:number}} opts
|
|
81
|
-
* @returns {string}
|
|
82
|
-
*/
|
|
83
|
-
export function buildAutodreamUserPrompt(opts) {
|
|
84
|
-
const { memoryDir, apply, hoursSince, sessionCount, now } = opts
|
|
85
|
-
const stage4 = apply
|
|
86
|
-
? `- 更新 \`MEMORY.md\` 索引,让它保持是索引而不是内容堆:
|
|
87
|
-
- 每条一行 \`- [标题](文件.md) — 一句话\`
|
|
88
|
-
- 移除指向已归档 / 已不存在文件的指针
|
|
89
|
-
- 补上新写的重要记忆
|
|
90
|
-
- 两条记忆互相矛盾时,把错的那条改对`
|
|
91
|
-
: `- 给出 \`MEMORY.md\` 该增删哪些行的**具体清单**(逐字给出要改成的那行文本)`
|
|
92
|
-
|
|
93
|
-
return `# Autodream:一次记忆整理
|
|
94
|
-
|
|
95
|
-
今天:${today(now)}
|
|
96
|
-
记忆目录:\`${memoryDir}\`
|
|
97
|
-
距上次整理:${hoursSince === 0 ? '(没有记录,当成第一次)' : hoursSince.toFixed(1) + ' 小时'}
|
|
98
|
-
期间有更新的会话数:${sessionCount}
|
|
99
|
-
|
|
100
|
-
---
|
|
101
|
-
|
|
102
|
-
## 阶段 1 — 定位
|
|
103
|
-
|
|
104
|
-
- 先用 \`list_memory\` 看清现在有什么
|
|
105
|
-
- 读 \`MEMORY.md\` 理解当前索引结构
|
|
106
|
-
- 挑几条与最近改动最相关的记忆读一读,**改进已有文件而不是造近似重复**
|
|
107
|
-
|
|
108
|
-
## 阶段 2 — 收集近期信号
|
|
109
|
-
|
|
110
|
-
值得留存的新信息,大致优先级:
|
|
111
|
-
|
|
112
|
-
1. **结构性毛病** —— 直接跑 \`audit_memory\`,它会把索引悬空、漏索引、type 与前缀不一致、双链断链、YAML 陷阱都列出来。这些是确定要做的事。
|
|
113
|
-
2. **漂移的旧记忆** —— 描述与内容已经对不上的、日期还是相对写法的、被后续进展推翻的。
|
|
114
|
-
3. **(仅在需要具体细节时)** 用 \`search_sessions\` 定向搜一个具体词。不要通读会话记录。
|
|
115
|
-
|
|
116
|
-
## 阶段 3 — 整合
|
|
117
|
-
|
|
118
|
-
对每件值得记住的事,改写或新建一条记忆。**合并新信息进已有文件**,不要为一点新东西新开一个文件。被推翻的事实改在源头,而不是在后面追加一句「其实不是这样」。
|
|
119
|
-
|
|
120
|
-
${apply ? '你有写工具,直接落盘。**每次 `write_memory` / `archive_memory` 都必须带 `reason`**(一句话:为什么动这条、动成了什么)。这一趟的每次改动都会被记进「整理声明」,人要照着它核对。' : '你没有写工具,把每条改动写成「文件 → 具体改成什么 → 为什么」的形式。'}
|
|
121
|
-
|
|
122
|
-
## 阶段 4 — 修剪与索引
|
|
123
|
-
|
|
124
|
-
${stage4}
|
|
125
|
-
|
|
126
|
-
---
|
|
127
|
-
|
|
128
|
-
结束时,用几句话回报你整合、修改、修剪了什么。如果什么都没改(记忆本来就很紧),就直说。`
|
|
129
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* sage-mem autodream — 提示词。
|
|
3
|
+
*
|
|
4
|
+
* 四阶段结构:定位 → 收集 → 整合 → 修剪与索引。两个非显然的取舍:
|
|
5
|
+
*
|
|
6
|
+
* 1. **写记忆的规范必须随 prompt 自带。** autodream 是一次独立调用,带不上外部的
|
|
7
|
+
* 技能文档。所以四类前缀、`type` 一致性、YAML 两个静默陷阱、受保护文件名这些
|
|
8
|
+
* **必须**出现在 prompt 里,否则模型会照着「看起来像」的方式写坏。
|
|
9
|
+
* 2. **按模式分叉。** 「只出报告」不是靠一句「请不要写」约束的——那一趟根本
|
|
10
|
+
* 没有写工具(见 autodream-tools.js)。所以 prompt 只说它有什么、该产出什么形状。
|
|
11
|
+
*
|
|
12
|
+
* ⚠️ 这里**不许出现任何具体的记忆文件名**(除了使用者通过 `reserved.json` 自己声明的)。
|
|
13
|
+
* 那段文字会发给每一个使用者的模型;把作者的记忆主题编进去就是泄漏
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/** 让 prompt 里的日期对人可读:模型算相对日期时不会写错。 */
|
|
17
|
+
function today(ms) {
|
|
18
|
+
const d = new Date(ms)
|
|
19
|
+
const p = (n) => String(n).padStart(2, '0')
|
|
20
|
+
return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}`
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* 系统提示:身份 + 记忆系统的约定 + 这一趟的权限边界。
|
|
25
|
+
* @param {{apply:boolean, withSessions:boolean, reservedFiles?:string[]}} opts
|
|
26
|
+
* `reservedFiles` 是**使用者自己声明**的受保护文件名(来自 `<状态根>/reserved.json`)。
|
|
27
|
+
* 由运行时注入而不是写死在这里——见文件头那条警告。
|
|
28
|
+
* @returns {string}
|
|
29
|
+
*/
|
|
30
|
+
export function buildAutodreamSystemPrompt(opts) {
|
|
31
|
+
const { apply, withSessions } = opts
|
|
32
|
+
const reserved = Array.isArray(opts.reservedFiles) ? opts.reservedFiles.filter(Boolean) : []
|
|
33
|
+
const reservedNote = reserved.length
|
|
34
|
+
? `- **不许改动的文件**(使用者指定的保护名单):${reserved.map((f) => `\`${f}\``).join('、')} —— 这些多半是手写单副本、人工维护的重要记忆,一个字都不要动`
|
|
35
|
+
: '- 有些记忆是使用者手写单副本、人工维护的;本次没有额外指定保护名单,遇到明显「成篇手写、结构不像流水」的记忆请保守处理'
|
|
36
|
+
const modeLine = apply
|
|
37
|
+
? '本次是**改写模式**:你有写工具,可以直接改记忆文件。运行前已经把整个 memory 目录快照了一份(这就是回滚点),改坏能整体退回去——但这只保证「可恢复」,不保证「改得对」。宁可少改,不要乱动。\n\n**每一次写入都请写清 `reason`。** 这一趟是无人值守跑的,人只能靠这份缘由判断你改得对不对。写清「为什么改这条、改成了什么」,不要写「整理优化」这种没有信息量的话。缺了**不会被拒绝**——改动照样生效——但整理声明里会把那条标成「未自述」,用宿主能追溯到的信息顶替。'
|
|
38
|
+
: '本次是**只读模式**:你没有任何写工具。把所有发现写成建议清单,写进你最后的回复里——要具体到「哪个文件、改成什么」,不要泛泛而谈。'
|
|
39
|
+
|
|
40
|
+
return `你是 sage-mem(一个文件式跨会话记忆系统)的整理进程。你的记忆不是数据库,是一个装着 markdown 文件的目录,靠 frontmatter 分类、靠双字组匹配检索。你要做的是一次反思性通读,把最近的经历沉淀成耐久、有条理的记忆。
|
|
41
|
+
|
|
42
|
+
${modeLine}
|
|
43
|
+
|
|
44
|
+
## 记忆的约定(必须遵守,违反了会静默损坏记忆)
|
|
45
|
+
|
|
46
|
+
**分四类,靠文件名前缀 + frontmatter 的 \`type\` 双重标记:**
|
|
47
|
+
|
|
48
|
+
| 前缀 | type | 装什么 |
|
|
49
|
+
|---|---|---|
|
|
50
|
+
| \`user_\` | \`user\` | 用户是谁:角色、背景、偏好、习惯 |
|
|
51
|
+
| \`feedback_\` | \`feedback\` | 用户给的工作方式指导。**正文必须带 \`**Why:**\` 与 \`**How to apply:**\` 两段** |
|
|
52
|
+
| \`project_\` | \`project\` | 进行中的工作与状态。**相对日期一律转成绝对日期** |
|
|
53
|
+
| \`reference_\` | \`reference\` | 外部信息在哪找:服务器、路径、端点、命令 |
|
|
54
|
+
|
|
55
|
+
**分类只看 \`type\`,不看前缀** —— 两者不一致时以 \`type\` 为准,而那种不一致本身就是该修的毛病。\`type\` 缺失会被兜底成 \`reference\`(静默归错类)。
|
|
56
|
+
|
|
57
|
+
**\`description\` 是检索的唯一入口。** 检索机制是把提问和「\`description\` + 文件名」都切成双字组比覆盖率,命中靠词面重合、不靠语义。所以 description 要写「将来我会怎么问它」时会出现的词,而不是一句抽象的概括。
|
|
58
|
+
|
|
59
|
+
**frontmatter 是 YAML,两个元字符会静默出事:**
|
|
60
|
+
- 未加引号的值里有 \`: \`(半角冒号+空格)→ 解析失败,整条描述与类型全空
|
|
61
|
+
- 未加引号的值里有 \` #\`(空格+井号)→ **解析成功但后半句被当注释吞掉**,页面与日志都没有任何提示
|
|
62
|
+
|
|
63
|
+
修法:短的值直接加单引号,长文本避开这两个形态。
|
|
64
|
+
|
|
65
|
+
**几个特殊文件:**
|
|
66
|
+
- \`MEMORY.md\` —— 索引。它是一份索引用,每条一行 \`- [标题](文件.md) — 一句话\`,**不要把记忆内容写进去**
|
|
67
|
+
- \`session-log.md\` —— 追加型流水账,**不是一条记忆**,不进检索、不进列表、不要当记忆改
|
|
68
|
+
${reservedNote}
|
|
69
|
+
- \`archive/\` —— 归档区,不在检索范围内。归档 = 把文件移进去,**可人工捞回**;这里没有真删除
|
|
70
|
+
|
|
71
|
+
**硬标准:靠读代码 / git / 系统提示能推出来的东西不要存。** 存的是推不出来的上下文。写之前先问一句「下一个我会真的用得上这条吗」。${withSessions ? '' : '\n\n**本次输入源不含会话记录**,所以你没有 search_sessions 工具。手上的材料就是记忆目录本身。'}`
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* 首条用户消息:四阶段流程 + 这一趟的实时上下文。
|
|
76
|
+
*
|
|
77
|
+
* 阶段名与顺序:定位(Orient)→ 收集(Gather)→ 整合(Consolidate)→
|
|
78
|
+
* 修剪与索引(Prune and index)。
|
|
79
|
+
*
|
|
80
|
+
* @param {{memoryDir:string, apply:boolean, hoursSince:number, sessionCount:number, now:number}} opts
|
|
81
|
+
* @returns {string}
|
|
82
|
+
*/
|
|
83
|
+
export function buildAutodreamUserPrompt(opts) {
|
|
84
|
+
const { memoryDir, apply, hoursSince, sessionCount, now } = opts
|
|
85
|
+
const stage4 = apply
|
|
86
|
+
? `- 更新 \`MEMORY.md\` 索引,让它保持是索引而不是内容堆:
|
|
87
|
+
- 每条一行 \`- [标题](文件.md) — 一句话\`
|
|
88
|
+
- 移除指向已归档 / 已不存在文件的指针
|
|
89
|
+
- 补上新写的重要记忆
|
|
90
|
+
- 两条记忆互相矛盾时,把错的那条改对`
|
|
91
|
+
: `- 给出 \`MEMORY.md\` 该增删哪些行的**具体清单**(逐字给出要改成的那行文本)`
|
|
92
|
+
|
|
93
|
+
return `# Autodream:一次记忆整理
|
|
94
|
+
|
|
95
|
+
今天:${today(now)}
|
|
96
|
+
记忆目录:\`${memoryDir}\`
|
|
97
|
+
距上次整理:${hoursSince === 0 ? '(没有记录,当成第一次)' : hoursSince.toFixed(1) + ' 小时'}
|
|
98
|
+
期间有更新的会话数:${sessionCount}
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## 阶段 1 — 定位
|
|
103
|
+
|
|
104
|
+
- 先用 \`list_memory\` 看清现在有什么
|
|
105
|
+
- 读 \`MEMORY.md\` 理解当前索引结构
|
|
106
|
+
- 挑几条与最近改动最相关的记忆读一读,**改进已有文件而不是造近似重复**
|
|
107
|
+
|
|
108
|
+
## 阶段 2 — 收集近期信号
|
|
109
|
+
|
|
110
|
+
值得留存的新信息,大致优先级:
|
|
111
|
+
|
|
112
|
+
1. **结构性毛病** —— 直接跑 \`audit_memory\`,它会把索引悬空、漏索引、type 与前缀不一致、双链断链、YAML 陷阱都列出来。这些是确定要做的事。
|
|
113
|
+
2. **漂移的旧记忆** —— 描述与内容已经对不上的、日期还是相对写法的、被后续进展推翻的。
|
|
114
|
+
3. **(仅在需要具体细节时)** 用 \`search_sessions\` 定向搜一个具体词。不要通读会话记录。
|
|
115
|
+
|
|
116
|
+
## 阶段 3 — 整合
|
|
117
|
+
|
|
118
|
+
对每件值得记住的事,改写或新建一条记忆。**合并新信息进已有文件**,不要为一点新东西新开一个文件。被推翻的事实改在源头,而不是在后面追加一句「其实不是这样」。
|
|
119
|
+
|
|
120
|
+
${apply ? '你有写工具,直接落盘。**每次 `write_memory` / `archive_memory` 都必须带 `reason`**(一句话:为什么动这条、动成了什么)。这一趟的每次改动都会被记进「整理声明」,人要照着它核对。' : '你没有写工具,把每条改动写成「文件 → 具体改成什么 → 为什么」的形式。'}
|
|
121
|
+
|
|
122
|
+
## 阶段 4 — 修剪与索引
|
|
123
|
+
|
|
124
|
+
${stage4}
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
结束时,用几句话回报你整合、修改、修剪了什么。如果什么都没改(记忆本来就很紧),就直说。`
|
|
129
|
+
}
|