@a9i5k4/dsh-auto-memory 2.4.0 → 2.4.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/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/OFFICIAL-DISCUSSION-DRAFT.md +80 -0
- package/docs/internal/SUBAGENT-REPORT-ROUTING-PRE-RESEARCH.md +261 -0
- package/lib/client.js +156 -84
- package/lib/index.js +98 -6
- 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.1 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,30 @@ window.__ModuleLoader__.load({
|
|
|
567
567
|
|
|
568
568
|
// ───────────────────────── 更新弹窗 / 首次指导 ─────────────────────────
|
|
569
569
|
var CHANGELOG = {
|
|
570
|
+
'2.4.1': { zh: [
|
|
571
|
+
'修复:已接续过的会话会被再次触发接续(重复建窗口)。新增「已接续闩锁」并落盘(session id 为键、重启有效):宿主路径在交接材料投料成功后落闩,浏览器路径在新会话真建成后落闩,失败不落闩以便重试。',
|
|
572
|
+
'修复:宿主已完成接续后,确认卡仍停在「同意接续/拒绝」并与「已自动接续到新会话」并存;arm 状态改为按会话过滤,不再在其他窗口弹「本会话水位已达」。',
|
|
573
|
+
'修复:水位双口径那一行(本会话水位 / 官方小圈读数 / 距硬墙余量)此前因漏拷贝 ring/wall 永不渲染。',
|
|
574
|
+
'内部:界面路径表引入唯一前缀常量与一致性锁(客户端路径必须都在宿主表内、表外零裸字面量);写配置 4 条路径收敛为一个出口;10 处重复的语义状态刷新收敛为一处。',
|
|
575
|
+
'流程:自本版起,发版必须同步 CHANGELOG、应用内更新说明与版本标识(release.mjs 已加校验,缺一即拒绝构建)。',
|
|
576
|
+
], en: [
|
|
577
|
+
'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.',
|
|
578
|
+
'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".',
|
|
579
|
+
'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.',
|
|
580
|
+
'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.',
|
|
581
|
+
'Process: from this version on, releases must update CHANGELOG, in-app release notes and version identifiers together (release.mjs now refuses to build otherwise).',
|
|
582
|
+
] },
|
|
583
|
+
'2.4.0': { zh: [
|
|
584
|
+
'★ 水位判据按「该会话当前模型」的预留额度自适应:消息可用上限 = 窗口 − 该路由的 maxTokens(实测 384,000 预留 → 可用 616,000),阈值随之落在健康区间;压缩与 CONTEXT_WINDOW_EXCEEDED 升为硬触发。',
|
|
585
|
+
'★ 修复自动接续静默失效:DSH 0.1.5 起 requestId 必填,宿主兜底接续漏传该字段时官方抛错被包装成误导性的 "prompt rejected" —— 现象是新会话建出来了、交接材料却从未送达。',
|
|
586
|
+
'★ 记忆容量口径重做:改为「文件容量上限」(noteCapacityChars / userCapacityChars,默认各 12000 字符),先 AI 折叠成要点、失败退回整条归档,写入不再被堵死。',
|
|
587
|
+
'★ Python 语义引擎贯通:安装向导写入正确的工作目录、模型落位与 onnxFile 契约对齐、向导常驻设置页,装了就能用。',
|
|
588
|
+
], en: [
|
|
589
|
+
'★ 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.',
|
|
590
|
+
'★ 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.',
|
|
591
|
+
'★ 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.',
|
|
592
|
+
'★ 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.',
|
|
593
|
+
] },
|
|
570
594
|
'2.3.0': { zh: [
|
|
571
595
|
'★ 检索改为分层语义唤回:memory_recall 先返回 L0 摘要列表(每条记忆约 90 字,含 id/得分/匹配原因),需要原文再按 id 展开——此前全文直接进 embedding,超过模型 512 token 上限的条目尾部全部丢失(实测最长一条 9,822 字符);现在摘要永远在限制内,长记忆不再有损。',
|
|
572
596
|
'★ 检索融合改为三臂 rank-space:词法 + 语义 + 时间 三路各自排名后按 RRF(k=60)融合,取代旧的 minmax 归一化加权——旧方案分数只反映「这一批里排第几」,候选集一换分数就漂移、候选只剩 1 条时退化为常数,而「要不要注入」的决策恰恰依赖分数与阈值比较。现在排序与决策解耦,分数不再说谎。',
|
|
@@ -855,45 +879,56 @@ window.__ModuleLoader__.load({
|
|
|
855
879
|
}
|
|
856
880
|
|
|
857
881
|
// ───────────────────────── API ─────────────────────────
|
|
882
|
+
// 路由前缀唯一常量(2026-09-10 第 0 步止血):表中每条路径由它拼出,不再逐个重复全串。
|
|
883
|
+
var ROUTE_PREFIX = '/api/dsh-auto-memory'
|
|
884
|
+
// 宿主半边(lib/index.js 的 API)声明的是同一批路径 —— 两个半边是独立 bundle(本文件经 __ModuleLoader__ 手写加载,
|
|
885
|
+
// 无法 import 宿主模块),所以"只有一份"这件事由 tests/smoke/ 下的路径表一致性锁保证:
|
|
886
|
+
// 客户端路径 ⊆ 宿主路径、宿主独有路径只允许显式白名单、本文件表外不得再出现裸路径字面量。
|
|
858
887
|
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
|
-
|
|
888
|
+
state: ROUTE_PREFIX + '/state',
|
|
889
|
+
list: ROUTE_PREFIX + '/list',
|
|
890
|
+
file: ROUTE_PREFIX + '/file',
|
|
891
|
+
recall: ROUTE_PREFIX + '/recall',
|
|
892
|
+
smartRecall: ROUTE_PREFIX + '/smart-recall',
|
|
893
|
+
workspaces: ROUTE_PREFIX + '/workspaces',
|
|
894
|
+
debug: ROUTE_PREFIX + '/debug',
|
|
895
|
+
scanDirty: ROUTE_PREFIX + '/scan-dirty',
|
|
896
|
+
browseDir: ROUTE_PREFIX + '/browse-dir',
|
|
897
|
+
pickDir: ROUTE_PREFIX + '/pick-dir',
|
|
898
|
+
updateCheck: ROUTE_PREFIX + '/update-check',
|
|
899
|
+
update: ROUTE_PREFIX + '/update',
|
|
900
|
+
config: ROUTE_PREFIX + '/config',
|
|
901
|
+
reflect: ROUTE_PREFIX + '/reflect',
|
|
902
|
+
reflectAuto: ROUTE_PREFIX + '/reflect-auto',
|
|
903
|
+
note: ROUTE_PREFIX + '/note',
|
|
904
|
+
external: ROUTE_PREFIX + '/external',
|
|
905
|
+
externalView: ROUTE_PREFIX + '/external-view',
|
|
906
|
+
externalRemove: ROUTE_PREFIX + '/external-remove',
|
|
907
|
+
externalImport: ROUTE_PREFIX + '/external-import',
|
|
908
|
+
calendar: ROUTE_PREFIX + '/calendar',
|
|
909
|
+
summarize: ROUTE_PREFIX + '/summarize',
|
|
910
|
+
greet: ROUTE_PREFIX + '/greet',
|
|
911
|
+
notices: ROUTE_PREFIX + '/notices',
|
|
912
|
+
models: ROUTE_PREFIX + '/models',
|
|
913
|
+
semanticDeepDetect: ROUTE_PREFIX + '/semantic-deep-detect',
|
|
914
|
+
semanticStatus: ROUTE_PREFIX + '/semantic-status',
|
|
915
|
+
semanticEmit: ROUTE_PREFIX + '/semantic-emit',
|
|
916
|
+
pyDetect: ROUTE_PREFIX + '/python-setup/detect',
|
|
917
|
+
pyVenv: ROUTE_PREFIX + '/python-setup/venv',
|
|
918
|
+
pyDeps: ROUTE_PREFIX + '/python-setup/deps',
|
|
919
|
+
pyModel: ROUTE_PREFIX + '/python-setup/model',
|
|
920
|
+
pyCancel: ROUTE_PREFIX + '/python-setup/cancel',
|
|
921
|
+
pyStatus: ROUTE_PREFIX + '/python-setup/status',
|
|
922
|
+
handoffState: ROUTE_PREFIX + '/handoff-state',
|
|
923
|
+
handoffContinue: ROUTE_PREFIX + '/handoff-continue',
|
|
924
|
+
handoffPermission: ROUTE_PREFIX + '/handoff-permission',
|
|
925
|
+
autoContState: ROUTE_PREFIX + '/auto-continue-state',
|
|
926
|
+
autoContDecide: ROUTE_PREFIX + '/auto-continue-decide',
|
|
927
|
+
semanticDownload: ROUTE_PREFIX + '/semantic-download',
|
|
928
|
+
shadowRecent: ROUTE_PREFIX + '/shadow-recent',
|
|
929
|
+
reviewFeedback: ROUTE_PREFIX + '/review-feedback',
|
|
930
|
+
memoryHub: ROUTE_PREFIX + '/memory-hub',
|
|
931
|
+
storageManage: ROUTE_PREFIX + '/storage-manage',
|
|
897
932
|
}
|
|
898
933
|
function query(params) {
|
|
899
934
|
var search = new URLSearchParams()
|
|
@@ -913,6 +948,28 @@ window.__ModuleLoader__.load({
|
|
|
913
948
|
if (!res.ok) throw new Error(body.error || ('POST ' + path + ' → HTTP ' + res.status))
|
|
914
949
|
return body
|
|
915
950
|
}
|
|
951
|
+
/**
|
|
952
|
+
* 写配置的唯一出口(2026-09-10 第 0 步止血):此前有 4 条互不相同的写配置路径
|
|
953
|
+
* (设置页整对象 POST / 接续页 patch / 向导两处 patch),端点、后置刷新、错误处理各写一遍。
|
|
954
|
+
* 现在全部经这里,以后加配置项或改保存行为只改这一处。
|
|
955
|
+
* hooks = { onSaved, onError } 可省;省略时静默失败(与原先的 fire-and-forget 行为一致)。
|
|
956
|
+
*/
|
|
957
|
+
function saveConfigPatch(patch, hooks) {
|
|
958
|
+
var h = hooks || {}
|
|
959
|
+
var p = apiPost(API.config, patch)
|
|
960
|
+
if (h.onSaved || h.onError) p.then(function (d) { if (h.onSaved) h.onSaved(d) }, function (e) { if (h.onError) h.onError(e) })
|
|
961
|
+
else p.catch(function () {})
|
|
962
|
+
return p
|
|
963
|
+
}
|
|
964
|
+
/**
|
|
965
|
+
* 语义引擎状态的统一刷新(2026-09-10 第 0 步止血):此前同一段 fetch + 状态写回在界面里逐字重复 10 处,
|
|
966
|
+
* 响应形状或失败兜底要改时得挨个改。apply 收合并后的状态对象(已带 loaded:true),onError 可省。
|
|
967
|
+
*/
|
|
968
|
+
function refreshSem(apply, onError) {
|
|
969
|
+
try {
|
|
970
|
+
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 () {})
|
|
971
|
+
} catch (_) {}
|
|
972
|
+
}
|
|
916
973
|
|
|
917
974
|
// ───────────────────────── 样式 ─────────────────────────
|
|
918
975
|
// 视觉:液态玻璃(毛玻璃)—— backdrop-filter + DSH 主题令牌(--dsw-alias-*),
|
|
@@ -1623,16 +1680,16 @@ window.__ModuleLoader__.load({
|
|
|
1623
1680
|
var setFb = fbPair[1]
|
|
1624
1681
|
useEffect(function () {
|
|
1625
1682
|
var alive = true
|
|
1626
|
-
fetch(
|
|
1683
|
+
fetch(API.shadowRecent).then(function (r) { return r.json() }).then(function (j) {
|
|
1627
1684
|
if (alive) setData((j && j.rows) || [])
|
|
1628
1685
|
}).catch(function (e) { if (alive) setErr(String(e && e.message)) })
|
|
1629
|
-
fetch(
|
|
1686
|
+
fetch(API.reviewFeedback).then(function (r) { return r.json() }).then(function (j) {
|
|
1630
1687
|
if (alive) setFb(j || {})
|
|
1631
1688
|
}).catch(function () {})
|
|
1632
1689
|
return function () { alive = false }
|
|
1633
1690
|
}, [])
|
|
1634
1691
|
function send(obsId, choice) {
|
|
1635
|
-
fetch(
|
|
1692
|
+
fetch(API.reviewFeedback, { method: 'POST',
|
|
1636
1693
|
headers: { 'Content-Type': 'application/json' },
|
|
1637
1694
|
body: JSON.stringify({ observationId: obsId, choice: choice }) })
|
|
1638
1695
|
.then(function (r) { return r.json() })
|
|
@@ -1801,7 +1858,7 @@ window.__ModuleLoader__.load({
|
|
|
1801
1858
|
var setActMsg = actMsgPair[1]
|
|
1802
1859
|
useEffect(function () {
|
|
1803
1860
|
var alive = true
|
|
1804
|
-
fetch(
|
|
1861
|
+
fetch(API.memoryHub).then(function (r) { return r.json() }).then(function (j) {
|
|
1805
1862
|
if (alive) setData(j || null)
|
|
1806
1863
|
}).catch(function (e) { if (alive) setErr(String(e && e.message)) })
|
|
1807
1864
|
return function () { alive = false }
|
|
@@ -1809,7 +1866,7 @@ window.__ModuleLoader__.load({
|
|
|
1809
1866
|
// M9 审批动作(G-02 同款 append-only 精神):晋升/激活/弃用走 /memory-hub POST,
|
|
1810
1867
|
// 后端走 store 门槛判定,不绕过任何 gate;动作后刷新 overview。
|
|
1811
1868
|
function hubAct(action, procedureId, v) {
|
|
1812
|
-
fetch(
|
|
1869
|
+
fetch(API.memoryHub, { method: 'POST',
|
|
1813
1870
|
headers: { 'Content-Type': 'application/json' },
|
|
1814
1871
|
body: JSON.stringify({ action: action, procedureId: procedureId, v: v }) })
|
|
1815
1872
|
.then(function (r) { return r.json() })
|
|
@@ -1903,7 +1960,7 @@ window.__ModuleLoader__.load({
|
|
|
1903
1960
|
var setDelFile = filePair[1]
|
|
1904
1961
|
useEffect(function () {
|
|
1905
1962
|
var alive = true
|
|
1906
|
-
fetch(
|
|
1963
|
+
fetch(API.storageManage).then(function (r) { return r.json() }).then(function (j) {
|
|
1907
1964
|
if (alive) setData(j || null)
|
|
1908
1965
|
}).catch(function (e) { if (alive) setErr(String(e && e.message)) })
|
|
1909
1966
|
return function () { alive = false }
|
|
@@ -1911,7 +1968,7 @@ window.__ModuleLoader__.load({
|
|
|
1911
1968
|
function act(action, payload, onDone) {
|
|
1912
1969
|
setMsg('')
|
|
1913
1970
|
var body = Object.assign({ action: action }, payload || {})
|
|
1914
|
-
fetch(
|
|
1971
|
+
fetch(API.storageManage, { method: 'POST',
|
|
1915
1972
|
headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) })
|
|
1916
1973
|
.then(function (r) { return r.json() })
|
|
1917
1974
|
.then(function (j) {
|
|
@@ -1921,7 +1978,7 @@ window.__ModuleLoader__.load({
|
|
|
1921
1978
|
}
|
|
1922
1979
|
setMsg(action + ': ' + reason)
|
|
1923
1980
|
if (onDone) onDone(j)
|
|
1924
|
-
var again = fetch(
|
|
1981
|
+
var again = fetch(API.storageManage).then(function (r2) { return r2.json() })
|
|
1925
1982
|
again.then(function (j2) { setData(j2 || null) })
|
|
1926
1983
|
})
|
|
1927
1984
|
.catch(function (e) { setMsg(action + ' failed: ' + String(e && e.message)) })
|
|
@@ -2177,7 +2234,7 @@ window.__ModuleLoader__.load({
|
|
|
2177
2234
|
}, [])
|
|
2178
2235
|
var autoSave = function (patch) {
|
|
2179
2236
|
setAutoCfg(function (p) { return Object.assign({}, p, patch) })
|
|
2180
|
-
|
|
2237
|
+
saveConfigPatch(patch)
|
|
2181
2238
|
}
|
|
2182
2239
|
async function oneClickContinue() {
|
|
2183
2240
|
// 一键接续 = 共用执行器(刷新仪式 → 材料 → workspaceId 建会话 → 沿用模型 → 注入),与自动接续同一路径
|
|
@@ -3089,10 +3146,10 @@ window.__ModuleLoader__.load({
|
|
|
3089
3146
|
if (!wizSt) {
|
|
3090
3147
|
wizSt = { loaded: false }
|
|
3091
3148
|
window['dsh-auto-memory.wizStatus'] = wizSt
|
|
3092
|
-
|
|
3093
|
-
Object.assign(wizSt,
|
|
3149
|
+
refreshSem(function (s) {
|
|
3150
|
+
Object.assign(wizSt, s)
|
|
3094
3151
|
try { dlgTick() } catch (e9) {}
|
|
3095
|
-
}
|
|
3152
|
+
}, function () { Object.assign(wizSt, { loaded: true, ready: false }) })
|
|
3096
3153
|
}
|
|
3097
3154
|
var tourDlPhase = wizSt && wizSt.download && wizSt.download.phase
|
|
3098
3155
|
var tourShowing = !!dialogState && (dialogState.kind === 'welcomeTour' || dialogState.kind === 'modelDownload')
|
|
@@ -3102,19 +3159,19 @@ window.__ModuleLoader__.load({
|
|
|
3102
3159
|
setTourStep(0)
|
|
3103
3160
|
wizSt.tourCfg = null
|
|
3104
3161
|
wizSt.cfgLoaded = false
|
|
3105
|
-
fetch(
|
|
3162
|
+
fetch(API.config).then(function (r) { return r.json() }).then(function (j) {
|
|
3106
3163
|
wizSt.tourCfg = (j && (j.config || j)) || {}
|
|
3107
3164
|
// mode 型开关(唤起注入模式)实际存储在 embedding-config.json(semantic-emit 接口维护),
|
|
3108
3165
|
// 需从 semantic-status 合并进来,导览开关才能显示真实当前值并与设置页联动。
|
|
3109
3166
|
try {
|
|
3110
|
-
|
|
3167
|
+
refreshSem(function (j2) {
|
|
3111
3168
|
if (j2 && j2.activationEmitMode !== undefined) { wizSt.tourCfg.activationEmitMode = j2.activationEmitMode; try { dlgTick() } catch (e13) {} }
|
|
3112
|
-
})
|
|
3169
|
+
})
|
|
3113
3170
|
} catch (_) {}
|
|
3114
3171
|
wizSt.cfgLoaded = true
|
|
3115
3172
|
try { dlgTick() } catch (e11) {}
|
|
3116
3173
|
}).catch(function () { wizSt.tourCfg = wizSt.tourCfg || {}; wizSt.cfgLoaded = true })
|
|
3117
|
-
fetch(
|
|
3174
|
+
fetch(API.external).then(function (r) { return r.json() }).then(function (j) {
|
|
3118
3175
|
wizSt.extScan = j
|
|
3119
3176
|
try { dlgTick() } catch (e12) {}
|
|
3120
3177
|
}).catch(function () {})
|
|
@@ -3124,9 +3181,9 @@ window.__ModuleLoader__.load({
|
|
|
3124
3181
|
if (!tourShowing) return undefined
|
|
3125
3182
|
if (wizSt.ready) return undefined // 已就绪无需轮询
|
|
3126
3183
|
var iv = setInterval(function () {
|
|
3127
|
-
|
|
3184
|
+
refreshSem(function (j) {
|
|
3128
3185
|
Object.assign(wizSt, j); try { dlgTick() } catch (e10) {}
|
|
3129
|
-
})
|
|
3186
|
+
})
|
|
3130
3187
|
}, 1500)
|
|
3131
3188
|
return function () { clearInterval(iv) }
|
|
3132
3189
|
}, [tourShowing, tourDlPhase, wizSt.ready])
|
|
@@ -3139,7 +3196,7 @@ window.__ModuleLoader__.load({
|
|
|
3139
3196
|
if (wizSt.ready || wizSt.loaded === false) return undefined
|
|
3140
3197
|
var ph = wizSt.download && wizSt.download.phase
|
|
3141
3198
|
if (ph === 'downloading' || ph === 'verifying' || ph === 'done' || ph === 'error') return undefined
|
|
3142
|
-
apiPost(
|
|
3199
|
+
apiPost(API.semanticDownload, { action: 'start', mirror: 'auto' }).catch(function () {})
|
|
3143
3200
|
}, [tourShowing, tourStep, wizSt.ready])
|
|
3144
3201
|
if (!dialogState) return null
|
|
3145
3202
|
// 左下角小卡片(记忆按钮上方),高透明毛玻璃,不遮全屏
|
|
@@ -3257,7 +3314,7 @@ window.__ModuleLoader__.load({
|
|
|
3257
3314
|
: t('dlDone'))
|
|
3258
3315
|
: (locale === 'zh' ? '未下载 · 词法检索照常可用' : 'Not downloaded · lexical search keeps working')
|
|
3259
3316
|
var startDl = function () {
|
|
3260
|
-
apiPost(
|
|
3317
|
+
apiPost(API.semanticDownload, { action: 'start', mirror: 'auto' }).catch(function () {})
|
|
3261
3318
|
}
|
|
3262
3319
|
// 功能开关:读当前值(带每项默认)/点击即时写配置(乐观更新+POST,失败不回滚——下次重看向导会以真实配置为准)
|
|
3263
3320
|
var EXT_SOURCE_KEYS = ['workbuddy-user', 'workbuddy-profile', 'codebuddy-memory', 'claude-global', 'project-conventions', 'workbuddy-sessions', 'claude-sessions', 'codex-sessions']
|
|
@@ -3283,9 +3340,9 @@ window.__ModuleLoader__.load({
|
|
|
3283
3340
|
// mode 型开关(唤起注入模式)走 semantic-emit 接口——与设置页同源(embedding-config.json),
|
|
3284
3341
|
// 保证导览切换与设置页双向联动;其余开关写常规 config。
|
|
3285
3342
|
if (tg.mode) {
|
|
3286
|
-
apiPost(
|
|
3343
|
+
apiPost(API.semanticEmit, { mode: patch[tg.key] }).catch(function () {})
|
|
3287
3344
|
} else {
|
|
3288
|
-
|
|
3345
|
+
saveConfigPatch(patch)
|
|
3289
3346
|
}
|
|
3290
3347
|
}
|
|
3291
3348
|
// 外部来源单源勾选(扫描行;勾选即写 externalSources 对象)
|
|
@@ -3296,7 +3353,7 @@ window.__ModuleLoader__.load({
|
|
|
3296
3353
|
cur[id] = (cur[id] !== false) ? false : true
|
|
3297
3354
|
c.externalSources = cur
|
|
3298
3355
|
dlgTick()
|
|
3299
|
-
|
|
3356
|
+
saveConfigPatch({ externalSources: cur })
|
|
3300
3357
|
}
|
|
3301
3358
|
var tourExtOn = function (id) {
|
|
3302
3359
|
var c = wizSt.tourCfg || {}
|
|
@@ -3582,8 +3639,11 @@ window.__ModuleLoader__.load({
|
|
|
3582
3639
|
useEffect(function () {
|
|
3583
3640
|
var alive = true
|
|
3584
3641
|
var load = function () {
|
|
3642
|
+
// 带上当前会话 id:宿主据此只在「属于本窗口的」armed 上弹确认卡(2026-09-10)
|
|
3643
|
+
var sidQ = ''
|
|
3644
|
+
try { sidQ = String(currentSessionIdClient() || '') } catch (eS) {}
|
|
3585
3645
|
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 () {})
|
|
3646
|
+
apiGet(API.autoContState, sidQ ? { sessionId: sidQ } : {}).then(function (s) { if (alive && s) setAcState(s) }).catch(function () {})
|
|
3587
3647
|
}
|
|
3588
3648
|
load()
|
|
3589
3649
|
var iv = setInterval(load, 3000)
|
|
@@ -3598,21 +3658,34 @@ window.__ModuleLoader__.load({
|
|
|
3598
3658
|
// 触发/倒计时/超时执行全部由宿主侧驱动(2.2.6),浏览器即使被节流也不影响接续。
|
|
3599
3659
|
var poll = function () {
|
|
3600
3660
|
if (!alive) return
|
|
3601
|
-
|
|
3661
|
+
var sidQ = ''
|
|
3662
|
+
try { sidQ = String(currentSessionIdClient() || '') } catch (eS) {}
|
|
3663
|
+
apiGet(API.autoContState, sidQ ? { sessionId: sidQ } : {}).then(function (s) {
|
|
3602
3664
|
if (!alive || !s) return
|
|
3603
3665
|
setAcState(s)
|
|
3604
3666
|
if (s.executing) {
|
|
3667
|
+
// 2026-09-10:执行中/已完成时必须**收起确认卡** —— 旧实现在这两个分支直接 return,
|
|
3668
|
+
// acConfirm 保持旧值,于是宿主已接续完成后,卡片仍停在「同意接续/拒绝」上
|
|
3669
|
+
// (实测:旧窗口一直显示确认卡 + 「✓ 已自动接续到新会话」并存,像是「它还想接续」)。
|
|
3670
|
+
setAcConfirm(null)
|
|
3671
|
+
setAcCd(0)
|
|
3605
3672
|
setAcSt(locale === 'zh' ? '宿主正在自动接续:刷新白板/账本 → 建新会话 → 注入材料…' : 'Host auto-continuing: refresh PLAN/ledger → new session → inject…')
|
|
3606
3673
|
return
|
|
3607
3674
|
}
|
|
3608
|
-
|
|
3675
|
+
var okAt = Number(s.lastOk && s.lastOk.at) || 0
|
|
3676
|
+
// 10 分钟有效期(2026-09-10):宿主状态是全局单值,提示不设期限会一直挂在每个窗口上
|
|
3677
|
+
if (s.lastOk && (okAt === 0 || Date.now() - okAt < 10 * 60 * 1000)) {
|
|
3678
|
+
setAcConfirm(null)
|
|
3679
|
+
setAcCd(0)
|
|
3609
3680
|
setAcSt((locale === 'zh' ? '✓ 已自动接续到新会话,可直接上手' : '✓ Auto-continued into a new session') + (s.lastOk.model ? ' · ' + s.lastOk.model + (s.lastOk.reasoningEffort ? '/' + s.lastOk.reasoningEffort : '') : ''))
|
|
3610
3681
|
return
|
|
3611
3682
|
}
|
|
3612
3683
|
if (s.error) { setAcSt('✗ ' + s.error); return }
|
|
3613
3684
|
var arm = s.armed
|
|
3614
3685
|
if (arm && arm.ratio >= thr) {
|
|
3615
|
-
|
|
3686
|
+
// ring/wall 必须一起带进来:卡片读的是 acConfirm.ring / acConfirm.wall,
|
|
3687
|
+
// 宿主透出了但这里不拷 → 双口径那行永远不渲染(2026-09-10 自查发现)
|
|
3688
|
+
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
3689
|
setAcCd(Math.max(0, Math.ceil((Number(arm.expiresAt) || 0) - Date.now()) / 1000))
|
|
3617
3690
|
} else {
|
|
3618
3691
|
setAcConfirm(null)
|
|
@@ -3653,7 +3726,9 @@ window.__ModuleLoader__.load({
|
|
|
3653
3726
|
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
3727
|
acConfirm ? h('div', { 'data-dam-autocont-confirm': '' }, [
|
|
3655
3728
|
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'
|
|
3729
|
+
h('div', { 'data-dam-hint': '' }, (acConfirm.tokens || 0).toLocaleString() + ' / ' + (acConfirm.window || 0).toLocaleString() + ' token'
|
|
3730
|
+
+ (acConfirm.ring > 0 ? ' · ' + (locale === 'zh' ? '官方小圈读数 ' : 'ring ') + String(Math.round((acConfirm.ring || 0) * 100)) + '%' : '')
|
|
3731
|
+
+ (acConfirm.wall > 0 ? ' · ' + (locale === 'zh' ? '距硬墙还剩 ' : 'to the wall ') + Math.max(0, (acConfirm.wall || 0) - (acConfirm.tokens || 0)).toLocaleString() + ' token' : '')),
|
|
3657
3732
|
h('div', { style: { marginTop: '8px' } },
|
|
3658
3733
|
h('button', { 'data-dam-btn': '', onClick: onAgree }, t('autoContAgree')),
|
|
3659
3734
|
h('button', { 'data-dam-btn': '', style: { marginLeft: '6px' }, onClick: onReject }, t('autoContReject')),
|
|
@@ -3852,9 +3927,7 @@ window.__ModuleLoader__.load({
|
|
|
3852
3927
|
var detBusy = detBusyPair[0]
|
|
3853
3928
|
var setDetBusy = detBusyPair[1]
|
|
3854
3929
|
useEffect(function () {
|
|
3855
|
-
|
|
3856
|
-
setSem(Object.assign({ loaded: true }, j))
|
|
3857
|
-
}).catch(function () { setSem({ loaded: true, ready: false }) })
|
|
3930
|
+
refreshSem(setSem, function () { setSem({ loaded: true, ready: false }) })
|
|
3858
3931
|
return function () {}
|
|
3859
3932
|
}, [])
|
|
3860
3933
|
// 下载进行中每 1.5s 轮询真实进度(服务端流式记账 bytesDone/bytesTotal/mirrorUsed)
|
|
@@ -3862,9 +3935,7 @@ window.__ModuleLoader__.load({
|
|
|
3862
3935
|
var ph = sem && sem.download && sem.download.phase
|
|
3863
3936
|
if (ph !== 'downloading' && ph !== 'verifying') return function () {}
|
|
3864
3937
|
var iv = setInterval(function () {
|
|
3865
|
-
|
|
3866
|
-
setSem(Object.assign({ loaded: true }, j))
|
|
3867
|
-
}).catch(function () {})
|
|
3938
|
+
refreshSem(setSem)
|
|
3868
3939
|
}, 1500)
|
|
3869
3940
|
return function () { clearInterval(iv) }
|
|
3870
3941
|
}, [sem && sem.download && sem.download.phase])
|
|
@@ -3888,8 +3959,10 @@ window.__ModuleLoader__.load({
|
|
|
3888
3959
|
function save() {
|
|
3889
3960
|
if (busy) return
|
|
3890
3961
|
setBusy(true); setMsg(''); setErr('')
|
|
3891
|
-
|
|
3892
|
-
|
|
3962
|
+
saveConfigPatch(cfg, {
|
|
3963
|
+
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) },
|
|
3964
|
+
onError: function (e) { setErr(e.message); setBusy(false) }
|
|
3965
|
+
})
|
|
3893
3966
|
}
|
|
3894
3967
|
function field(label, control, hint) {
|
|
3895
3968
|
return h('div', { 'data-dam-settings-row': '' },
|
|
@@ -3921,7 +3994,7 @@ window.__ModuleLoader__.load({
|
|
|
3921
3994
|
else if ((rec === 'download-model' || rec === 'install-peer' || rec === 'setup-both') && mode !== 'python') setGuide('js')
|
|
3922
3995
|
else if (rec === 'setup-both' && mode === 'python') setGuide('python')
|
|
3923
3996
|
}).catch(function () { setDet({ failed: true }); setDetBusy(false) })
|
|
3924
|
-
|
|
3997
|
+
refreshSem(setSem)
|
|
3925
3998
|
}
|
|
3926
3999
|
function onEngineModeChange(e) {
|
|
3927
4000
|
var v = e.target.value
|
|
@@ -3941,12 +4014,11 @@ window.__ModuleLoader__.load({
|
|
|
3941
4014
|
// 2026-08-27 修复显示不跟随:切换后重新 fetch semantic-status,刷新「当前生效检索」
|
|
3942
4015
|
// (sem.resolvedTier 原只在挂载/下载时更新,切换后不刷新导致一直显示旧档位)。
|
|
3943
4016
|
try {
|
|
3944
|
-
|
|
3945
|
-
var s2 = Object.assign({ loaded: true }, j2)
|
|
4017
|
+
refreshSem(function (s2) {
|
|
3946
4018
|
setSem(s2)
|
|
3947
4019
|
if (v === 'js' && s2.ready === false) setGuide('js')
|
|
3948
4020
|
else if (v === 'python' && s2.pythonInt8Present === false) setGuide('python')
|
|
3949
|
-
})
|
|
4021
|
+
})
|
|
3950
4022
|
} catch (_) {}
|
|
3951
4023
|
}
|
|
3952
4024
|
var sectionLabels = {
|
|
@@ -3973,7 +4045,7 @@ window.__ModuleLoader__.load({
|
|
|
3973
4045
|
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
4046
|
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
4047
|
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(
|
|
4048
|
+
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
4049
|
h('option', { value: 'shadow' }, locale === 'zh' ? 'shadow 只记录' : 'shadow (record only)'),
|
|
3978
4050
|
h('option', { value: 'canary-explicit' }, locale === 'zh' ? 'canary 显式回忆注入' : 'canary (explicit recall)'),
|
|
3979
4051
|
h('option', { value: 'active' }, locale === 'zh' ? 'active 全部注入' : 'active (all)')), t('fEmitModeHint')),
|
|
@@ -4076,13 +4148,13 @@ window.__ModuleLoader__.load({
|
|
|
4076
4148
|
: (isJs && dl.phase === 'done' && !ready ? t('dlDone') + (locale === 'zh' ? '(缺运行库时需安装 @huggingface/transformers)' : ' (install @huggingface/transformers if runtime missing)')
|
|
4077
4149
|
: (locale === 'zh' ? '下载进度' : 'Download progress'))))
|
|
4078
4150
|
var refreshSem = function () {
|
|
4079
|
-
|
|
4151
|
+
refreshSem(setSem)
|
|
4080
4152
|
}
|
|
4081
4153
|
var startDl = function () {
|
|
4082
|
-
apiPost(
|
|
4154
|
+
apiPost(API.semanticDownload, { action: 'start', mirror: mirror }).then(refreshSem).catch(function () {})
|
|
4083
4155
|
}
|
|
4084
4156
|
var cancelDl = function () {
|
|
4085
|
-
apiPost(
|
|
4157
|
+
apiPost(API.semanticDownload, { action: 'cancel', mirror: mirror }).then(refreshSem).catch(function () {})
|
|
4086
4158
|
}
|
|
4087
4159
|
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
4160
|
h('div', { 'data-dam-row': '', style: { alignItems: 'center' } },
|