openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
199 lines (198 loc) • 8.33 kB
JavaScript
import { l as asPositiveSafeInteger } from "./number-coercion-CJQ8TR--.js";
import { a as readRecentSessionMessagesWithStatsAsync, d as readSessionMessagesAsync, t as attachOpenClawTranscriptMeta } from "./session-utils.fs-D_8-X4jl.js";
import "./session-utils-CHNCuY8a.js";
import { a as projectChatDisplayMessages } from "./chat-display-projection-Bv2HD4Rr.js";
//#region src/gateway/session-history-state.ts
/** Computes an oversized raw transcript tail window for projected chat history. */
function resolveSessionHistoryTailReadOptions(limit) {
const rawWindow = Math.max(1, Math.floor(limit)) * 20 + 20;
return {
maxMessages: rawWindow,
maxLines: rawWindow
};
}
function resolveCursorSeq(cursor) {
if (!cursor) return;
const normalized = cursor.startsWith("seq:") ? cursor.slice(4) : cursor;
if (!/^\d+$/.test(normalized)) return;
const value = Number(normalized);
return Number.isSafeInteger(value) && value > 0 ? value : void 0;
}
function toSessionHistoryMessages(messages) {
return messages.filter((message) => Boolean(message) && typeof message === "object" && !Array.isArray(message));
}
function buildPaginatedSessionHistory(params) {
return {
items: params.messages,
messages: params.messages,
hasMore: params.hasMore,
...params.nextCursor ? { nextCursor: params.nextCursor } : {}
};
}
function resolveMessageSeq(message) {
return asPositiveSafeInteger(message?.["__openclaw"]?.seq);
}
function isMessageToolMirrorMessage(message) {
return message.openclawMessageToolMirror !== void 0;
}
function paginateSessionMessages(messages, limit, cursor) {
const cursorSeq = resolveCursorSeq(cursor);
let endExclusive = messages.length;
if (typeof cursorSeq === "number") {
endExclusive = messages.findIndex((message, index) => {
const seq = resolveMessageSeq(message);
if (typeof seq === "number") return seq >= cursorSeq;
return index + 1 >= cursorSeq;
});
if (endExclusive < 0) endExclusive = messages.length;
}
const start = typeof limit === "number" && limit > 0 ? Math.max(0, endExclusive - limit) : 0;
const paginatedMessages = messages.slice(start, endExclusive);
const firstSeq = resolveMessageSeq(paginatedMessages[0]);
return buildPaginatedSessionHistory({
messages: paginatedMessages,
hasMore: start > 0,
...start > 0 && typeof firstSeq === "number" ? { nextCursor: String(firstSeq) } : {}
});
}
/** Builds the display history snapshot and raw transcript sequence watermark. */
function buildSessionHistorySnapshot(params) {
const history = paginateSessionMessages(toSessionHistoryMessages(projectChatDisplayMessages(params.rawMessages, { maxChars: params.maxChars ?? 8e3 })), params.limit, params.cursor);
if (!params.cursor && typeof params.totalRawMessages === "number" && params.totalRawMessages > params.rawMessages.length && history.messages.length > 0) {
const firstSeq = resolveMessageSeq(history.messages[0]);
history.hasMore = true;
if (typeof firstSeq === "number") history.nextCursor = String(firstSeq);
}
const rawHistoryMessages = toSessionHistoryMessages(params.rawMessages);
return {
history,
rawTranscriptSeq: params.rawTranscriptSeq ?? resolveMessageSeq(rawHistoryMessages.at(-1)) ?? rawHistoryMessages.length
};
}
/** Tracks session-history SSE state and decides when inline appends are still valid. */
var SessionHistorySseState = class SessionHistorySseState {
static fromRawSnapshot(params) {
return new SessionHistorySseState({
target: params.target,
maxChars: params.maxChars,
limit: params.limit,
cursor: params.cursor,
initialRawMessages: params.rawMessages,
rawTranscriptSeq: params.rawTranscriptSeq,
totalRawMessages: params.totalRawMessages
});
}
constructor(params) {
this.target = params.target;
this.maxChars = params.maxChars ?? 8e3;
this.limit = params.limit;
this.cursor = params.cursor;
const snapshot = this.buildSnapshot({
rawMessages: params.initialRawMessages,
...typeof params.rawTranscriptSeq === "number" ? { rawTranscriptSeq: params.rawTranscriptSeq } : {},
...typeof params.totalRawMessages === "number" ? { totalRawMessages: params.totalRawMessages } : {}
});
this.sentHistory = snapshot.history;
this.rawTranscriptSeq = snapshot.rawTranscriptSeq;
}
snapshot() {
return this.sentHistory;
}
appendInlineMessage(update) {
if (this.limit !== void 0 || this.cursor !== void 0) return null;
const carriedSeq = asPositiveSafeInteger(update.messageSeq);
if (carriedSeq !== void 0) {
if (carriedSeq <= this.rawTranscriptSeq) return { shouldRefresh: true };
this.rawTranscriptSeq = carriedSeq;
} else this.rawTranscriptSeq += 1;
const nextMessage = attachOpenClawTranscriptMeta(update.message, {
...typeof update.messageId === "string" ? { id: update.messageId } : {},
seq: this.rawTranscriptSeq
});
const projectedMessages = toSessionHistoryMessages(projectChatDisplayMessages([...this.sentHistory.messages, nextMessage], { maxChars: this.maxChars }));
if (projectedMessages.length > this.sentHistory.messages.length) {
const addedMessages = projectedMessages.slice(this.sentHistory.messages.length);
if (addedMessages.length > 1) {
this.sentHistory = buildPaginatedSessionHistory({
messages: projectedMessages,
hasMore: false
});
return { shouldRefresh: true };
}
const projectedMessage = addedMessages[0];
if (projectedMessage !== void 0) {
const emittedMessage = isMessageToolMirrorMessage(projectedMessage) || resolveMessageSeq(projectedMessage) === void 0 ? attachOpenClawTranscriptMeta(projectedMessage, { seq: this.rawTranscriptSeq }) : projectedMessage;
const nextMessages = [...this.sentHistory.messages, emittedMessage];
this.sentHistory = buildPaginatedSessionHistory({
messages: nextMessages,
hasMore: false
});
return {
message: emittedMessage,
messageSeq: resolveMessageSeq(emittedMessage)
};
}
}
const [sanitizedMessage] = toSessionHistoryMessages(projectChatDisplayMessages([nextMessage], { maxChars: this.maxChars }));
if (!sanitizedMessage) {
if (projectedMessages.length < this.sentHistory.messages.length) {
this.sentHistory = buildPaginatedSessionHistory({
messages: projectedMessages,
hasMore: false
});
return { shouldRefresh: true };
}
return null;
}
if (projectedMessages.length <= this.sentHistory.messages.length) {
this.sentHistory = buildPaginatedSessionHistory({
messages: projectedMessages,
hasMore: false
});
return { shouldRefresh: true };
}
const projectedMessage = projectedMessages.at(-1) ?? sanitizedMessage;
const nextMessages = [...this.sentHistory.messages, projectedMessage];
this.sentHistory = buildPaginatedSessionHistory({
messages: nextMessages,
hasMore: false
});
return {
message: projectedMessage,
messageSeq: resolveMessageSeq(projectedMessage)
};
}
async refreshAsync() {
const rawSnapshot = await this.readRawSnapshotAsync();
const snapshot = this.buildSnapshot(rawSnapshot);
this.rawTranscriptSeq = snapshot.rawTranscriptSeq;
this.sentHistory = snapshot.history;
return snapshot.history;
}
buildSnapshot(rawSnapshot) {
return buildSessionHistorySnapshot({
rawMessages: rawSnapshot.rawMessages,
maxChars: this.maxChars,
limit: this.limit,
cursor: this.cursor,
...typeof rawSnapshot.rawTranscriptSeq === "number" ? { rawTranscriptSeq: rawSnapshot.rawTranscriptSeq } : {},
...typeof rawSnapshot.totalRawMessages === "number" ? { totalRawMessages: rawSnapshot.totalRawMessages } : {}
});
}
async readRawSnapshotAsync() {
if (this.cursor === void 0 && typeof this.limit === "number") {
const snapshot = await readRecentSessionMessagesWithStatsAsync(this.target.sessionId, this.target.storePath, this.target.sessionFile, { ...resolveSessionHistoryTailReadOptions(this.limit) });
return {
rawMessages: snapshot.messages,
rawTranscriptSeq: snapshot.totalMessages,
totalRawMessages: snapshot.totalMessages
};
}
return { rawMessages: await readSessionMessagesAsync(this.target.sessionId, this.target.storePath, this.target.sessionFile, {
mode: "full",
reason: "session history cursor pagination"
}) };
}
};
//#endregion
export { buildSessionHistorySnapshot as n, resolveSessionHistoryTailReadOptions as r, SessionHistorySseState as t };