@a9i5k4/dsh-auto-memory 2.4.0 → 2.4.2
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/UI-INVENTORY-RAW.md +200 -0
- package/docs/UI-REFACTOR-PRE-RESEARCH.md +222 -0
- package/docs/internal/ART-DIRECTION-WIREFRAME.md +181 -0
- package/docs/internal/CUA-VISION-FIX-NOTES.md +78 -0
- package/docs/internal/DESIGN-OVERHAUL-PRE-RESEARCH.md +292 -0
- package/docs/internal/NEXT-VERSION-TODO.md +67 -0
- package/docs/internal/OFFICIAL-DISCUSSION-DRAFT.md +80 -0
- package/docs/internal/RELEASE-PROCESS.md +83 -0
- package/docs/internal/SUBAGENT-REPORT-ROUTING-PRE-RESEARCH.md +261 -0
- package/lib/client.js +163 -84
- package/lib/index.js +104 -8
- 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
|
|
10
|
+
console.log('[dsh-auto-memory] client v2.4.2 fingerprint: drawer-summary-ok')
|
|
11
11
|
window.__ModuleLoader__.load({
|
|
12
12
|
id: '@a9i5k4/dsh-auto-memory',
|
|
13
13
|
factory: (require) => {
|
|
@@ -284,7 +284,7 @@ window.__ModuleLoader__.load({
|
|
|
284
284
|
fMaintSchedule: '定时 30 天蒸馏', fMaintScheduleHint: '每天到点自动把超过 30 天的旧日志蒸馏归档(等价 memory_maintain);无旧日志时零成本跳过。',
|
|
285
285
|
fMaintScheduleTime: '蒸馏触发时间', fMaintScheduleTimeHint: 'HH:MM,默认 10:00(与固化时间错开)。',
|
|
286
286
|
waterCardTitle: '上下文水位', waterAutoSrc: '自动检测', waterManualSrc: '手动设定', waterOfficialSrc: '官方路由容量', waterFallbackSrc: '回退默认值', waterNotMeasured: '本会话尚未测量(发送一轮后自动更新)', waterMeterOfficial: '官方计量', waterMeterHeuristic: '启发式降级', waterThresholdHint: '水位达到 {t} 时注入交接建议并自动补写账本;计量优先用官方 token-meter(与聊天框 context ring 同源),不可用时降级启发式估算;窗口优先取官方路由容量(request/context 的 contextWindow),其次按当前模型查 settings.yaml contextWindow,也可在下方手动覆盖。',
|
|
287
|
-
autoContTitle: '自动接续', autoContHint: '开启后:水位达到阈值、且旧会话已空闲(harness 权威 running 位在轮次边界转为 false,取代旧「两轮水位持平」)时,弹出确认卡——同意=立即接续,拒绝=本轮跳过(同一边界不再提示),30-40 秒无操作视为挂机自动接续;接续前先让旧 Agent 刷新白板 PLAN 与交接账本,再建新会话(沿用工作区/模型/思考档位)并注入分层交接材料;触发后默认 30 分钟内不重复。', autoContOn: '自动接续:开', autoContOff: '自动接续:关', autoContThreshold: '阈值', autoContCountdown: '⏳ {s} 秒后自动接续…', autoContCancel: '取消', autoContConfirm: '
|
|
287
|
+
autoContTitle: '自动接续', autoContHint: '开启后:水位达到阈值、且旧会话已空闲(harness 权威 running 位在轮次边界转为 false,取代旧「两轮水位持平」)时,弹出确认卡——同意=立即接续,拒绝=本轮跳过(同一边界不再提示),30-40 秒无操作视为挂机自动接续;接续前先让旧 Agent 刷新白板 PLAN 与交接账本,再建新会话(沿用工作区/模型/思考档位)并注入分层交接材料;触发后默认 30 分钟内不重复。', autoContOn: '自动接续:开', autoContOff: '自动接续:关', autoContThreshold: '阈值', autoContCountdown: '⏳ {s} 秒后自动接续…', autoContCancel: '取消', autoContConfirm: '本会话「可用额度」已用 {p}%(小圈读数见下行),旧会话已空闲(轮次边界)——现在接续到新会话?', autoContAgree: '同意接续', autoContReject: '拒绝', autoContTimeout: '{s} 秒后自动接续(视为挂机)', autoContRejected: '已跳过本次接续(下一个轮次边界再提醒)',
|
|
288
288
|
continueCardTitle: '一键接续', continueCardHint: '写好交接账本→创建新会话→把交接材料作为首条消息预载,并自动切换到新会话继续任务(接续材料含白板节选+最新账本;当前工作区无账本时自动取全局最近一篇)。', continueBtn: '一键接续到新会话', continueBusy: '处理中…',
|
|
289
289
|
fStyle: '反思风格', fStyleHint: '生活化 / 专业性 / 由内容决定。',
|
|
290
290
|
fLocale: '界面语言', fLocaleHint: '默认跟随 DSH 系统语言;也可手动指定中文 / English。', followSystem: '跟随系统语言',
|
|
@@ -567,6 +567,37 @@ window.__ModuleLoader__.load({
|
|
|
567
567
|
|
|
568
568
|
// ───────────────────────── 更新弹窗 / 首次指导 ─────────────────────────
|
|
569
569
|
var CHANGELOG = {
|
|
570
|
+
'2.4.2': { zh: [
|
|
571
|
+
'变更:自动接续改为**出厂默认关闭**(老用户已落盘的显式设置不受影响)。原因:现有水位判据把「预留输出」从分母里扣掉,实测在上下文刚过半时就触发接续,与官方「约 80% 才压缩」相差约 45%,对不熟悉的人就是白烧 token —— 先默认关,等判据改成与官方压缩同一坐标系后再评估翻回。',
|
|
572
|
+
'想用的人可以在「白板 / 接续」页签里手动打开(开关一直没动,只是默认值变了)。',
|
|
573
|
+
], en: [
|
|
574
|
+
'Change: auto-continue is now OFF by default (explicit values already saved by existing users are unaffected). Reason: the current water-level ratio subtracts the route\'s reserved output from the denominator, so it fired when the context was barely half full — about 45% earlier than the official compaction point (~80%). Defaulting off stops burning tokens until the ratio is aligned with the official coordinate system.',
|
|
575
|
+
'You can still turn it on yourself from the Whiteboard / Handoff tab; only the default changed.',
|
|
576
|
+
] },
|
|
577
|
+
'2.4.1': { zh: [
|
|
578
|
+
'修复:已接续过的会话会被再次触发接续(重复建窗口)。新增「已接续闩锁」并落盘(session id 为键、重启有效):宿主路径在交接材料投料成功后落闩,浏览器路径在新会话真建成后落闩,失败不落闩以便重试。',
|
|
579
|
+
'修复:宿主已完成接续后,确认卡仍停在「同意接续/拒绝」并与「已自动接续到新会话」并存;arm 状态改为按会话过滤,不再在其他窗口弹「本会话水位已达」。',
|
|
580
|
+
'修复:水位双口径那一行(本会话水位 / 官方小圈读数 / 距硬墙余量)此前因漏拷贝 ring/wall 永不渲染。',
|
|
581
|
+
'内部:界面路径表引入唯一前缀常量与一致性锁(客户端路径必须都在宿主表内、表外零裸字面量);写配置 4 条路径收敛为一个出口;10 处重复的语义状态刷新收敛为一处。',
|
|
582
|
+
'流程:自本版起,发版必须同步 CHANGELOG、应用内更新说明与版本标识(release.mjs 已加校验,缺一即拒绝构建)。',
|
|
583
|
+
], en: [
|
|
584
|
+
'Fix: a session that had already been continued could be armed again, creating duplicate windows. A persisted continuation latch (keyed by session id, survives restarts) is now written only after the handoff material is actually delivered (host path) or the new session really exists (browser path); failures do not latch, so retries still work.',
|
|
585
|
+
'Fix: after the host finished continuing, the confirm card still sat on Agree/Decline next to "continued into a new session"; arming is now filtered per session, so other windows no longer show "this session reached the water level".',
|
|
586
|
+
'Fix: the dual-scale water line (session level / official ring reading / distance to the hard wall) never rendered because ring/wall were not copied through.',
|
|
587
|
+
'Internal: one prefix constant plus a consistency lock for the panel route table, a single config-write exit, and one shared semantic-status refresh replacing ten copies.',
|
|
588
|
+
'Process: from this version on, releases must update CHANGELOG, in-app release notes and version identifiers together (release.mjs now refuses to build otherwise).',
|
|
589
|
+
] },
|
|
590
|
+
'2.4.0': { zh: [
|
|
591
|
+
'★ 水位判据按「该会话当前模型」的预留额度自适应:消息可用上限 = 窗口 − 该路由的 maxTokens(实测 384,000 预留 → 可用 616,000),阈值随之落在健康区间;压缩与 CONTEXT_WINDOW_EXCEEDED 升为硬触发。',
|
|
592
|
+
'★ 修复自动接续静默失效:DSH 0.1.5 起 requestId 必填,宿主兜底接续漏传该字段时官方抛错被包装成误导性的 "prompt rejected" —— 现象是新会话建出来了、交接材料却从未送达。',
|
|
593
|
+
'★ 记忆容量口径重做:改为「文件容量上限」(noteCapacityChars / userCapacityChars,默认各 12000 字符),先 AI 折叠成要点、失败退回整条归档,写入不再被堵死。',
|
|
594
|
+
'★ Python 语义引擎贯通:安装向导写入正确的工作目录、模型落位与 onnxFile 契约对齐、向导常驻设置页,装了就能用。',
|
|
595
|
+
], en: [
|
|
596
|
+
'★ Water-level judgement now adapts to the reserve of the session\'s current route: usable budget = window − that route\'s maxTokens (measured 384,000 reserve → 616,000 usable), so thresholds land in a healthy range; compaction and CONTEXT_WINDOW_EXCEEDED became hard triggers.',
|
|
597
|
+
'★ Fixed silent auto-continue failure: since DSH 0.1.5 requestId is required, and the host fallback omitted it, so the official error surfaced as a misleading "prompt rejected" — the new session existed but the handoff material never arrived.',
|
|
598
|
+
'★ Memory capacity reworked around per-file character caps (noteCapacityChars / userCapacityChars, 12000 each): fold into key points first, fall back to whole-entry archiving, so writes are never blocked.',
|
|
599
|
+
'★ Python semantic engine works end to end: the wizard writes the right working directory, model placement and onnxFile contracts line up, and the wizard is always reachable from Settings.',
|
|
600
|
+
] },
|
|
570
601
|
'2.3.0': { zh: [
|
|
571
602
|
'★ 检索改为分层语义唤回:memory_recall 先返回 L0 摘要列表(每条记忆约 90 字,含 id/得分/匹配原因),需要原文再按 id 展开——此前全文直接进 embedding,超过模型 512 token 上限的条目尾部全部丢失(实测最长一条 9,822 字符);现在摘要永远在限制内,长记忆不再有损。',
|
|
572
603
|
'★ 检索融合改为三臂 rank-space:词法 + 语义 + 时间 三路各自排名后按 RRF(k=60)融合,取代旧的 minmax 归一化加权——旧方案分数只反映「这一批里排第几」,候选集一换分数就漂移、候选只剩 1 条时退化为常数,而「要不要注入」的决策恰恰依赖分数与阈值比较。现在排序与决策解耦,分数不再说谎。',
|
|
@@ -855,45 +886,56 @@ window.__ModuleLoader__.load({
|
|
|
855
886
|
}
|
|
856
887
|
|
|
857
888
|
// ───────────────────────── API ─────────────────────────
|
|
889
|
+
// 路由前缀唯一常量(2026-09-10 第 0 步止血):表中每条路径由它拼出,不再逐个重复全串。
|
|
890
|
+
var ROUTE_PREFIX = '/api/dsh-auto-memory'
|
|
891
|
+
// 宿主半边(lib/index.js 的 API)声明的是同一批路径 —— 两个半边是独立 bundle(本文件经 __ModuleLoader__ 手写加载,
|
|
892
|
+
// 无法 import 宿主模块),所以"只有一份"这件事由 tests/smoke/ 下的路径表一致性锁保证:
|
|
893
|
+
// 客户端路径 ⊆ 宿主路径、宿主独有路径只允许显式白名单、本文件表外不得再出现裸路径字面量。
|
|
858
894
|
var API = {
|
|
859
|
-
state: '/
|
|
860
|
-
list: '/
|
|
861
|
-
file: '/
|
|
862
|
-
recall: '/
|
|
863
|
-
smartRecall: '/
|
|
864
|
-
workspaces: '/
|
|
865
|
-
debug: '/
|
|
866
|
-
scanDirty: '/
|
|
867
|
-
browseDir: '/
|
|
868
|
-
pickDir: '/
|
|
869
|
-
updateCheck: '/
|
|
870
|
-
update: '/
|
|
871
|
-
config: '/
|
|
872
|
-
reflect: '/
|
|
873
|
-
reflectAuto: '/
|
|
874
|
-
note: '/
|
|
875
|
-
external: '/
|
|
876
|
-
externalView: '/
|
|
877
|
-
externalRemove: '/
|
|
878
|
-
externalImport: '/
|
|
879
|
-
calendar: '/
|
|
880
|
-
summarize: '/
|
|
881
|
-
greet: '/
|
|
882
|
-
notices: '/
|
|
883
|
-
models: '/
|
|
884
|
-
semanticDeepDetect: '/
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
895
|
+
state: ROUTE_PREFIX + '/state',
|
|
896
|
+
list: ROUTE_PREFIX + '/list',
|
|
897
|
+
file: ROUTE_PREFIX + '/file',
|
|
898
|
+
recall: ROUTE_PREFIX + '/recall',
|
|
899
|
+
smartRecall: ROUTE_PREFIX + '/smart-recall',
|
|
900
|
+
workspaces: ROUTE_PREFIX + '/workspaces',
|
|
901
|
+
debug: ROUTE_PREFIX + '/debug',
|
|
902
|
+
scanDirty: ROUTE_PREFIX + '/scan-dirty',
|
|
903
|
+
browseDir: ROUTE_PREFIX + '/browse-dir',
|
|
904
|
+
pickDir: ROUTE_PREFIX + '/pick-dir',
|
|
905
|
+
updateCheck: ROUTE_PREFIX + '/update-check',
|
|
906
|
+
update: ROUTE_PREFIX + '/update',
|
|
907
|
+
config: ROUTE_PREFIX + '/config',
|
|
908
|
+
reflect: ROUTE_PREFIX + '/reflect',
|
|
909
|
+
reflectAuto: ROUTE_PREFIX + '/reflect-auto',
|
|
910
|
+
note: ROUTE_PREFIX + '/note',
|
|
911
|
+
external: ROUTE_PREFIX + '/external',
|
|
912
|
+
externalView: ROUTE_PREFIX + '/external-view',
|
|
913
|
+
externalRemove: ROUTE_PREFIX + '/external-remove',
|
|
914
|
+
externalImport: ROUTE_PREFIX + '/external-import',
|
|
915
|
+
calendar: ROUTE_PREFIX + '/calendar',
|
|
916
|
+
summarize: ROUTE_PREFIX + '/summarize',
|
|
917
|
+
greet: ROUTE_PREFIX + '/greet',
|
|
918
|
+
notices: ROUTE_PREFIX + '/notices',
|
|
919
|
+
models: ROUTE_PREFIX + '/models',
|
|
920
|
+
semanticDeepDetect: ROUTE_PREFIX + '/semantic-deep-detect',
|
|
921
|
+
semanticStatus: ROUTE_PREFIX + '/semantic-status',
|
|
922
|
+
semanticEmit: ROUTE_PREFIX + '/semantic-emit',
|
|
923
|
+
pyDetect: ROUTE_PREFIX + '/python-setup/detect',
|
|
924
|
+
pyVenv: ROUTE_PREFIX + '/python-setup/venv',
|
|
925
|
+
pyDeps: ROUTE_PREFIX + '/python-setup/deps',
|
|
926
|
+
pyModel: ROUTE_PREFIX + '/python-setup/model',
|
|
927
|
+
pyCancel: ROUTE_PREFIX + '/python-setup/cancel',
|
|
928
|
+
pyStatus: ROUTE_PREFIX + '/python-setup/status',
|
|
929
|
+
handoffState: ROUTE_PREFIX + '/handoff-state',
|
|
930
|
+
handoffContinue: ROUTE_PREFIX + '/handoff-continue',
|
|
931
|
+
handoffPermission: ROUTE_PREFIX + '/handoff-permission',
|
|
932
|
+
autoContState: ROUTE_PREFIX + '/auto-continue-state',
|
|
933
|
+
autoContDecide: ROUTE_PREFIX + '/auto-continue-decide',
|
|
934
|
+
semanticDownload: ROUTE_PREFIX + '/semantic-download',
|
|
935
|
+
shadowRecent: ROUTE_PREFIX + '/shadow-recent',
|
|
936
|
+
reviewFeedback: ROUTE_PREFIX + '/review-feedback',
|
|
937
|
+
memoryHub: ROUTE_PREFIX + '/memory-hub',
|
|
938
|
+
storageManage: ROUTE_PREFIX + '/storage-manage',
|
|
897
939
|
}
|
|
898
940
|
function query(params) {
|
|
899
941
|
var search = new URLSearchParams()
|
|
@@ -913,6 +955,28 @@ window.__ModuleLoader__.load({
|
|
|
913
955
|
if (!res.ok) throw new Error(body.error || ('POST ' + path + ' → HTTP ' + res.status))
|
|
914
956
|
return body
|
|
915
957
|
}
|
|
958
|
+
/**
|
|
959
|
+
* 写配置的唯一出口(2026-09-10 第 0 步止血):此前有 4 条互不相同的写配置路径
|
|
960
|
+
* (设置页整对象 POST / 接续页 patch / 向导两处 patch),端点、后置刷新、错误处理各写一遍。
|
|
961
|
+
* 现在全部经这里,以后加配置项或改保存行为只改这一处。
|
|
962
|
+
* hooks = { onSaved, onError } 可省;省略时静默失败(与原先的 fire-and-forget 行为一致)。
|
|
963
|
+
*/
|
|
964
|
+
function saveConfigPatch(patch, hooks) {
|
|
965
|
+
var h = hooks || {}
|
|
966
|
+
var p = apiPost(API.config, patch)
|
|
967
|
+
if (h.onSaved || h.onError) p.then(function (d) { if (h.onSaved) h.onSaved(d) }, function (e) { if (h.onError) h.onError(e) })
|
|
968
|
+
else p.catch(function () {})
|
|
969
|
+
return p
|
|
970
|
+
}
|
|
971
|
+
/**
|
|
972
|
+
* 语义引擎状态的统一刷新(2026-09-10 第 0 步止血):此前同一段 fetch + 状态写回在界面里逐字重复 10 处,
|
|
973
|
+
* 响应形状或失败兜底要改时得挨个改。apply 收合并后的状态对象(已带 loaded:true),onError 可省。
|
|
974
|
+
*/
|
|
975
|
+
function refreshSem(apply, onError) {
|
|
976
|
+
try {
|
|
977
|
+
fetch(API.semanticStatus).then(function (r) { return r.json() }).then(function (j) { if (typeof apply === 'function') apply(Object.assign({ loaded: true }, j)) }, function (e) { if (typeof onError === 'function') onError(e) }).catch(function () {})
|
|
978
|
+
} catch (_) {}
|
|
979
|
+
}
|
|
916
980
|
|
|
917
981
|
// ───────────────────────── 样式 ─────────────────────────
|
|
918
982
|
// 视觉:液态玻璃(毛玻璃)—— backdrop-filter + DSH 主题令牌(--dsw-alias-*),
|
|
@@ -1623,16 +1687,16 @@ window.__ModuleLoader__.load({
|
|
|
1623
1687
|
var setFb = fbPair[1]
|
|
1624
1688
|
useEffect(function () {
|
|
1625
1689
|
var alive = true
|
|
1626
|
-
fetch(
|
|
1690
|
+
fetch(API.shadowRecent).then(function (r) { return r.json() }).then(function (j) {
|
|
1627
1691
|
if (alive) setData((j && j.rows) || [])
|
|
1628
1692
|
}).catch(function (e) { if (alive) setErr(String(e && e.message)) })
|
|
1629
|
-
fetch(
|
|
1693
|
+
fetch(API.reviewFeedback).then(function (r) { return r.json() }).then(function (j) {
|
|
1630
1694
|
if (alive) setFb(j || {})
|
|
1631
1695
|
}).catch(function () {})
|
|
1632
1696
|
return function () { alive = false }
|
|
1633
1697
|
}, [])
|
|
1634
1698
|
function send(obsId, choice) {
|
|
1635
|
-
fetch(
|
|
1699
|
+
fetch(API.reviewFeedback, { method: 'POST',
|
|
1636
1700
|
headers: { 'Content-Type': 'application/json' },
|
|
1637
1701
|
body: JSON.stringify({ observationId: obsId, choice: choice }) })
|
|
1638
1702
|
.then(function (r) { return r.json() })
|
|
@@ -1801,7 +1865,7 @@ window.__ModuleLoader__.load({
|
|
|
1801
1865
|
var setActMsg = actMsgPair[1]
|
|
1802
1866
|
useEffect(function () {
|
|
1803
1867
|
var alive = true
|
|
1804
|
-
fetch(
|
|
1868
|
+
fetch(API.memoryHub).then(function (r) { return r.json() }).then(function (j) {
|
|
1805
1869
|
if (alive) setData(j || null)
|
|
1806
1870
|
}).catch(function (e) { if (alive) setErr(String(e && e.message)) })
|
|
1807
1871
|
return function () { alive = false }
|
|
@@ -1809,7 +1873,7 @@ window.__ModuleLoader__.load({
|
|
|
1809
1873
|
// M9 审批动作(G-02 同款 append-only 精神):晋升/激活/弃用走 /memory-hub POST,
|
|
1810
1874
|
// 后端走 store 门槛判定,不绕过任何 gate;动作后刷新 overview。
|
|
1811
1875
|
function hubAct(action, procedureId, v) {
|
|
1812
|
-
fetch(
|
|
1876
|
+
fetch(API.memoryHub, { method: 'POST',
|
|
1813
1877
|
headers: { 'Content-Type': 'application/json' },
|
|
1814
1878
|
body: JSON.stringify({ action: action, procedureId: procedureId, v: v }) })
|
|
1815
1879
|
.then(function (r) { return r.json() })
|
|
@@ -1903,7 +1967,7 @@ window.__ModuleLoader__.load({
|
|
|
1903
1967
|
var setDelFile = filePair[1]
|
|
1904
1968
|
useEffect(function () {
|
|
1905
1969
|
var alive = true
|
|
1906
|
-
fetch(
|
|
1970
|
+
fetch(API.storageManage).then(function (r) { return r.json() }).then(function (j) {
|
|
1907
1971
|
if (alive) setData(j || null)
|
|
1908
1972
|
}).catch(function (e) { if (alive) setErr(String(e && e.message)) })
|
|
1909
1973
|
return function () { alive = false }
|
|
@@ -1911,7 +1975,7 @@ window.__ModuleLoader__.load({
|
|
|
1911
1975
|
function act(action, payload, onDone) {
|
|
1912
1976
|
setMsg('')
|
|
1913
1977
|
var body = Object.assign({ action: action }, payload || {})
|
|
1914
|
-
fetch(
|
|
1978
|
+
fetch(API.storageManage, { method: 'POST',
|
|
1915
1979
|
headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) })
|
|
1916
1980
|
.then(function (r) { return r.json() })
|
|
1917
1981
|
.then(function (j) {
|
|
@@ -1921,7 +1985,7 @@ window.__ModuleLoader__.load({
|
|
|
1921
1985
|
}
|
|
1922
1986
|
setMsg(action + ': ' + reason)
|
|
1923
1987
|
if (onDone) onDone(j)
|
|
1924
|
-
var again = fetch(
|
|
1988
|
+
var again = fetch(API.storageManage).then(function (r2) { return r2.json() })
|
|
1925
1989
|
again.then(function (j2) { setData(j2 || null) })
|
|
1926
1990
|
})
|
|
1927
1991
|
.catch(function (e) { setMsg(action + ' failed: ' + String(e && e.message)) })
|
|
@@ -2177,7 +2241,7 @@ window.__ModuleLoader__.load({
|
|
|
2177
2241
|
}, [])
|
|
2178
2242
|
var autoSave = function (patch) {
|
|
2179
2243
|
setAutoCfg(function (p) { return Object.assign({}, p, patch) })
|
|
2180
|
-
|
|
2244
|
+
saveConfigPatch(patch)
|
|
2181
2245
|
}
|
|
2182
2246
|
async function oneClickContinue() {
|
|
2183
2247
|
// 一键接续 = 共用执行器(刷新仪式 → 材料 → workspaceId 建会话 → 沿用模型 → 注入),与自动接续同一路径
|
|
@@ -3089,10 +3153,10 @@ window.__ModuleLoader__.load({
|
|
|
3089
3153
|
if (!wizSt) {
|
|
3090
3154
|
wizSt = { loaded: false }
|
|
3091
3155
|
window['dsh-auto-memory.wizStatus'] = wizSt
|
|
3092
|
-
|
|
3093
|
-
Object.assign(wizSt,
|
|
3156
|
+
refreshSem(function (s) {
|
|
3157
|
+
Object.assign(wizSt, s)
|
|
3094
3158
|
try { dlgTick() } catch (e9) {}
|
|
3095
|
-
}
|
|
3159
|
+
}, function () { Object.assign(wizSt, { loaded: true, ready: false }) })
|
|
3096
3160
|
}
|
|
3097
3161
|
var tourDlPhase = wizSt && wizSt.download && wizSt.download.phase
|
|
3098
3162
|
var tourShowing = !!dialogState && (dialogState.kind === 'welcomeTour' || dialogState.kind === 'modelDownload')
|
|
@@ -3102,19 +3166,19 @@ window.__ModuleLoader__.load({
|
|
|
3102
3166
|
setTourStep(0)
|
|
3103
3167
|
wizSt.tourCfg = null
|
|
3104
3168
|
wizSt.cfgLoaded = false
|
|
3105
|
-
fetch(
|
|
3169
|
+
fetch(API.config).then(function (r) { return r.json() }).then(function (j) {
|
|
3106
3170
|
wizSt.tourCfg = (j && (j.config || j)) || {}
|
|
3107
3171
|
// mode 型开关(唤起注入模式)实际存储在 embedding-config.json(semantic-emit 接口维护),
|
|
3108
3172
|
// 需从 semantic-status 合并进来,导览开关才能显示真实当前值并与设置页联动。
|
|
3109
3173
|
try {
|
|
3110
|
-
|
|
3174
|
+
refreshSem(function (j2) {
|
|
3111
3175
|
if (j2 && j2.activationEmitMode !== undefined) { wizSt.tourCfg.activationEmitMode = j2.activationEmitMode; try { dlgTick() } catch (e13) {} }
|
|
3112
|
-
})
|
|
3176
|
+
})
|
|
3113
3177
|
} catch (_) {}
|
|
3114
3178
|
wizSt.cfgLoaded = true
|
|
3115
3179
|
try { dlgTick() } catch (e11) {}
|
|
3116
3180
|
}).catch(function () { wizSt.tourCfg = wizSt.tourCfg || {}; wizSt.cfgLoaded = true })
|
|
3117
|
-
fetch(
|
|
3181
|
+
fetch(API.external).then(function (r) { return r.json() }).then(function (j) {
|
|
3118
3182
|
wizSt.extScan = j
|
|
3119
3183
|
try { dlgTick() } catch (e12) {}
|
|
3120
3184
|
}).catch(function () {})
|
|
@@ -3124,9 +3188,9 @@ window.__ModuleLoader__.load({
|
|
|
3124
3188
|
if (!tourShowing) return undefined
|
|
3125
3189
|
if (wizSt.ready) return undefined // 已就绪无需轮询
|
|
3126
3190
|
var iv = setInterval(function () {
|
|
3127
|
-
|
|
3191
|
+
refreshSem(function (j) {
|
|
3128
3192
|
Object.assign(wizSt, j); try { dlgTick() } catch (e10) {}
|
|
3129
|
-
})
|
|
3193
|
+
})
|
|
3130
3194
|
}, 1500)
|
|
3131
3195
|
return function () { clearInterval(iv) }
|
|
3132
3196
|
}, [tourShowing, tourDlPhase, wizSt.ready])
|
|
@@ -3139,7 +3203,7 @@ window.__ModuleLoader__.load({
|
|
|
3139
3203
|
if (wizSt.ready || wizSt.loaded === false) return undefined
|
|
3140
3204
|
var ph = wizSt.download && wizSt.download.phase
|
|
3141
3205
|
if (ph === 'downloading' || ph === 'verifying' || ph === 'done' || ph === 'error') return undefined
|
|
3142
|
-
apiPost(
|
|
3206
|
+
apiPost(API.semanticDownload, { action: 'start', mirror: 'auto' }).catch(function () {})
|
|
3143
3207
|
}, [tourShowing, tourStep, wizSt.ready])
|
|
3144
3208
|
if (!dialogState) return null
|
|
3145
3209
|
// 左下角小卡片(记忆按钮上方),高透明毛玻璃,不遮全屏
|
|
@@ -3257,7 +3321,7 @@ window.__ModuleLoader__.load({
|
|
|
3257
3321
|
: t('dlDone'))
|
|
3258
3322
|
: (locale === 'zh' ? '未下载 · 词法检索照常可用' : 'Not downloaded · lexical search keeps working')
|
|
3259
3323
|
var startDl = function () {
|
|
3260
|
-
apiPost(
|
|
3324
|
+
apiPost(API.semanticDownload, { action: 'start', mirror: 'auto' }).catch(function () {})
|
|
3261
3325
|
}
|
|
3262
3326
|
// 功能开关:读当前值(带每项默认)/点击即时写配置(乐观更新+POST,失败不回滚——下次重看向导会以真实配置为准)
|
|
3263
3327
|
var EXT_SOURCE_KEYS = ['workbuddy-user', 'workbuddy-profile', 'codebuddy-memory', 'claude-global', 'project-conventions', 'workbuddy-sessions', 'claude-sessions', 'codex-sessions']
|
|
@@ -3283,9 +3347,9 @@ window.__ModuleLoader__.load({
|
|
|
3283
3347
|
// mode 型开关(唤起注入模式)走 semantic-emit 接口——与设置页同源(embedding-config.json),
|
|
3284
3348
|
// 保证导览切换与设置页双向联动;其余开关写常规 config。
|
|
3285
3349
|
if (tg.mode) {
|
|
3286
|
-
apiPost(
|
|
3350
|
+
apiPost(API.semanticEmit, { mode: patch[tg.key] }).catch(function () {})
|
|
3287
3351
|
} else {
|
|
3288
|
-
|
|
3352
|
+
saveConfigPatch(patch)
|
|
3289
3353
|
}
|
|
3290
3354
|
}
|
|
3291
3355
|
// 外部来源单源勾选(扫描行;勾选即写 externalSources 对象)
|
|
@@ -3296,7 +3360,7 @@ window.__ModuleLoader__.load({
|
|
|
3296
3360
|
cur[id] = (cur[id] !== false) ? false : true
|
|
3297
3361
|
c.externalSources = cur
|
|
3298
3362
|
dlgTick()
|
|
3299
|
-
|
|
3363
|
+
saveConfigPatch({ externalSources: cur })
|
|
3300
3364
|
}
|
|
3301
3365
|
var tourExtOn = function (id) {
|
|
3302
3366
|
var c = wizSt.tourCfg || {}
|
|
@@ -3582,8 +3646,11 @@ window.__ModuleLoader__.load({
|
|
|
3582
3646
|
useEffect(function () {
|
|
3583
3647
|
var alive = true
|
|
3584
3648
|
var load = function () {
|
|
3649
|
+
// 带上当前会话 id:宿主据此只在「属于本窗口的」armed 上弹确认卡(2026-09-10)
|
|
3650
|
+
var sidQ = ''
|
|
3651
|
+
try { sidQ = String(currentSessionIdClient() || '') } catch (eS) {}
|
|
3585
3652
|
apiGet(API.config).then(function (c) { if (alive) setAcCfg(c || {}) }).catch(function () {})
|
|
3586
|
-
apiGet(API.autoContState).then(function (s) { if (alive && s) setAcState(s) }).catch(function () {})
|
|
3653
|
+
apiGet(API.autoContState, sidQ ? { sessionId: sidQ } : {}).then(function (s) { if (alive && s) setAcState(s) }).catch(function () {})
|
|
3587
3654
|
}
|
|
3588
3655
|
load()
|
|
3589
3656
|
var iv = setInterval(load, 3000)
|
|
@@ -3598,21 +3665,34 @@ window.__ModuleLoader__.load({
|
|
|
3598
3665
|
// 触发/倒计时/超时执行全部由宿主侧驱动(2.2.6),浏览器即使被节流也不影响接续。
|
|
3599
3666
|
var poll = function () {
|
|
3600
3667
|
if (!alive) return
|
|
3601
|
-
|
|
3668
|
+
var sidQ = ''
|
|
3669
|
+
try { sidQ = String(currentSessionIdClient() || '') } catch (eS) {}
|
|
3670
|
+
apiGet(API.autoContState, sidQ ? { sessionId: sidQ } : {}).then(function (s) {
|
|
3602
3671
|
if (!alive || !s) return
|
|
3603
3672
|
setAcState(s)
|
|
3604
3673
|
if (s.executing) {
|
|
3674
|
+
// 2026-09-10:执行中/已完成时必须**收起确认卡** —— 旧实现在这两个分支直接 return,
|
|
3675
|
+
// acConfirm 保持旧值,于是宿主已接续完成后,卡片仍停在「同意接续/拒绝」上
|
|
3676
|
+
// (实测:旧窗口一直显示确认卡 + 「✓ 已自动接续到新会话」并存,像是「它还想接续」)。
|
|
3677
|
+
setAcConfirm(null)
|
|
3678
|
+
setAcCd(0)
|
|
3605
3679
|
setAcSt(locale === 'zh' ? '宿主正在自动接续:刷新白板/账本 → 建新会话 → 注入材料…' : 'Host auto-continuing: refresh PLAN/ledger → new session → inject…')
|
|
3606
3680
|
return
|
|
3607
3681
|
}
|
|
3608
|
-
|
|
3682
|
+
var okAt = Number(s.lastOk && s.lastOk.at) || 0
|
|
3683
|
+
// 10 分钟有效期(2026-09-10):宿主状态是全局单值,提示不设期限会一直挂在每个窗口上
|
|
3684
|
+
if (s.lastOk && (okAt === 0 || Date.now() - okAt < 10 * 60 * 1000)) {
|
|
3685
|
+
setAcConfirm(null)
|
|
3686
|
+
setAcCd(0)
|
|
3609
3687
|
setAcSt((locale === 'zh' ? '✓ 已自动接续到新会话,可直接上手' : '✓ Auto-continued into a new session') + (s.lastOk.model ? ' · ' + s.lastOk.model + (s.lastOk.reasoningEffort ? '/' + s.lastOk.reasoningEffort : '') : ''))
|
|
3610
3688
|
return
|
|
3611
3689
|
}
|
|
3612
3690
|
if (s.error) { setAcSt('✗ ' + s.error); return }
|
|
3613
3691
|
var arm = s.armed
|
|
3614
3692
|
if (arm && arm.ratio >= thr) {
|
|
3615
|
-
|
|
3693
|
+
// ring/wall 必须一起带进来:卡片读的是 acConfirm.ring / acConfirm.wall,
|
|
3694
|
+
// 宿主透出了但这里不拷 → 双口径那行永远不渲染(2026-09-10 自查发现)
|
|
3695
|
+
setAcConfirm({ ratio: Number(arm.ratio) || 0, tokens: Number(arm.tokens) || 0, window: Number(arm.window) || 0, ring: Number(arm.ring) || 0, wall: Number(arm.wall) || 0, edgeAt: Number(arm.edgeAt) || 0 })
|
|
3616
3696
|
setAcCd(Math.max(0, Math.ceil((Number(arm.expiresAt) || 0) - Date.now()) / 1000))
|
|
3617
3697
|
} else {
|
|
3618
3698
|
setAcConfirm(null)
|
|
@@ -3653,7 +3733,9 @@ window.__ModuleLoader__.load({
|
|
|
3653
3733
|
return h('div', { 'data-dam-autocont': '', style: { position: 'fixed', right: '16px', bottom: '16px', zIndex: 9000, maxWidth: 'min(440px, calc(100vw - 32px))', boxSizing: 'border-box', padding: '10px 12px', borderRadius: '12px', border: '1px solid rgba(128,128,128,.35)', background: 'rgba(18,22,30,.93)', color: '#e8eaed', fontSize: '12px', lineHeight: '1.55', fontFamily: 'system-ui', boxShadow: '0 10px 30px rgba(0,0,0,.35)', backdropFilter: 'blur(10px)' } },
|
|
3654
3734
|
acConfirm ? h('div', { 'data-dam-autocont-confirm': '' }, [
|
|
3655
3735
|
h('div', { style: { marginBottom: '4px' } }, t('autoContConfirm').replace('{p}', String(Math.round((acConfirm.ratio || 0) * 100)))),
|
|
3656
|
-
h('div', { 'data-dam-hint': '' }, (acConfirm.tokens || 0).toLocaleString() + ' / ' + (acConfirm.window || 0).toLocaleString() + ' token'
|
|
3736
|
+
h('div', { 'data-dam-hint': '' }, (acConfirm.tokens || 0).toLocaleString() + ' / ' + (acConfirm.window || 0).toLocaleString() + ' token'
|
|
3737
|
+
+ (acConfirm.ring > 0 ? ' · ' + (locale === 'zh' ? '官方小圈读数 ' : 'ring ') + String(Math.round((acConfirm.ring || 0) * 100)) + '%' : '')
|
|
3738
|
+
+ (acConfirm.wall > 0 ? ' · ' + (locale === 'zh' ? '距硬墙还剩 ' : 'to the wall ') + Math.max(0, (acConfirm.wall || 0) - (acConfirm.tokens || 0)).toLocaleString() + ' token' : '')),
|
|
3657
3739
|
h('div', { style: { marginTop: '8px' } },
|
|
3658
3740
|
h('button', { 'data-dam-btn': '', onClick: onAgree }, t('autoContAgree')),
|
|
3659
3741
|
h('button', { 'data-dam-btn': '', style: { marginLeft: '6px' }, onClick: onReject }, t('autoContReject')),
|
|
@@ -3852,9 +3934,7 @@ window.__ModuleLoader__.load({
|
|
|
3852
3934
|
var detBusy = detBusyPair[0]
|
|
3853
3935
|
var setDetBusy = detBusyPair[1]
|
|
3854
3936
|
useEffect(function () {
|
|
3855
|
-
|
|
3856
|
-
setSem(Object.assign({ loaded: true }, j))
|
|
3857
|
-
}).catch(function () { setSem({ loaded: true, ready: false }) })
|
|
3937
|
+
refreshSem(setSem, function () { setSem({ loaded: true, ready: false }) })
|
|
3858
3938
|
return function () {}
|
|
3859
3939
|
}, [])
|
|
3860
3940
|
// 下载进行中每 1.5s 轮询真实进度(服务端流式记账 bytesDone/bytesTotal/mirrorUsed)
|
|
@@ -3862,9 +3942,7 @@ window.__ModuleLoader__.load({
|
|
|
3862
3942
|
var ph = sem && sem.download && sem.download.phase
|
|
3863
3943
|
if (ph !== 'downloading' && ph !== 'verifying') return function () {}
|
|
3864
3944
|
var iv = setInterval(function () {
|
|
3865
|
-
|
|
3866
|
-
setSem(Object.assign({ loaded: true }, j))
|
|
3867
|
-
}).catch(function () {})
|
|
3945
|
+
refreshSem(setSem)
|
|
3868
3946
|
}, 1500)
|
|
3869
3947
|
return function () { clearInterval(iv) }
|
|
3870
3948
|
}, [sem && sem.download && sem.download.phase])
|
|
@@ -3888,8 +3966,10 @@ window.__ModuleLoader__.load({
|
|
|
3888
3966
|
function save() {
|
|
3889
3967
|
if (busy) return
|
|
3890
3968
|
setBusy(true); setMsg(''); setErr('')
|
|
3891
|
-
|
|
3892
|
-
|
|
3969
|
+
saveConfigPatch(cfg, {
|
|
3970
|
+
onSaved: function (d) { setCfg(d.config); setDirty(false); setMsg(t('saved') + (d.migrated ? ' ' + d.migrated : '')); setBusy(false); if (d && d.config && d.config.locale) applyLocalePref(d.config.locale); refreshSem(setSem) },
|
|
3971
|
+
onError: function (e) { setErr(e.message); setBusy(false) }
|
|
3972
|
+
})
|
|
3893
3973
|
}
|
|
3894
3974
|
function field(label, control, hint) {
|
|
3895
3975
|
return h('div', { 'data-dam-settings-row': '' },
|
|
@@ -3921,7 +4001,7 @@ window.__ModuleLoader__.load({
|
|
|
3921
4001
|
else if ((rec === 'download-model' || rec === 'install-peer' || rec === 'setup-both') && mode !== 'python') setGuide('js')
|
|
3922
4002
|
else if (rec === 'setup-both' && mode === 'python') setGuide('python')
|
|
3923
4003
|
}).catch(function () { setDet({ failed: true }); setDetBusy(false) })
|
|
3924
|
-
|
|
4004
|
+
refreshSem(setSem)
|
|
3925
4005
|
}
|
|
3926
4006
|
function onEngineModeChange(e) {
|
|
3927
4007
|
var v = e.target.value
|
|
@@ -3941,12 +4021,11 @@ window.__ModuleLoader__.load({
|
|
|
3941
4021
|
// 2026-08-27 修复显示不跟随:切换后重新 fetch semantic-status,刷新「当前生效检索」
|
|
3942
4022
|
// (sem.resolvedTier 原只在挂载/下载时更新,切换后不刷新导致一直显示旧档位)。
|
|
3943
4023
|
try {
|
|
3944
|
-
|
|
3945
|
-
var s2 = Object.assign({ loaded: true }, j2)
|
|
4024
|
+
refreshSem(function (s2) {
|
|
3946
4025
|
setSem(s2)
|
|
3947
4026
|
if (v === 'js' && s2.ready === false) setGuide('js')
|
|
3948
4027
|
else if (v === 'python' && s2.pythonInt8Present === false) setGuide('python')
|
|
3949
|
-
})
|
|
4028
|
+
})
|
|
3950
4029
|
} catch (_) {}
|
|
3951
4030
|
}
|
|
3952
4031
|
var sectionLabels = {
|
|
@@ -3973,7 +4052,7 @@ window.__ModuleLoader__.load({
|
|
|
3973
4052
|
field(t('fJsCooldown'), h('input', { 'data-dam-input': '', type: 'number', min: 0, max: 60, value: cfg.jsDecideCooldownRounds === undefined ? 1 : cfg.jsDecideCooldownRounds, onChange: function (e) { set('jsDecideCooldownRounds', (function () { var v = Number(e.target.value); return (Number.isFinite(v) && v >= 0) ? v : 1 })()) /* #19: 0=不冷却 是合法值,|| 1 会吃掉 0 */ } }), t('fJsCooldownHint')),
|
|
3974
4053
|
field(t('fJsDelta'), h('input', { 'data-dam-input': '', type: 'number', min: 0, max: 1, step: 0.005, value: cfg.jsDecideDeltaExp === undefined ? 0.01 : cfg.jsDecideDeltaExp, onChange: function (e) { var v = Number(e.target.value); set('jsDecideDeltaExp', Number.isFinite(v) && v >= 0 ? v : 0.01) } }), t('fJsDeltaHint')),
|
|
3975
4054
|
field(t('fJsExcerpt'), h('input', { 'data-dam-input': '', type: 'number', min: 20, max: 480, value: cfg.jsDecideExcerptChars === undefined ? 40 : cfg.jsDecideExcerptChars, onChange: function (e) { set('jsDecideExcerptChars', Math.max(20, Math.min(480, Number(e.target.value) || 40))) } }), t('fJsExcerptHint')),
|
|
3976
|
-
field(t('fEmitMode'), h('select', { 'data-dam-select': '', value: (sem && sem.activationEmitMode) || 'shadow', onChange: function (e) { var m = e.target.value; apiPost(
|
|
4055
|
+
field(t('fEmitMode'), h('select', { 'data-dam-select': '', value: (sem && sem.activationEmitMode) || 'shadow', onChange: function (e) { var m = e.target.value; apiPost(API.semanticEmit, { mode: m }).then(function () { refreshSem(setSem) }).catch(function () {}) } },
|
|
3977
4056
|
h('option', { value: 'shadow' }, locale === 'zh' ? 'shadow 只记录' : 'shadow (record only)'),
|
|
3978
4057
|
h('option', { value: 'canary-explicit' }, locale === 'zh' ? 'canary 显式回忆注入' : 'canary (explicit recall)'),
|
|
3979
4058
|
h('option', { value: 'active' }, locale === 'zh' ? 'active 全部注入' : 'active (all)')), t('fEmitModeHint')),
|
|
@@ -4076,13 +4155,13 @@ window.__ModuleLoader__.load({
|
|
|
4076
4155
|
: (isJs && dl.phase === 'done' && !ready ? t('dlDone') + (locale === 'zh' ? '(缺运行库时需安装 @huggingface/transformers)' : ' (install @huggingface/transformers if runtime missing)')
|
|
4077
4156
|
: (locale === 'zh' ? '下载进度' : 'Download progress'))))
|
|
4078
4157
|
var refreshSem = function () {
|
|
4079
|
-
|
|
4158
|
+
refreshSem(setSem)
|
|
4080
4159
|
}
|
|
4081
4160
|
var startDl = function () {
|
|
4082
|
-
apiPost(
|
|
4161
|
+
apiPost(API.semanticDownload, { action: 'start', mirror: mirror }).then(refreshSem).catch(function () {})
|
|
4083
4162
|
}
|
|
4084
4163
|
var cancelDl = function () {
|
|
4085
|
-
apiPost(
|
|
4164
|
+
apiPost(API.semanticDownload, { action: 'cancel', mirror: mirror }).then(refreshSem).catch(function () {})
|
|
4086
4165
|
}
|
|
4087
4166
|
return h('div', { style: { border: '1px solid color-mix(in srgb, var(--dam-accent, #2456c4) 40%, transparent)', borderRadius: '10px', padding: '10px 12px', marginBottom: '8px', fontSize: 'calc(11.5px * var(--dam-scale))', lineHeight: 1.6 } },
|
|
4088
4167
|
h('div', { 'data-dam-row': '', style: { alignItems: 'center' } },
|