From 23318ea0dcb884989b499aa337e99f532d4c48b7 Mon Sep 17 00:00:00 2001 From: youzini Date: Sun, 31 May 2026 13:22:49 +0000 Subject: [PATCH] refactor(sync): extract saveGraphToChat + luker cache action (Phase 5e) --- index.js | 251 ++--------------------------- sync/graph-load-persist.js | 306 ++++++++++++++++++++++++++++++++++++ tests/graph-persistence.mjs | 37 +++++ 3 files changed, 357 insertions(+), 237 deletions(-) diff --git a/index.js b/index.js index 33a287f..e64b8da 100644 --- a/index.js +++ b/index.js @@ -354,7 +354,9 @@ import { buildBmeSyncRuntimeOptionsImpl, loadGraphFromChatImpl, maybeCaptureGraphShadowSnapshotImpl, + onRebuildLocalCacheFromLukerSidecarImpl, persistExtractionBatchResultImpl, + saveGraphToChatImpl, shouldUseAuthorityGraphStoreImpl, shouldUseAuthorityJobsImpl, syncGraphLoadFromLiveContextImpl, @@ -1392,9 +1394,14 @@ function createGraphLoadPersistRuntime() { isAuthorityJobTypeSupported, isAuthorityVectorConfig, isGraphEffectivelyEmpty, + isGraphLoadStateDbReady, + isGraphMetadataWriteAllowed, isIndexedDbSnapshotMeaningful, + isLukerPrimaryPersistenceHost, + loadGraphFromLukerSidecarV2, loadGraphFromChat, loadGraphFromIndexedDb, + normalizeIndexedDbRevision, normalizeAuthorityCapabilityState, normalizeAuthorityJobConfig, normalizeAuthoritySettings, @@ -1403,6 +1410,7 @@ function createGraphLoadPersistRuntime() { persistGraphToChatMetadata, persistGraphToConfiguredDurableTier, queueGraphPersist, + queueGraphPersistToIndexedDb, readCachedIndexedDbSnapshot, recordLocalPersistEarlyFailure, recordAuthorityBlobSnapshot, @@ -1412,11 +1420,14 @@ function createGraphLoadPersistRuntime() { rememberResolvedGraphIdentityAlias, resolveCompatibleGraphShadowSnapshot, resolveCurrentChatIdentity, + resolveCurrentChatStateTarget, + resolvePersistRevisionFloor, resolvePersistenceChatId, resolvePreferredGraphLocalStorePresentation, resolveSnapshotGraphStorePresentation, restoreRecallUiStateFromPersistence, runAuthorityConsistencyAudit, + scheduleBmeIndexedDbTask, scheduleGraphChatStateProbe, scheduleIndexedDbGraphProbe, schedulePersistedRecallMessageUiRefresh, @@ -1427,6 +1438,7 @@ function createGraphLoadPersistRuntime() { stampGraphPersistenceMeta, syncCommitMarkerToPersistenceState, updateGraphPersistenceState, + toastr, writeAuthorityLukerCheckpointBlob, writeGraphShadowSnapshot, }; @@ -14505,205 +14517,7 @@ function queueGraphPersistToIndexedDb( } function saveGraphToChat(options = {}) { - const context = getContext(); - if (!context || !currentGraph) { - return buildGraphPersistResult({ - saved: false, - blocked: true, - reason: "missing-context-or-graph", - }); - } - const chatId = resolvePersistenceChatId(context, currentGraph); - const { - reason = "graph-save", - markMutation = true, - persistMetadata = false, - captureShadow = Boolean(persistMetadata), - immediate = markMutation, - } = options; - - ensureCurrentGraphRuntimeState(); - currentGraph.historyState.extractionCount = extractionCount; - if (!chatId) { - recordLocalPersistEarlyFailure("missing-chat-id", { - chatId, - revision: 0, - }); - return buildGraphPersistResult({ - saved: false, - blocked: true, - reason: "missing-chat-id", - }); - } - - const revision = markMutation - ? allocateRequestedPersistRevision(0, currentGraph) - : resolvePersistRevisionFloor(0, currentGraph); - const persistenceEnvironment = buildPersistenceEnvironment( - context, - getPreferredGraphLocalStorePresentationSync(), - ); - - if (captureShadow) { - maybeCaptureGraphShadowSnapshot(reason); - } - - const shouldQueueIndexedDbPersist = - (persistenceEnvironment.hostProfile !== "luker" || - persistenceEnvironment.primaryStorageTier === "authority-sql") && - (markMutation || !isGraphEffectivelyEmpty(currentGraph)); - if (shouldQueueIndexedDbPersist) { - queueGraphPersistToIndexedDb(chatId, currentGraph, { - revision, - reason, - }); - } - - const metadataFallbackEnabled = - Boolean(persistMetadata) || !ensureBmeChatManager(); - - if (!markMutation) { - const hasMeaningfulGraphData = !isGraphEffectivelyEmpty(currentGraph); - if ( - !hasMeaningfulGraphData || - graphPersistenceState.loadState === GRAPH_LOAD_STATES.EMPTY_CONFIRMED - ) { - return buildGraphPersistResult({ - saved: false, - blocked: false, - reason: hasMeaningfulGraphData - ? "passive-empty-confirmed-skipped" - : "passive-empty-graph-skipped", - revision, - }); - } - } - - if (persistenceEnvironment.primaryStorageTier === "luker-chat-state") { - const persistGraph = cloneGraphForPersistence(currentGraph, chatId); - const chatStateTarget = resolveCurrentChatStateTarget(context); - const lastProcessedAssistantFloor = Number.isFinite( - Number(persistGraph?.historyState?.lastProcessedAssistantFloor), - ) - ? Number(persistGraph.historyState.lastProcessedAssistantFloor) - : null; - scheduleBmeIndexedDbTask(async () => { - const persistResult = await persistGraphToConfiguredDurableTier( - context, - persistGraph, - { - chatId, - revision, - reason, - lastProcessedAssistantFloor, - chatStateTarget, - graphDetached: true, - }, - ); - if (!persistResult?.accepted) { - queueGraphPersist(reason, revision, { - immediate, - graph: persistGraph, - chatId, - captureShadow, - }); - } - refreshPanelLiveState(); - }); - updateGraphPersistenceState({ - hostProfile: persistenceEnvironment.hostProfile, - primaryStorageTier: persistenceEnvironment.primaryStorageTier, - cacheStorageTier: persistenceEnvironment.cacheStorageTier, - lastPersistReason: String(reason || "graph-save"), - lastPersistMode: "luker-chat-state-queued", - }); - return buildGraphPersistResult({ - saved: false, - queued: true, - blocked: false, - accepted: false, - reason: "luker-chat-state-queued", - revision, - saveMode: "luker-chat-state-queued", - storageTier: "luker-chat-state", - primaryTier: persistenceEnvironment.primaryStorageTier, - cacheTier: persistenceEnvironment.cacheStorageTier, - }); - } - - if (!metadataFallbackEnabled) { - const preferredLocalStore = getPreferredGraphLocalStorePresentationSync(); - const saveMode = shouldQueueIndexedDbPersist - ? `${preferredLocalStore.reasonPrefix}-queued` - : `${preferredLocalStore.reasonPrefix}-skip`; - updateGraphPersistenceState({ - storagePrimary: preferredLocalStore.storagePrimary, - storageMode: preferredLocalStore.storageMode, - dbReady: - graphPersistenceState.dbReady ?? - isGraphLoadStateDbReady(graphPersistenceState.loadState), - lastPersistReason: String(reason || "graph-save"), - lastPersistMode: saveMode, - pendingPersist: false, - queuedPersistChatId: "", - queuedPersistMode: "", - queuedPersistReason: "", - queuedPersistRotateIntegrity: false, - dualWriteLastResult: { - action: "save", - target: preferredLocalStore.storagePrimary, - queued: Boolean(shouldQueueIndexedDbPersist), - success: true, - chatId, - revision: normalizeIndexedDbRevision(revision), - reason: String(reason || "graph-save"), - at: Date.now(), - }, - }); - return buildGraphPersistResult({ - saved: false, - queued: Boolean(shouldQueueIndexedDbPersist), - blocked: false, - accepted: false, - reason: shouldQueueIndexedDbPersist - ? `${preferredLocalStore.reasonPrefix}-queued` - : `${preferredLocalStore.reasonPrefix}-empty-skip`, - revision, - saveMode, - storageTier: shouldQueueIndexedDbPersist - ? preferredLocalStore.storagePrimary - : "none", - }); - } - - if (!isGraphMetadataWriteAllowed()) { - console.warn( - `[ST-BME] 图谱写回已被安全保护拦截(chat=${chatId},state=${graphPersistenceState.loadState},reason=${reason})`, - ); - return queueGraphPersist(reason, revision, { immediate }); - } - - const metadataPersistResult = persistGraphToChatMetadata(context, { - reason, - revision, - immediate, - }); - updateGraphPersistenceState({ - storageMode: "metadata-full", - dualWriteLastResult: { - action: "save", - target: "metadata", - success: Boolean(metadataPersistResult?.saved), - queued: Boolean(metadataPersistResult?.queued), - blocked: Boolean(metadataPersistResult?.blocked), - chatId, - revision: normalizeIndexedDbRevision(revision), - reason: String(reason || "graph-save"), - at: Date.now(), - }, - }); - - return metadataPersistResult; + return saveGraphToChatImpl(createGraphLoadPersistRuntime(), options); } function handleGraphShadowSnapshotPageHide() { @@ -17677,44 +17491,7 @@ async function onProbeGraphLoad() { } async function onRebuildLocalCacheFromLukerSidecar() { - const context = getContext(); - const chatStateTarget = resolveCurrentChatStateTarget(context); - if (!isLukerPrimaryPersistenceHost(context)) { - toastr.info("当前宿主不是 Luker,无需从主 sidecar 重建本地缓存"); - return { handledToast: true, reason: "not-luker" }; - } - const chatId = getCurrentChatId(context); - if (!chatId) { - toastr.warning("当前没有聊天上下文"); - return { handledToast: true, reason: "missing-chat-id" }; - } - - const loadResult = await loadGraphFromLukerSidecarV2(chatId, { - source: "panel-manual-luker-cache-rebuild", - allowOverride: true, - chatStateTarget, - }); - if (!loadResult?.loaded || !currentGraph) { - toastr.warning( - `无法从 Luker 主 sidecar 重建本地缓存: ${loadResult?.reason || "sidecar not available"}`, - ); - return { handledToast: true, result: loadResult }; - } - - queueGraphPersistToIndexedDb(chatId, cloneGraphForPersistence(currentGraph, chatId), { - revision: Math.max( - Number(graphPersistenceState.lukerManifestRevision || 0), - Number(getGraphPersistedRevision(currentGraph) || 0), - Number(graphPersistenceState.revision || 0), - ), - reason: "panel-manual-luker-cache-rebuild", - persistRole: "cache-mirror", - scheduleCloudUpload: false, - graphDetached: true, - }); - refreshPanelLiveState(); - toastr.success("已开始从 Luker 主 sidecar 重建本地缓存"); - return { handledToast: true, result: loadResult }; + return await onRebuildLocalCacheFromLukerSidecarImpl(createGraphLoadPersistRuntime()); } async function onRepairLukerSidecar() { diff --git a/sync/graph-load-persist.js b/sync/graph-load-persist.js index 2a8d44b..4b41af5 100644 --- a/sync/graph-load-persist.js +++ b/sync/graph-load-persist.js @@ -1838,3 +1838,309 @@ export function loadGraphFromChatImpl(runtime, options = {}) { }; } + +export function saveGraphToChatImpl(runtime, options = {}) { + const graphPersistenceState = new Proxy({}, { + get(_target, key) { + return (runtime.getGraphPersistenceState?.() || {})[key]; + }, + set(_target, key, value) { + const state = runtime.getGraphPersistenceState?.() || {}; + state[key] = value; + return true; + }, + }); + let currentGraph = runtime.getCurrentGraph?.() || null; + const GRAPH_LOAD_STATES = runtime.GRAPH_LOAD_STATES; + const allocateRequestedPersistRevision = runtime.allocateRequestedPersistRevision; + const buildGraphPersistResult = runtime.buildGraphPersistResult; + const buildPersistenceEnvironment = runtime.buildPersistenceEnvironment; + const canPersistGraphToMetadataFallback = runtime.canPersistGraphToMetadataFallback; + const cloneGraphForPersistence = runtime.cloneGraphForPersistence; + const ensureBmeChatManager = runtime.ensureBmeChatManager; + const ensureCurrentGraphRuntimeState = runtime.ensureCurrentGraphRuntimeState; + const getContext = runtime.getContext; + const getGraphPersistedRevision = runtime.getGraphPersistedRevision; + const getPreferredGraphLocalStorePresentationSync = runtime.getPreferredGraphLocalStorePresentationSync; + const isGraphEffectivelyEmpty = runtime.isGraphEffectivelyEmpty; + const isGraphLoadStateDbReady = runtime.isGraphLoadStateDbReady; + const isGraphMetadataWriteAllowed = runtime.isGraphMetadataWriteAllowed; + const normalizeIndexedDbRevision = runtime.normalizeIndexedDbRevision; + const persistGraphToChatMetadata = runtime.persistGraphToChatMetadata; + const persistGraphToConfiguredDurableTier = runtime.persistGraphToConfiguredDurableTier; + const queueGraphPersist = runtime.queueGraphPersist; + const queueGraphPersistToIndexedDb = runtime.queueGraphPersistToIndexedDb; + const recordLocalPersistEarlyFailure = runtime.recordLocalPersistEarlyFailure; + const refreshPanelLiveState = runtime.refreshPanelLiveState; + const resolveCurrentChatStateTarget = runtime.resolveCurrentChatStateTarget; + const resolvePersistRevisionFloor = runtime.resolvePersistRevisionFloor; + const resolvePersistenceChatId = runtime.resolvePersistenceChatId; + const scheduleBmeIndexedDbTask = runtime.scheduleBmeIndexedDbTask; + const updateGraphPersistenceState = runtime.updateGraphPersistenceState; + const console = runtime.console || globalThis.console; + + const context = getContext(); + if (!context || !currentGraph) { + return buildGraphPersistResult({ + saved: false, + blocked: true, + reason: "missing-context-or-graph", + }); + } + const chatId = resolvePersistenceChatId(context, currentGraph); + const { + reason = "graph-save", + markMutation = true, + persistMetadata = false, + captureShadow = Boolean(persistMetadata), + immediate = markMutation, + } = options; + + ensureCurrentGraphRuntimeState(); + currentGraph = runtime.getCurrentGraph?.() || null; + currentGraph.historyState.extractionCount = runtime.getExtractionCount?.() || 0; + if (!chatId) { + recordLocalPersistEarlyFailure("missing-chat-id", { + chatId, + revision: 0, + }); + return buildGraphPersistResult({ + saved: false, + blocked: true, + reason: "missing-chat-id", + }); + } + + const revision = markMutation + ? allocateRequestedPersistRevision(0, currentGraph) + : resolvePersistRevisionFloor(0, currentGraph); + const persistenceEnvironment = buildPersistenceEnvironment( + context, + getPreferredGraphLocalStorePresentationSync(), + ); + + if (captureShadow) { + maybeCaptureGraphShadowSnapshotImpl(runtime, reason); + } + + const shouldQueueIndexedDbPersist = + (persistenceEnvironment.hostProfile !== "luker" || + persistenceEnvironment.primaryStorageTier === "authority-sql") && + (markMutation || !isGraphEffectivelyEmpty(currentGraph)); + if (shouldQueueIndexedDbPersist) { + queueGraphPersistToIndexedDb(chatId, currentGraph, { + revision, + reason, + }); + } + + const metadataFallbackEnabled = + Boolean(persistMetadata) || !ensureBmeChatManager(); + + if (!markMutation) { + const hasMeaningfulGraphData = !isGraphEffectivelyEmpty(currentGraph); + if ( + !hasMeaningfulGraphData || + graphPersistenceState.loadState === GRAPH_LOAD_STATES.EMPTY_CONFIRMED + ) { + return buildGraphPersistResult({ + saved: false, + blocked: false, + reason: hasMeaningfulGraphData + ? "passive-empty-confirmed-skipped" + : "passive-empty-graph-skipped", + revision, + }); + } + } + + if (persistenceEnvironment.primaryStorageTier === "luker-chat-state") { + const persistGraph = cloneGraphForPersistence(currentGraph, chatId); + const chatStateTarget = resolveCurrentChatStateTarget(context); + const lastProcessedAssistantFloor = Number.isFinite( + Number(persistGraph?.historyState?.lastProcessedAssistantFloor), + ) + ? Number(persistGraph.historyState.lastProcessedAssistantFloor) + : null; + scheduleBmeIndexedDbTask(async () => { + const persistResult = await persistGraphToConfiguredDurableTier( + context, + persistGraph, + { + chatId, + revision, + reason, + lastProcessedAssistantFloor, + chatStateTarget, + graphDetached: true, + }, + ); + if (!persistResult?.accepted) { + queueGraphPersist(reason, revision, { + immediate, + graph: persistGraph, + chatId, + captureShadow, + }); + } + refreshPanelLiveState(); + }); + updateGraphPersistenceState({ + hostProfile: persistenceEnvironment.hostProfile, + primaryStorageTier: persistenceEnvironment.primaryStorageTier, + cacheStorageTier: persistenceEnvironment.cacheStorageTier, + lastPersistReason: String(reason || "graph-save"), + lastPersistMode: "luker-chat-state-queued", + }); + return buildGraphPersistResult({ + saved: false, + queued: true, + blocked: false, + accepted: false, + reason: "luker-chat-state-queued", + revision, + saveMode: "luker-chat-state-queued", + storageTier: "luker-chat-state", + primaryTier: persistenceEnvironment.primaryStorageTier, + cacheTier: persistenceEnvironment.cacheStorageTier, + }); + } + + if (!metadataFallbackEnabled) { + const preferredLocalStore = getPreferredGraphLocalStorePresentationSync(); + const saveMode = shouldQueueIndexedDbPersist + ? `${preferredLocalStore.reasonPrefix}-queued` + : `${preferredLocalStore.reasonPrefix}-skip`; + updateGraphPersistenceState({ + storagePrimary: preferredLocalStore.storagePrimary, + storageMode: preferredLocalStore.storageMode, + dbReady: + graphPersistenceState.dbReady ?? + isGraphLoadStateDbReady(graphPersistenceState.loadState), + lastPersistReason: String(reason || "graph-save"), + lastPersistMode: saveMode, + pendingPersist: false, + queuedPersistChatId: "", + queuedPersistMode: "", + queuedPersistReason: "", + queuedPersistRotateIntegrity: false, + dualWriteLastResult: { + action: "save", + target: preferredLocalStore.storagePrimary, + queued: Boolean(shouldQueueIndexedDbPersist), + success: true, + chatId, + revision: normalizeIndexedDbRevision(revision), + reason: String(reason || "graph-save"), + at: Date.now(), + }, + }); + return buildGraphPersistResult({ + saved: false, + queued: Boolean(shouldQueueIndexedDbPersist), + blocked: false, + accepted: false, + reason: shouldQueueIndexedDbPersist + ? `${preferredLocalStore.reasonPrefix}-queued` + : `${preferredLocalStore.reasonPrefix}-empty-skip`, + revision, + saveMode, + storageTier: shouldQueueIndexedDbPersist + ? preferredLocalStore.storagePrimary + : "none", + }); + } + + if (!isGraphMetadataWriteAllowed()) { + console.warn( + `[ST-BME] 图谱写回已被安全保护拦截(chat=${chatId},state=${graphPersistenceState.loadState},reason=${reason})`, + ); + return queueGraphPersist(reason, revision, { immediate }); + } + + const metadataPersistResult = persistGraphToChatMetadata(context, { + reason, + revision, + immediate, + }); + updateGraphPersistenceState({ + storageMode: "metadata-full", + dualWriteLastResult: { + action: "save", + target: "metadata", + success: Boolean(metadataPersistResult?.saved), + queued: Boolean(metadataPersistResult?.queued), + blocked: Boolean(metadataPersistResult?.blocked), + chatId, + revision: normalizeIndexedDbRevision(revision), + reason: String(reason || "graph-save"), + at: Date.now(), + }, + }); + + return metadataPersistResult; +} + +export async function onRebuildLocalCacheFromLukerSidecarImpl(runtime) { + const graphPersistenceState = new Proxy({}, { + get(_target, key) { + return (runtime.getGraphPersistenceState?.() || {})[key]; + }, + set(_target, key, value) { + const state = runtime.getGraphPersistenceState?.() || {}; + state[key] = value; + return true; + }, + }); + let currentGraph = runtime.getCurrentGraph?.() || null; + const cloneGraphForPersistence = runtime.cloneGraphForPersistence; + const getContext = runtime.getContext; + const getCurrentChatId = runtime.getCurrentChatId; + const getGraphPersistedRevision = runtime.getGraphPersistedRevision; + const isLukerPrimaryPersistenceHost = runtime.isLukerPrimaryPersistenceHost; + const loadGraphFromLukerSidecarV2 = runtime.loadGraphFromLukerSidecarV2; + const queueGraphPersistToIndexedDb = runtime.queueGraphPersistToIndexedDb; + const refreshPanelLiveState = runtime.refreshPanelLiveState; + const resolveCurrentChatStateTarget = runtime.resolveCurrentChatStateTarget; + const toastr = runtime.toastr; + + const context = getContext(); + const chatStateTarget = resolveCurrentChatStateTarget(context); + if (!isLukerPrimaryPersistenceHost(context)) { + toastr.info("当前宿主不是 Luker,无需从主 sidecar 重建本地缓存"); + return { handledToast: true, reason: "not-luker" }; + } + const chatId = getCurrentChatId(context); + if (!chatId) { + toastr.warning("当前没有聊天上下文"); + return { handledToast: true, reason: "missing-chat-id" }; + } + + const loadResult = await loadGraphFromLukerSidecarV2(chatId, { + source: "panel-manual-luker-cache-rebuild", + allowOverride: true, + chatStateTarget, + }); + currentGraph = runtime.getCurrentGraph?.() || null; + if (!loadResult?.loaded || !currentGraph) { + toastr.warning( + `无法从 Luker 主 sidecar 重建本地缓存: ${loadResult?.reason || "sidecar not available"}`, + ); + return { handledToast: true, result: loadResult }; + } + + queueGraphPersistToIndexedDb(chatId, cloneGraphForPersistence(currentGraph, chatId), { + revision: Math.max( + Number(graphPersistenceState.lukerManifestRevision || 0), + Number(getGraphPersistedRevision(currentGraph) || 0), + Number(graphPersistenceState.revision || 0), + ), + reason: "panel-manual-luker-cache-rebuild", + persistRole: "cache-mirror", + scheduleCloudUpload: false, + graphDetached: true, + }); + refreshPanelLiveState(); + toastr.success("已开始从 Luker 主 sidecar 重建本地缓存"); + return { handledToast: true, result: loadResult }; +} diff --git a/tests/graph-persistence.mjs b/tests/graph-persistence.mjs index 5a0320b..541a523 100644 --- a/tests/graph-persistence.mjs +++ b/tests/graph-persistence.mjs @@ -192,7 +192,9 @@ import { buildBmeSyncRuntimeOptionsImpl, loadGraphFromChatImpl, maybeCaptureGraphShadowSnapshotImpl, + onRebuildLocalCacheFromLukerSidecarImpl, persistExtractionBatchResultImpl, + saveGraphToChatImpl, shouldUseAuthorityGraphStoreImpl, shouldUseAuthorityJobsImpl, syncGraphLoadFromLiveContextImpl, @@ -860,7 +862,9 @@ async function createGraphPersistenceHarness({ buildBmeSyncRuntimeOptionsImpl, loadGraphFromChatImpl, maybeCaptureGraphShadowSnapshotImpl, + onRebuildLocalCacheFromLukerSidecarImpl, persistExtractionBatchResultImpl, + saveGraphToChatImpl, shouldUseAuthorityGraphStoreImpl, shouldUseAuthorityJobsImpl, syncGraphLoadFromLiveContextImpl, @@ -1368,6 +1372,39 @@ async function createGraphPersistenceHarness({ recordLocalPersistEarlyFailure(reason = "") { runtimeContext.__lastLocalPersistEarlyFailure = String(reason || ""); }, + isGraphLoadStateDbReady(loadState = runtimeContext.graphPersistenceState?.loadState) { + return loadState === GRAPH_LOAD_STATES.LOADED || loadState === GRAPH_LOAD_STATES.EMPTY_CONFIRMED; + }, + isGraphMetadataWriteAllowed() { + return true; + }, + isLukerPrimaryPersistenceHost(context = runtimeContext.getContext?.()) { + return resolveBmeHostProfile(context) === "luker"; + }, + async loadGraphFromLukerSidecarV2(chatId, options = {}) { + runtimeContext.__lastLukerSidecarLoad = { chatId, options }; + return { loaded: Boolean(runtimeContext.currentGraph), reason: "test-luker-sidecar" }; + }, + normalizeIndexedDbRevision(value, fallbackValue = 0) { + const numeric = Number(value); + if (Number.isFinite(numeric) && numeric > 0) return Math.floor(numeric); + const fallback = Number(fallbackValue); + return Number.isFinite(fallback) && fallback > 0 ? Math.floor(fallback) : 0; + }, + resolvePersistRevisionFloor(baseRevision = 0, graph = runtimeContext.currentGraph) { + return Math.max( + 0, + Math.floor(Number(baseRevision || 0)), + Math.floor(Number(graph?.meta?.revision || 0)), + Math.floor(Number(getGraphPersistedRevision(graph) || 0)), + ); + }, + resolveCurrentChatStateTarget(context = runtimeContext.getContext?.()) { + return resolveCurrentBmeChatStateTarget(context); + }, + scheduleBmeIndexedDbTask(task) { + return Promise.resolve().then(() => task()); + }, __syncNowCalls: [], async syncNow(chatId, options = {}) { runtimeContext.__syncNowCalls.push({