dsh-edit-turn 0.5.10 → 0.5.12
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.md +14 -0
- package/lib/client.js +4 -31
- package/lib/index.js +23 -12
- package/package.json +7 -4
package/README.md
CHANGED
|
@@ -277,6 +277,20 @@ ln -s /path/to/dsh-install/node_modules ./node_modules
|
|
|
277
277
|
|
|
278
278
|
### 更新日志
|
|
279
279
|
|
|
280
|
+
**0.5.12** —— `/state` 只折一次表面;临时诊断撤掉。
|
|
281
|
+
|
|
282
|
+
- **复制已确认修好**(根因是 `/state` 太大太慢:25 MB / 13 秒,那段时间里浏览器半部没有任何编辑条目,按下复制不会被接管)。所以 0.5.10 的自报诊断按计划撤掉。
|
|
283
|
+
|
|
284
|
+
- **再提速**:`rollbackLedger` 自己又折了一遍表面,而 `stateOf` 已经折过一次 —— 31k 事件的会话等于把整份日志走了两遍,还各自重建了 `bySeq`、`turnIndex`。现在折一次、索引复用,四个遍历共用同一份。实测同一份日志:`editableReplies` 的轻量路径 **2,227 ms → 29 ms**(0.5.11),这一版再省掉重复的那一遍折叠。
|
|
285
|
+
|
|
286
|
+
- **缓存试过又撤了**:按"会话 + 事件数 + 配置"缓存 state 能把重复打开变成瞬时,但测试里暴露出陈旧风险(状态快照没跟上新事件时,键不变、结果却是旧的)。宁可慢一点,也不要给编辑器一份过期的状态。这条以后要做得从宿主的 seq 严格判定失效。
|
|
287
|
+
|
|
288
|
+
**0.5.11** —— `/state` 的回复列表加轻量路径:不再为每条回复构建分块。
|
|
289
|
+
|
|
290
|
+
- 0.5.9 把回复全文从 `/state` 拿掉了,但服务端**仍然为每条回复构建了全部分块**(还要为每条回复遍历它那一整轮的所有事件做合并)。实测一个会话 290 条回复:**2,227 ms、22.4 MB 的中间数据** —— 而 `/state` 只需要身份。
|
|
291
|
+
|
|
292
|
+
- 新增 `light` 选项:跳过分块构建与"整轮合并",只保留判定所需的字段。同一份日志:**29 ms、1.1 MB**(77 倍)。`/reply`(按需取)仍走完整路径。
|
|
293
|
+
|
|
280
294
|
**0.5.10** —— 临时诊断:复制没被接管时,把原因写进标签页标题。
|
|
281
295
|
|
|
282
296
|
- 这台机器上没有可用的浏览器(浏览器工具找不到 Chrome),拿不到真实 DOM,而两次按代码推断的修法都没命中。所以加一条**自报**:如果这次复制按下没被接管,标签页标题会变成 `[复制诊断] <原因>`(例如「这一行没登记文本(登记 3 行)」「按钮不在行内」「写入没匹配上」);接管成功时标题自动恢复。不污染剪贴板、不改页面内容。
|
package/lib/client.js
CHANGED
|
@@ -45,7 +45,7 @@ window.__ModuleLoader__.load({
|
|
|
45
45
|
const RERUN_PREFIX = '/dsh-rerun-turn'
|
|
46
46
|
|
|
47
47
|
/** Keep in sync with package.json and lib/index.js. */
|
|
48
|
-
const PLUGIN_VERSION = '0.5.
|
|
48
|
+
const PLUGIN_VERSION = '0.5.12'
|
|
49
49
|
|
|
50
50
|
// --- copy -----------------------------------------------------------------
|
|
51
51
|
|
|
@@ -2001,24 +2001,6 @@ window.__ModuleLoader__.load({
|
|
|
2001
2001
|
})()
|
|
2002
2002
|
const rowCopyText = copyRegistry.text
|
|
2003
2003
|
|
|
2004
|
-
// TEMP DIAG (删除时机:复制修好后). 复制按下没被接管时,把原因写进标签页标题:
|
|
2005
|
-
// 不污染剪贴板、不改页面内容,用户念一眼标题即可;成功接管时自动恢复原标题。
|
|
2006
|
-
const copyDiag = (page) => {
|
|
2007
|
-
try {
|
|
2008
|
-
if (page.__dshetTitle === undefined) page.__dshetTitle = document.title
|
|
2009
|
-
const base = String(page.__dshetTitle ?? '')
|
|
2010
|
-
const show = (reason) => {
|
|
2011
|
-
document.title = `[复制诊断] ${reason}${base === '' ? '' : ' · ' + base}`
|
|
2012
|
-
}
|
|
2013
|
-
const clear = () => {
|
|
2014
|
-
if (String(document.title).startsWith('[复制诊断]')) document.title = base
|
|
2015
|
-
}
|
|
2016
|
-
return { show, clear }
|
|
2017
|
-
} catch {
|
|
2018
|
-
return { show: () => {}, clear: () => {} }
|
|
2019
|
-
}
|
|
2020
|
-
}
|
|
2021
|
-
const titleDiag = typeof globalThis !== 'undefined' ? copyDiag(globalThis) : { show: () => {}, clear: () => {} }
|
|
2022
2004
|
const rowHostText = copyRegistry.host
|
|
2023
2005
|
|
|
2024
2006
|
/**
|
|
@@ -2150,11 +2132,6 @@ window.__ModuleLoader__.load({
|
|
|
2150
2132
|
const native = clipboard.writeText.bind(clipboard)
|
|
2151
2133
|
const wrapped = (value) => {
|
|
2152
2134
|
const rewritten = rewriteCopiedText(String(value ?? ''))
|
|
2153
|
-
if (rewritten === null && rowCopyText.size > 0) {
|
|
2154
|
-
titleDiag.show(`写入没匹配上(登记 ${rowCopyText.size} 行,长度 ${String(value ?? '').length})`)
|
|
2155
|
-
} else if (rewritten !== null) {
|
|
2156
|
-
titleDiag.clear()
|
|
2157
|
-
}
|
|
2158
2135
|
return rewritten === null ? native(value) : native(rewritten)
|
|
2159
2136
|
}
|
|
2160
2137
|
wrapped[CLIPBOARD_WRAPPED] = true
|
|
@@ -4995,15 +4972,11 @@ window.__ModuleLoader__.load({
|
|
|
4995
4972
|
// Our own buttons sit in the same bar: theirs is not this press.
|
|
4996
4973
|
if (pluginOwned(button)) return
|
|
4997
4974
|
const row = button.closest('[data-chat-flow-key]')
|
|
4998
|
-
if (row === null)
|
|
4975
|
+
if (row === null) return
|
|
4999
4976
|
const key = row.getAttribute('data-chat-flow-key')
|
|
5000
|
-
if (typeof key !== 'string' || key === '')
|
|
4977
|
+
if (typeof key !== 'string' || key === '') return
|
|
5001
4978
|
const text = rowCopyText.get(key)
|
|
5002
|
-
if (typeof text !== 'string')
|
|
5003
|
-
titleDiag.show(`这一行没登记文本(登记 ${rowCopyText.size} 行)`)
|
|
5004
|
-
return
|
|
5005
|
-
}
|
|
5006
|
-
titleDiag.clear()
|
|
4979
|
+
if (typeof text !== 'string') return
|
|
5007
4980
|
const write = primitives !== null && primitives !== undefined && typeof primitives.writeClipboard === 'function'
|
|
5008
4981
|
? primitives.writeClipboard
|
|
5009
4982
|
: null
|
package/lib/index.js
CHANGED
|
@@ -78,7 +78,7 @@ export const PLUGIN_ID = 'dsh-edit-turn'
|
|
|
78
78
|
export const name = PLUGIN_ID
|
|
79
79
|
|
|
80
80
|
/** Keep in sync with package.json and lib/client.js. */
|
|
81
|
-
export const PLUGIN_VERSION = '0.5.
|
|
81
|
+
export const PLUGIN_VERSION = '0.5.12'
|
|
82
82
|
|
|
83
83
|
// A ring of the last few requests, answering "did the browser half even ask, and
|
|
84
84
|
// what did it get?" without needing its console. Read-only diagnostics: the
|
|
@@ -699,10 +699,13 @@ function copyBlock(block) {
|
|
|
699
699
|
* @returns `{ hidden, edits }`: one hidden entry per shadowed seq, and one
|
|
700
700
|
* record per landed rollback.
|
|
701
701
|
*/
|
|
702
|
-
export function rollbackLedger(events) {
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
702
|
+
export function rollbackLedger(events, options = {}) {
|
|
703
|
+
// A caller that already folded the surface (the state route does) hands it in:
|
|
704
|
+
// folding twice over a long session is the single most expensive thing this
|
|
705
|
+
// route did - 31k events, and every fold walks all of them.
|
|
706
|
+
const folded = options.folded ?? foldSurface(events)
|
|
707
|
+
const bySeq = options.bySeq ?? new Map(events.map((event) => [event.seq, event]))
|
|
708
|
+
const turnOf = options.turnOf ?? turnIndex(events)
|
|
706
709
|
const hidden = []
|
|
707
710
|
const edits = []
|
|
708
711
|
for (const replacement of folded.replacements) {
|
|
@@ -1244,8 +1247,8 @@ export function buildReplayWrites(events, plan, baseTurn, replayId, startSeq, pr
|
|
|
1244
1247
|
* @returns one entry per editable prompt, in conversation order.
|
|
1245
1248
|
*/
|
|
1246
1249
|
export function editableTurns(events, surfaceNodes, options = {}) {
|
|
1247
|
-
const eventBySeq = new Map(events.map((event) => [event.seq, event]))
|
|
1248
|
-
const turnOf = turnIndex(events)
|
|
1250
|
+
const eventBySeq = options.bySeq ?? new Map(events.map((event) => [event.seq, event]))
|
|
1251
|
+
const turnOf = options.turnOf ?? turnIndex(events)
|
|
1249
1252
|
const head = surfaceNodes[0]
|
|
1250
1253
|
const out = []
|
|
1251
1254
|
// Surface order, not log order: a prompt rewritten in place travels as a
|
|
@@ -1301,10 +1304,15 @@ export function editableTurns(events, surfaceNodes, options = {}) {
|
|
|
1301
1304
|
*/
|
|
1302
1305
|
export function editableReplies(events, surfaceNodes, options = {}) {
|
|
1303
1306
|
const nodeIndex = new Map(surfaceNodes.map((seq, index) => [seq, index]))
|
|
1304
|
-
const turnOf = turnIndex(events)
|
|
1307
|
+
const turnOf = options.turnOf ?? turnIndex(events)
|
|
1305
1308
|
const head = surfaceNodes[0]
|
|
1306
1309
|
const patches = options.patches instanceof Map ? options.patches : new Map()
|
|
1307
1310
|
const patch = options.patch === true
|
|
1311
|
+
// `/state` publishes identities only, so the blocks never leave this function.
|
|
1312
|
+
// Skipping them also skips the per-turn widening below, which walks every reply
|
|
1313
|
+
// of the same turn for every reply: that widening is what made one refresh of a
|
|
1314
|
+
// long session take seconds.
|
|
1315
|
+
const light = options.light === true
|
|
1308
1316
|
const out = []
|
|
1309
1317
|
for (const event of events) {
|
|
1310
1318
|
if (!isAssistantReply(event)) continue
|
|
@@ -1324,7 +1332,7 @@ export function editableReplies(events, surfaceNodes, options = {}) {
|
|
|
1324
1332
|
// text of that turn is offered, each annotated with the seq it lives in.
|
|
1325
1333
|
let listed = blocks.map((block) => ({ ...block, seq: event.seq }))
|
|
1326
1334
|
const carriesAnswer = blocks.some((block) => block.kind === 'text' && block.text.trim() !== '')
|
|
1327
|
-
if (patch && carriesAnswer && typeof turn === 'number') {
|
|
1335
|
+
if (!light && patch && carriesAnswer && typeof turn === 'number') {
|
|
1328
1336
|
const acrossTurn = []
|
|
1329
1337
|
for (const other of events) {
|
|
1330
1338
|
if (!isAssistantReply(other) || turnOf.get(other.seq) !== turn) continue
|
|
@@ -1338,6 +1346,7 @@ export function editableReplies(events, surfaceNodes, options = {}) {
|
|
|
1338
1346
|
if (acrossTurn.length > listed.length) listed = acrossTurn
|
|
1339
1347
|
}
|
|
1340
1348
|
out.push({
|
|
1349
|
+
...(light ? {} : { blocks: listed }),
|
|
1341
1350
|
seq: event.seq,
|
|
1342
1351
|
turn: typeof turn === 'number' ? turn : null,
|
|
1343
1352
|
messageId: messageIdOf(event) ?? null,
|
|
@@ -1763,7 +1772,9 @@ async function stateOf(ctx, sessionId, config) {
|
|
|
1763
1772
|
}
|
|
1764
1773
|
}
|
|
1765
1774
|
const folded = foldSurface(events)
|
|
1766
|
-
const
|
|
1775
|
+
const bySeq = new Map(events.map((event) => [event.seq, event]))
|
|
1776
|
+
const turnOf = turnIndex(events)
|
|
1777
|
+
const ledger = rollbackLedger(events, { folded, bySeq, turnOf })
|
|
1767
1778
|
const patches = patchesOf(events)
|
|
1768
1779
|
// A patch whose target has been retired (a later rerun shadowed it) is inert:
|
|
1769
1780
|
// the projection skips it and no row can show it. Reporting it in /state made
|
|
@@ -1777,8 +1788,8 @@ async function stateOf(ctx, sessionId, config) {
|
|
|
1777
1788
|
// now, so a store that is unmounted (or not yet mounted) cannot leave the
|
|
1778
1789
|
// editor promising to keep blocks the host would have to drop.
|
|
1779
1790
|
const store = resolveAttachmentStore(ctx)
|
|
1780
|
-
const turns = editableTurns(events, folded.nodes, { preview: canPreviewAttachments(store) })
|
|
1781
|
-
const replies = editableReplies(events, folded.nodes, { patches, patch: config.patch === true }).map(lightReply)
|
|
1791
|
+
const turns = editableTurns(events, folded.nodes, { preview: canPreviewAttachments(store), bySeq, turnOf })
|
|
1792
|
+
const replies = editableReplies(events, folded.nodes, { patches, patch: config.patch === true, light: true, bySeq, turnOf }).map(lightReply)
|
|
1782
1793
|
noteRequest({
|
|
1783
1794
|
kind: 'state',
|
|
1784
1795
|
sessionId,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-edit-turn",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.12",
|
|
4
4
|
"description": "Edit any message in DeepSeek Harness in place: a prompt is replaced where it stands; a model reply block (answer text or reasoning) is patched through a plugin record and a message projection, so the turn stays where it is, later turns are untouched and the log grows by one small record. Falls back to the replay splice on hosts without the projection API. The append-only session log is never rewritten.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -45,14 +45,17 @@
|
|
|
45
45
|
"verify:ui": "node tools/verify-live-ui.mjs"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
+
"@deepseek-ai/cordis": "^4.0.5",
|
|
49
|
+
"@deepseek-ai/dsh-scope": "^0.2.1-rc.5",
|
|
48
50
|
"@deepseek-ai/schemastery": "^3.18.1"
|
|
49
51
|
},
|
|
50
52
|
"devDependencies": {
|
|
51
|
-
"@deepseek-ai/dsh-session": "^0.2.1-rc.2"
|
|
53
|
+
"@deepseek-ai/dsh-session": "^0.2.1-rc.2",
|
|
54
|
+
"@deepseek-ai/dsh-session-format-catalog": "^0.2.1-rc.5"
|
|
52
55
|
},
|
|
53
56
|
"peerDependencies": {
|
|
54
|
-
"@deepseek-ai/dsh-
|
|
55
|
-
"@deepseek-ai/dsh-
|
|
57
|
+
"@deepseek-ai/dsh-session": "^0.2.0-rc.1 || ^0.2.1-rc.2",
|
|
58
|
+
"@deepseek-ai/dsh-settings": "^0.1.0-rc.7 || ^0.1.1-rc.2 || ^0.1.2-alpha.2 || ^0.1.3-alpha.2 || ^0.1.5-alpha.1 || ^0.2.0-rc.1"
|
|
56
59
|
},
|
|
57
60
|
"dsh": {
|
|
58
61
|
"bundle": {
|