@morlay/ui-conversation-message-actions 0.0.13 → 0.0.14-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.
- package/dist/client.js +28 -28
- package/dist/index.mjs +1 -1
- package/dist/{src-CXYW0Bc0.mjs → src-Da2ZVKlq.mjs} +19 -10
- package/dist/testing.d.mts +3 -1
- package/dist/testing.mjs +5415 -788
- package/package.json +19 -19
- package/src/client/chat-node/MessageItem.tsx +4 -5
- package/src/index.ts +39 -16
- package/src/plan.ts +4 -1
- package/src/testing.ts +9 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@morlay/ui-conversation-message-actions",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.14-alpha.0",
|
|
4
4
|
"description": "Rewind / retry / fork orchestration (host) plus conversation.chat.node renderer replacement (browser) — message edit / retry actions on user bubbles.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"conversation-chat-node",
|
|
@@ -35,10 +35,10 @@
|
|
|
35
35
|
"./client": "./dist/client.js"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@morlay/session-branch": "^0.0.
|
|
38
|
+
"@morlay/session-branch": "^0.0.4-alpha.0"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
|
-
"@morlay/session-rdb": "^0.0.
|
|
41
|
+
"@morlay/session-rdb": "^0.0.16-alpha.0",
|
|
42
42
|
"@types/react": "^19.2.18",
|
|
43
43
|
"@types/react-dom": "^19.2.5",
|
|
44
44
|
"lightningcss": "^1.32.0",
|
|
@@ -47,22 +47,22 @@
|
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
49
|
"@deepseek-ai/cordis": "^4.0.2",
|
|
50
|
-
"@deepseek-ai/dsh-api-session-controller": "^0.1.
|
|
51
|
-
"@deepseek-ai/dsh-attachment": "^0.1.
|
|
52
|
-
"@deepseek-ai/dsh-client-connection": "^0.1.
|
|
53
|
-
"@deepseek-ai/dsh-client-store": "^0.1.
|
|
54
|
-
"@deepseek-ai/dsh-client-ui-attachment": "^0.1.
|
|
55
|
-
"@deepseek-ai/dsh-client-ui-chat": "^0.1.
|
|
56
|
-
"@deepseek-ai/dsh-client-ui-conversation": "^0.1.
|
|
57
|
-
"@deepseek-ai/dsh-client-ui-primitives": "^0.1.
|
|
58
|
-
"@deepseek-ai/dsh-client-ui-renderer": "^0.1.
|
|
59
|
-
"@deepseek-ai/dsh-client-ui-slots": "^0.1.
|
|
60
|
-
"@deepseek-ai/dsh-invariants": "^0.1.
|
|
61
|
-
"@deepseek-ai/dsh-llm": "^0.1.
|
|
62
|
-
"@deepseek-ai/dsh-session": "^0.1.
|
|
63
|
-
"@deepseek-ai/dsh-session-persistence": "^0.1.
|
|
64
|
-
"@deepseek-ai/dsh-session-projection": "^0.1.
|
|
65
|
-
"@deepseek-ai/dsh-token-meter": "^0.1.
|
|
50
|
+
"@deepseek-ai/dsh-api-session-controller": "^0.1.3-alpha.2",
|
|
51
|
+
"@deepseek-ai/dsh-attachment": "^0.1.3-alpha.2",
|
|
52
|
+
"@deepseek-ai/dsh-client-connection": "^0.1.3-alpha.2",
|
|
53
|
+
"@deepseek-ai/dsh-client-store": "^0.1.3-alpha.2",
|
|
54
|
+
"@deepseek-ai/dsh-client-ui-attachment": "^0.1.3-alpha.2",
|
|
55
|
+
"@deepseek-ai/dsh-client-ui-chat": "^0.1.3-alpha.2",
|
|
56
|
+
"@deepseek-ai/dsh-client-ui-conversation": "^0.1.3-alpha.2",
|
|
57
|
+
"@deepseek-ai/dsh-client-ui-primitives": "^0.1.3-alpha.2",
|
|
58
|
+
"@deepseek-ai/dsh-client-ui-renderer": "^0.1.3-alpha.2",
|
|
59
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.1.3-alpha.2",
|
|
60
|
+
"@deepseek-ai/dsh-invariants": "^0.1.3-alpha.2",
|
|
61
|
+
"@deepseek-ai/dsh-llm": "^0.1.3-alpha.2",
|
|
62
|
+
"@deepseek-ai/dsh-session": "^0.1.3-alpha.2",
|
|
63
|
+
"@deepseek-ai/dsh-session-persistence": "^0.1.3-alpha.2",
|
|
64
|
+
"@deepseek-ai/dsh-session-projection": "^0.1.3-alpha.2",
|
|
65
|
+
"@deepseek-ai/dsh-token-meter": "^0.1.3-alpha.2"
|
|
66
66
|
},
|
|
67
67
|
"dsh": {
|
|
68
68
|
"bundle": {
|
|
@@ -8,7 +8,7 @@ import type { ReactNode } from "react";
|
|
|
8
8
|
import type { InjectFace } from "@deepseek-ai/dsh-client-ui-slots";
|
|
9
9
|
import { Button, Modal } from "@deepseek-ai/dsh-client-ui-primitives";
|
|
10
10
|
import type { UserMessageNode } from "@deepseek-ai/dsh-client-ui-chat/client";
|
|
11
|
-
import { JsonBlock
|
|
11
|
+
import { JsonBlock } from "@deepseek-ai/dsh-client-ui-primitives";
|
|
12
12
|
import type { ChatNodeViewProps, ChatViewSlotProps } from "@deepseek-ai/dsh-client-ui-chat/client";
|
|
13
13
|
import type { RenderMessageImages } from "@deepseek-ai/dsh-client-ui-conversation/client";
|
|
14
14
|
import { MessageIconActions } from "./MessageIconActions.tsx";
|
|
@@ -45,8 +45,7 @@ function projectUserText(text: string): ReactNode {
|
|
|
45
45
|
while ((m = re.exec(text)) !== null) {
|
|
46
46
|
const tokenStart = m.index + (m[1]?.length ?? 0);
|
|
47
47
|
const label = m[2] ?? "";
|
|
48
|
-
if (tokenStart > cursor)
|
|
49
|
-
parts.push(<MessageText key={cursor} text={text.slice(cursor, tokenStart)} />);
|
|
48
|
+
if (tokenStart > cursor) parts.push(<span key={cursor}>{text.slice(cursor, tokenStart)}</span>);
|
|
50
49
|
parts.push(
|
|
51
50
|
<span
|
|
52
51
|
key={tokenStart}
|
|
@@ -58,8 +57,8 @@ function projectUserText(text: string): ReactNode {
|
|
|
58
57
|
);
|
|
59
58
|
cursor = tokenStart + label.length;
|
|
60
59
|
}
|
|
61
|
-
if (parts.length === 0) return <
|
|
62
|
-
if (cursor < text.length) parts.push(<
|
|
60
|
+
if (parts.length === 0) return <span>{text}</span>;
|
|
61
|
+
if (cursor < text.length) parts.push(<span key={cursor}>{text.slice(cursor)}</span>);
|
|
63
62
|
return <>{parts}</>;
|
|
64
63
|
}
|
|
65
64
|
|
package/src/index.ts
CHANGED
|
@@ -142,10 +142,9 @@ function appendManualTurn(
|
|
|
142
142
|
appendSurfaceSeedEvent(
|
|
143
143
|
events,
|
|
144
144
|
"assistant/message",
|
|
145
|
-
{ turn, step: 1, message: assistant },
|
|
145
|
+
{ turn, step: 1, message: assistant, stream: [] },
|
|
146
146
|
{
|
|
147
147
|
surfaceOp: "append",
|
|
148
|
-
sourceEventSeqs: [],
|
|
149
148
|
},
|
|
150
149
|
);
|
|
151
150
|
appendLogSeedEvent(events, "step/end", { turn, step: 1 });
|
|
@@ -155,7 +154,11 @@ function appendManualTurn(
|
|
|
155
154
|
});
|
|
156
155
|
}
|
|
157
156
|
|
|
158
|
-
function appendSeedSuffixLive(
|
|
157
|
+
async function appendSeedSuffixLive(
|
|
158
|
+
session: Session,
|
|
159
|
+
seedSuffix: readonly SessionEvent[],
|
|
160
|
+
appendDirect: (events: readonly SessionEvent[]) => Promise<void>,
|
|
161
|
+
): Promise<void> {
|
|
159
162
|
for (const event of seedSuffix) {
|
|
160
163
|
// 版本效果事件携带 ignorable 标记(上游类型无此字段,duck-type 读取)。
|
|
161
164
|
const ignorable = (event as { ignorable?: boolean }).ignorable === true;
|
|
@@ -164,10 +167,13 @@ function appendSeedSuffixLive(session: Session, seedSuffix: readonly SessionEven
|
|
|
164
167
|
log: SessionEvent[];
|
|
165
168
|
eventsSnapshot?: unknown;
|
|
166
169
|
};
|
|
167
|
-
//
|
|
168
|
-
//
|
|
169
|
-
//
|
|
170
|
-
|
|
170
|
+
// 原样存储语义:版本效果事件经 RDB write handle 直接落库(带
|
|
171
|
+
// ignorable 信封,与 JSONL 一致)。session.append 不保留 ignorable
|
|
172
|
+
// 标记且类型系统禁止 append 未知类型,因此不发布、直接 push 内存
|
|
173
|
+
// log(seq 按 log 续接重编号,与 handle cursor 同空间)。
|
|
174
|
+
const seq = s.log.length;
|
|
175
|
+
await appendDirect([{ ...event, seq } as SessionEvent]);
|
|
176
|
+
s.log.push({ ...event, seq } as SessionEvent);
|
|
171
177
|
s.eventsSnapshot = undefined;
|
|
172
178
|
continue;
|
|
173
179
|
}
|
|
@@ -308,10 +314,25 @@ export class SessionEditor extends Service {
|
|
|
308
314
|
await this.ctx.sessionBranch.rewind(operation.sessionId, boundary, signal);
|
|
309
315
|
if (seedSuffix.length > 0) {
|
|
310
316
|
if (live !== undefined) {
|
|
311
|
-
// live
|
|
312
|
-
//
|
|
313
|
-
|
|
314
|
-
this.ctx.
|
|
317
|
+
// live:版本效果经 RDB write handle 直接落库(带 ignorable 信封,
|
|
318
|
+
// 与 JSONL 一致)、manualTurn 走 append;同步 cursor 后显式 flush
|
|
319
|
+
// (直接落库不发布、不进缓冲,cursor 会落后)。
|
|
320
|
+
const persistence = this.ctx.sessionPersistence as unknown as {
|
|
321
|
+
tracker: {
|
|
322
|
+
writerOf(id: SessionId):
|
|
323
|
+
| {
|
|
324
|
+
append(events: readonly SessionEvent[]): Promise<void>;
|
|
325
|
+
}
|
|
326
|
+
| undefined;
|
|
327
|
+
};
|
|
328
|
+
};
|
|
329
|
+
const handle = persistence.tracker.writerOf(operation.sessionId);
|
|
330
|
+
if (handle === undefined) {
|
|
331
|
+
throw new Error(
|
|
332
|
+
`session "${operation.sessionId}" has no live write handle for the version effect`,
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
await appendSeedSuffixLive(live, seedSuffix, (events) => handle.append(events));
|
|
315
336
|
await this.ctx.sessions.flush(live);
|
|
316
337
|
} else {
|
|
317
338
|
// cold:续写 seq 从平衡后的保留前缀接续(exclusive 截断可能残留
|
|
@@ -325,7 +346,12 @@ export class SessionEditor extends Service {
|
|
|
325
346
|
seq: keepLength + index,
|
|
326
347
|
}) as SessionEvent,
|
|
327
348
|
);
|
|
328
|
-
await this.ctx.sessionPersistence.
|
|
349
|
+
const handle = await this.ctx.sessionPersistence.open(operation.sessionId, "write");
|
|
350
|
+
try {
|
|
351
|
+
await handle.append(renumbered);
|
|
352
|
+
} finally {
|
|
353
|
+
await handle.close();
|
|
354
|
+
}
|
|
329
355
|
}
|
|
330
356
|
}
|
|
331
357
|
|
|
@@ -401,10 +427,7 @@ export class SessionEditor extends Service {
|
|
|
401
427
|
const fallbackProvider = config?.provider ?? "";
|
|
402
428
|
const fallbackModel = config?.model ?? "";
|
|
403
429
|
if (fallbackProvider.length === 0 || fallbackModel.length === 0) {
|
|
404
|
-
throw new SessionBranchError(
|
|
405
|
-
"无法重放:会话没有可解析的模型配置。",
|
|
406
|
-
"INVALID_BOUNDARY",
|
|
407
|
-
);
|
|
430
|
+
throw new SessionBranchError("无法重放:会话没有可解析的模型配置。", "INVALID_BOUNDARY");
|
|
408
431
|
}
|
|
409
432
|
const handle = await agents.resume({
|
|
410
433
|
resumeSessionId: sessionId,
|
package/src/plan.ts
CHANGED
|
@@ -298,7 +298,10 @@ export function retryPlan(operation: RetryOperation, turns: readonly ClosedTurn[
|
|
|
298
298
|
};
|
|
299
299
|
}
|
|
300
300
|
|
|
301
|
-
export function rerollPlan(
|
|
301
|
+
export function rerollPlan(
|
|
302
|
+
operation: RerollOperation,
|
|
303
|
+
turns: readonly ClosedTurn[],
|
|
304
|
+
): OperationPlan {
|
|
302
305
|
for (let index = turns.length - 1; index >= 0; index -= 1) {
|
|
303
306
|
const turn = turns[index];
|
|
304
307
|
// 未闭合轮次无已落定的助手回复可重生成。
|
package/src/testing.ts
CHANGED
|
@@ -65,17 +65,16 @@ export async function createPersisted(
|
|
|
65
65
|
events: readonly SessionEvent[],
|
|
66
66
|
header: SessionHeader = meta(id),
|
|
67
67
|
): Promise<void> {
|
|
68
|
-
await ctx.sessionPersistence.create(header);
|
|
69
|
-
|
|
68
|
+
const handle = await ctx.sessionPersistence.create(header);
|
|
69
|
+
try {
|
|
70
|
+
await handle.append([...events]);
|
|
71
|
+
} finally {
|
|
72
|
+
await handle.close();
|
|
73
|
+
}
|
|
70
74
|
}
|
|
71
75
|
|
|
72
76
|
/** 构造一条 user/message 事件。 */
|
|
73
|
-
export function userMessage(
|
|
74
|
-
seq: number,
|
|
75
|
-
id: string,
|
|
76
|
-
text: string,
|
|
77
|
-
time = seq,
|
|
78
|
-
): SessionEvent {
|
|
77
|
+
export function userMessage(seq: number, id: string, text: string, time = seq): SessionEvent {
|
|
79
78
|
return {
|
|
80
79
|
type: "user/message",
|
|
81
80
|
seq: SessionSeq(seq),
|
|
@@ -112,9 +111,10 @@ export function assistantMessage(
|
|
|
112
111
|
content: [{ type: "text", text }],
|
|
113
112
|
source: { kind: "model", provider: "mock", model: "mock" },
|
|
114
113
|
},
|
|
114
|
+
stream: [] as const,
|
|
115
115
|
},
|
|
116
116
|
surfaceOp: "append",
|
|
117
|
-
} as SessionEvent;
|
|
117
|
+
} as unknown as SessionEvent;
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
/** 真实 agent-loop 形状的一轮:轮首输入 + 可选 followup,可闭合。 */
|