dsh-mini-remote 0.1.0 → 0.1.4

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/lib/history.js ADDED
@@ -0,0 +1,72 @@
1
+ /**
2
+ * 把一个会话**已经跑完的历史**重放成手机上要显示的那一串。
3
+ *
4
+ * 为什么需要它:手机能点开**全部**会话(一个工作区里常有上百条),而插件手里那份
5
+ * 记录只有「它在场时看见过的事件」——重启前跑过的、在电脑上开的会话,点进去是空的。
6
+ * 用户 2026-09-25 提的要求是:点开一个会话,就该看到它之前的每一轮。
7
+ *
8
+ * 数据来源是 DSH 自己的会话记录(`sessionQuery.readSession`,见 index.js),
9
+ * 这里只负责把那一串原始事件**重放**成聊天记录。
10
+ *
11
+ * **关键:提取器必须和实时那条路是同一个**(`events.js` 的 `createTurnTracker`)。
12
+ * 两条路各写一套判断的话,「哪条才算最终回答」迟早会走散——一边显示旁白、
13
+ * 一边显示回答,用户看到的就是同一个会话在两个地方长得不一样。
14
+ */
15
+ import { createTurnTracker } from './events.js'
16
+ import { foldCommandEvent } from './commands.js'
17
+
18
+ /**
19
+ * @param {Array<{type:string, time?:number, data?:any}>} events DSH 的原始事件,**按发生顺序**。
20
+ * @returns {Array<{role:'user'|'assistant'|'command', text?:string|null, timestamp:number|null,
21
+ * id?:string|null, reason?:string, interrupted?:boolean,
22
+ * commandId?:string, name?:string, args?:string, kind?:string}>}
23
+ */
24
+ export function replayHistory(events) {
25
+ const tracker = createTurnTracker()
26
+ const out = []
27
+ for (const event of events ?? []) {
28
+ // 斜杠指令(`command/run` / `command/done`)不是「一轮」,提取器不认它——
29
+ // 可它确实写在会话记录里,电脑端也确实画在正文里。用和实时那条路**同一个**
30
+ // 折叠函数(lib/commands.js)接住,两边才不会一个显示「执行中」、一个显示结果。
31
+ if (foldCommandEvent(out, event)) continue
32
+ // 会话 id 在这里没有意义:一个会话的日志只会喂给一个 tracker,
33
+ // 传个固定值就行,不必把 id 一路带进来。
34
+ const action = tracker.feed('replay', event)
35
+ if (!action) continue
36
+ const timestamp = typeof event?.time === 'number' ? event.time : null
37
+ if (action.kind === 'user') {
38
+ out.push({ role: 'user', text: action.text, id: action.id ?? null, timestamp })
39
+ } else {
40
+ out.push({
41
+ role: 'assistant',
42
+ text: action.text,
43
+ timestamp,
44
+ reason: action.reason,
45
+ interrupted: action.interrupted === true,
46
+ })
47
+ }
48
+ }
49
+ return out
50
+ }
51
+
52
+ /**
53
+ * 从重放出来的记录里取最后一条最终回复,做成单帧模式要的那个形状。
54
+ *
55
+ * 返回 null 表示「这个会话还没有过最终回复」——那时单帧模式该显示空,
56
+ * **不能拿用户自己那句话顶上**,也不能拿旁白顶上。
57
+ */
58
+ export function lastReply(entries, sessionId) {
59
+ for (let i = (entries?.length ?? 0) - 1; i >= 0; i--) {
60
+ const m = entries[i]
61
+ if (m.role !== 'assistant') continue
62
+ return {
63
+ text: m.text,
64
+ sessionId,
65
+ title: '',
66
+ timestamp: m.timestamp,
67
+ reason: m.reason,
68
+ interrupted: m.interrupted === true,
69
+ }
70
+ }
71
+ return null
72
+ }