@morlay/ui-conversation-message-actions 0.0.12 → 0.0.13

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/index.mjs CHANGED
@@ -1,607 +1,3 @@
1
- import { Service } from "@deepseek-ai/cordis";
2
- import { randomUUID } from "node:crypto";
3
- import { SESSION_BRANCH_VERSION_SCHEMA, SESSION_BRANCH_VERSION_SCHEMA as SESSION_BRANCH_VERSION_SCHEMA$1, SessionBranchError, balanceRewindPrefix } from "@morlay/session-branch";
4
- //#region src/shared.ts
5
- const SESSION_EDITOR_PATH = "/session-editor";
6
- function toTimelinePayload(sessionId, timeline, messages, retryableTurns) {
7
- const currentPath = /* @__PURE__ */ new Set();
8
- for (const node of timeline.nodes) currentPath.add(String(node.sessionId));
9
- const versions = timeline.nodes.map((node) => ({
10
- sessionId: String(node.sessionId),
11
- ...node.parentSessionId === void 0 ? {} : { parentSessionId: String(node.parentSessionId) },
12
- ...node.effect === void 0 ? {} : {
13
- effectId: node.effect.id,
14
- inverseSessionId: String(node.inverseSessionId),
15
- operation: node.effect.operation,
16
- cascade: node.effect.cascade,
17
- targetTurn: node.effect.targetTurn,
18
- ...node.effect.blockKind === void 0 ? {} : { blockKind: node.effect.blockKind },
19
- ...node.effect.before === void 0 ? {} : { before: node.effect.before },
20
- ...node.effect.after === void 0 ? {} : { after: node.effect.after }
21
- },
22
- createdAt: node.createdAt,
23
- depth: depthOf(timeline, node.sessionId),
24
- current: String(node.sessionId) === String(sessionId),
25
- onCurrentEffectPath: currentPath.has(String(node.sessionId))
26
- }));
27
- const versionsById = new Map(versions.map((version) => [version.sessionId, version]));
28
- const undoStack = [];
29
- let cursor = versionsById.get(String(sessionId));
30
- while (cursor?.inverseSessionId !== void 0) {
31
- if (undoStack.includes(cursor.inverseSessionId)) break;
32
- undoStack.push(cursor.inverseSessionId);
33
- cursor = versionsById.get(cursor.inverseSessionId);
34
- }
35
- const redoSessionIds = versions.filter((version) => version.inverseSessionId === String(sessionId)).map((version) => version.sessionId);
36
- return {
37
- sessionId: String(sessionId),
38
- messages,
39
- retryableTurns,
40
- versions,
41
- undoStack,
42
- redoSessionIds
43
- };
44
- }
45
- function depthOf(timeline, sessionId) {
46
- const byId = new Map(timeline.nodes.map((node) => [String(node.sessionId), node]));
47
- let depth = 0;
48
- let cursor = byId.get(String(sessionId));
49
- const seen = /* @__PURE__ */ new Set();
50
- while (cursor?.parentSessionId !== void 0 && !seen.has(String(cursor.sessionId))) {
51
- seen.add(String(cursor.sessionId));
52
- depth += 1;
53
- cursor = byId.get(String(cursor.parentSessionId));
54
- }
55
- return depth;
56
- }
57
- //#endregion
58
- //#region src/index.ts
59
- function closedTurns(events) {
60
- const result = [];
61
- let current;
62
- for (const event of events) {
63
- if (event.type === "turn/start") {
64
- current = {
65
- turn: event.data.turn,
66
- startSeq: event.seq,
67
- assistants: []
68
- };
69
- continue;
70
- }
71
- if (current === void 0) continue;
72
- if (event.type === "user/message" && current.user === void 0 && event.data.source.kind === "user") {
73
- current.user = event;
74
- continue;
75
- }
76
- if (event.type === "assistant/message" && event.data.turn === current.turn) {
77
- current.assistants.push(event);
78
- continue;
79
- }
80
- if (event.type === "turn/end" && event.data.turn === current.turn) {
81
- result.push({
82
- ...current,
83
- endSeq: event.seq,
84
- closed: true
85
- });
86
- current = void 0;
87
- }
88
- }
89
- if (current !== void 0) result.push({
90
- ...current,
91
- closed: false
92
- });
93
- return result;
94
- }
95
- function isTextualBlock(block) {
96
- return block?.type === "text" || block?.type === "reasoning";
97
- }
98
- function userText(message) {
99
- return message.content.filter((block) => block.type === "text").map((block) => block.text).join("\n");
100
- }
101
- function cloneUser(message, content = structuredClone(message.content)) {
102
- return Object.freeze({
103
- id: randomUUID(),
104
- role: "user",
105
- content: Object.freeze(content),
106
- source: Object.freeze({ kind: "user" })
107
- });
108
- }
109
- function replaceTextBlock(content, blockIndex, text) {
110
- const block = content[blockIndex];
111
- if (!isTextualBlock(block)) throw new SessionBranchError("所选内容块不是可编辑文本。", "INVALID_BOUNDARY");
112
- return content.map((candidate, index) => index === blockIndex ? {
113
- ...candidate,
114
- text
115
- } : structuredClone(candidate));
116
- }
117
- function editableMessages(turns) {
118
- const result = [];
119
- for (const turn of turns) {
120
- if (turn.user !== void 0) for (const [blockIndex, block] of turn.user.data.content.entries()) {
121
- if (block.type !== "text") continue;
122
- result.push({
123
- key: `${String(turn.user.seq)}:${String(blockIndex)}`,
124
- turn: turn.turn,
125
- eventSeq: turn.user.seq,
126
- blockIndex,
127
- kind: "user",
128
- text: block.text,
129
- time: turn.user.time
130
- });
131
- }
132
- if (!turn.closed) continue;
133
- for (const event of turn.assistants) for (const [blockIndex, block] of event.data.message.content.entries()) {
134
- if (!isTextualBlock(block)) continue;
135
- result.push({
136
- key: `${String(event.seq)}:${String(blockIndex)}`,
137
- turn: turn.turn,
138
- eventSeq: event.seq,
139
- blockIndex,
140
- kind: block.type === "reasoning" ? "assistant.reasoning" : "assistant.response",
141
- text: block.text,
142
- time: event.time
143
- });
144
- }
145
- }
146
- return result;
147
- }
148
- function retryableTurns(turns) {
149
- return turns.flatMap((turn) => turn.user === void 0 || !turn.closed ? [] : [{
150
- turn: turn.turn,
151
- userEventSeq: turn.user.seq,
152
- preview: userText(turn.user.data),
153
- time: turn.user.time
154
- }]);
155
- }
156
- function downstreamUsers(turns, start) {
157
- return turns.slice(start).flatMap((turn) => turn.user === void 0 ? [] : [cloneUser(turn.user.data)]);
158
- }
159
- function assistantReplacement(event, blockIndex, text) {
160
- const replaced = replaceTextBlock(event.data.message.content, blockIndex, text).filter((block) => block.type === "text" || block.type === "reasoning");
161
- return Object.freeze({
162
- id: randomUUID(),
163
- role: "assistant",
164
- content: Object.freeze(replaced),
165
- source: Object.freeze({
166
- kind: "model",
167
- provider: event.data.message.source.provider,
168
- model: event.data.message.source.model
169
- })
170
- });
171
- }
172
- function pairVersionEffect(sourceSessionId, effect) {
173
- return {
174
- schemaVersion: SESSION_BRANCH_VERSION_SCHEMA$1,
175
- effect: {
176
- ...effect,
177
- id: randomUUID()
178
- },
179
- inverse: {
180
- kind: "restore-version",
181
- sessionId: sourceSessionId
182
- }
183
- };
184
- }
185
- function editPlan(operation, turns) {
186
- const turnIndex = turns.findIndex((turn) => operation.eventSeq > turn.startSeq && (turn.endSeq === void 0 || operation.eventSeq < turn.endSeq));
187
- const turn = turns[turnIndex];
188
- if (turn === void 0) throw new SessionBranchError("所选消息不属于已落定回合。", "INVALID_BOUNDARY");
189
- const event = turn.user?.seq === operation.eventSeq ? turn.user : turn.assistants.find((candidate) => candidate.seq === operation.eventSeq);
190
- if (event === void 0) throw new SessionBranchError("所选消息不存在或不可编辑。", "INVALID_BOUNDARY");
191
- if (event.type === "user/message") {
192
- const before = event.data.content[operation.blockIndex];
193
- if (before?.type !== "text") throw new SessionBranchError("所选用户消息块不是文本。", "INVALID_BOUNDARY");
194
- const edited = cloneUser(event.data, replaceTextBlock(event.data.content, operation.blockIndex, operation.text));
195
- const later = operation.cascade === "preserve" ? downstreamUsers(turns, turnIndex + 1) : [];
196
- return {
197
- anchorSeq: turn.startSeq,
198
- ...turn.closed ? {} : { rewindBoundary: event.seq },
199
- version: pairVersionEffect(operation.sessionId, {
200
- operation: "edit",
201
- cascade: operation.cascade,
202
- targetTurn: turn.turn,
203
- targetEventSeq: event.seq,
204
- targetBlockIndex: operation.blockIndex,
205
- blockKind: "user",
206
- before: before.text,
207
- after: operation.text
208
- }),
209
- queuedUsers: [edited, ...later]
210
- };
211
- }
212
- if (!turn.closed) throw new SessionBranchError("未闭合轮次的助手消息不可编辑。", "INVALID_BOUNDARY");
213
- const before = event.data.message.content[operation.blockIndex];
214
- if (!isTextualBlock(before)) throw new SessionBranchError("所选助手消息块不是文本或思考。", "INVALID_BOUNDARY");
215
- const blockKind = before.type === "reasoning" ? "assistant.reasoning" : "assistant.response";
216
- if (turn.user === void 0) throw new SessionBranchError("所选助手消息没有可重建的用户输入。", "INVALID_BOUNDARY");
217
- return {
218
- anchorSeq: turn.startSeq,
219
- version: pairVersionEffect(operation.sessionId, {
220
- operation: "edit",
221
- cascade: operation.cascade,
222
- targetTurn: turn.turn,
223
- targetEventSeq: event.seq,
224
- targetBlockIndex: operation.blockIndex,
225
- blockKind,
226
- before: before.text,
227
- after: operation.text
228
- }),
229
- manualTurn: {
230
- turn: turn.turn,
231
- user: cloneUser(turn.user.data),
232
- assistant: assistantReplacement(event, operation.blockIndex, operation.text)
233
- },
234
- queuedUsers: operation.cascade === "preserve" ? downstreamUsers(turns, turnIndex + 1) : []
235
- };
236
- }
237
- function retryPlan(operation, turns) {
238
- const turnIndex = turns.findIndex((turn) => turn.turn === operation.turn);
239
- const turn = turns[turnIndex];
240
- if (turn?.user === void 0) throw new SessionBranchError("所选回合没有可重放的用户输入。", "INVALID_BOUNDARY");
241
- return {
242
- anchorSeq: turn.startSeq,
243
- version: pairVersionEffect(operation.sessionId, {
244
- operation: "retry",
245
- cascade: operation.cascade,
246
- targetTurn: turn.turn,
247
- targetEventSeq: turn.user.seq
248
- }),
249
- queuedUsers: operation.cascade === "preserve" ? downstreamUsers(turns, turnIndex) : [cloneUser(turn.user.data)]
250
- };
251
- }
252
- function rerollPlan(operation, turns) {
253
- for (let index = turns.length - 1; index >= 0; index -= 1) {
254
- const turn = turns[index];
255
- if (turn?.user === void 0 || !turn.closed) continue;
256
- const target = turn.assistants.findLast((event) => event.data.message.content.some(isTextualBlock));
257
- if (target === void 0) continue;
258
- return {
259
- anchorSeq: turn.startSeq,
260
- version: pairVersionEffect(operation.sessionId, {
261
- operation: "reroll",
262
- cascade: "truncate",
263
- targetTurn: turn.turn,
264
- targetEventSeq: target.seq
265
- }),
266
- queuedUsers: [cloneUser(turn.user.data)]
267
- };
268
- }
269
- throw new SessionBranchError("当前会话没有可重生成的已落定助手回复。", "INVALID_BOUNDARY");
270
- }
271
- function appendLogSeedEvent(events, type, data, ignorable = false) {
272
- events.push({
273
- type,
274
- seq: events.length,
275
- time: Date.now(),
276
- data,
277
- ...ignorable ? { ignorable: true } : {}
278
- });
279
- }
280
- function appendSurfaceSeedEvent(events, type, data, intent) {
281
- events.push({
282
- type,
283
- seq: events.length,
284
- time: Date.now(),
285
- data,
286
- surfaceOp: intent.surfaceOp,
287
- ...intent.sourceEventSeqs === void 0 ? {} : { sourceEventSeqs: intent.sourceEventSeqs }
288
- });
289
- }
290
- function appendManualTurn(events, manual) {
291
- const { turn, user, assistant } = manual;
292
- appendLogSeedEvent(events, "turn/start", { turn });
293
- appendSurfaceSeedEvent(events, "user/message", user, { surfaceOp: "append" });
294
- appendLogSeedEvent(events, "step/start", {
295
- turn,
296
- step: 1
297
- });
298
- appendSurfaceSeedEvent(events, "assistant/message", {
299
- turn,
300
- step: 1,
301
- message: assistant
302
- }, {
303
- surfaceOp: "append",
304
- sourceEventSeqs: []
305
- });
306
- appendLogSeedEvent(events, "step/end", {
307
- turn,
308
- step: 1
309
- });
310
- appendLogSeedEvent(events, "turn/end", {
311
- turn,
312
- reason: { kind: "completed" }
313
- });
314
- }
315
- function appendSeedSuffixLive(session, seedSuffix) {
316
- for (const event of seedSuffix) {
317
- if (event.ignorable === true) {
318
- const s = session;
319
- s.log.push({
320
- ...event,
321
- seq: s.log.length
322
- });
323
- s.eventsSnapshot = void 0;
324
- continue;
325
- }
326
- const s = session;
327
- const raw = event;
328
- if (raw.surfaceOp !== void 0) s.append(event.type, event.data, {
329
- surfaceOp: raw.surfaceOp,
330
- ...raw.sourceEventSeqs === void 0 ? {} : { sourceEventSeqs: raw.sourceEventSeqs }
331
- });
332
- else s.append(event.type, event.data);
333
- }
334
- }
335
- var SessionEditor = class extends Service {
336
- static inject = [
337
- "sessionBranch",
338
- "sessionPersistence",
339
- "sessions"
340
- ];
341
- constructor(ctx) {
342
- super(ctx, "sessionEditor");
343
- registerHttpRoutes(ctx);
344
- }
345
- readBranchPrefix(id, atSeq, mode, signal) {
346
- return this.ctx.sessionBranch.readBranchPrefix(id, atSeq, mode, signal);
347
- }
348
- fork(sourceId, atSeq, childSessionId, meta, signal) {
349
- return this.ctx.sessionBranch.forkFrom(sourceId, {
350
- ...atSeq === void 0 ? {} : { atSeq },
351
- ...childSessionId === void 0 ? {} : { childSessionId },
352
- ...meta === void 0 ? {} : { meta }
353
- }, signal);
354
- }
355
- rewind(id, toBoundary, signal) {
356
- return this.ctx.sessionBranch.rewind(id, toBoundary, signal);
357
- }
358
- timeline(sessionId, signal) {
359
- return this.ctx.sessionBranch.timeline(sessionId, signal);
360
- }
361
- edit(operation, signal) {
362
- return this.branchOperation(operation, signal);
363
- }
364
- reroll(operation, signal) {
365
- return this.branchOperation(operation, signal);
366
- }
367
- retry(operation, signal) {
368
- return this.branchOperation(operation, signal);
369
- }
370
- async editableMessages(sessionId, signal) {
371
- return editableMessages(closedTurns(await this.readEvents(sessionId, signal)));
372
- }
373
- async retryableTurns(sessionId, signal) {
374
- return retryableTurns(closedTurns(await this.readEvents(sessionId, signal)));
375
- }
376
- async branchOperation(operation, signal) {
377
- signal?.throwIfAborted();
378
- const events = await this.readEvents(operation.sessionId, signal);
379
- const turns = closedTurns(events);
380
- const plan = operation.action === "edit" ? editPlan(operation, turns) : operation.action === "retry" ? retryPlan(operation, turns) : rerollPlan(operation, turns);
381
- const headerConfig = events.findLast((event) => event.type === "request/header")?.data.header.config;
382
- const seedSuffix = [];
383
- appendLogSeedEvent(seedSuffix, "session-branch/version", plan.version, true);
384
- if (plan.manualTurn !== void 0) appendManualTurn(seedSuffix, plan.manualTurn);
385
- const turnIndex = turns.findIndex((turn) => turn.startSeq === plan.anchorSeq);
386
- const boundary = plan.rewindBoundary !== void 0 ? plan.rewindBoundary : turnIndex <= 0 ? -1 : turns[turnIndex - 1].endSeq;
387
- const live = this.ctx.sessions.get(operation.sessionId);
388
- await this.ctx.sessionBranch.rewind(operation.sessionId, boundary, signal);
389
- if (seedSuffix.length > 0) {
390
- if (live !== void 0) {
391
- appendSeedSuffixLive(live, seedSuffix);
392
- this.ctx.sessionBranch.syncLiveCursor(operation.sessionId);
393
- await this.ctx.sessions.flush(live);
394
- } else {
395
- const rawKeepLength = boundary + (plan.rewindBoundary === void 0 ? 1 : 0);
396
- const keepLength = balanceRewindPrefix(events.slice(0, rawKeepLength)).length;
397
- const renumbered = seedSuffix.map((event, index) => ({
398
- ...event,
399
- seq: keepLength + index
400
- }));
401
- await this.ctx.sessionPersistence.append(operation.sessionId, renumbered);
402
- }
403
- }
404
- const queuedTurns = await this.driveAgent(operation.sessionId, plan.queuedUsers, signal, headerConfig);
405
- return {
406
- sessionId: operation.sessionId,
407
- queuedTurns,
408
- live: this.ctx.sessions.get(operation.sessionId) !== void 0
409
- };
410
- }
411
- async readEvents(sessionId, signal) {
412
- const live = this.ctx.sessions.get(sessionId);
413
- if (live !== void 0) return live.snapshotEvents();
414
- return (await this.ctx.sessionBranch.readRawEvents(sessionId, signal)).events;
415
- }
416
- async driveAgent(sessionId, queuedUsers, signal, headerConfig) {
417
- if (queuedUsers.length === 0) return 0;
418
- signal?.throwIfAborted();
419
- if (this.ctx.get("agents") === void 0) return 0;
420
- const provider = headerConfig?.provider ?? "";
421
- const model = headerConfig?.model ?? "";
422
- if (provider.length === 0 || model.length === 0) {
423
- const config = (await this.readEvents(sessionId, signal)).findLast((event) => event.type === "request/header")?.data.header.config;
424
- const fallbackProvider = config?.provider ?? "";
425
- const fallbackModel = config?.model ?? "";
426
- if (fallbackProvider.length === 0 || fallbackModel.length === 0) return 0;
427
- return this.queueThroughAgent(sessionId, queuedUsers, signal, fallbackProvider, fallbackModel);
428
- }
429
- return this.queueThroughAgent(sessionId, queuedUsers, signal, provider, model);
430
- }
431
- async queueThroughAgent(sessionId, queuedUsers, signal, provider, model) {
432
- const agents = this.ctx.get("agents");
433
- if (agents === void 0) return 0;
434
- const existing = agents.get(sessionId);
435
- if (existing !== void 0) {
436
- for (const message of queuedUsers) existing.followup(message);
437
- await this.ctx.sessions.flush(existing.session);
438
- return queuedUsers.length;
439
- }
440
- const handle = await agents.resume({
441
- resumeSessionId: sessionId,
442
- agentOptions: {
443
- provider,
444
- model
445
- }
446
- }).catch((error) => {
447
- this.ctx.logger.warn("session-editor: agent resume failed (%s); version remains durable", String(error));
448
- });
449
- if (handle === void 0) return 0;
450
- for (const message of queuedUsers) handle.agent.followup(message);
451
- await this.ctx.sessions.flush(handle.agent.session);
452
- return queuedUsers.length;
453
- }
454
- };
455
- function objectValue(value) {
456
- if (typeof value !== "object" || value === null || Array.isArray(value)) throw new TypeError("请求体必须是 JSON 对象。");
457
- return value;
458
- }
459
- function sessionIdOf(value) {
460
- if (typeof value !== "string" || value.length === 0) throw new TypeError("sessionId 必须是非空字符串。");
461
- return value;
462
- }
463
- function integerOf(value, name) {
464
- if (!Number.isSafeInteger(value) || value < 0) throw new TypeError(`${name} 必须是非负安全整数。`);
465
- return value;
466
- }
467
- function cascadeOf(value) {
468
- if (value !== "truncate" && value !== "preserve") throw new TypeError("cascade 必须是 truncate 或 preserve。");
469
- return value;
470
- }
471
- function decodeOperation(value) {
472
- const record = objectValue(value);
473
- const sessionId = sessionIdOf(record["sessionId"]);
474
- switch (record["action"]) {
475
- case "edit":
476
- if (typeof record["text"] !== "string") throw new TypeError("text 必须是字符串。");
477
- return {
478
- action: "edit",
479
- sessionId,
480
- eventSeq: integerOf(record["eventSeq"], "eventSeq"),
481
- blockIndex: integerOf(record["blockIndex"], "blockIndex"),
482
- text: record["text"],
483
- cascade: cascadeOf(record["cascade"])
484
- };
485
- case "reroll": return {
486
- action: "reroll",
487
- sessionId
488
- };
489
- case "retry": return {
490
- action: "retry",
491
- sessionId,
492
- turn: integerOf(record["turn"], "turn"),
493
- cascade: cascadeOf(record["cascade"])
494
- };
495
- case "rewind": return {
496
- action: "rewind",
497
- sessionId,
498
- toBoundary: integerOf(record["toBoundary"], "toBoundary")
499
- };
500
- case "fork": return {
501
- action: "fork",
502
- sessionId,
503
- ...record["atSeq"] === void 0 ? {} : { atSeq: integerOf(record["atSeq"], "atSeq") },
504
- ...record["childSessionId"] === void 0 ? {} : { childSessionId: sessionIdOf(record["childSessionId"]) }
505
- };
506
- default: throw new TypeError("action 必须是 edit、reroll、retry、rewind 或 fork。");
507
- }
508
- }
509
- function requestJson(request) {
510
- return new Promise((resolve, reject) => {
511
- const decoder = new TextDecoder();
512
- let text = "";
513
- request.on("data", (chunk) => {
514
- text += typeof chunk === "string" ? chunk : decoder.decode(chunk, { stream: true });
515
- });
516
- request.on("end", () => {
517
- try {
518
- text += decoder.decode();
519
- resolve(JSON.parse(text));
520
- } catch (error) {
521
- reject(error);
522
- }
523
- });
524
- request.on("error", reject);
525
- });
526
- }
527
- function respondJson(response, status, value) {
528
- response.writeHead(status, {
529
- "content-type": "application/json; charset=utf-8",
530
- "cache-control": "no-store"
531
- });
532
- response.end(JSON.stringify(value));
533
- }
534
- async function readTimeline(editor, sessionId) {
535
- return toTimelinePayload(sessionId, await editor.timeline(sessionId), await editor.editableMessages(sessionId), await editor.retryableTurns(sessionId));
536
- }
537
- async function runOperation(editor, operation) {
538
- switch (operation.action) {
539
- case "edit": {
540
- const result = await editor.edit(operation);
541
- return {
542
- sessionId: result.sessionId,
543
- queuedTurns: result.queuedTurns,
544
- ...result.live === void 0 ? {} : { live: result.live }
545
- };
546
- }
547
- case "reroll": {
548
- const result = await editor.reroll(operation);
549
- return {
550
- sessionId: result.sessionId,
551
- queuedTurns: result.queuedTurns,
552
- ...result.live === void 0 ? {} : { live: result.live }
553
- };
554
- }
555
- case "retry": {
556
- const result = await editor.retry(operation);
557
- return {
558
- sessionId: result.sessionId,
559
- queuedTurns: result.queuedTurns,
560
- ...result.live === void 0 ? {} : { live: result.live }
561
- };
562
- }
563
- case "rewind":
564
- await editor.rewind(operation.sessionId, operation.toBoundary);
565
- return {
566
- sessionId: operation.sessionId,
567
- queuedTurns: 0
568
- };
569
- case "fork": return {
570
- sessionId: await editor.fork(operation.sessionId, operation.atSeq, operation.childSessionId),
571
- queuedTurns: 0
572
- };
573
- }
574
- }
575
- async function handleRoute(editor, request, response) {
576
- try {
577
- if (request.method === "GET") {
578
- respondJson(response, 200, await readTimeline(editor, sessionIdOf(new URL(request.url ?? "/session-editor", "http://session-editor.local").searchParams.get("sessionId"))));
579
- return;
580
- }
581
- if (request.method === "POST") {
582
- respondJson(response, 200, await runOperation(editor, decodeOperation(await requestJson(request))));
583
- return;
584
- }
585
- response.writeHead(405);
586
- response.end();
587
- } catch (error) {
588
- const message = error instanceof Error ? error.message : String(error);
589
- respondJson(response, error instanceof TypeError ? 400 : 409, { error: message });
590
- }
591
- }
592
- function registerHttpRoutes(ctx) {
593
- const webServer = ctx.get("webServer");
594
- if (webServer === void 0) return;
595
- ctx.effect(() => {
596
- const editor = ctx.sessionEditor;
597
- return webServer.register({
598
- kind: "exact",
599
- path: SESSION_EDITOR_PATH,
600
- handler: (request, response) => handleRoute(editor, request, response)
601
- });
602
- }, "session-editor: HTTP route");
603
- }
604
- //#endregion
1
+ import { n as SessionEditor, t as SESSION_BRANCH_VERSION_SCHEMA } from "./src-CXYW0Bc0.mjs";
2
+ import { closedTurns, editableMessages, retryableTurns } from "./plan.mjs";
605
3
  export { SESSION_BRANCH_VERSION_SCHEMA, SessionEditor, SessionEditor as default, closedTurns, editableMessages, retryableTurns };
606
-
607
- //# sourceMappingURL=index.mjs.map
@@ -4,5 +4,4 @@ declare const name = "ui-conversation-message-actions-invariant";
4
4
  declare const inject: string[];
5
5
  declare const apply: (ctx: Context) => Promise<() => void>;
6
6
  //#endregion
7
- export { apply, inject, name };
8
- //# sourceMappingURL=invariant.d.mts.map
7
+ export { apply, inject, name };
@@ -6,5 +6,3 @@ const install = () => {};
6
6
  const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
7
7
  //#endregion
8
8
  export { apply, inject, name };
9
-
10
- //# sourceMappingURL=invariant.mjs.map