dsh-sessions-manager 3.7.0 → 3.7.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/README.en.md +1 -1
- package/README.md +1 -1
- package/lib/client.js +24 -24
- package/lib/client.js.map +2 -2
- package/lib/index.js +32 -3
- package/lib/index.js.map +2 -2
- package/package.json +1 -1
- package/src/client/index.jsx +14 -14
- package/src/handle-era-ops.js +46 -2
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-sessions-manager",
|
|
3
3
|
"description": "DSH 设置面板会话管理器:归档 / 恢复 / 彻底删除 / 移动到其他工作区,带工作区标签与会话日期;统一「会话管理」面板。Session manager for the DeepSeek Harness settings panel — archive / restore / permanently delete / move sessions across workspaces, with workspace tags & session dates in one unified panel.",
|
|
4
|
-
"version": "3.7.
|
|
4
|
+
"version": "3.7.2",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"exports": {
|
package/src/client/index.jsx
CHANGED
|
@@ -1539,8 +1539,16 @@ function SessionPanel({ workspacesSvc }) {
|
|
|
1539
1539
|
<button type="button" className="sess-farrow" aria-expanded={moreOpen} aria-label={moreOpen ? '收起更多筛选' : '展开更多筛选'} title={moreOpen ? '收起更多筛选' : '展开更多筛选(已收藏 / 空白 / 回收站)'} onClick={() => setMoreOpen(!moreOpen)}>
|
|
1540
1540
|
<svg width="12" height="12" viewBox="0 0 12 12" aria-hidden="true"><path d="M4 2l4 4-4 4" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"/></svg>
|
|
1541
1541
|
</button>
|
|
1542
|
-
|
|
1543
|
-
|
|
1542
|
+
</div>
|
|
1543
|
+
</div>
|
|
1544
|
+
|
|
1545
|
+
|
|
1546
|
+
{/* 维护栏是面板级工具,与当前查看哪一组会话无关,故所有视图都显示。 */}
|
|
1547
|
+
<div className="maint-bar">
|
|
1548
|
+
<button type="button" className="archv-btn" aria-expanded={storageOpen} onClick={() => { const next = !storageOpen; setStorageOpen(next); if (next && (!storage || storageDirty.current) && !storageBusy) loadStorage() }}>
|
|
1549
|
+
存储占用{storage ? ` · ${fmtBytes(storage.totalBytes) || '0 B'}` : ''}
|
|
1550
|
+
</button>
|
|
1551
|
+
{/* 标签管理:面板级低频工具(v3.7.x 从筛选行移入维护栏,存储占用右边);不新增顶层视图 tab(UI 红线)。 */}
|
|
1544
1552
|
<button
|
|
1545
1553
|
type="button"
|
|
1546
1554
|
className="archv-btn"
|
|
@@ -1549,7 +1557,10 @@ function SessionPanel({ workspacesSvc }) {
|
|
|
1549
1557
|
title={tagsReady ? '新建 / 重命名 / 合并 / 删除标签' : tagsBlockedTitle}
|
|
1550
1558
|
onClick={() => { setOpenTagMgr(!openTagMgr); setRenamingTagId(null) }}
|
|
1551
1559
|
>标签管理{tagsReady && tagDefs.length ? ` (${tagDefs.length})` : ''}</button>
|
|
1552
|
-
|
|
1560
|
+
<button type="button" className={'archv-btn' + (aa.settings.inactiveDays ? ' archv-go' : '')} aria-expanded={aaOpen} onClick={() => setAaOpen(!aaOpen)}>
|
|
1561
|
+
自动归档{aa.settings.inactiveDays ? `:${aa.settings.inactiveDays} 天未活跃` : ':未启用'}
|
|
1562
|
+
</button>
|
|
1563
|
+
{aa.lastRunAt ? <span className="maint-note">上次检查 {fmtDate(aa.lastRunAt)},归档 {aa.lastArchivedCount} 个</span> : <span className="maint-note">尚未检查</span>}
|
|
1553
1564
|
</div>
|
|
1554
1565
|
|
|
1555
1566
|
{/* T4 标签管理 sheet:照自动归档/待移动队列的 mv-sheet 先例。
|
|
@@ -1636,17 +1647,6 @@ function SessionPanel({ workspacesSvc }) {
|
|
|
1636
1647
|
<div className="dtl-note">标签是会话的自定义标记:新建、重命名、合并、删除都只改标记本身,不会删除或移动任何会话。单个会话的标签数量与标签总数都有服务端上限,超限时会提示并自动回退本地改动。</div>
|
|
1637
1648
|
</div>
|
|
1638
1649
|
)}
|
|
1639
|
-
|
|
1640
|
-
{/* 维护栏是面板级工具,与当前查看哪一组会话无关,故所有视图都显示。 */}
|
|
1641
|
-
<div className="maint-bar">
|
|
1642
|
-
<button type="button" className="archv-btn" aria-expanded={storageOpen} onClick={() => { const next = !storageOpen; setStorageOpen(next); if (next && (!storage || storageDirty.current) && !storageBusy) loadStorage() }}>
|
|
1643
|
-
存储占用{storage ? ` · ${fmtBytes(storage.totalBytes) || '0 B'}` : ''}
|
|
1644
|
-
</button>
|
|
1645
|
-
<button type="button" className={'archv-btn' + (aa.settings.inactiveDays ? ' archv-go' : '')} aria-expanded={aaOpen} onClick={() => setAaOpen(!aaOpen)}>
|
|
1646
|
-
自动归档{aa.settings.inactiveDays ? `:${aa.settings.inactiveDays} 天未活跃` : ':未启用'}
|
|
1647
|
-
</button>
|
|
1648
|
-
{aa.lastRunAt ? <span className="maint-note">上次检查 {fmtDate(aa.lastRunAt)},归档 {aa.lastArchivedCount} 个</span> : <span className="maint-note">尚未检查</span>}
|
|
1649
|
-
</div>
|
|
1650
1650
|
{pendingQueue.length > 0 && (
|
|
1651
1651
|
<div className="mv-sheet" aria-label="待移动队列">
|
|
1652
1652
|
<div className="mv-sheet-head">
|
package/src/handle-era-ops.js
CHANGED
|
@@ -129,6 +129,37 @@ function failureText(e) {
|
|
|
129
129
|
return `${(e && e.name) || ''} ${(e && e.message) || e}`
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
+
// 0.1.7 起官方把 Session 格式升到 v4,create() 严格要求 header.version ===
|
|
133
|
+
// 后端当前版本(`encodeCurrent requires Session format vN`)。旧代(v2/v3)
|
|
134
|
+
// 会话的 header 直接透传必炸。从该错误里解析后端声明的当前版本号。
|
|
135
|
+
function requiredFormatVersionFromError(e) {
|
|
136
|
+
const m = /requires Session format v(\d+)/.exec(String((e && e.message) || ''))
|
|
137
|
+
return m ? Number(m[1]) : null
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// 官方 v2→v4 一直接受的 header 字段。旧代 header 可能携带后续版本已移除的
|
|
141
|
+
// 字段(如 seedLength),而 v4 起严格拒绝任何未知字段("unexpected field"),
|
|
142
|
+
// 重试前必须把 header 收敛到这个长期稳定集合。
|
|
143
|
+
const OFFICIAL_HEADER_FIELDS = ['id', 'createdAt', 'cwd', 'isSeeded', 'delegationDepth', 'parentSession', 'origin', 'agentPreset']
|
|
144
|
+
|
|
145
|
+
// 兼容性 create:先用调用方/采样得到的 header 直接建;若后端以「版本不符」
|
|
146
|
+
// 拒绝,则把版本号提到后端声明的当前值、字段收敛到官方稳定集合后重试一次。
|
|
147
|
+
// 只重试这一类错误:真正的词汇表/事件校验错误原样上抛,绝不静默降级。
|
|
148
|
+
async function createWithCompatibleHeader(sp, header, createOptions) {
|
|
149
|
+
try {
|
|
150
|
+
return await sp.create(header, createOptions)
|
|
151
|
+
} catch (e) {
|
|
152
|
+
const required = requiredFormatVersionFromError(e)
|
|
153
|
+
if (required === null || header.version === required) throw e
|
|
154
|
+
const normalized = {}
|
|
155
|
+
for (const key of OFFICIAL_HEADER_FIELDS) {
|
|
156
|
+
if (Object.hasOwn(header, key)) normalized[key] = header[key]
|
|
157
|
+
}
|
|
158
|
+
normalized.version = required
|
|
159
|
+
return await sp.create(normalized, createOptions)
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
132
163
|
function isAlreadyOwned(e) {
|
|
133
164
|
return /already owned/i.test(failureText(e))
|
|
134
165
|
}
|
|
@@ -278,7 +309,20 @@ export async function moveSessionToCwd({ sp, sid, header, canonical, events = []
|
|
|
278
309
|
error.code = 'DSM_SESSION_DUP_LOG'
|
|
279
310
|
throw error
|
|
280
311
|
}
|
|
281
|
-
const
|
|
312
|
+
const newHeaderBase = Object.assign({}, header, { cwd: canonical })
|
|
313
|
+
// 写所有权探测可能已为旧代会话发布了当前代;探测成功后再用官方 stat 重新
|
|
314
|
+
// 采样一次 header —— 后端自己编码的 header 天然携带当前格式版本与完整词汇表
|
|
315
|
+
// (0.1.7 的 create 严格要求 header.version === 后端当前版本)。stat 失败则
|
|
316
|
+
// 保留调用方 header,交给 createWithCompatibleHeader 的错误驱动重试兜底。
|
|
317
|
+
let createHeader = newHeaderBase
|
|
318
|
+
if (typeof sp.stat === 'function') {
|
|
319
|
+
try {
|
|
320
|
+
const fresh = await sp.stat(sid)
|
|
321
|
+
if (fresh && fresh.header && fresh.header.id === sid) {
|
|
322
|
+
createHeader = Object.assign({}, fresh.header, { cwd: canonical })
|
|
323
|
+
}
|
|
324
|
+
} catch (e) { /* 保持调用方 header */ }
|
|
325
|
+
}
|
|
282
326
|
const firstSeq = events.length ? Number(events[0].seq) : 0
|
|
283
327
|
const replay = firstSeq !== 0 ? events.map((event, index) => ({ ...event, seq: index })) : events
|
|
284
328
|
const createOptions = header.isSeeded && Number.isSafeInteger(inheritedEventCount) && inheritedEventCount > 0
|
|
@@ -298,7 +342,7 @@ export async function moveSessionToCwd({ sp, sid, header, canonical, events = []
|
|
|
298
342
|
let writer = null
|
|
299
343
|
try {
|
|
300
344
|
try {
|
|
301
|
-
writer = await sp
|
|
345
|
+
writer = await createWithCompatibleHeader(sp, createHeader, createOptions)
|
|
302
346
|
for (let i = 0; i < replay.length; i += MOVE_BATCH) {
|
|
303
347
|
await writer.append(replay.slice(i, i + MOVE_BATCH))
|
|
304
348
|
}
|