@team-harness/memory-algorithms 0.1.0 → 0.1.1
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 +5 -1
- package/dist/runtime/l1.js +33 -9
- package/dist/runtime/run.d.ts +1 -1
- package/dist/runtime/run.js +1 -1
- package/package.json +1 -1
- package/upstream/baseline.json +1 -1
- package/upstream/changes.md +20 -0
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@ npm pack
|
|
|
26
26
|
在你的应用目录安装生成的文件:
|
|
27
27
|
|
|
28
28
|
```sh
|
|
29
|
-
npm install /path/to/memory-algorithms/team-harness-memory-algorithms-0.1.
|
|
29
|
+
npm install /path/to/memory-algorithms/team-harness-memory-algorithms-0.1.1.tgz
|
|
30
30
|
```
|
|
31
31
|
|
|
32
32
|
## 提取流程
|
|
@@ -172,6 +172,10 @@ L1 的 `update` / `merge` 会产生新的 `after.id`,不是原 ID 就地更新
|
|
|
172
172
|
|
|
173
173
|
失败或取消时没有可提交变更,不推进分析进度。参数或依赖配置错误也可能直接抛出异常,调用层仍需捕获异常。L3 和 Skill 是候选,是否启用由应用决定。
|
|
174
174
|
|
|
175
|
+
L1 提取调用使用窗口内的短编号:`N1/N2/...` 对应本批新消息,`B1/B2/...` 对应仅供理解上下文的背景消息。模型只能引用合法的 `N` 编号;Lib 校验后恢复调用方提供的完整消息 ID,再进行去重和结果构建。输入、消息正文、最终 `source_message_ids`、`evidence`、`coverage` 均不改用短编号。编号每个窗口独立生成,同窗口重试沿用同一映射;调用方无需适配。
|
|
176
|
+
|
|
177
|
+
L1 模型输出引用了本批新消息以外的编号,或记忆缺少来源编号时,会在剩余预算内最多重新生成一次,并显式给出合法编号列表。额外调用计入同一次运行的耗时、调用数和 token 用量;再次校验失败仍不返回变更。不会猜测替换编号、删除错误引用或跳过失败批次。`diagnostics` 会记录纠正尝试。
|
|
178
|
+
|
|
175
179
|
L1 的证据按记录关联;L2/L3/Skill 使用保守的整次运行输入依赖,不是逐句引用。`provenance.evidenceGranularity` 标明 `record` 或 `run`;token 用量无法取得时为 `null`。
|
|
176
180
|
|
|
177
181
|
## 分批、预算与取消
|
package/dist/runtime/l1.js
CHANGED
|
@@ -59,9 +59,19 @@ export function validateMessages(messages) {
|
|
|
59
59
|
}
|
|
60
60
|
uniqueRefs(messages.flatMap(m => m.evidence));
|
|
61
61
|
}
|
|
62
|
+
function evidenceError(parsed, allowed) {
|
|
63
|
+
for (const scene of parsed.scenes) {
|
|
64
|
+
if (scene.message_ids.some(id => !allowed.has(id)))
|
|
65
|
+
return "Scene cites evidence outside new messages";
|
|
66
|
+
for (const memory of scene.memories) {
|
|
67
|
+
if (!memory.source_message_ids.length || memory.source_message_ids.some(id => !allowed.has(id)))
|
|
68
|
+
return "Memory cites evidence outside new messages";
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
62
72
|
export async function extractL1(deps, input) {
|
|
63
73
|
input = { ...input, messages: structuredClone(input.messages), strategy: structuredClone(input.strategy) };
|
|
64
|
-
const inputHash = hash({ scopeKey: input.scopeKey, mode: input.mode ?? "code", messages: input.messages, strategy: input.strategy,
|
|
74
|
+
const inputHash = hash({ citationEncoding: "short-message-ids-v1", scopeKey: input.scopeKey, mode: input.mode ?? "code", messages: input.messages, strategy: input.strategy,
|
|
65
75
|
maxNewMessages: input.maxNewMessages ?? 10, maxBackgroundMessages: input.maxBackgroundMessages ?? 5, conflictRecallTopK: input.conflictRecallTopK ?? 5 });
|
|
66
76
|
const run = new Run(deps, input, "l1", inputHash);
|
|
67
77
|
const cov = coverage(input.messages.map(m => m.id));
|
|
@@ -86,25 +96,39 @@ export async function extractL1(deps, input) {
|
|
|
86
96
|
const extracted = [];
|
|
87
97
|
let sceneNames = [];
|
|
88
98
|
if (fresh.length) {
|
|
99
|
+
// Labels belong only to this window; restore validated references before dedup and persistence.
|
|
100
|
+
const sourceIds = new Map(fresh.map((message, index) => [`N${index + 1}`, message.id]));
|
|
101
|
+
const newMessages = fresh.map((message, index) => ({ ...message, id: `N${index + 1}` }));
|
|
102
|
+
const backgroundMessages = background.map((message, index) => ({ ...message, id: `B${index + 1}` }));
|
|
89
103
|
const system = composeMemorySystemPrompt(getExtractMemoriesSystemPrompt(input.mode ?? "code"), input.strategy);
|
|
90
|
-
const
|
|
91
|
-
|
|
104
|
+
const prompt = formatExtractionPrompt({ newMessages, backgroundMessages, previousSceneName: input.continuation?.previousSceneName });
|
|
105
|
+
let parsed = parseExtractionResult(await run.model(system, prompt));
|
|
92
106
|
if (parsed.emptyReason && parsed.emptyReason !== "empty_scenes")
|
|
93
107
|
throw new Error(`L1 parse failed: ${parsed.emptyReason}`);
|
|
108
|
+
const allowed = new Set(sourceIds.keys());
|
|
109
|
+
const invalid = evidenceError(parsed, allowed);
|
|
110
|
+
// Regenerate once within the same run budget; never repair citations by guessing or dropping IDs.
|
|
111
|
+
if (invalid) {
|
|
112
|
+
if (run.calls >= run.limits.maxCalls)
|
|
113
|
+
throw new Error(invalid);
|
|
114
|
+
run.diagnostics.push(`${invalid}; regenerating once with explicit allowed message IDs`);
|
|
115
|
+
const correction = `\n\nEvidence validation rejected the previous response. Regenerate the complete JSON result from the messages above. Both scene.message_ids and memory.source_message_ids must contain only exact IDs from this JSON array: ${JSON.stringify([...allowed])}. Do not use IDs mentioned inside message bodies, background IDs, altered labels, or invented IDs. Every memory must cite at least one allowed ID. Return [] only if the new messages contain no extractable memories.`;
|
|
116
|
+
parsed = parseExtractionResult(await run.model(system, prompt + correction));
|
|
117
|
+
if (parsed.emptyReason && parsed.emptyReason !== "empty_scenes")
|
|
118
|
+
throw new Error(`L1 parse failed: ${parsed.emptyReason}`);
|
|
119
|
+
const remainingError = evidenceError(parsed, allowed);
|
|
120
|
+
if (remainingError)
|
|
121
|
+
throw new Error(remainingError);
|
|
122
|
+
}
|
|
94
123
|
sceneNames = parsed.scenes.map(s => s.scene_name);
|
|
95
|
-
const allowed = new Set(fresh.map(m => m.id));
|
|
96
124
|
for (const scene of parsed.scenes) {
|
|
97
|
-
if (scene.message_ids.some(id => !allowed.has(id)))
|
|
98
|
-
throw new Error("Scene cites evidence outside new messages");
|
|
99
125
|
for (const memory of scene.memories) {
|
|
100
126
|
const type = normalizeType(memory.type);
|
|
101
127
|
if (!type)
|
|
102
128
|
throw new Error("Invalid memory type");
|
|
103
|
-
if (!memory.source_message_ids.length || memory.source_message_ids.some(id => !allowed.has(id)))
|
|
104
|
-
throw new Error("Memory cites evidence outside new messages");
|
|
105
129
|
if (!Number.isFinite(memory.priority) || memory.priority < -1 || memory.priority > 100)
|
|
106
130
|
throw new Error("Invalid priority");
|
|
107
|
-
extracted.push({ ...memory, metadata: memory.metadata, type, scene_name: scene.scene_name, record_id: run.id() });
|
|
131
|
+
extracted.push({ ...memory, source_message_ids: memory.source_message_ids.map(id => sourceIds.get(id)), metadata: memory.metadata, type, scene_name: scene.scene_name, record_id: run.id() });
|
|
108
132
|
if (extracted.length > run.limits.maxExtractedMemories)
|
|
109
133
|
throw new Error("Extracted memory budget exhausted");
|
|
110
134
|
}
|
package/dist/runtime/run.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Change, Coverage, Dependencies, ExtractionResult, Limits, RunInput, Stage, VersionRef } from "../contracts.js";
|
|
2
2
|
import type { LocalTool } from "./tools.js";
|
|
3
|
-
export declare const ALGORITHM_VERSION = "0.1.
|
|
3
|
+
export declare const ALGORITHM_VERSION = "0.1.1";
|
|
4
4
|
export declare const UPSTREAM_COMMIT = "906b5823b5106eed8f842b62f16d23228838149a";
|
|
5
5
|
export declare function hash(value: unknown): string;
|
|
6
6
|
export declare function uniqueRefs<T extends {
|
package/dist/runtime/run.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
2
|
import { Ajv } from "ajv";
|
|
3
|
-
export const ALGORITHM_VERSION = "0.1.
|
|
3
|
+
export const ALGORITHM_VERSION = "0.1.1";
|
|
4
4
|
export const UPSTREAM_COMMIT = "906b5823b5106eed8f842b62f16d23228838149a";
|
|
5
5
|
export function hash(value) {
|
|
6
6
|
return createHash("sha256").update(JSON.stringify(value, (_key, item) => {
|
package/package.json
CHANGED
package/upstream/baseline.json
CHANGED
|
@@ -353,7 +353,7 @@
|
|
|
353
353
|
"destinations": [
|
|
354
354
|
"src/runtime/l1.ts"
|
|
355
355
|
],
|
|
356
|
-
"adaptation": "A01/A02/B01/B02: host-neutral orchestration; see changes.md"
|
|
356
|
+
"adaptation": "A01/A02/B01/B02/B04/B05: host-neutral orchestration; bounded evidence correction and model-side short message labels; see changes.md"
|
|
357
357
|
},
|
|
358
358
|
{
|
|
359
359
|
"sourcePath": "MemoryCore/src/core/record/l1-writer.ts",
|
package/upstream/changes.md
CHANGED
|
@@ -4,6 +4,26 @@
|
|
|
4
4
|
|
|
5
5
|
原始模块及 hash 见 baseline.json;sourceSha256 对应上游原文,copiedSha256 记录首次提取/裁切的中间内容,adaptedSha256 记录当前已登记的适配内容。Git 历史先独立提交完整上游原文副本(chore(lib): record upstream algorithm source baseline),再提交可运行适配,能够直接比较两者。通过 `git log -- Lib`(父仓库)或独立仓库 Git 历史追溯。
|
|
6
6
|
|
|
7
|
+
## B05:L1 模型侧短消息编号(2026-09-16)
|
|
8
|
+
|
|
9
|
+
分类:behavior-change / host-contract。位置:`src/runtime/l1.ts`;不修改复制的上游提示词和解析器。
|
|
10
|
+
|
|
11
|
+
仅在 L1 提取请求中将筛选后的新消息标识替换为 N1/N2/...,背景标识替换为 B1/B2/...。映射仅保留在本次调用内,B04 重试复用同一映射。所有场景和记忆引用先按新消息编号校验;记忆来源恢复真实 ID 后才进入召回、去重和变更构建。背景编号、错误编号及非编号形式的真实 ID 均不作为回退接受。消息正文不替换,最终来源、证据版本和覆盖范围保持真实 ID。
|
|
12
|
+
|
|
13
|
+
输入 hash 增加 citationEncoding 标记,拒绝旧编码模式的 continuation。外部函数签名不变;自定义模型适配器应原样传输请求和响应,不得自行替换编号。短编号不应用到 L2/L3/Skill 或去重候选的实体 ID。
|
|
14
|
+
|
|
15
|
+
差分与成对回放增加显式的测试侧 citation bridge:严格核对整个原始提示词,仅重建消息标识并结构化转换提取响应的引用数组。原 cassette 保留且仍受 hash 门禁约束;正文、去重调用和解析失败原文不被替换。报告明确区分原始提示词不相等与编号转换后等价,不把编号变化当作逐字上游一致。
|
|
16
|
+
|
|
17
|
+
## B04:L1 引用越界的有界纠正(2026-09-16)
|
|
18
|
+
|
|
19
|
+
分类:bug-fix / behavior-change / host-contract。位置:`src/runtime/l1.ts`,来源仍为已登记的 L1 extractor。
|
|
20
|
+
|
|
21
|
+
本地 Loop 的分析记录显示第一批模型调用因场景引用越界失败(892 条待处理,0 条完成,1 次调用)。未持久化原始失败响应,不能断言模型引用了正文中的 ID、缩写 ID 或背景 ID。
|
|
22
|
+
|
|
23
|
+
首次调用保持上游提示词和输入格式。场景或记忆证据校验失败时,在同一个 Run 剩余预算内最多重新生成一次,附加精确的合法消息 ID 列表及引用规则。重试受原超时、调用数、输入和输出上限约束,token 和提示词 hash 均记录。上游没有此纠正步骤,这是 Lib 的显式适配;不改复制的上游源码,也不放宽 B01 的证据范围。
|
|
24
|
+
|
|
25
|
+
禁止替换、过滤无效 ID 后提交,禁止失败批次推进进度。第二次仍无效、解析失败或取消时返回无变更。回归覆盖场景/记忆越界、空来源、背景及未来 ID、预算耗尽、取消、用量合计、失败覆盖范围;原成功路径继续由差分和固定响应回放验证。
|
|
26
|
+
|
|
7
27
|
## A01:算法与持久化拆分
|
|
8
28
|
|
|
9
29
|
分类:dependency-adaptation / host-contract。
|