@a9i5k4/dsh-auto-memory 2.5.2 → 2.5.3
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/lib/client.js +10 -1
- package/lib/water-window.js +186 -177
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* 3. settings.section — 自动记忆设置页(存储位置、注入、反思风格)
|
|
8
8
|
* Data flows over /api/dsh-auto-memory/* (loopback-only host routes).
|
|
9
9
|
*/
|
|
10
|
-
console.log('[dsh-auto-memory] client v2.5.
|
|
10
|
+
console.log('[dsh-auto-memory] client v2.5.3 fingerprint: per-session-workspace')
|
|
11
11
|
window.__ModuleLoader__.load({
|
|
12
12
|
id: '@a9i5k4/dsh-auto-memory',
|
|
13
13
|
factory: (require) => {
|
|
@@ -567,6 +567,15 @@ window.__ModuleLoader__.load({
|
|
|
567
567
|
|
|
568
568
|
// ───────────────────────── 更新弹窗 / 首次指导 ─────────────────────────
|
|
569
569
|
var CHANGELOG = {
|
|
570
|
+
'2.5.3': { zh: [
|
|
571
|
+
'修复:provider 前缀式模型 id(如 deepseek/deepseek-v4.1-flash、z-ai/glm-5.3-flash、meituan/LongCat-2.0:free)的上下文窗口此前被静默丢弃——解析窗口的字符类不含斜杠,整条 id 行匹配不上,该路由的窗口退回兜底 131072;真实窗口 100 万的会话水位被放大 7.63 倍(12.8% 显示成 98%),未到阈值就误弹「接续到新会话」确认卡。现字符类含斜杠,前缀式 id 能取到真实窗口。',
|
|
572
|
+
'修复:id 行存在但格式不被识别时(带引号/含空格等),紧随其后的 contextWindow 不再被错记到上一条模型头上——错配会给出偏小的窗口、让水位漏报,比读不到更危险。',
|
|
573
|
+
'内部:群日报与群反馈跟踪的 CI 脚本同步(对使用无影响)。',
|
|
574
|
+
], en: [
|
|
575
|
+
'Fix: context windows for provider-prefixed model ids (deepseek/deepseek-v4.1-flash, z-ai/glm-5.3-flash, meituan/LongCat-2.0:free) were silently dropped — the id character class had no "/", so the whole id line failed to match and that route fell back to 131072; on a session whose real window is 1,000,000 the water level was inflated 7.63x (12.8% shown as 98%), firing the "continue in a new session" prompt below the threshold. The character class now includes "/".',
|
|
576
|
+
'Fix: when an id line exists but still cannot be parsed (quoted, spaces, etc.), the following contextWindow is no longer attributed to the previous model — that misattribution yields a too-small window and under-reports the water level, which is worse than not reading it at all.',
|
|
577
|
+
'Internal: CI scripts for the daily group digest and group feedback tracking synced (no user-facing effect).',
|
|
578
|
+
] },
|
|
570
579
|
'2.5.2': { zh: [
|
|
571
580
|
'修复:开启「无人值守模式」的用户切换工作区失灵(面板恒显示启动目录、日志 0)——防漂移锁此前全局钉死首个工作区,现改为按会话锁定,只防本会话内的目录漂移;未开启该模式的用户行为不变。',
|
|
572
581
|
'修复:面板白板/账本恒显示「最近活跃会话」的内容——现在按你正在查看的会话取数;会话经工作区绑定创建(无 cwd)时也能正确解析;解析不出身份时如实提示「当前会话未绑定工作区」,不再展示错误路径。',
|
package/lib/water-window.js
CHANGED
|
@@ -1,177 +1,186 @@
|
|
|
1
|
-
/** 上下文窗口解析(2.2.4,2026-09-08)。
|
|
2
|
-
*
|
|
3
|
-
* 背景:水位 = 当前上下文占用 / 模型窗口。占用取官方 tokenMeter(与聊天框 context ring 同源),
|
|
4
|
-
* 窗口此前有三条来源:手动覆盖 > 官方 request/context 的 contextWindow > settings.yaml 解析 > fallback。
|
|
5
|
-
* 实测发现两条路同时失效,导致窗口恒为 fallback 131072(真实模型 1M),水位被算成 150%+:
|
|
6
|
-
* 1) 官方 request/context 只在会话早期追加(本会话 2999 个事件里仅 2 条),而旧代码只扫最近 256 条 → 永远扫不到;
|
|
7
|
-
* 2) settings.yaml 的 llm-deepseek 段是 flow 风格 YAML(`{ models: [ { id: x, contextWindow: 1000000, ... } ] }`),
|
|
8
|
-
* 旧解析器只认 block 风格(`- id: x` + 行尾纯数字),整段解析不出来。
|
|
9
|
-
*
|
|
10
|
-
* 本模块把「settings.yaml → 模型窗口映射」与「按 provider/model 选窗口」抽成纯函数,便于 smoke 覆盖。
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* 从 settings.yaml 文本解析 `模型 id → contextWindow` 映射。
|
|
15
|
-
* 同时支持 block 风格(llm-pi-ai 那种 `- id: x` / `contextWindow: N`)与 flow 风格(llm-deepseek 那种 `id: x,` / `contextWindow: N,`)。
|
|
16
|
-
* @param {string} text - settings.yaml 原文。
|
|
17
|
-
* @returns {{byRoute:Record<string,number>,byModel:Record<string,number>}} byRoute 键为 provider/id,byModel 键为 id。
|
|
18
|
-
*/
|
|
19
|
-
export function parseModelWindowsPre(text) {
|
|
20
|
-
const byRoute = {}
|
|
21
|
-
const byModel = {}
|
|
22
|
-
if (!text || typeof text !== 'string') return { byRoute, byModel }
|
|
23
|
-
const lines = String(text).split(/\r?\n/)
|
|
24
|
-
let currentProvider = ''
|
|
25
|
-
let currentId = null
|
|
26
|
-
for (const line of lines) {
|
|
27
|
-
// provider 段(block 风格,2 空格缩进的裸键,如 `opencode-go:`);flow 风格段名同样以冒号结尾但缩进更深
|
|
28
|
-
const pm = line.match(/^ {2}([A-Za-z0-9_.-]+):\s*$/)
|
|
29
|
-
if (pm) { currentProvider = pm[1]; currentId = null; continue }
|
|
30
|
-
// 模型 id:兼容 block(`- id: x`)与 flow(`id: x,`)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
//
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
*
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
if (!
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
return 0
|
|
177
|
-
|
|
1
|
+
/** 上下文窗口解析(2.2.4,2026-09-08)。
|
|
2
|
+
*
|
|
3
|
+
* 背景:水位 = 当前上下文占用 / 模型窗口。占用取官方 tokenMeter(与聊天框 context ring 同源),
|
|
4
|
+
* 窗口此前有三条来源:手动覆盖 > 官方 request/context 的 contextWindow > settings.yaml 解析 > fallback。
|
|
5
|
+
* 实测发现两条路同时失效,导致窗口恒为 fallback 131072(真实模型 1M),水位被算成 150%+:
|
|
6
|
+
* 1) 官方 request/context 只在会话早期追加(本会话 2999 个事件里仅 2 条),而旧代码只扫最近 256 条 → 永远扫不到;
|
|
7
|
+
* 2) settings.yaml 的 llm-deepseek 段是 flow 风格 YAML(`{ models: [ { id: x, contextWindow: 1000000, ... } ] }`),
|
|
8
|
+
* 旧解析器只认 block 风格(`- id: x` + 行尾纯数字),整段解析不出来。
|
|
9
|
+
*
|
|
10
|
+
* 本模块把「settings.yaml → 模型窗口映射」与「按 provider/model 选窗口」抽成纯函数,便于 smoke 覆盖。
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* 从 settings.yaml 文本解析 `模型 id → contextWindow` 映射。
|
|
15
|
+
* 同时支持 block 风格(llm-pi-ai 那种 `- id: x` / `contextWindow: N`)与 flow 风格(llm-deepseek 那种 `id: x,` / `contextWindow: N,`)。
|
|
16
|
+
* @param {string} text - settings.yaml 原文。
|
|
17
|
+
* @returns {{byRoute:Record<string,number>,byModel:Record<string,number>}} byRoute 键为 provider/id,byModel 键为 id。
|
|
18
|
+
*/
|
|
19
|
+
export function parseModelWindowsPre(text) {
|
|
20
|
+
const byRoute = {}
|
|
21
|
+
const byModel = {}
|
|
22
|
+
if (!text || typeof text !== 'string') return { byRoute, byModel }
|
|
23
|
+
const lines = String(text).split(/\r?\n/)
|
|
24
|
+
let currentProvider = ''
|
|
25
|
+
let currentId = null
|
|
26
|
+
for (const line of lines) {
|
|
27
|
+
// provider 段(block 风格,2 空格缩进的裸键,如 `opencode-go:`);flow 风格段名同样以冒号结尾但缩进更深
|
|
28
|
+
const pm = line.match(/^ {2}([A-Za-z0-9_.-]+):\s*$/)
|
|
29
|
+
if (pm) { currentProvider = pm[1]; currentId = null; continue }
|
|
30
|
+
// 模型 id:兼容 block(`- id: x`)与 flow(`id: x,`)两种写法,允许行尾逗号。
|
|
31
|
+
// 字符类须含 `/`:provider 前缀式 id(`deepseek/deepseek-v4.1-flash`、`z-ai/glm-5.3-flash`、
|
|
32
|
+
// `meituan/LongCat-2.0:free`)是常见形态,旧字符类 `[A-Za-z0-9._:-]` 不含斜杠 →
|
|
33
|
+
// 整条 id 行匹配失败,其 contextWindow 被静默丢弃,窗口退化为 fallback 131072。
|
|
34
|
+
// 实测:真实窗口 1,000,000 的会话被按 131,072 当分母,水位放大 7.63 倍(12.8% 显示成 98%),
|
|
35
|
+
// 未达 0.75 阈值即误弹「接续到新会话」确认卡。
|
|
36
|
+
const im = line.match(/^\s*(?:-\s*)?id:\s*([A-Za-z0-9._:/-]+?)\s*,?\s*$/)
|
|
37
|
+
if (im) { currentId = im[1]; if (byModel[currentId] === undefined) byModel[currentId] = 0; continue }
|
|
38
|
+
// id 行存在但格式仍不被识别(带引号/含空格/中文等):重置 currentId,避免紧随其后的
|
|
39
|
+
// contextWindow 被记到上一条模型头上 —— 静默错配比"读不到"更危险:读不到只退化为 fallback,
|
|
40
|
+
// 错配会给出**偏小**的窗口,使水位漏报。
|
|
41
|
+
if (/^\s*(?:-\s*)?id:/.test(line)) { currentId = null; continue }
|
|
42
|
+
// 窗口:允许行尾逗号(flow 风格)
|
|
43
|
+
const cm = line.match(/^\s*contextWindow:\s*([0-9]+)\s*,?\s*$/)
|
|
44
|
+
if (cm && currentId) {
|
|
45
|
+
const value = Number(cm[1])
|
|
46
|
+
const route = currentProvider + '/' + currentId
|
|
47
|
+
if (byRoute[route] === undefined) byRoute[route] = value
|
|
48
|
+
if (!byModel[currentId]) byModel[currentId] = value
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return { byRoute, byModel }
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* 按 provider/model 选窗口:先精确匹配 provider/model,再按 model 名全局兜底。
|
|
56
|
+
* @param {{byRoute:Record<string,number>,byModel:Record<string,number>}} windows - parseModelWindowsPre 结果。
|
|
57
|
+
* @param {string} provider - 路由 provider(可空)。
|
|
58
|
+
* @param {string} model - 模型 id。
|
|
59
|
+
* @returns {number} 窗口 token 数;未命中返回 0。
|
|
60
|
+
*/
|
|
61
|
+
export function pickWindowPre(windows, provider, model) {
|
|
62
|
+
if (!windows || !model) return 0
|
|
63
|
+
const byRoute = windows.byRoute || {}
|
|
64
|
+
const byModel = windows.byModel || {}
|
|
65
|
+
const exact = byRoute[(provider || '') + '/' + model]
|
|
66
|
+
if (Number.isFinite(exact) && exact > 0) return exact
|
|
67
|
+
const direct = byModel[model]
|
|
68
|
+
if (Number.isFinite(direct) && direct > 0) return direct
|
|
69
|
+
// 后缀兜底:路由 id 可能带 provider 前缀或版本后缀差异
|
|
70
|
+
for (const route of Object.keys(byRoute)) {
|
|
71
|
+
if (route.endsWith('/' + model) && byRoute[route] > 0) return byRoute[route]
|
|
72
|
+
}
|
|
73
|
+
for (const id of Object.keys(byModel)) {
|
|
74
|
+
if (id.endsWith('-' + model) && byModel[id] > 0) return byModel[id]
|
|
75
|
+
}
|
|
76
|
+
return 0
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* 从会话事件里取最近一条 request/header|request/context 的真实 provider/model(+ 顺带 contextWindow)。
|
|
81
|
+
*
|
|
82
|
+
* 背景(2026-09-08 用户实测):水位卡此前把「窗口来源」标成 settings.yaml 的 agent-default-model(新会话默认模型),
|
|
83
|
+
* 用户在 GUI 里切过默认模型后,当前会话实际跑的模型与卡片显示不符
|
|
84
|
+
* (实测:会话日志 request/header = deepseek-official/deepseek-v4.1-flash-expires-on-0910,卡片却写 opencode-go/deepseek-v4-flash)。
|
|
85
|
+
* 会话自己的请求头才是「这个会话正在用哪个模型」的权威来源;默认模型只作兜底。
|
|
86
|
+
*
|
|
87
|
+
* @param {object[]} events - 会话事件数组。
|
|
88
|
+
* @param {number} maxScan - 最多向前扫多少条(默认全部)。
|
|
89
|
+
* @returns {{provider:string,model:string,contextWindow:number}} 未找到时字段为空/0。
|
|
90
|
+
*/
|
|
91
|
+
export function findSessionModelPre(events, maxScan = 0) {
|
|
92
|
+
const out = { provider: '', model: '', contextWindow: 0, maxTokens: 0 }
|
|
93
|
+
if (!Array.isArray(events) || !events.length) return out
|
|
94
|
+
const floor = maxScan > 0 ? Math.max(0, events.length - maxScan) : 0
|
|
95
|
+
for (let i = events.length - 1; i >= floor; i--) {
|
|
96
|
+
const ev = events[i]
|
|
97
|
+
const t = ev && ev.type
|
|
98
|
+
if (t !== 'request/header' && t !== 'request/context') continue
|
|
99
|
+
const d = (ev && ev.data) || {}
|
|
100
|
+
const cfg = (d.header && d.header.config) || d.config || {}
|
|
101
|
+
const provider = String(cfg.provider || d.provider || '')
|
|
102
|
+
const model = String(cfg.model || d.model || '')
|
|
103
|
+
const cw = Number(d.contextWindow) || 0
|
|
104
|
+
const mt = Number(cfg.maxTokens) || 0
|
|
105
|
+
if (!out.model && model) { out.model = model; out.provider = provider }
|
|
106
|
+
if (!out.contextWindow && cw > 0) out.contextWindow = cw
|
|
107
|
+
// maxTokens = 路由为**本次请求预留的输出预算**,provider 会把它计入 "requested tokens"(实测:
|
|
108
|
+
// 消息 666,044 + 预留 384,000 = 1,050,044 > 上限 1,048,576 → 400 CONTEXT_WINDOW_EXCEEDED)。
|
|
109
|
+
if (!out.maxTokens && mt > 0) out.maxTokens = mt
|
|
110
|
+
if (out.model && out.contextWindow && out.maxTokens) break
|
|
111
|
+
}
|
|
112
|
+
return out
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* 水位「硬信号」扫描(2026-09-10,实机取证后新增)。
|
|
117
|
+
*
|
|
118
|
+
* 背景(用户实测:官方压缩又抢在 75% 接续之前):本机 provider 的硬上限是 1,048,576 token,
|
|
119
|
+
* 而**预留输出预算 384,000 也算在请求里** ⇒ 消息实际只能用到约 664,576。插件此前拿
|
|
120
|
+
* `contextWindow`(1,000,000)当分母、拿本地计量当分子,两边都偏乐观,于是 75% 永远到不了,
|
|
121
|
+
* 直到上游直接 400:
|
|
122
|
+
* "This model's maximum context length is 1048576 tokens. However, you requested 1050044 tokens
|
|
123
|
+
* (666044 in the messages, 384000 in the completion)."
|
|
124
|
+
* 这条错误是**唯一权威**的口径来源。本函数把它与 compaction 事件一起扫出来,供上层:
|
|
125
|
+
* ① 解析真实窗口/预留额度/消息实占 → 自我校准分子;
|
|
126
|
+
* ② 一旦发生 overflow 或 compaction,直接当作「已到阈值」触发接续(不再依赖估算)。
|
|
127
|
+
*
|
|
128
|
+
* @param {object[]} events - 会话事件数组。
|
|
129
|
+
* @returns {{reservedTokens:number,overflow:{seq:number,windowTokens:number,requestedTokens:number,messageTokens:number,completionTokens:number,code:string}|null,compactionSeq:number}}
|
|
130
|
+
*/
|
|
131
|
+
export function scanPressureSignalsPre(events) {
|
|
132
|
+
const out = { reservedTokens: 0, overflow: null, compactionSeq: 0 }
|
|
133
|
+
if (!Array.isArray(events) || !events.length) return out
|
|
134
|
+
for (let i = events.length - 1; i >= 0; i--) {
|
|
135
|
+
const ev = events[i]
|
|
136
|
+
if (!ev) continue
|
|
137
|
+
const seq = Number(ev.seq) || 0
|
|
138
|
+
const t = ev.type
|
|
139
|
+
if (!out.reservedTokens && (t === 'request/header' || t === 'request/context')) {
|
|
140
|
+
const d = (ev.data || {})
|
|
141
|
+
const cfg = (d.header && d.header.config) || d.config || {}
|
|
142
|
+
const mt = Number(cfg.maxTokens) || 0
|
|
143
|
+
if (mt > 0) out.reservedTokens = mt
|
|
144
|
+
}
|
|
145
|
+
if (!out.compactionSeq && (t === 'compaction/start' || t === 'compaction/summary')) out.compactionSeq = seq
|
|
146
|
+
if (out.overflow && out.compactionSeq && out.reservedTokens) break
|
|
147
|
+
if (out.overflow || t !== 'assistant/attempt') continue
|
|
148
|
+
const stream = (ev.data && ev.data.stream) || []
|
|
149
|
+
for (const item of stream) {
|
|
150
|
+
const chunk = (item && item.chunk) || {}
|
|
151
|
+
if (chunk.type !== 'finish') continue
|
|
152
|
+
const failure = (chunk.reason && chunk.reason.failure) || {}
|
|
153
|
+
const code = String(failure.code || '')
|
|
154
|
+
const msg = String(failure.message || '')
|
|
155
|
+
if (code !== 'CONTEXT_WINDOW_EXCEEDED' && !/maximum context length/i.test(msg)) continue
|
|
156
|
+
const win = Number((msg.match(/maximum context length is\s+(\d+)\s+tokens/i) || [])[1]) || 0
|
|
157
|
+
const req = Number((msg.match(/you requested\s+(\d+)\s+tokens/i) || [])[1]) || 0
|
|
158
|
+
const inMsg = Number((msg.match(/\((\d+)\s+in the messages/i) || [])[1]) || 0
|
|
159
|
+
const inComp = Number((msg.match(/(\d+)\s+in the completion\)/i) || [])[1]) || 0
|
|
160
|
+
out.overflow = { seq, windowTokens: win, requestedTokens: req, messageTokens: inMsg, completionTokens: inComp, code: code || 'CONTEXT_WINDOW_EXCEEDED' }
|
|
161
|
+
break
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return out
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* 从会话事件里取最近一条 request/context 的 contextWindow(路由权威值)。
|
|
170
|
+
* 不限制扫描窗口:该事件只在请求头部变化时追加,长会话里往往只存在于开头。
|
|
171
|
+
* @param {object[]} events - 会话事件数组。
|
|
172
|
+
* @param {number} maxScan - 最多向前扫多少条(默认全部;设小值可限制开销)。
|
|
173
|
+
* @returns {number} contextWindow;未找到返回 0。
|
|
174
|
+
*/
|
|
175
|
+
export function findOfficialContextWindowPre(events, maxScan = 0) {
|
|
176
|
+
if (!Array.isArray(events) || !events.length) return 0
|
|
177
|
+
const floor = maxScan > 0 ? Math.max(0, events.length - maxScan) : 0
|
|
178
|
+
for (let i = events.length - 1; i >= floor; i--) {
|
|
179
|
+
const ev = events[i]
|
|
180
|
+
if (ev && ev.type === 'request/context') {
|
|
181
|
+
const cw = Number(ev && ev.data && ev.data.contextWindow)
|
|
182
|
+
return Number.isFinite(cw) && cw > 0 ? cw : 0
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return 0
|
|
186
|
+
}
|
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": "2.5.
|
|
4
|
+
"version": "2.5.3",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"exports": {
|