@morlay/ui-conversation-message-actions 0.0.17 → 0.0.19-alpha.0

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.
Files changed (45) hide show
  1. package/README.md +29 -98
  2. package/dist/client.cjs +1380 -0
  3. package/dist/client.d.cts +6 -0
  4. package/dist/client.d.mts +6 -0
  5. package/dist/index.d.cts +98 -0
  6. package/dist/index.d.mts +3 -10
  7. package/dist/index.mjs +460 -2
  8. package/dist/invariant.d.cts +7 -0
  9. package/dist/{plan-BSa7k9HO.d.mts → plan-CT_6Y1fD.d.cts} +11 -9
  10. package/dist/plan-Cm9hCord.d.mts +101 -0
  11. package/dist/plan.d.cts +2 -0
  12. package/dist/plan.d.mts +2 -2
  13. package/dist/plan.mjs +145 -11
  14. package/dist/shared-LK3nImWW.mjs +55 -0
  15. package/dist/testing.d.cts +3242 -0
  16. package/dist/testing.d.mts +25 -19
  17. package/dist/testing.mjs +2140 -3939
  18. package/package.json +35 -32
  19. package/src/client/chat-node/MessageIconActions.styles.ts +76 -0
  20. package/src/client/chat-node/MessageIconActions.tsx +30 -11
  21. package/src/client/chat-node/MessageItem.styles.ts +247 -0
  22. package/src/client/chat-node/MessageItem.tsx +32 -39
  23. package/src/client/chat-node/message-chrome.ts +0 -2
  24. package/src/client/chat-node/register.ts +3 -4
  25. package/src/client/chat-node/use-calendar-day.ts +0 -4
  26. package/src/client/composer-stats/StatsPills.styles.ts +57 -0
  27. package/src/client/composer-stats/StatsPills.tsx +384 -0
  28. package/src/client/composer-stats/register.ts +17 -0
  29. package/src/client/composer-stats/stat-dialog.styles.ts +70 -0
  30. package/src/client/composer-stats/stat-dialog.ts +52 -0
  31. package/src/client/composer-stats/token-format.ts +85 -0
  32. package/src/client/composer-stats/turn-reading.ts +35 -0
  33. package/src/client/controller.ts +32 -114
  34. package/src/client/index.ts +8 -24
  35. package/src/index.ts +102 -111
  36. package/src/plan.ts +189 -24
  37. package/src/shared.ts +1 -1
  38. package/src/testing.ts +1 -5
  39. package/src/types.ts +1 -1
  40. package/dist/client.js +0 -806
  41. package/dist/src-B4HwXsob.mjs +0 -499
  42. package/src/client/chat-node/MessageIconActions.module.css +0 -86
  43. package/src/client/chat-node/MessageItem.module.css +0 -290
  44. package/src/client/css-modules.d.ts +0 -4
  45. package/src/client/import-action.tsx +0 -74
@@ -0,0 +1,101 @@
1
+ import { SessionEvent, SessionId } from "@deepseek-ai/dsh-session";
2
+ import { BranchTimeline, CascadePolicy, SessionBranchVersionEvent } from "@morlay/session-branch";
3
+ import { AssistantMessage, UserMessage } from "@deepseek-ai/dsh-llm";
4
+ //#region src/types.d.ts
5
+ interface EditOperation {
6
+ action: "edit";
7
+ sessionId: SessionId;
8
+ eventSeq: number;
9
+ blockIndex: number;
10
+ text: string;
11
+ cascade: CascadePolicy;
12
+ }
13
+ interface RerollOperation {
14
+ action: "reroll";
15
+ sessionId: SessionId;
16
+ }
17
+ interface RetryOperation {
18
+ action: "retry";
19
+ sessionId: SessionId;
20
+ turn: number;
21
+ cascade: CascadePolicy;
22
+ }
23
+ interface RewindOperation {
24
+ action: "rewind";
25
+ sessionId: SessionId;
26
+ toBoundary: number;
27
+ }
28
+ interface RecallOperation {
29
+ action: "recall";
30
+ sessionId: SessionId;
31
+ eventSeq: number;
32
+ }
33
+ interface ForkOperation {
34
+ action: "fork";
35
+ sessionId: SessionId;
36
+ atSeq?: number;
37
+ childSessionId?: SessionId;
38
+ }
39
+ type SessionEditorOperation = EditOperation | RerollOperation | RetryOperation | RewindOperation | RecallOperation | ForkOperation;
40
+ interface SessionEditorResult {
41
+ sessionId: SessionId;
42
+ queuedTurns: number;
43
+ live?: boolean;
44
+ }
45
+ interface EditableMessageBlock {
46
+ key: string;
47
+ turn?: number;
48
+ eventSeq: number;
49
+ blockIndex: number;
50
+ kind: "user" | "assistant.reasoning" | "assistant.response";
51
+ text: string;
52
+ time: number;
53
+ }
54
+ interface RetryableTurn {
55
+ turn: number;
56
+ userEventSeq: number;
57
+ preview: string;
58
+ time: number;
59
+ }
60
+ //#endregion
61
+ //#region src/plan.d.ts
62
+ interface ClosedTurn {
63
+ turn: number;
64
+ startSeq: number;
65
+ endSeq?: number;
66
+ closed: boolean;
67
+ user?: SessionEvent<"user/message">;
68
+ users: SessionEvent<"user/message">[];
69
+ assistants: SessionEvent<"assistant/message">[];
70
+ }
71
+ interface OperationPlan {
72
+ anchorSeq: number;
73
+ rewindBoundary?: number;
74
+ version: SessionBranchVersionEvent;
75
+ manualTurn?: {
76
+ turn: number;
77
+ user: UserMessage;
78
+ assistant: AssistantMessage;
79
+ };
80
+ queuedUsers: UserMessage[];
81
+ targetSeq: number;
82
+ }
83
+ declare function closedTurns(events: readonly SessionEvent[]): ClosedTurn[];
84
+ declare function editableMessages(turns: readonly ClosedTurn[]): EditableMessageBlock[];
85
+ declare function retryableTurns(turns: readonly ClosedTurn[]): RetryableTurn[];
86
+ declare function precedingContentIndex(turns: readonly ClosedTurn[], turnIndex: number): number;
87
+ declare function downstreamUsers(turns: readonly ClosedTurn[], start: number): UserMessage[];
88
+ declare function recallBoundary(events: readonly SessionEvent[], turns: readonly ClosedTurn[], eventSeq: number): number;
89
+ declare function editPlan(operation: EditOperation, turns: readonly ClosedTurn[]): OperationPlan;
90
+ declare function retryPlan(operation: RetryOperation, turns: readonly ClosedTurn[]): OperationPlan;
91
+ declare function rerollPlan(operation: RerollOperation, turns: readonly ClosedTurn[]): OperationPlan;
92
+ interface CompactionBracket {
93
+ start: SessionEvent;
94
+ summary?: SessionEvent;
95
+ checkpoint: SessionEvent;
96
+ end: SessionEvent;
97
+ }
98
+ declare function droppedCompactions(events: readonly SessionEvent[], keepFrom: number, targetSeq: number): CompactionBracket[];
99
+ declare function restoreCompactions(brackets: readonly CompactionBracket[], keepFrom: number, base: number): SessionEvent[];
100
+ //#endregion
101
+ export { SessionEditorResult as C, SessionEditorOperation as S, RecallOperation as _, downstreamUsers as a, RetryableTurn as b, editableMessages as c, rerollPlan as d, restoreCompactions as f, EditableMessageBlock as g, EditOperation as h, closedTurns as i, precedingContentIndex as l, retryableTurns as m, CompactionBracket as n, droppedCompactions as o, retryPlan as p, OperationPlan as r, editPlan as s, ClosedTurn as t, recallBoundary as u, RerollOperation as v, RewindOperation as x, RetryOperation as y };
@@ -0,0 +1,2 @@
1
+ import { a as downstreamUsers, c as editableMessages, d as rerollPlan, f as restoreCompactions, i as closedTurns, l as precedingContentIndex, m as retryableTurns, n as CompactionBracket, o as droppedCompactions, p as retryPlan, r as OperationPlan, s as editPlan, t as ClosedTurn, u as recallBoundary } from "./plan-CT_6Y1fD.cjs";
2
+ export { ClosedTurn, CompactionBracket, OperationPlan, closedTurns, downstreamUsers, droppedCompactions, editPlan, editableMessages, precedingContentIndex, recallBoundary, rerollPlan, restoreCompactions, retryPlan, retryableTurns };
package/dist/plan.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { a as editPlan, c as recallBoundary, d as retryableTurns, i as downstreamUsers, l as rerollPlan, n as OperationPlan, o as editableMessages, r as closedTurns, s as precedingContentIndex, t as ClosedTurn, u as retryPlan } from "./plan-BSa7k9HO.mjs";
2
- export { ClosedTurn, OperationPlan, closedTurns, downstreamUsers, editPlan, editableMessages, precedingContentIndex, recallBoundary, rerollPlan, retryPlan, retryableTurns };
1
+ import { a as downstreamUsers, c as editableMessages, d as rerollPlan, f as restoreCompactions, i as closedTurns, l as precedingContentIndex, m as retryableTurns, n as CompactionBracket, o as droppedCompactions, p as retryPlan, r as OperationPlan, s as editPlan, t as ClosedTurn, u as recallBoundary } from "./plan-Cm9hCord.mjs";
2
+ export { ClosedTurn, CompactionBracket, OperationPlan, closedTurns, downstreamUsers, droppedCompactions, editPlan, editableMessages, precedingContentIndex, recallBoundary, rerollPlan, restoreCompactions, retryPlan, retryableTurns };
package/dist/plan.mjs CHANGED
@@ -1,3 +1,4 @@
1
+ import { SessionSeq } from "@deepseek-ai/dsh-session";
1
2
  import { SESSION_BRANCH_VERSION_SCHEMA, SessionBranchError } from "@morlay/session-branch";
2
3
  import { randomUUID } from "node:crypto";
3
4
  //#region src/plan.ts
@@ -100,12 +101,6 @@ function retryableTurns(turns) {
100
101
  time: turn.user.time
101
102
  }]);
102
103
  }
103
- /**
104
- * 目标轮之前最后一个「有内容」轮次的下标:跳过既无用户输入也无助手回复的
105
- * 空轮。空轮是早期重放缺陷的遗留(turn/start 后直接 turn/end),在轨迹 /
106
- * 轮次导航里表现为点不开的空项,并让后续轮号出现空洞。rewind 把它们随
107
- * 目标轮一起截断,重放按保留前缀续号即可自愈。
108
- */
109
104
  function precedingContentIndex(turns, turnIndex) {
110
105
  let index = turnIndex - 1;
111
106
  while (index >= 0) {
@@ -187,7 +182,8 @@ function editPlan(operation, turns) {
187
182
  edited,
188
183
  ...sameTurnFollowups,
189
184
  ...later
190
- ]
185
+ ],
186
+ targetSeq: event.seq
191
187
  };
192
188
  }
193
189
  if (!turn.closed) throw new SessionBranchError("未闭合轮次的助手消息不可编辑。", "INVALID_BOUNDARY");
@@ -212,7 +208,8 @@ function editPlan(operation, turns) {
212
208
  user: cloneUser(turn.user.data),
213
209
  assistant: assistantReplacement(event, operation.blockIndex, operation.text)
214
210
  },
215
- queuedUsers: operation.cascade === "preserve" ? downstreamUsers(turns, turnIndex + 1) : []
211
+ queuedUsers: operation.cascade === "preserve" ? downstreamUsers(turns, turnIndex + 1) : [],
212
+ targetSeq: event.seq
216
213
  };
217
214
  }
218
215
  function retryPlan(operation, turns) {
@@ -227,7 +224,8 @@ function retryPlan(operation, turns) {
227
224
  targetTurn: turn.turn,
228
225
  targetEventSeq: turn.user.seq
229
226
  }),
230
- queuedUsers: operation.cascade === "preserve" ? downstreamUsers(turns, turnIndex) : turn.users.map((user) => cloneUser(user.data))
227
+ queuedUsers: operation.cascade === "preserve" ? downstreamUsers(turns, turnIndex) : turn.users.map((user) => cloneUser(user.data)),
228
+ targetSeq: turn.user.seq
231
229
  };
232
230
  }
233
231
  function rerollPlan(operation, turns) {
@@ -244,10 +242,146 @@ function rerollPlan(operation, turns) {
244
242
  targetTurn: turn.turn,
245
243
  targetEventSeq: target.seq
246
244
  }),
247
- queuedUsers: turn.users.map((user) => cloneUser(user.data))
245
+ queuedUsers: turn.users.map((user) => cloneUser(user.data)),
246
+ targetSeq: target.seq
248
247
  };
249
248
  }
250
249
  throw new SessionBranchError("当前会话没有可重生成的已落定助手回复。", "INVALID_BOUNDARY");
251
250
  }
251
+ const COMPACTION_TYPES = /* @__PURE__ */ new Set([
252
+ "compaction/start",
253
+ "compaction/summary",
254
+ "compaction/end"
255
+ ]);
256
+ function compactionTypeOf(event) {
257
+ return COMPACTION_TYPES.has(event.type) ? event.type : void 0;
258
+ }
259
+ function isCompactionCheckpoint(event) {
260
+ if (event.type !== "user/message") return false;
261
+ const op = event.surfaceOp;
262
+ if (typeof op !== "object" || op === null) return false;
263
+ const source = event.data.source;
264
+ return source.kind === "plugin" && source.plugin === "compact";
265
+ }
266
+ function compactionBrackets(events) {
267
+ const brackets = [];
268
+ let open;
269
+ for (const event of events) {
270
+ const type = compactionTypeOf(event);
271
+ if (type === "compaction/start") {
272
+ open = { start: event };
273
+ continue;
274
+ }
275
+ if (open === void 0) continue;
276
+ if (type === "compaction/summary") {
277
+ open.summary = event;
278
+ continue;
279
+ }
280
+ if (type === "compaction/end") {
281
+ if (open.checkpoint !== void 0) brackets.push({
282
+ start: open.start,
283
+ ...open.summary === void 0 ? {} : { summary: open.summary },
284
+ checkpoint: open.checkpoint,
285
+ end: event
286
+ });
287
+ open = void 0;
288
+ continue;
289
+ }
290
+ if (isCompactionCheckpoint(event)) open.checkpoint = event;
291
+ }
292
+ return brackets;
293
+ }
294
+ function droppedCompactions(events, keepFrom, targetSeq) {
295
+ return compactionBrackets(events).filter((bracket) => bracket.start.seq >= keepFrom && bracket.end.seq < targetSeq);
296
+ }
297
+ function replacementRange(event) {
298
+ const op = event.surfaceOp;
299
+ if (op?.op !== "replace") return void 0;
300
+ if (typeof op.startSeq !== "number" || typeof op.endSeq !== "number") return void 0;
301
+ return {
302
+ start: op.startSeq,
303
+ end: op.endSeq
304
+ };
305
+ }
306
+ function numberList(value) {
307
+ return Array.isArray(value) ? value.filter((item) => typeof item === "number") : [];
308
+ }
309
+ function restoreCompactions(brackets, keepFrom, base) {
310
+ const moved = /* @__PURE__ */ new Map();
311
+ const resolve = (seq) => seq < keepFrom ? seq : moved.get(seq);
312
+ const resolveAll = (seqs) => {
313
+ const resolved = [];
314
+ for (const seq of seqs) {
315
+ const next = resolve(seq);
316
+ if (next === void 0) return void 0;
317
+ resolved.push(next);
318
+ }
319
+ return resolved;
320
+ };
321
+ const restored = [];
322
+ for (const bracket of brackets) {
323
+ const range = replacementRange(bracket.checkpoint);
324
+ const shadowedSeqs = resolveAll(numberList((bracket.summary?.data)?.shadowedSeqs));
325
+ const start = range === void 0 ? void 0 : resolve(range.start);
326
+ const end = range === void 0 ? void 0 : resolve(range.end);
327
+ if (start === void 0 || end === void 0 || shadowedSeqs === void 0) continue;
328
+ const at = base + restored.length;
329
+ const summary = bracket.summary;
330
+ const checkpointSeq = at + (summary === void 0 ? 1 : 2);
331
+ moved.set(bracket.start.seq, at);
332
+ if (summary !== void 0) moved.set(summary.seq, at + 1);
333
+ moved.set(bracket.checkpoint.seq, checkpointSeq);
334
+ moved.set(bracket.end.seq, checkpointSeq + 1);
335
+ const sources = [
336
+ at,
337
+ ...summary === void 0 ? [] : [at + 1],
338
+ ...shadowedSeqs
339
+ ];
340
+ restored.push(standaloneCompaction(bracket.start, at), ...summary === void 0 ? [] : [meteringShadowPrice(summary, at + 1, {
341
+ start,
342
+ end
343
+ }, shadowedSeqs)], replacementCheckpoint(bracket.checkpoint, checkpointSeq, {
344
+ start,
345
+ end
346
+ }, sources), standaloneCompaction(bracket.end, checkpointSeq + 1));
347
+ }
348
+ return restored;
349
+ }
350
+ function standaloneCompaction(event, seq) {
351
+ return {
352
+ ...event,
353
+ seq: SessionSeq(seq),
354
+ data: {
355
+ ...event.data,
356
+ turn: null
357
+ }
358
+ };
359
+ }
360
+ function meteringShadowPrice(event, seq, range, shadowedSeqs) {
361
+ return {
362
+ ...event,
363
+ seq: SessionSeq(seq),
364
+ data: {
365
+ ...event.data,
366
+ shadowedRange: {
367
+ start: range.start,
368
+ end: range.end
369
+ },
370
+ shadowedSeqs: [...shadowedSeqs]
371
+ }
372
+ };
373
+ }
374
+ function replacementCheckpoint(event, seq, range, sources) {
375
+ return {
376
+ ...event,
377
+ seq: SessionSeq(seq),
378
+ surfaceOp: {
379
+ op: "replace",
380
+ startSeq: range.start,
381
+ endSeq: range.end
382
+ },
383
+ sourceEventSeqs: [...sources]
384
+ };
385
+ }
252
386
  //#endregion
253
- export { closedTurns, downstreamUsers, editPlan, editableMessages, precedingContentIndex, recallBoundary, rerollPlan, retryPlan, retryableTurns };
387
+ export { closedTurns, downstreamUsers, droppedCompactions, editPlan, editableMessages, precedingContentIndex, recallBoundary, rerollPlan, restoreCompactions, retryPlan, retryableTurns };
@@ -0,0 +1,55 @@
1
+ //#region src/shared.ts
2
+ const SESSION_EDITOR_PATH = "/session-editor";
3
+ function toTimelinePayload(sessionId, timeline, messages, retryableTurns) {
4
+ const currentPath = /* @__PURE__ */ new Set();
5
+ for (const node of timeline.nodes) currentPath.add(String(node.sessionId));
6
+ const versions = timeline.nodes.map((node) => ({
7
+ sessionId: String(node.sessionId),
8
+ ...node.parentSessionId === void 0 ? {} : { parentSessionId: String(node.parentSessionId) },
9
+ ...node.effect === void 0 ? {} : {
10
+ effectId: node.effect.id,
11
+ inverseSessionId: String(node.inverseSessionId),
12
+ operation: node.effect.operation,
13
+ cascade: node.effect.cascade,
14
+ targetTurn: node.effect.targetTurn,
15
+ ...node.effect.blockKind === void 0 ? {} : { blockKind: node.effect.blockKind },
16
+ ...node.effect.before === void 0 ? {} : { before: node.effect.before },
17
+ ...node.effect.after === void 0 ? {} : { after: node.effect.after }
18
+ },
19
+ createdAt: node.createdAt,
20
+ depth: depthOf(timeline, node.sessionId),
21
+ current: String(node.sessionId) === String(sessionId),
22
+ onCurrentEffectPath: currentPath.has(String(node.sessionId))
23
+ }));
24
+ const versionsById = new Map(versions.map((version) => [version.sessionId, version]));
25
+ const undoStack = [];
26
+ let cursor = versionsById.get(String(sessionId));
27
+ while (cursor?.inverseSessionId !== void 0) {
28
+ if (undoStack.includes(cursor.inverseSessionId)) break;
29
+ undoStack.push(cursor.inverseSessionId);
30
+ cursor = versionsById.get(cursor.inverseSessionId);
31
+ }
32
+ const redoSessionIds = versions.filter((version) => version.inverseSessionId === String(sessionId)).map((version) => version.sessionId);
33
+ return {
34
+ sessionId: String(sessionId),
35
+ messages,
36
+ retryableTurns,
37
+ versions,
38
+ undoStack,
39
+ redoSessionIds
40
+ };
41
+ }
42
+ function depthOf(timeline, sessionId) {
43
+ const byId = new Map(timeline.nodes.map((node) => [String(node.sessionId), node]));
44
+ let depth = 0;
45
+ let cursor = byId.get(String(sessionId));
46
+ const seen = /* @__PURE__ */ new Set();
47
+ while (cursor?.parentSessionId !== void 0 && !seen.has(String(cursor.sessionId))) {
48
+ seen.add(String(cursor.sessionId));
49
+ depth += 1;
50
+ cursor = byId.get(String(cursor.parentSessionId));
51
+ }
52
+ return depth;
53
+ }
54
+ //#endregion
55
+ export { toTimelinePayload as n, SESSION_EDITOR_PATH as t };