UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

231 lines (230 loc) • 9.79 kB
import { a as asOptionalRecord } from "./record-coerce-TIsquoVR.mjs"; import { s as resolveSessionTranscriptActiveLeafEntryId } from "./transcript-tree-D_mugOJ0.mjs"; import { t as readLegacyCompactionHistory } from "./legacy-compaction-history-x3brqeU3.mjs"; import { r as augmentChatHistoryWithCanvasBlocks } from "./chat-display-projection.canvas-Bq1n-BMM.mjs"; import { o as isHeartbeatHistoryTurnBoundaryMessage, r as dropPreSessionStartAnnouncePairs } from "./chat-display-projection.history-YCIu7ZzW.mjs"; import { s as projectChatDisplayMessagesWithState, t as createChatHistoryRecoveryProjection } from "./chat-display-projection.core-XxfS96zH.mjs"; import { a as readIncrementalChatHistoryTail, i as readChatHistoryRecoveryContext, n as readChatHistoryMessageId, r as readChatHistoryMessageSeq, t as dropChatHistoryOverreadContextMessage } from "./session-history-tail-vcLIqMjD.mjs"; //#region src/gateway/server-methods/chat-history-page-kernel.ts function resolveChatHistoryNextOffset(params) { let oldestSeq; let boundedSiblings = 0; for (const message of params.messages) { const seq = readChatHistoryMessageSeq(message); oldestSeq ??= seq; if (seq !== void 0 && seq === oldestSeq) boundedSiblings += 1; } if (oldestSeq === void 0) return params.offset + params.rawPageMessages; const recordOffset = params.totalMessages - oldestSeq + 1; const replayOffset = recordOffset - 1; if (replayOffset > params.offset) { let projectedSiblings = 0; for (const message of params.projected) if (readChatHistoryMessageSeq(message) === oldestSeq) { projectedSiblings += 1; if (projectedSiblings > boundedSiblings) return replayOffset; } } return Math.max(params.offset + 1, recordOffset); } function resolveChatHistoryActiveLeafEntryId(readPage) { if (readPage.transcriptSource !== "active") return null; if (Object.hasOwn(readPage, "activeLeafEntryId")) return readPage.activeLeafEntryId ?? null; return resolveSessionTranscriptActiveLeafEntryId(readPage.transcriptEvents ?? []) ?? null; } /** Preserve token metrics saved by pre-removal builds; new markers own their metrics. */ function enrichChatHistoryCompactionMarkers(messages, entry) { let checkpoints; try { checkpoints = readLegacyCompactionHistory(entry); } catch { return messages; } if (checkpoints.length === 0) return messages; const checkpointByEntryId = new Map(checkpoints.flatMap((checkpoint) => { const entryId = checkpoint.postCompaction.entryId; return entryId ? [[entryId, checkpoint]] : []; })); let changed = false; const enriched = messages.map((message) => { const record = asOptionalRecord(message); const metadata = asOptionalRecord(record?.["__openclaw"]); if (metadata?.kind !== "compaction" || typeof metadata.id !== "string") return message; const checkpoint = checkpointByEntryId.get(metadata.id); if (!checkpoint) return message; const tokensBefore = checkpoint.tokensBefore; const tokensAfter = checkpoint.tokensAfter; if (tokensBefore === void 0 && tokensAfter === void 0) return message; changed = true; return { ...record, __openclaw: { ...metadata, ...tokensBefore !== void 0 ? { tokensBefore } : {}, ...tokensAfter !== void 0 ? { tokensAfter } : {} } }; }); return changed ? enriched : messages; } function resolveChatHistoryMessageGroup(messages, index, messageCost) { const seq = readChatHistoryMessageSeq(messages[index]); let start = index; let end = index + 1; let cost = messageCost(messages[index]); if (seq === void 0) return { start, end, cost }; while (start > 0 && readChatHistoryMessageSeq(messages[start - 1]) === seq) { start -= 1; cost += messageCost(messages[start]); } while (end < messages.length && readChatHistoryMessageSeq(messages[end]) === seq) { cost += messageCost(messages[end]); end += 1; } return { start, end, cost }; } function capChatHistoryAroundMessage(params) { const anchorIndex = params.messages.findIndex((message) => readChatHistoryMessageId(message) === params.messageId); if (anchorIndex === -1) return []; const messageCost = params.messageCost ?? (() => 1); const anchorGroup = resolveChatHistoryMessageGroup(params.messages, anchorIndex, messageCost); if (!(anchorGroup.cost <= params.maxCost)) return [params.messages[anchorIndex]]; let { start, end, cost } = anchorGroup; let canGrowOlder = start > 0; let canGrowNewer = end < params.messages.length; while (canGrowOlder || canGrowNewer) { if (canGrowOlder) { const olderGroup = resolveChatHistoryMessageGroup(params.messages, start - 1, messageCost); if (cost + olderGroup.cost <= params.maxCost) { start = olderGroup.start; cost += olderGroup.cost; } else canGrowOlder = false; } canGrowOlder &&= start > 0; if (canGrowNewer) { const newerGroup = resolveChatHistoryMessageGroup(params.messages, end, messageCost); if (cost + newerGroup.cost <= params.maxCost) { end = newerGroup.end; cost += newerGroup.cost; } else canGrowNewer = false; } canGrowNewer &&= end < params.messages.length; } return params.messages.slice(start, end); } /** Assemble one page from admitted readers; host imports and profile discovery stay outside. */ async function readChatHistoryPageKernel(params, options) { const { entry, sessionId, storePath, sessionAgentId, canonicalKey, max, maxHistoryBytes, effectiveMaxChars, offset, messageId } = params; if (!sessionId || !storePath) { if (messageId) return { messages: [] }; return { ...(offset ?? 0) === 0 ? { activeLeafEntryId: null } : {}, messages: [], ...offset !== void 0 ? { responseOffset: offset } : {}, pagination: { offset: offset ?? 0, totalMessages: 0, rawPageMessages: 0 } }; } const readScope = { agentId: sessionAgentId, sessionEntry: entry, sessionId, sessionKey: canonicalKey, storePath }; const cliSessionId = options.cliSessionId; if (messageId && !cliSessionId) { const readPage = await options.readers.readSessionMessagesAroundIdWithStatsAsync(readScope, { messageId, maxMessages: max, allowResetArchiveFallback: true, readOnly: options.readOnly }); if (!readPage.found) return { messages: [] }; const overreadContextMessage = readPage.hasOverreadContext || readPage.messages.length > max ? readPage.messages[0] : void 0; const localMessages = dropChatHistoryOverreadContextMessage(dropPreSessionStartAnnouncePairs(readPage.messages, typeof entry?.sessionStartedAt === "number" ? entry.sessionStartedAt : void 0), overreadContextMessage); const project = (messages) => projectChatDisplayMessagesWithState(messages, { subagentCoordination: options.readers.subagentCoordination, includeCommentaryFallbacks: true, maxChars: effectiveMaxChars, resolveCronJobName: options.resolveCronJobName, ...options.deferProfileDisplay ? {} : { resolveCurrentUserProfileDisplay: options.resolveCurrentUserProfileDisplay }, turnBoundaryPending: isHeartbeatHistoryTurnBoundaryMessage(overreadContextMessage) }); const projection = project(localMessages); let projected = projection.messages; const newestPageSeq = readChatHistoryMessageSeq(localMessages.at(-1)); if (readPage.offset > 0 && newestPageSeq !== void 0 && projection.assistantErrorPending) { const recoveryContext = await readChatHistoryRecoveryContext({ messages: localMessages, createRecovery: (messages) => { const recovery = createChatHistoryRecoveryProjection({ maxChars: effectiveMaxChars, subagentCoordination: options.readers.subagentCoordination }); recovery.append(messages); return recovery; }, readScope, readers: options.readers, displaySource: readPage.displaySource, maxBytes: maxHistoryBytes, readOnly: options.readOnly }); if (recoveryContext.length > 0) projected = project([...localMessages, ...recoveryContext]).messages.filter((message) => (readChatHistoryMessageSeq(message) ?? Infinity) <= newestPageSeq); } return { messages: augmentChatHistoryWithCanvasBlocks(capChatHistoryAroundMessage({ messages: projected, messageId, maxCost: max })), ...projection.activity.length ? { activity: projection.activity } : {} }; } const incrementalTail = await readIncrementalChatHistoryTail({ entry, readScope, effectiveMaxChars, max, maxBytes: maxHistoryBytes, offset, ...options }); const { readPage } = incrementalTail; const currentOffset = incrementalTail.windowReset ? 0 : offset; const isOffsetPage = currentOffset !== void 0 && !cliSessionId; const includeActiveLeaf = !isOffsetPage || currentOffset === 0; const activeLeafEntryId = includeActiveLeaf ? resolveChatHistoryActiveLeafEntryId(readPage) : null; const buildTailPage = (messages) => ({ ...incrementalTail.windowReset ? { windowReset: true } : {}, ...includeActiveLeaf ? { activeLeafEntryId } : {}, ...includeActiveLeaf && readPage.transcriptSource === "active" && readPage.deltaCursor && !incrementalTail.projection.assistantErrorPending ? { deltaCursor: readPage.deltaCursor } : {}, messages: augmentChatHistoryWithCanvasBlocks(messages), ...incrementalTail.projection.activity.length ? { activity: incrementalTail.projection.activity } : {}, ...isOffsetPage ? { responseOffset: currentOffset } : {}, pagination: { offset: currentOffset ?? 0, totalMessages: readPage.totalMessages, rawPageMessages: incrementalTail.rawPageMessages } }); return !isOffsetPage && options.readCliTailPage ? options.readCliTailPage({ readScope, incrementalTail, activeLeafEntryId, buildTailPage }) : buildTailPage(incrementalTail.projected); } //#endregion export { resolveChatHistoryNextOffset as i, enrichChatHistoryCompactionMarkers as n, readChatHistoryPageKernel as r, capChatHistoryAroundMessage as t };