@morlay/ui-conversation-message-actions 0.0.14-alpha.1 → 0.0.14-alpha.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/README.md +12 -1
- package/dist/client.js +39 -27
- package/dist/index.d.mts +2 -10
- package/dist/index.mjs +1 -1
- package/dist/{plan-Bw4zoI4N.d.mts → plan-BdU_mnTv.d.mts} +9 -2
- package/dist/plan.d.mts +2 -2
- package/dist/plan.mjs +17 -2
- package/dist/{src-Da2ZVKlq.mjs → src-ks167MhO.mjs} +54 -8
- package/dist/testing.d.mts +1 -1
- package/dist/testing.mjs +1293 -233
- package/package.json +19 -19
- package/src/client/chat-node/MessageIconActions.tsx +4 -2
- package/src/client/controller.ts +21 -2
- package/src/index.ts +100 -24
- package/src/plan.ts +20 -3
package/README.md
CHANGED
|
@@ -21,6 +21,12 @@
|
|
|
21
21
|
- **edit / retry / reroll 是就地操作**:`rewind` 截断到目标轮之前的闭合
|
|
22
22
|
`turn/end` 边界,再 `append` 版本效果 + 手工回合 / 重放输入回**同一会话**
|
|
23
23
|
——session id 不变,版本树保持单根;
|
|
24
|
+
- **重放复用目标轮号**:轮首 user 编辑(含未闭合轮)整轮截断,重放接回原
|
|
25
|
+
轮号;编辑 assistant 写入 manualTurn 后同步 agent 的轮次游标;
|
|
26
|
+
- **空轮吸收**:目标轮之前的空轮(`turn/start` 后直接 `turn/end`,早期重放
|
|
27
|
+
缺陷的遗留形状)随截断一并删除,重放按保留前缀续号——轮次导航不再出现
|
|
28
|
+
点不开的空项与轮号空洞;manualTurn 的轮号同样按保留前缀收敛(正常会话
|
|
29
|
+
等于目标轮号)。版本效果仍记录**原**目标轮号(操作历史);
|
|
24
30
|
- **只有 `fork` 创建新 id**(`ForkOperation` / `forkFrom`,纯 append 派生);
|
|
25
31
|
- 版本效果事件(`session-branch/version`)携带 `ignorable: true`:live log 可见、
|
|
26
32
|
**不落 canonical log**(rdb 持久化时过滤并稠密化剩余事件);
|
|
@@ -32,6 +38,8 @@
|
|
|
32
38
|
缺失时退化为「已 durable 的就地版本」(可随时 resume 续跑):
|
|
33
39
|
|
|
34
40
|
- **live agent**(会话驻留 / 已恢复):直接 `followup` 排队,不重建、不换 id;
|
|
41
|
+
编辑前只等 agent 停下(`whenIdle`),残留排队输入由 rewind 在截断后强制
|
|
42
|
+
durable 取消(session-rdb 的 `LiveSessionHooks.inbox.clear`);
|
|
35
43
|
- **cold 会话**:`resume` 已持久化会话(`create` 对已持久化日志必失败),
|
|
36
44
|
resume 后 agent 驻留(不 dispose,避免 session 被移出 store 破坏客户端窗口);
|
|
37
45
|
- 模型 provider/model 在 **rewind 之前**从 `request/header` 解析(就地编辑
|
|
@@ -50,7 +58,10 @@
|
|
|
50
58
|
增长;重试先弹确认;未闭合轮次不显示重试;
|
|
51
59
|
- **操作后刷新**:就地编辑后优先调用客户端会话级 `resync()`(重置窗口并重新
|
|
52
60
|
拉取历史,不整页重载——rewind 的删除无法经 append-only 事件流表达,seq
|
|
53
|
-
|
|
61
|
+
回退只做增量会残留旧节点),随后丢弃该会话的全部投影行
|
|
62
|
+
(`projections.truncate(-1)`)——投影 store 按 higher-seq-wins 保留 rewind
|
|
63
|
+
前的高 seq 旧值,截断后的正确值 seq 更小,永远覆盖不上(轮次导航残留已
|
|
64
|
+
删除的轮次);不可用时回退 `location.reload()`;
|
|
54
65
|
- **构建约束**:client bundle 必须是**单文件**(client-modules 只服务/加载
|
|
55
66
|
`client.js`)——`noExternal` 全内联第三方 + `inlineDynamicImports` 合并
|
|
56
67
|
动态 import,`@deepseek-ai/*` 一律 external(平台 seed 词或独立插件,
|
package/dist/client.js
CHANGED
|
@@ -14,6 +14,10 @@ window.__ModuleLoader__.load({
|
|
|
14
14
|
const SESSION_EDITOR_PATH = "/session-editor";
|
|
15
15
|
//#endregion
|
|
16
16
|
//#region src/client/controller.ts
|
|
17
|
+
/** desktop 壳(官方 dsh-desktop-host)注入的 transport 标记:ownsHost 时 API 走 /api 前缀。 */
|
|
18
|
+
function editorApiPath() {
|
|
19
|
+
return globalThis.__DSH_TRANSPORT__?.ownsHost === true ? `/api${SESSION_EDITOR_PATH}` : SESSION_EDITOR_PATH;
|
|
20
|
+
}
|
|
17
21
|
function messageOf(error) {
|
|
18
22
|
return error instanceof Error ? error.message : String(error);
|
|
19
23
|
}
|
|
@@ -127,7 +131,7 @@ window.__ModuleLoader__.load({
|
|
|
127
131
|
state.error = null;
|
|
128
132
|
});
|
|
129
133
|
try {
|
|
130
|
-
const response = await fetch(`${
|
|
134
|
+
const response = await fetch(`${editorApiPath()}?sessionId=${encodeURIComponent(this.sessionId)}`, {
|
|
131
135
|
method: "GET",
|
|
132
136
|
headers: { accept: "application/json" },
|
|
133
137
|
cache: "no-store"
|
|
@@ -161,7 +165,7 @@ window.__ModuleLoader__.load({
|
|
|
161
165
|
state.error = null;
|
|
162
166
|
});
|
|
163
167
|
try {
|
|
164
|
-
const response = await fetch(
|
|
168
|
+
const response = await fetch(editorApiPath(), {
|
|
165
169
|
method: "POST",
|
|
166
170
|
headers: {
|
|
167
171
|
accept: "application/json",
|
|
@@ -187,6 +191,12 @@ window.__ModuleLoader__.load({
|
|
|
187
191
|
const resync = face.resync;
|
|
188
192
|
if (resync !== void 0) try {
|
|
189
193
|
await resync.call(face);
|
|
194
|
+
const projections = face.projections;
|
|
195
|
+
if (typeof projections?.truncate !== "function") {
|
|
196
|
+
location.reload();
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
projections.truncate(-1);
|
|
190
200
|
this.load();
|
|
191
201
|
return true;
|
|
192
202
|
} catch {}
|
|
@@ -351,11 +361,11 @@ window.__ModuleLoader__.load({
|
|
|
351
361
|
document.head.appendChild(tag);
|
|
352
362
|
}
|
|
353
363
|
var MessageIconActions_module_css_default = {
|
|
354
|
-
"actions": "lKOSUG_actions",
|
|
355
|
-
"timeStart": "lKOSUG_timeStart",
|
|
356
|
-
"timeEnd": "lKOSUG_timeEnd",
|
|
357
364
|
"runTimeDot": "lKOSUG_runTimeDot",
|
|
365
|
+
"timeEnd": "lKOSUG_timeEnd",
|
|
358
366
|
"action": "lKOSUG_action",
|
|
367
|
+
"timeStart": "lKOSUG_timeStart",
|
|
368
|
+
"actions": "lKOSUG_actions",
|
|
359
369
|
"visuallyHidden": "lKOSUG_visuallyHidden"
|
|
360
370
|
};
|
|
361
371
|
//#endregion
|
|
@@ -409,7 +419,9 @@ window.__ModuleLoader__.load({
|
|
|
409
419
|
children: "·"
|
|
410
420
|
}),
|
|
411
421
|
" ",
|
|
412
|
-
t("
|
|
422
|
+
t("message.turnTime.ttft"),
|
|
423
|
+
" ",
|
|
424
|
+
t("duration.seconds", { seconds: formatLatencySeconds(ttftMs) })
|
|
413
425
|
] }),
|
|
414
426
|
tokensPerSecond !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
415
427
|
" ",
|
|
@@ -496,8 +508,8 @@ window.__ModuleLoader__.load({
|
|
|
496
508
|
document.head.appendChild(tag);
|
|
497
509
|
}
|
|
498
510
|
var MessageEditDialog_module_css_default = {
|
|
499
|
-
"
|
|
500
|
-
"
|
|
511
|
+
"input": "_7VqDXa_input",
|
|
512
|
+
"actions": "_7VqDXa_actions"
|
|
501
513
|
};
|
|
502
514
|
//#endregion
|
|
503
515
|
//#region src/client/chat-node/MessageEditDialog.tsx
|
|
@@ -568,34 +580,34 @@ window.__ModuleLoader__.load({
|
|
|
568
580
|
document.head.appendChild(tag);
|
|
569
581
|
}
|
|
570
582
|
var MessageItem_module_css_default = {
|
|
571
|
-
"turnErrorTitle": "okW0Ua_turnErrorTitle",
|
|
572
|
-
"retryText": "okW0Ua_retryText",
|
|
573
|
-
"compactionLeading": "okW0Ua_compactionLeading",
|
|
574
583
|
"retryRow": "okW0Ua_retryRow",
|
|
575
|
-
"contextRow": "okW0Ua_contextRow",
|
|
576
|
-
"compactionContextIcon": "okW0Ua_compactionContextIcon",
|
|
577
|
-
"retryDetails": "okW0Ua_retryDetails",
|
|
578
|
-
"compactionRow": "okW0Ua_compactionRow",
|
|
579
|
-
"compactionTitle": "okW0Ua_compactionTitle",
|
|
580
584
|
"turnErrorRow": "okW0Ua_turnErrorRow",
|
|
581
|
-
"
|
|
585
|
+
"bubble": "okW0Ua_bubble",
|
|
582
586
|
"confirmActions": "okW0Ua_confirmActions",
|
|
583
|
-
"compactionDisclosureIcon": "okW0Ua_compactionDisclosureIcon",
|
|
584
|
-
"compactionSummary": "okW0Ua_compactionSummary",
|
|
585
|
-
"turnErrorDot": "okW0Ua_turnErrorDot",
|
|
586
|
-
"turnErrorCopy": "okW0Ua_turnErrorCopy",
|
|
587
|
-
"userStack": "okW0Ua_userStack",
|
|
588
|
-
"compactionSep": "okW0Ua_compactionSep",
|
|
589
587
|
"userRow": "okW0Ua_userRow",
|
|
590
|
-
"
|
|
588
|
+
"compactionLeading": "okW0Ua_compactionLeading",
|
|
591
589
|
"compactionButton": "okW0Ua_compactionButton",
|
|
592
|
-
"
|
|
590
|
+
"retry-shimmer": "okW0Ua_retry-shimmer",
|
|
591
|
+
"compactionContextIcon": "okW0Ua_compactionContextIcon",
|
|
592
|
+
"turnErrorDot": "okW0Ua_turnErrorDot",
|
|
593
|
+
"compactionSummary": "okW0Ua_compactionSummary",
|
|
593
594
|
"compactionBody": "okW0Ua_compactionBody",
|
|
595
|
+
"turnErrorCopy": "okW0Ua_turnErrorCopy",
|
|
596
|
+
"userStack": "okW0Ua_userStack",
|
|
597
|
+
"turnErrorTitle": "okW0Ua_turnErrorTitle",
|
|
598
|
+
"turnErrorMessage": "okW0Ua_turnErrorMessage",
|
|
599
|
+
"retrySummary": "okW0Ua_retrySummary",
|
|
594
600
|
"retryDetailLabel": "okW0Ua_retryDetailLabel",
|
|
595
|
-
"turnErrorCode": "okW0Ua_turnErrorCode",
|
|
596
601
|
"maxTokensTitle": "okW0Ua_maxTokensTitle",
|
|
602
|
+
"compactionDisclosureIcon": "okW0Ua_compactionDisclosureIcon",
|
|
603
|
+
"retryText": "okW0Ua_retryText",
|
|
597
604
|
"refChip": "okW0Ua_refChip",
|
|
598
|
-
"
|
|
605
|
+
"turnErrorCode": "okW0Ua_turnErrorCode",
|
|
606
|
+
"compactionRow": "okW0Ua_compactionRow",
|
|
607
|
+
"retryDetails": "okW0Ua_retryDetails",
|
|
608
|
+
"contextRow": "okW0Ua_contextRow",
|
|
609
|
+
"compactionTitle": "okW0Ua_compactionTitle",
|
|
610
|
+
"compactionSep": "okW0Ua_compactionSep"
|
|
599
611
|
};
|
|
600
612
|
//#endregion
|
|
601
613
|
//#region src/client/chat-node/MessageItem.tsx
|
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { _ as
|
|
1
|
+
import { _ as SessionEditorOperation, d as EditOperation, f as EditableMessageBlock, g as RewindOperation, h as RetryableTurn, m as RetryOperation, o as editableMessages, p as RerollOperation, r as closedTurns, u as retryableTurns, v as SessionEditorResult } from "./plan-BdU_mnTv.mjs";
|
|
2
2
|
import { Context, Service } from "@deepseek-ai/cordis";
|
|
3
|
-
import { BranchBoundary, BranchTimeline, BranchTimeline as BranchTimeline$1, CascadePolicy, EditableBlockKind, SESSION_BRANCH_VERSION_SCHEMA, SessionBranchVersionEvent } from "@morlay/session-branch";
|
|
4
3
|
import { Session, SessionEvent, SessionId } from "@deepseek-ai/dsh-session";
|
|
4
|
+
import { BranchBoundary, BranchTimeline, BranchTimeline as BranchTimeline$1, CascadePolicy, EditableBlockKind, SESSION_BRANCH_VERSION_SCHEMA, SessionBranchVersionEvent } from "@morlay/session-branch";
|
|
5
5
|
import { UserMessage } from "@deepseek-ai/dsh-llm";
|
|
6
6
|
//#region src/index.d.ts
|
|
7
7
|
declare module "@deepseek-ai/cordis" {
|
|
@@ -14,14 +14,6 @@ interface EditorAgent {
|
|
|
14
14
|
followup(message: UserMessage): void;
|
|
15
15
|
/** 等待 agent 到达 quiescence(当前 turn/任务结束后 resolve)。 */
|
|
16
16
|
whenIdle(): Promise<void>;
|
|
17
|
-
/**
|
|
18
|
-
* 队列中是否有待处理输入(next-turn / next-step)。rewind 截断会删除这些
|
|
19
|
-
* 输入所属轮次的事件,残留的 inbox 状态会与截断后的 log 失配,使后续
|
|
20
|
-
* splice 落库后无法从日志重放——编辑前必须清空。
|
|
21
|
-
*/
|
|
22
|
-
readonly inboxPending: boolean;
|
|
23
|
-
/** 清空待处理输入(落库 canceled splice,须在 rewind 前调用)。 */
|
|
24
|
-
clearInbox(): void;
|
|
25
17
|
}
|
|
26
18
|
interface EditorAgentHandle {
|
|
27
19
|
readonly agent: EditorAgent;
|
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { n as SessionEditor, t as SESSION_BRANCH_VERSION_SCHEMA } from "./src-
|
|
1
|
+
import { n as SessionEditor, t as SESSION_BRANCH_VERSION_SCHEMA } from "./src-ks167MhO.mjs";
|
|
2
2
|
import { closedTurns, editableMessages, retryableTurns } from "./plan.mjs";
|
|
3
3
|
export { SESSION_BRANCH_VERSION_SCHEMA, SessionEditor, SessionEditor as default, closedTurns, editableMessages, retryableTurns };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { BranchTimeline, CascadePolicy, SessionBranchVersionEvent } from "@morlay/session-branch";
|
|
2
1
|
import { SessionEvent, SessionId } from "@deepseek-ai/dsh-session";
|
|
2
|
+
import { BranchTimeline, CascadePolicy, SessionBranchVersionEvent } from "@morlay/session-branch";
|
|
3
3
|
import { AssistantMessage, UserMessage } from "@deepseek-ai/dsh-llm";
|
|
4
4
|
//#region src/types.d.ts
|
|
5
5
|
interface EditOperation {
|
|
@@ -78,9 +78,16 @@ interface OperationPlan {
|
|
|
78
78
|
declare function closedTurns(events: readonly SessionEvent[]): ClosedTurn[];
|
|
79
79
|
declare function editableMessages(turns: readonly ClosedTurn[]): EditableMessageBlock[];
|
|
80
80
|
declare function retryableTurns(turns: readonly ClosedTurn[]): RetryableTurn[];
|
|
81
|
+
/**
|
|
82
|
+
* 目标轮之前最后一个「有内容」轮次的下标:跳过既无用户输入也无助手回复的
|
|
83
|
+
* 空轮。空轮是早期重放缺陷的遗留(turn/start 后直接 turn/end),在轨迹 /
|
|
84
|
+
* 轮次导航里表现为点不开的空项,并让后续轮号出现空洞。rewind 把它们随
|
|
85
|
+
* 目标轮一起截断,重放按保留前缀续号即可自愈。
|
|
86
|
+
*/
|
|
87
|
+
declare function precedingContentIndex(turns: readonly ClosedTurn[], turnIndex: number): number;
|
|
81
88
|
declare function downstreamUsers(turns: readonly ClosedTurn[], start: number): UserMessage[];
|
|
82
89
|
declare function editPlan(operation: EditOperation, turns: readonly ClosedTurn[]): OperationPlan;
|
|
83
90
|
declare function retryPlan(operation: RetryOperation, turns: readonly ClosedTurn[]): OperationPlan;
|
|
84
91
|
declare function rerollPlan(operation: RerollOperation, turns: readonly ClosedTurn[]): OperationPlan;
|
|
85
92
|
//#endregion
|
|
86
|
-
export {
|
|
93
|
+
export { SessionEditorOperation as _, editPlan as a, rerollPlan as c, EditOperation as d, EditableMessageBlock as f, RewindOperation as g, RetryableTurn as h, downstreamUsers as i, retryPlan as l, RetryOperation as m, OperationPlan as n, editableMessages as o, RerollOperation as p, closedTurns as r, precedingContentIndex as s, ClosedTurn as t, retryableTurns as u, SessionEditorResult as v };
|
package/dist/plan.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as editPlan, c as
|
|
2
|
-
export { ClosedTurn, OperationPlan, closedTurns, downstreamUsers, editPlan, editableMessages, rerollPlan, retryPlan, retryableTurns };
|
|
1
|
+
import { a as editPlan, c as rerollPlan, i as downstreamUsers, l as retryPlan, n as OperationPlan, o as editableMessages, r as closedTurns, s as precedingContentIndex, t as ClosedTurn, u as retryableTurns } from "./plan-BdU_mnTv.mjs";
|
|
2
|
+
export { ClosedTurn, OperationPlan, closedTurns, downstreamUsers, editPlan, editableMessages, precedingContentIndex, rerollPlan, retryPlan, retryableTurns };
|
package/dist/plan.mjs
CHANGED
|
@@ -100,6 +100,21 @@ function retryableTurns(turns) {
|
|
|
100
100
|
time: turn.user.time
|
|
101
101
|
}]);
|
|
102
102
|
}
|
|
103
|
+
/**
|
|
104
|
+
* 目标轮之前最后一个「有内容」轮次的下标:跳过既无用户输入也无助手回复的
|
|
105
|
+
* 空轮。空轮是早期重放缺陷的遗留(turn/start 后直接 turn/end),在轨迹 /
|
|
106
|
+
* 轮次导航里表现为点不开的空项,并让后续轮号出现空洞。rewind 把它们随
|
|
107
|
+
* 目标轮一起截断,重放按保留前缀续号即可自愈。
|
|
108
|
+
*/
|
|
109
|
+
function precedingContentIndex(turns, turnIndex) {
|
|
110
|
+
let index = turnIndex - 1;
|
|
111
|
+
while (index >= 0) {
|
|
112
|
+
const turn = turns[index];
|
|
113
|
+
if (turn.users.length > 0 || turn.assistants.length > 0) break;
|
|
114
|
+
index -= 1;
|
|
115
|
+
}
|
|
116
|
+
return index;
|
|
117
|
+
}
|
|
103
118
|
function downstreamUsers(turns, start) {
|
|
104
119
|
return turns.slice(start).flatMap((turn) => turn.users.map((user) => cloneUser(user.data)));
|
|
105
120
|
}
|
|
@@ -144,7 +159,7 @@ function editPlan(operation, turns) {
|
|
|
144
159
|
const later = operation.cascade === "preserve" ? downstreamUsers(turns, turnIndex + 1) : [];
|
|
145
160
|
return {
|
|
146
161
|
anchorSeq: turn.startSeq,
|
|
147
|
-
...
|
|
162
|
+
...userIndex === 0 ? {} : { rewindBoundary: event.seq },
|
|
148
163
|
version: pairVersionEffect(operation.sessionId, {
|
|
149
164
|
operation: "edit",
|
|
150
165
|
cascade: operation.cascade,
|
|
@@ -222,4 +237,4 @@ function rerollPlan(operation, turns) {
|
|
|
222
237
|
throw new SessionBranchError("当前会话没有可重生成的已落定助手回复。", "INVALID_BOUNDARY");
|
|
223
238
|
}
|
|
224
239
|
//#endregion
|
|
225
|
-
export { closedTurns, downstreamUsers, editPlan, editableMessages, rerollPlan, retryPlan, retryableTurns };
|
|
240
|
+
export { closedTurns, downstreamUsers, editPlan, editableMessages, precedingContentIndex, rerollPlan, retryPlan, retryableTurns };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { closedTurns, editPlan, editableMessages, rerollPlan, retryPlan, retryableTurns } from "./plan.mjs";
|
|
1
|
+
import { closedTurns, editPlan, editableMessages, precedingContentIndex, rerollPlan, retryPlan, retryableTurns } from "./plan.mjs";
|
|
2
2
|
import { Service } from "@deepseek-ai/cordis";
|
|
3
|
+
import { SessionSeq } from "@deepseek-ai/dsh-session";
|
|
3
4
|
import { SESSION_BRANCH_VERSION_SCHEMA as SESSION_BRANCH_VERSION_SCHEMA$1, SessionBranchError, balanceRewindPrefix } from "@morlay/session-branch";
|
|
4
5
|
//#region src/shared.ts
|
|
5
6
|
const SESSION_EDITOR_PATH = "/session-editor";
|
|
@@ -68,7 +69,7 @@ function appendLogSeedEvent(events, type, data, ignorable = false) {
|
|
|
68
69
|
function appendSurfaceSeedEvent(events, type, data, intent) {
|
|
69
70
|
events.push({
|
|
70
71
|
type,
|
|
71
|
-
seq: events.length,
|
|
72
|
+
seq: SessionSeq(events.length),
|
|
72
73
|
time: Date.now(),
|
|
73
74
|
data,
|
|
74
75
|
surfaceOp: intent.surfaceOp,
|
|
@@ -172,18 +173,24 @@ var SessionEditor = class extends Service {
|
|
|
172
173
|
const turns = closedTurns(events);
|
|
173
174
|
const plan = operation.action === "edit" ? editPlan(operation, turns) : operation.action === "retry" ? retryPlan(operation, turns) : rerollPlan(operation, turns);
|
|
174
175
|
const headerConfig = events.findLast((event) => event.type === "request/header")?.data.header.config;
|
|
176
|
+
const turnIndex = turns.findIndex((turn) => turn.startSeq === plan.anchorSeq);
|
|
177
|
+
const preceding = precedingContentIndex(turns, turnIndex);
|
|
178
|
+
const boundary = plan.rewindBoundary !== void 0 ? plan.rewindBoundary : preceding < 0 ? -1 : turns[preceding].endSeq;
|
|
179
|
+
const replayTurn = preceding < 0 ? 1 : turns[preceding].turn + 1;
|
|
175
180
|
const seedSuffix = [];
|
|
176
181
|
appendLogSeedEvent(seedSuffix, "session-branch/version", plan.version, true);
|
|
177
|
-
if (plan.manualTurn !== void 0) appendManualTurn(seedSuffix,
|
|
182
|
+
if (plan.manualTurn !== void 0) appendManualTurn(seedSuffix, {
|
|
183
|
+
...plan.manualTurn,
|
|
184
|
+
turn: replayTurn
|
|
185
|
+
});
|
|
178
186
|
const replay = await this.prepareReplay(operation.sessionId, plan.queuedUsers, signal, headerConfig);
|
|
179
|
-
const turnIndex = turns.findIndex((turn) => turn.startSeq === plan.anchorSeq);
|
|
180
|
-
const boundary = plan.rewindBoundary !== void 0 ? plan.rewindBoundary : turnIndex <= 0 ? -1 : turns[turnIndex - 1].endSeq;
|
|
181
187
|
const live = this.ctx.sessions.get(operation.sessionId);
|
|
182
188
|
await this.ctx.sessionBranch.rewind(operation.sessionId, boundary, signal);
|
|
183
189
|
if (seedSuffix.length > 0) {
|
|
184
190
|
if (live !== void 0) {
|
|
185
191
|
const handle = this.ctx.sessionPersistence.tracker.writerOf(operation.sessionId);
|
|
186
192
|
if (handle === void 0) throw new Error(`session "${operation.sessionId}" has no live write handle for the version effect`);
|
|
193
|
+
await this.ctx.sessions.flush(live);
|
|
187
194
|
await appendSeedSuffixLive(live, seedSuffix, (events) => handle.append(events));
|
|
188
195
|
await this.ctx.sessions.flush(live);
|
|
189
196
|
} else {
|
|
@@ -203,6 +210,11 @@ var SessionEditor = class extends Service {
|
|
|
203
210
|
}
|
|
204
211
|
let queuedTurns = 0;
|
|
205
212
|
if (replay.agent !== void 0 && plan.queuedUsers.length > 0) {
|
|
213
|
+
const lastSeedTurn = seedSuffix.findLast((event) => event.type === "turn/start")?.data.turn;
|
|
214
|
+
if (lastSeedTurn !== void 0) {
|
|
215
|
+
const phase = replay.agent.phase;
|
|
216
|
+
if (phase !== void 0) phase.lastTurn = lastSeedTurn;
|
|
217
|
+
}
|
|
206
218
|
for (const message of plan.queuedUsers) replay.agent.followup(message);
|
|
207
219
|
await this.ctx.sessions.flush(replay.agent.session);
|
|
208
220
|
queuedTurns = plan.queuedUsers.length;
|
|
@@ -233,7 +245,6 @@ var SessionEditor = class extends Service {
|
|
|
233
245
|
if (existing !== void 0) {
|
|
234
246
|
await existing.whenIdle();
|
|
235
247
|
signal?.throwIfAborted();
|
|
236
|
-
if (existing.inboxPending) existing.clearInbox();
|
|
237
248
|
return { agent: existing };
|
|
238
249
|
}
|
|
239
250
|
const provider = headerConfig?.provider ?? "";
|
|
@@ -402,9 +413,9 @@ async function handleRoute(editor, request, response) {
|
|
|
402
413
|
}
|
|
403
414
|
}
|
|
404
415
|
function registerHttpRoutes(ctx) {
|
|
405
|
-
const webServer = ctx.get("webServer");
|
|
406
|
-
if (webServer === void 0) return;
|
|
407
416
|
ctx.effect(() => {
|
|
417
|
+
const webServer = ctx.get("webServer");
|
|
418
|
+
if (webServer === void 0) return () => {};
|
|
408
419
|
const editor = ctx.sessionEditor;
|
|
409
420
|
return webServer.register({
|
|
410
421
|
kind: "exact",
|
|
@@ -412,6 +423,41 @@ function registerHttpRoutes(ctx) {
|
|
|
412
423
|
handler: (request, response) => handleRoute(editor, request, response)
|
|
413
424
|
});
|
|
414
425
|
}, "session-editor: HTTP route");
|
|
426
|
+
const registerConnectionRoute = () => {
|
|
427
|
+
const connection = ctx.get("connection", false);
|
|
428
|
+
if (connection === void 0) return;
|
|
429
|
+
const editor = ctx.sessionEditor;
|
|
430
|
+
ctx.effect(() => connection.fetch.register({
|
|
431
|
+
path: `/api${SESSION_EDITOR_PATH}`,
|
|
432
|
+
methods: ["GET", "POST"],
|
|
433
|
+
requestBody: "buffered",
|
|
434
|
+
fetch: (request) => handleFetchRoute(editor, request)
|
|
435
|
+
}), "session-editor: connection fetch route");
|
|
436
|
+
};
|
|
437
|
+
ctx.on("internal/service", (name) => {
|
|
438
|
+
if (name === "connection") registerConnectionRoute();
|
|
439
|
+
});
|
|
440
|
+
registerConnectionRoute();
|
|
441
|
+
}
|
|
442
|
+
/** connection.fetch 路由的 Fetch 形态处理(与 webServer 的 node:http 形态同语义)。 */
|
|
443
|
+
async function handleFetchRoute(editor, request) {
|
|
444
|
+
try {
|
|
445
|
+
if (request.method === "GET") return jsonResponse(200, await readTimeline(editor, sessionIdOf(new URL(request.url).searchParams.get("sessionId"))));
|
|
446
|
+
if (request.method === "POST") return jsonResponse(200, await runOperation(editor, decodeOperation(await request.json())));
|
|
447
|
+
return new Response(null, { status: 405 });
|
|
448
|
+
} catch (error) {
|
|
449
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
450
|
+
return jsonResponse(error instanceof TypeError ? 400 : 409, { error: message });
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
function jsonResponse(status, value) {
|
|
454
|
+
return new Response(JSON.stringify(value), {
|
|
455
|
+
status,
|
|
456
|
+
headers: {
|
|
457
|
+
"content-type": "application/json; charset=utf-8",
|
|
458
|
+
"cache-control": "no-store"
|
|
459
|
+
}
|
|
460
|
+
});
|
|
415
461
|
}
|
|
416
462
|
//#endregion
|
|
417
463
|
export { SessionEditor as n, SESSION_BRANCH_VERSION_SCHEMA$1 as t };
|
package/dist/testing.d.mts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { SessionEditor } from "./index.mjs";
|
|
2
2
|
import { Context, Service } from "@deepseek-ai/cordis";
|
|
3
|
+
import { Session, Session as Session$1, SessionEvent, SessionEvent as SessionEvent$1, SessionHeader, SessionHeader as SessionHeader$1, SessionId, SessionId as SessionIdBrand, SessionLogOffset, SessionSeq, SessionStore } from "@deepseek-ai/dsh-session";
|
|
3
4
|
import { BranchBoundary, BranchTimeline, SessionBranch } from "@morlay/session-branch";
|
|
4
5
|
import { TokenMeter } from "@deepseek-ai/dsh-token-meter";
|
|
5
|
-
import { Session, Session as Session$1, SessionEvent, SessionEvent as SessionEvent$1, SessionHeader, SessionHeader as SessionHeader$1, SessionId, SessionId as SessionIdBrand, SessionLogOffset, SessionSeq, SessionStore } from "@deepseek-ai/dsh-session";
|
|
6
6
|
import { SessionPersistence, SessionPersistenceRevision, SessionStorageMetadata } from "@deepseek-ai/dsh-session-persistence";
|
|
7
7
|
//#region ../../vendor/deepseek-harness/vendor/cosmokit/lib/types/types.d.ts
|
|
8
8
|
declare function isArrayBufferLike(value: any): value is ArrayBufferLike;
|