@a9i5k4/dsh-auto-memory 3.1.6 → 3.1.7
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/docs/prompts/RELEASE-AGENT.md +4 -4
- package/lib/client.js +34 -9
- package/lib/index.js +45 -6
- package/package.json +1 -1
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
你是 dsh-auto-memory 的发版执行子代理。工作目录:`D:\dsh-auto-memory
|
|
9
|
+
你是 dsh-auto-memory 的发版执行子代理。工作目录:`D:\dsh-auto-memory`(开发树,即唯一工作树;发布基座在 `D:\dsh_debug\_publish_dsh-auto-memory`,下称 REL)。**本次要发布的版本号:`<ver>`(由主对话填入,形如 2.5.0)。**
|
|
10
10
|
|
|
11
11
|
## 硬性禁令(违反任何一条=任务失败)
|
|
12
12
|
|
|
@@ -63,7 +63,7 @@ node tools\release.mjs <ver> --dry-run
|
|
|
63
63
|
node tools\release.mjs <ver>
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
-
复核输出含 `开发树版本回写: … → <ver>`。构建会写 REL
|
|
66
|
+
复核输出含 `开发树版本回写: … → <ver>`。构建会写 REL 树。**构建后开发树的 `package.json` 已被回写为新版本 → 立即补一笔提交**(v2.5.0 教训:不补会留下脏文件,污染下一次发版的脏树闸门):
|
|
67
67
|
|
|
68
68
|
```powershell
|
|
69
69
|
cd D:\dsh-auto-memory
|
|
@@ -115,11 +115,11 @@ Invoke-RestMethod 'https://registry.npmjs.org/@a9i5k4%2Fdsh-auto-memory/latest'
|
|
|
115
115
|
## 回报格式(原样 JSON,不追加散文)
|
|
116
116
|
|
|
117
117
|
```json
|
|
118
|
-
{ "ok": true, "version": "<ver>", "pre_sha": "
|
|
118
|
+
{ "ok": true, "version": "<ver>", "pre_sha": "<开发树 commit 短 sha>", "rel_sha": "<REL commit 短 sha>", "tag": "v<ver>", "npm_latest": "<ver>", "failed_step": "", "error_tail": "" }
|
|
119
119
|
```
|
|
120
120
|
|
|
121
121
|
失败时:`ok:false`,`failed_step` 填阶段号(0/1/2/3/4/5/6/7/8;dry-run 闸门不过记 3),`error_tail` 贴**最后 ≤20 行**原始报错(凭据替换为 `<redacted>`)。已知会出现且**不算失败**的情况:无。任何意外都按失败回报,由主对话决定处置。
|
|
122
122
|
|
|
123
123
|
---
|
|
124
124
|
|
|
125
|
-
*回滚提示(供主对话,子代理不执行):未 push/publish
|
|
125
|
+
*回滚提示(供主对话,子代理不执行):未 push/publish 前一切可回滚(开发树 `git reset`、REL 线 `git reset` + `git tag -d`);publish 后不可撤,只能发 patch。*
|
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 v3.1.
|
|
10
|
+
console.log('[dsh-auto-memory] client v3.1.7 fingerprint: three-reported-bugs-fix')
|
|
11
11
|
window.__ModuleLoader__.load({
|
|
12
12
|
id: '@a9i5k4/dsh-auto-memory',
|
|
13
13
|
factory: (require) => {
|
|
@@ -453,7 +453,7 @@ window.__ModuleLoader__.load({
|
|
|
453
453
|
fConsScheduleDays: '固化回看天数', fConsScheduleDaysHint: '定时固化读取最近 N 天日志;默认 7。',
|
|
454
454
|
fMaintSchedule: '定时 30 天蒸馏', fMaintScheduleHint: '每天到点自动把超过 30 天的旧日志蒸馏归档(等价 memory_maintain);无旧日志时零成本跳过。',
|
|
455
455
|
fMaintScheduleTime: '蒸馏触发时间', fMaintScheduleTimeHint: 'HH:MM,默认 10:00(与固化时间错开)。',
|
|
456
|
-
waterCardTitle: '上下文水位', waterAutoSrc: '自动检测', waterManualSrc: '手动设定', waterOfficialSrc: '官方路由容量', waterFallbackSrc: '回退默认值', waterNotMeasured: '本会话尚未测量(发送一轮后自动更新)', waterMeterOfficial: '官方计量', waterMeterHeuristic: '启发式降级', waterThresholdHint: '水位达到 {t} 时注入交接建议并自动补写账本;计量优先用官方 token-meter(与聊天框 context ring 同源),不可用时降级启发式估算;窗口优先取官方路由容量(request/context 的 contextWindow),其次按当前模型查 settings.yaml contextWindow,也可在下方手动覆盖;触发分母=官方声明窗口(provider 自报过硬限时取较小值),预留输出不参与扣减。',
|
|
456
|
+
waterCardTitle: '上下文水位', waterAutoSrc: '自动检测', waterManualSrc: '手动设定', waterOfficialSrc: '官方路由容量', waterFallbackSrc: '回退默认值', waterNotMeasured: '本会话尚未测量(发送一轮后自动更新)', waterWindowUnknown: '未测出上下文窗口大小', waterWindowUnknownHint: '未能从本会话读取模型窗口(旧会话记录可能已归档/回收)。发送一轮消息后可自动测出;或在下方手动填写「水位窗口」后即恢复。', waterMeterOfficial: '官方计量', waterMeterHeuristic: '启发式降级', waterThresholdHint: '水位达到 {t} 时注入交接建议并自动补写账本;计量优先用官方 token-meter(与聊天框 context ring 同源),不可用时降级启发式估算;窗口优先取官方路由容量(request/context 的 contextWindow),其次按当前模型查 settings.yaml contextWindow,也可在下方手动覆盖;触发分母=官方声明窗口(provider 自报过硬限时取较小值),预留输出不参与扣减。',
|
|
457
457
|
autoContTitle: '自动接续', autoContHint: '开启后:水位达到阈值即弹出确认卡——同意=立即接续,拒绝=本轮跳过(同一边界不再提示),30-40 秒无操作视为挂机自动接续;接续流程为先终止旧会话当前回合(未完成的回答会被打断)→ 让旧 Agent 刷新白板 PLAN 与交接账本并等它真正执行完 → 再建新会话(沿用工作区/模型/思考档位)并注入分层交接材料;触发后默认 30 分钟内不重复。', autoContOn: '自动接续:开', autoContOff: '自动接续:关', autoContThreshold: '阈值', autoContCountdown: '⏳ {s} 秒后自动接续…', autoContCancel: '取消', autoContConfirm: '本会话上下文已用 {p}%(按官方声明窗口计,官方约 80% 才压缩)。现在接续到新会话?点「同意接续」会先终止旧会话当前回合(未完成的回答会被打断),再让旧会话刷新白板与账本,然后建新会话。', autoContAgree: '同意接续', autoContReject: '拒绝', autoContTimeout: '{s} 秒后自动接续(视为挂机)', autoContRejected: '已跳过本次接续(下一个轮次边界再提醒)',
|
|
458
458
|
continueCardTitle: '一键接续', continueCardHint: '写好交接账本→创建新会话→把交接材料作为首条消息预载,并自动切换到新会话继续任务(接续材料含白板节选+最新账本;当前工作区无账本时自动取全局最近一篇)。', continueBtn: '一键接续到新会话', continueBusy: '处理中…',
|
|
459
459
|
fStyle: '反思风格', fStyleHint: '生活化 / 专业性 / 由内容决定。',
|
|
@@ -717,7 +717,7 @@ window.__ModuleLoader__.load({
|
|
|
717
717
|
fConsScheduleDays: 'Consolidation lookback (days)', fConsScheduleDaysHint: 'How many recent days of logs the scheduled consolidation reads; default 7.',
|
|
718
718
|
fMaintSchedule: 'Scheduled 30-day distill', fMaintScheduleHint: 'Once a day at the set time, distill & archive logs older than 30 days (same flow as memory_maintain); zero cost when nothing to archive.',
|
|
719
719
|
fMaintScheduleTime: 'Distill time', fMaintScheduleTimeHint: 'HH:MM, default 10:00 (staggered from the consolidation time).',
|
|
720
|
-
waterCardTitle: 'Context water level', waterAutoSrc: 'auto-detected', waterManualSrc: 'manual', waterOfficialSrc: 'route capacity', waterFallbackSrc: 'fallback default', waterNotMeasured: 'Not measured for this session yet (updates after the next turn)', waterMeterOfficial: 'official meter', waterMeterHeuristic: 'heuristic fallback', waterThresholdHint: 'Past {t} of the window, the handoff advisory is injected and the skeleton ledger is backfilled; metering prefers the official token-meter (same source as the chat context ring), falling back to the heuristic when unavailable; the window prefers the official route capacity (request/context contextWindow), then the active model in settings.yaml, or your manual override below; the trigger denominator is the official declared window (min with any provider-reported hard limit) — the reserved output budget is not deducted.',
|
|
720
|
+
waterCardTitle: 'Context water level', waterAutoSrc: 'auto-detected', waterManualSrc: 'manual', waterOfficialSrc: 'route capacity', waterFallbackSrc: 'fallback default', waterNotMeasured: 'Not measured for this session yet (updates after the next turn)', waterWindowUnknown: 'Context window size unknown', waterWindowUnknownHint: 'Could not read the model window from this session (its record may have been archived or recycled). Send a turn to detect it, or set the water-level window manually below.', waterMeterOfficial: 'official meter', waterMeterHeuristic: 'heuristic fallback', waterThresholdHint: 'Past {t} of the window, the handoff advisory is injected and the skeleton ledger is backfilled; metering prefers the official token-meter (same source as the chat context ring), falling back to the heuristic when unavailable; the window prefers the official route capacity (request/context contextWindow), then the active model in settings.yaml, or your manual override below; the trigger denominator is the official declared window (min with any provider-reported hard limit) — the reserved output budget is not deducted.',
|
|
721
721
|
autoContTitle: 'Auto continue', autoContHint: 'When on: a confirm card appears once the water level passes the threshold — Agree = continue now, Decline = skip this boundary (no re-prompt for it), no answer for 30-40s = unattended, continue automatically. The handoff sequence is: stop the old turn first (an unfinished answer will be interrupted) → have the old agent refresh PLAN + ledger and wait until it has really run → then create the new session, which keeps workspace/model/reasoning effort and receives layered handoff material; suppressed for 30 minutes after each run.', autoContOn: 'Auto continue: on', autoContOff: 'Auto continue: off', autoContThreshold: 'Threshold', autoContCountdown: '⏳ Auto-continue in {s}s…', autoContCancel: 'Cancel', autoContConfirm: 'Context at {p}% of the official declared window (official compaction starts ~80%). Continue into a new session now? Agreeing stops the old turn first (an unfinished answer will be interrupted), then the old agent refreshes PLAN + ledger before the new session is created.', autoContAgree: 'Continue', autoContReject: 'Decline', autoContTimeout: 'auto-continue in {s}s (unattended)', autoContRejected: 'Skipped this handoff (will ask again at the next turn boundary)',
|
|
722
722
|
continueCardTitle: 'One-click continue', continueCardHint: 'Writes the handoff ledger → creates a new session → injects the handoff material as the first message → switches to the new session automatically (material = PLAN excerpt + latest ledger; falls back to the most recent global ledger when this workspace has none).', continueBtn: 'Continue in a new session', continueBusy: 'Working…',
|
|
723
723
|
fStyle: 'Reflection style', fStyleHint: 'Life-style / Professional / Auto.',
|
|
@@ -860,7 +860,18 @@ window.__ModuleLoader__.load({
|
|
|
860
860
|
|
|
861
861
|
// ───────────────────────── 更新弹窗 / 首次指导 ─────────────────────────
|
|
862
862
|
var CHANGELOG = {
|
|
863
|
-
|
|
863
|
+
'3.1.7': { zh: [
|
|
864
|
+
'★ 修复:一键接续有时会把「最近活跃会话」认成一个后台子代理,导致新会话建到错误的工作区,而且不报错。现在会正确识别源会话;真的定位不到时会明确告诉你,不再静默。',
|
|
865
|
+
'★ 修复:写白板时锚点标记的空格被吃掉,导致卡片识别失败。现在锚点行会被原样保留。',
|
|
866
|
+
'★ 修复:面板里不显示水位。以前读不到窗口大小时整张卡会直接消失(连「尚未测量」提示也被一起藏住);现在会照常显示,并说明原因与恢复方法。',
|
|
867
|
+
'本版三处修复都在宿主加载期代码里,需重启 dsh web 后生效。',
|
|
868
|
+
], en: [
|
|
869
|
+
'★ Fix: the continue handoff could mistake a background subagent for your most recent session and create the new session in the wrong workspace — silently. It now resolves the correct source session, and tells you when it genuinely cannot.',
|
|
870
|
+
'★ Fix: the anchor marker lost its space when writing the board, breaking card recognition. Anchor lines are now preserved verbatim.',
|
|
871
|
+
'★ Fix: the context water level was not shown. When the window size was unknown the whole card disappeared (hiding the "not measured yet" note with it); it now renders and explains the cause and the fix.',
|
|
872
|
+
'All three fixes live in host-side code — restart dsh web for them to take effect.',
|
|
873
|
+
] },
|
|
874
|
+
'3.1.6': { zh: [
|
|
864
875
|
'★★ 紧急修复:配置迁移自 3.1.0 起从未真正执行——宿主每次加载都因内部变量作用域错误抛异常,而异常被兜底逻辑静默吞掉。结果是"你保存过的设置会变回默认值"(注入预算/记忆根目录/看板模式/容量上限等),且日志里没有任何报错,极易被误判成"自己没配"。',
|
|
865
876
|
'现在配置改名与补齐已改到唯一的加载汇聚点,并强制走损坏隔离读。升级后你此前的设置会从旧配置文件自动补回来,无需手动重配。',
|
|
866
877
|
'重要:本版修的是宿主加载期代码,**必须重启 dsh web 才生效**。',
|
|
@@ -4414,6 +4425,14 @@ var ROUTE_PREFIX = '/api/dsh-auto-memory'
|
|
|
4414
4425
|
var fromSidForCarry = String(lastRefreshSessionId || currentSessionIdClient() || '')
|
|
4415
4426
|
var d = await apiPost(API.handoffContinue, fromSidForCarry ? { fromSessionId: fromSidForCarry } : {})
|
|
4416
4427
|
if (!d || !d.ok) throw new Error((d && d.error) || 'no handoff material')
|
|
4428
|
+
// ★2026-09-23:源会话工作区无法定位时,宿主会回退到「当前工作区」并把 wsFallback 置真。
|
|
4429
|
+
// 这正是「接续落到错误工作区」的现场(旧会话文件被 GC 搬走时)。不再静默:明确告知用户
|
|
4430
|
+
// 新会话建在哪个工作区、以及为什么 —— 用户据此可判断要不要手动切回正确工作区。
|
|
4431
|
+
if (d.wsFallback) {
|
|
4432
|
+
onMsg(locale === 'zh'
|
|
4433
|
+
? '⚠ 无法定位源会话的工作区(旧会话记录可能已被子代理回收搬走),新会话将建在当前工作区:' + (d.ws || '(未知)')
|
|
4434
|
+
: '⚠ Could not locate the source session\'s workspace (its record may have been GC\'d). The new session will use the current workspace: ' + (d.ws || '(unknown)'))
|
|
4435
|
+
}
|
|
4417
4436
|
var newId = await executeContinue(d, onMsg)
|
|
4418
4437
|
try { localStorage.setItem('dsh-auto-memory.autoCont.last', String(Date.now())) } catch (eL) {}
|
|
4419
4438
|
return { ok: true, sessionId: newId, workspaceId: d.workspaceId || '', model: d.model || '', reasoningEffort: d.reasoningEffort || '' }
|
|
@@ -4627,22 +4646,28 @@ var ROUTE_PREFIX = '/api/dsh-auto-memory'
|
|
|
4627
4646
|
})
|
|
4628
4647
|
if (secKids.length) rows.push(h(Card, { title: locale === 'zh' ? '新版看板 · 段视图' : 'Graph board · sections' }, h('div', { 'data-dam-content': '' }, secKids)))
|
|
4629
4648
|
}
|
|
4630
|
-
|
|
4649
|
+
// ★2026-09-23 修复「目录里不显示水位」:旧条件 `wl.window > 0` 使**未测出窗口时整张卡消失**,
|
|
4650
|
+
// 连本节下方那句「本会话尚未测量」提示都不可达(它原本就在同一门内)⇒ 用户只看到"没有水位"
|
|
4651
|
+
// 而得不到任何解释。现改为:只要有 waterLevel 对象就渲染,未测出窗口时如实说明原因与下一步。
|
|
4652
|
+
if (wl && (wl.window > 0 || wl.at || Object.keys(wl).length)) {
|
|
4631
4653
|
// 2.2.4:百分比显示真实水位(旧版截断到 150% 会把「窗口算错」伪装成「刚好超一点」);进度条宽度仍按 100% 截断。
|
|
4632
4654
|
var rawPct = Math.round((wl.ratio || 0) * 100)
|
|
4633
4655
|
var pct = Math.min(rawPct, 999)
|
|
4656
|
+
var hasWin = (wl.window || 0) > 0
|
|
4634
4657
|
var barColor = rawPct >= 80 ? '#d4744f' : rawPct >= 50 ? '#d4a94f' : 'var(--dam-accent, #4f7cff)'
|
|
4635
4658
|
var srcLabel = !wl.source ? '' : wl.source.indexOf('auto') === 0 ? t('waterAutoSrc') + ': ' + wl.source.slice(5) : (wl.source === 'official-context' ? t('waterOfficialSrc') : (wl.source === 'fallback' ? t('waterFallbackSrc') : t('waterManualSrc')))
|
|
4636
4659
|
rows.unshift(h(Card, { title: t('waterCardTitle') }, h('div', { 'data-dam-content': '' }, [
|
|
4637
4660
|
h('div', { style: { fontSize: 'calc(12px * var(--dam-scale))', marginBottom: '6px' } },
|
|
4638
|
-
(wl.tokens || 0).toLocaleString() + ' / ' + wl.window.toLocaleString() + ' token · ' + pct + '%',
|
|
4661
|
+
hasWin ? ((wl.tokens || 0).toLocaleString() + ' / ' + wl.window.toLocaleString() + ' token · ' + pct + '%') : t('waterWindowUnknown'),
|
|
4639
4662
|
' ', h('span', { 'data-dam-hint': '' }, srcLabel),
|
|
4640
4663
|
' ', h('span', { 'data-dam-hint': '' }, wl.meter ? (wl.meter.indexOf('official') === 0 ? '· ' + t('waterMeterOfficial') + '(' + String(wl.meter).slice(9) + ')' : '· ' + t('waterMeterHeuristic')) : '')),
|
|
4641
4664
|
// 本会话尚未测量(刚重启/刚切过来还没发消息):明说,而不是显示成 0 让人以为上下文是空的
|
|
4642
4665
|
wl.at ? null : h('div', { 'data-dam-hint': '', style: { marginBottom: '4px' } }, t('waterNotMeasured')),
|
|
4643
|
-
h('div', { style: { height: '6px', borderRadius: '3px', background: 'color-mix(in srgb, currentColor 12%, transparent)', overflow: 'hidden' } },
|
|
4644
|
-
h('div', { style: { height: '100%', width: Math.min(pct, 100) + '%', background: barColor, borderRadius: '3px' } })),
|
|
4645
|
-
h('div', { 'data-dam-hint': '', style: { marginTop: '4px' } },
|
|
4666
|
+
hasWin ? h('div', { style: { height: '6px', borderRadius: '3px', background: 'color-mix(in srgb, currentColor 12%, transparent)', overflow: 'hidden' } },
|
|
4667
|
+
h('div', { style: { height: '100%', width: Math.min(pct, 100) + '%', background: barColor, borderRadius: '3px' } })) : null,
|
|
4668
|
+
h('div', { 'data-dam-hint': '', style: { marginTop: '4px' } }, hasWin
|
|
4669
|
+
? t('waterThresholdHint').replace('{t}', String(wl.threshold || 0.75))
|
|
4670
|
+
: t('waterWindowUnknownHint')),
|
|
4646
4671
|
])))
|
|
4647
4672
|
}
|
|
4648
4673
|
if (autoCfg && wl.window > 0) {
|
package/lib/index.js
CHANGED
|
@@ -4140,11 +4140,17 @@ this._readError = undefined
|
|
|
4140
4140
|
return tokens
|
|
4141
4141
|
}
|
|
4142
4142
|
|
|
4143
|
-
/** 当前(最近活跃)会话 id;无 agent 时返回 ''。
|
|
4143
|
+
/** 当前(最近活跃)会话 id;无 agent 时返回 ''。
|
|
4144
|
+
* ★2026-09-23 修 bug:子代理**不得**作为「最近活跃会话」。本插件自动沉淀 spawn 的子代理若被
|
|
4145
|
+
* 当成当前会话,刷新仪式会把指令注进子代理(等于白做,注释 L4156 已述),而「一键接续」会拿
|
|
4146
|
+
* 它当源 —— 该会话目录随即被 subagentGcSweep 搬走 ⇒ 材料构造失败后回退到错误工作区。
|
|
4147
|
+
* 判据统一 isSubAgentSession()(看 origin/delegationDepth,不看 parentSession)。 */
|
|
4144
4148
|
currentSessionId() {
|
|
4145
4149
|
try {
|
|
4146
4150
|
const agent = this._lastAgent
|
|
4147
|
-
|
|
4151
|
+
if (!agent || !agent.session) return ''
|
|
4152
|
+
if (isSubAgentSession(agent)) return ''
|
|
4153
|
+
return agent.session.id ? String(agent.session.id) : ''
|
|
4148
4154
|
} catch (e) { return '' }
|
|
4149
4155
|
}
|
|
4150
4156
|
|
|
@@ -4555,7 +4561,18 @@ this._readError = undefined
|
|
|
4555
4561
|
const staleNote = planMt && ledgerMt && planMt < ledgerMt ? '(白板比账本旧——以账本为准)' : ''
|
|
4556
4562
|
// 修A(2026-09-08):解析旧会话所属工作区,交给 client 用 workspaceId 建新会话(官方只有 workspaceId 分支会 attachSession)。
|
|
4557
4563
|
const prevSid = (pack && pack.sessionId) || this.currentSessionId()
|
|
4558
|
-
|
|
4564
|
+
// ★2026-09-23 修 bug(静默落到错误工作区):pack 有两种「无 cwd」形态 ——
|
|
4565
|
+
// ①取不到持久化文件时只返回 { sessionId }(见 buildPrevSessionPack 的 `if (!file) return { sessionId: sid }`);
|
|
4566
|
+
// ②会话文件已被 subagentGcSweep 搬走时返回 null。
|
|
4567
|
+
// 此时直接 `|| p.ws` 会把新会话建到**插件此刻的当前工作区**,而不是源会话的工作区。
|
|
4568
|
+
// 改为按源会话 id 反查真实工作区;真查不到才退回 p.ws,并留可见痕迹(diag + wsFallback),不再静默。
|
|
4569
|
+
let wsForSession = (pack && pack.cwd) || ''
|
|
4570
|
+
let wsFallback = false
|
|
4571
|
+
if (!wsForSession && prevSid) {
|
|
4572
|
+
try { wsForSession = (await this.sessionWorkspaceFallback(prevSid)) || '' } catch (eWs) {}
|
|
4573
|
+
}
|
|
4574
|
+
if (!wsForSession) { wsForSession = p.ws; wsFallback = !!prevSid }
|
|
4575
|
+
if (wsFallback) diag('continue carry: 源会话工作区无法定位(sid=' + prevSid + ' pack=' + (pack ? 'partial' : 'null') + '),已回退当前工作区 ' + (p.ws || '(空)'))
|
|
4559
4576
|
const workspaceId = this.resolveWorkspaceIdForSession(prevSid, wsForSession)
|
|
4560
4577
|
// ★2026-09-17(L3): 材料改为**三段**结构, 由 assembleCarryPre 组装 ——
|
|
4561
4578
|
// headParts(指令, 固定) / navParts(导航区, **永不截断**) / bulkParts(正文层, 可截断)。
|
|
@@ -4677,6 +4694,7 @@ this._readError = undefined
|
|
|
4677
4694
|
reasoningEffort: (pack && pack.reasoningEffort) || '',
|
|
4678
4695
|
agentPreset: (pack && pack.agentPreset) || '',
|
|
4679
4696
|
wsBase: wsBase,
|
|
4697
|
+
wsFallback: wsFallback,
|
|
4680
4698
|
contSeq: contSeq,
|
|
4681
4699
|
prevSessionId: (pack && pack.sessionId) || '',
|
|
4682
4700
|
transcriptPath: (pack && pack.transcriptPath) || '',
|
|
@@ -9599,9 +9617,25 @@ function sanitizeForWrite(text, opts) {
|
|
|
9599
9617
|
|
|
9600
9618
|
/** M3b-4 保留语法卫生:字面 '<!-- memory:' 与记忆 anchor 保留语法冲突(写入后 parseAnchors 判 orphan-content,
|
|
9601
9619
|
* 开启 memoryAnchorEnabled 时文件锁死不可写)。写入前统一改写为豁免形式 '<!--memory:'(冒号后无空格),
|
|
9602
|
-
* 不构成保留子串,内容语义不变(文档/示例可读)。
|
|
9620
|
+
* 不构成保留子串,内容语义不变(文档/示例可读)。
|
|
9621
|
+
*
|
|
9622
|
+
* ★2026-09-23 修复「锚点空格被吃掉」:**整行就是合法锚点行时必须原样保留**。
|
|
9623
|
+
* 旧实现无差别全局替换,把模型按提示抄回的锚点行 `<!-- memory:mem_<32hex> -->` 也改写成
|
|
9624
|
+
* `<!--memory:...`;而 memory-anchor.js 的 MARKER_RE 写死了空格(/^<!-- memory:…/,无 \s*),
|
|
9625
|
+
* 于是该行不再被认作锚点 ⇒ parseAnchors 把整篇降级成 legacy 块(实测:实测白板 18/20 个锚点
|
|
9626
|
+
* 都已是紧凑形态,锚点条目数为 0),白板卡片识别随之失败。**注意另 6 条锚点正则带 \s* 都容缺,**
|
|
9627
|
+
* 所以症状只出现在走严格 MARKER_RE 的通路上 —— 不能靠"正则放宽"兜,两端口径必须一致。
|
|
9628
|
+
* 判据:仅当整行 trim 后恰好是合法锚点行时豁免,其余照旧改写(保留防 orphan-content 的原意)。
|
|
9629
|
+
* 兼容两种 id 形态:白板/工具生成的 `mem_<32hex>`,以及契约里任何 `mem_` 开头的十六进制 id。 */
|
|
9630
|
+
const ANCHOR_LINE_SAFE_RE = /^<!--\s*memory:(mem_[0-9a-f]{32})\s*-->$/
|
|
9603
9631
|
function sanitizeReservedSyntax(text) {
|
|
9604
|
-
|
|
9632
|
+
const s = String(text || '')
|
|
9633
|
+
if (!s.includes('<!-- memory:')) return s
|
|
9634
|
+
return s.split(/\r?\n/).map((line) => {
|
|
9635
|
+
// 整行合法锚点 ⇒ 原样保留(改写它会打穿 MARKER_RE 的严格口径)
|
|
9636
|
+
if (ANCHOR_LINE_SAFE_RE.test(line.replace(/\r$/, '').trim())) return line
|
|
9637
|
+
return line.replace(/<!-- memory:/g, '<!--memory:')
|
|
9638
|
+
}).join(s.includes('\r\n') ? '\r\n' : '\n')
|
|
9605
9639
|
}
|
|
9606
9640
|
/** 写闸门拦截原因 → 中文说明(供三个写入工具返回信息)。 */
|
|
9607
9641
|
var WRITE_GATE_REASON = { empty: '内容为空', mojibake: '疑似乱码/错误编码往返', stutter: '疑似复读退化', 'duplicate-lines': '疑似重复内容块', 'raw-json': '疑似外部画像 raw JSON envelope', base64: '疑似 base64 编码残骸行' }
|
|
@@ -10773,7 +10807,12 @@ engine._recallStats = createRecallStatsPre({
|
|
|
10773
10807
|
try {
|
|
10774
10808
|
if (payload && payload.agent && engine.hasReliableSessionIdentity(payload.agent)) {
|
|
10775
10809
|
const rt = engine.runtimeFor(payload.agent)
|
|
10776
|
-
|
|
10810
|
+
// ★2026-09-23 修 bug(接续源被污染):本插件每轮自动沉淀会 spawn 子代理,其 session-start
|
|
10811
|
+
// 同样走到这里。**不带子代理判据就会让子代理一启动就顶掉 _lastAgent** —— 而该子代理
|
|
10812
|
+
// 会话很快被 subagentGcSweep 搬进 subagent-gc-backup,于是「一键接续」取不到转写文件、
|
|
10813
|
+
// 静默回退到插件当前工作区,新会话被建到错误工作区(用户实测)。
|
|
10814
|
+
// 判据与 restoreLastAgent(见 L8194)及本文件既有纪律(L4165)一致:isSubAgentSession()。
|
|
10815
|
+
if (!isSubAgentSession(payload.agent)) engine._lastAgent = payload.agent
|
|
10777
10816
|
// M2.4: 对已存在的 session.events 按原生 seq 补放(live feed 之后从游标续接去重)
|
|
10778
10817
|
engine.seedRuntimeFromSession(payload.agent)
|
|
10779
10818
|
engine.ingestAgentLifecycle(payload.agent, 'agent/session-start', { payload: { source: boundedStr(payload && payload.source) } })
|
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": "3.1.
|
|
4
|
+
"version": "3.1.7",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"exports": {
|