mirror of
https://github.com/Youzini-afk/ST-Bionic-Memory-Ecology.git
synced 2026-05-15 22:30:38 +08:00
fix: harden mobile local store recovery flow
This commit is contained in:
379
index.js
379
index.js
@@ -544,6 +544,147 @@ function clearCurrentChatCommitMarker(
|
||||
};
|
||||
}
|
||||
|
||||
function clearCurrentChatMetadataGraphFallback(
|
||||
{
|
||||
context = getContext(),
|
||||
reason = "manual-clear-graph-metadata-fallback",
|
||||
immediate = true,
|
||||
clearPendingPersist = false,
|
||||
} = {},
|
||||
) {
|
||||
if (!context) {
|
||||
return {
|
||||
cleared: false,
|
||||
reason: "missing-context",
|
||||
saveMode: "",
|
||||
};
|
||||
}
|
||||
|
||||
const hadGraphMetadata =
|
||||
context?.chatMetadata &&
|
||||
Object.prototype.hasOwnProperty.call(context.chatMetadata, GRAPH_METADATA_KEY) &&
|
||||
context.chatMetadata[GRAPH_METADATA_KEY] != null;
|
||||
writeChatMetadataPatch(context, {
|
||||
[GRAPH_METADATA_KEY]: null,
|
||||
});
|
||||
const saveMode = triggerChatMetadataSave(context, { immediate });
|
||||
updateGraphPersistenceState({
|
||||
persistMismatchReason: "",
|
||||
lastPersistReason: String(
|
||||
reason || "manual-clear-graph-metadata-fallback",
|
||||
),
|
||||
lastPersistMode: `metadata-full-clear:${saveMode}`,
|
||||
lastRecoverableStorageTier:
|
||||
graphPersistenceState.lastRecoverableStorageTier === "metadata-full"
|
||||
? "none"
|
||||
: graphPersistenceState.lastRecoverableStorageTier,
|
||||
pendingPersist:
|
||||
clearPendingPersist === true ? false : graphPersistenceState.pendingPersist,
|
||||
writesBlocked:
|
||||
clearPendingPersist === true ? false : graphPersistenceState.writesBlocked,
|
||||
queuedPersistRevision:
|
||||
clearPendingPersist === true ? 0 : graphPersistenceState.queuedPersistRevision,
|
||||
queuedPersistChatId:
|
||||
clearPendingPersist === true ? "" : graphPersistenceState.queuedPersistChatId,
|
||||
queuedPersistMode:
|
||||
clearPendingPersist === true ? "" : graphPersistenceState.queuedPersistMode,
|
||||
queuedPersistRotateIntegrity:
|
||||
clearPendingPersist === true
|
||||
? false
|
||||
: graphPersistenceState.queuedPersistRotateIntegrity,
|
||||
queuedPersistReason:
|
||||
clearPendingPersist === true ? "" : graphPersistenceState.queuedPersistReason,
|
||||
});
|
||||
if (clearPendingPersist === true) {
|
||||
clearPendingGraphPersistRetry();
|
||||
}
|
||||
|
||||
return {
|
||||
cleared: hadGraphMetadata,
|
||||
reason: String(reason || "manual-clear-graph-metadata-fallback"),
|
||||
saveMode,
|
||||
};
|
||||
}
|
||||
|
||||
function clearCurrentChatRecoveryAnchors(
|
||||
{
|
||||
context = getContext(),
|
||||
chatId = getCurrentChatId(context),
|
||||
reason = "manual-clear-recovery-anchors",
|
||||
immediate = true,
|
||||
clearMetadataFull = true,
|
||||
clearCommitMarker = true,
|
||||
clearPendingPersist = true,
|
||||
} = {},
|
||||
) {
|
||||
const normalizedChatId = normalizeChatIdCandidate(chatId);
|
||||
const shadowCleared = normalizedChatId
|
||||
? removeGraphShadowSnapshot(normalizedChatId)
|
||||
: false;
|
||||
const metadataResult = clearMetadataFull
|
||||
? clearCurrentChatMetadataGraphFallback({
|
||||
context,
|
||||
reason: `${reason}:metadata-full`,
|
||||
immediate,
|
||||
clearPendingPersist,
|
||||
})
|
||||
: {
|
||||
cleared: false,
|
||||
reason: "metadata-full-retained",
|
||||
saveMode: "",
|
||||
};
|
||||
const markerResult = clearCommitMarker
|
||||
? clearCurrentChatCommitMarker({
|
||||
context,
|
||||
reason: `${reason}:commit-marker`,
|
||||
immediate,
|
||||
resetAcceptedRevision: clearPendingPersist === true,
|
||||
})
|
||||
: {
|
||||
cleared: false,
|
||||
reason: "commit-marker-retained",
|
||||
saveMode: "",
|
||||
marker: null,
|
||||
};
|
||||
|
||||
updateGraphPersistenceState({
|
||||
shadowSnapshotUsed: false,
|
||||
shadowSnapshotRevision: 0,
|
||||
shadowSnapshotUpdatedAt: "",
|
||||
shadowSnapshotReason: "",
|
||||
lastRecoverableStorageTier:
|
||||
shadowCleared || metadataResult?.cleared ? "none" : graphPersistenceState.lastRecoverableStorageTier,
|
||||
pendingPersist:
|
||||
clearPendingPersist === true ? false : graphPersistenceState.pendingPersist,
|
||||
writesBlocked:
|
||||
clearPendingPersist === true ? false : graphPersistenceState.writesBlocked,
|
||||
queuedPersistRevision:
|
||||
clearPendingPersist === true ? 0 : graphPersistenceState.queuedPersistRevision,
|
||||
queuedPersistChatId:
|
||||
clearPendingPersist === true ? "" : graphPersistenceState.queuedPersistChatId,
|
||||
queuedPersistMode:
|
||||
clearPendingPersist === true ? "" : graphPersistenceState.queuedPersistMode,
|
||||
queuedPersistRotateIntegrity:
|
||||
clearPendingPersist === true
|
||||
? false
|
||||
: graphPersistenceState.queuedPersistRotateIntegrity,
|
||||
queuedPersistReason:
|
||||
clearPendingPersist === true ? "" : graphPersistenceState.queuedPersistReason,
|
||||
});
|
||||
if (clearPendingPersist === true) {
|
||||
clearPendingGraphPersistRetry();
|
||||
}
|
||||
|
||||
return {
|
||||
chatId: normalizedChatId,
|
||||
shadowCleared,
|
||||
metadataCleared: metadataResult?.cleared === true,
|
||||
markerCleared: markerResult?.cleared === true,
|
||||
metadataResult,
|
||||
markerResult,
|
||||
};
|
||||
}
|
||||
|
||||
function isAcceptedPersistTier(storageTier = "none") {
|
||||
const normalizedTier = String(storageTier || "none").trim().toLowerCase();
|
||||
return normalizedTier === "indexeddb" || normalizedTier === "chat-state";
|
||||
@@ -4112,6 +4253,182 @@ async function createPreferredGraphLocalStore(
|
||||
return new BmeDatabase(chatId);
|
||||
}
|
||||
|
||||
async function refreshCurrentChatLocalStoreBinding(
|
||||
{
|
||||
chatId = getCurrentChatId(getContext()),
|
||||
forceCapabilityRefresh = false,
|
||||
reopenCurrentDb = false,
|
||||
source = "manual-refresh",
|
||||
} = {},
|
||||
) {
|
||||
const normalizedChatId = normalizeChatIdCandidate(chatId);
|
||||
const settings = getSettings();
|
||||
const requestedMode = getRequestedGraphLocalStorageMode(settings);
|
||||
const shouldProbeCapability =
|
||||
forceCapabilityRefresh === true ||
|
||||
!bmeLocalStoreCapabilitySnapshot.checked ||
|
||||
requestedMode === "auto" ||
|
||||
isGraphLocalStorageModeOpfs(requestedMode);
|
||||
|
||||
if (shouldProbeCapability) {
|
||||
await getGraphLocalStoreCapability(forceCapabilityRefresh === true);
|
||||
}
|
||||
|
||||
const preferredLocalStore =
|
||||
await resolvePreferredGraphLocalStorePresentation(settings);
|
||||
let resolvedLocalStore = preferredLocalStore;
|
||||
let localStoreDiagnostics = {
|
||||
resolvedLocalStore: buildGraphLocalStoreSelectorKey(preferredLocalStore),
|
||||
localStoreFormatVersion:
|
||||
preferredLocalStore.storagePrimary === "opfs" ? 2 : 1,
|
||||
localStoreMigrationState: "idle",
|
||||
opfsWalDepth: 0,
|
||||
opfsPendingBytes: 0,
|
||||
opfsCompactionState: null,
|
||||
};
|
||||
let opfsWriteLockState = cloneRuntimeDebugValue(
|
||||
graphPersistenceState.opfsWriteLockState,
|
||||
null,
|
||||
);
|
||||
let reopenError = "";
|
||||
|
||||
if (
|
||||
reopenCurrentDb === true &&
|
||||
normalizedChatId &&
|
||||
bmeChatManager &&
|
||||
typeof bmeChatManager.getCurrentChatId === "function" &&
|
||||
typeof bmeChatManager.closeCurrent === "function" &&
|
||||
bmeChatManager.getCurrentChatId() === normalizedChatId
|
||||
) {
|
||||
await bmeChatManager.closeCurrent();
|
||||
}
|
||||
|
||||
if (normalizedChatId) {
|
||||
clearCachedIndexedDbSnapshot(normalizedChatId);
|
||||
try {
|
||||
const manager = ensureBmeChatManager();
|
||||
if (manager) {
|
||||
const db = await manager.getCurrentDb(normalizedChatId);
|
||||
resolvedLocalStore = resolveDbGraphStorePresentation(db);
|
||||
localStoreDiagnostics = readLocalStoreDiagnosticsSync(
|
||||
db,
|
||||
resolvedLocalStore,
|
||||
);
|
||||
opfsWriteLockState =
|
||||
typeof db?.getWriteLockSnapshot === "function"
|
||||
? cloneRuntimeDebugValue(db.getWriteLockSnapshot(), null)
|
||||
: opfsWriteLockState;
|
||||
}
|
||||
} catch (error) {
|
||||
reopenError = error?.message || String(error);
|
||||
console.warn(
|
||||
"[ST-BME] 刷新当前聊天本地存储绑定失败:",
|
||||
{
|
||||
chatId: normalizedChatId,
|
||||
source,
|
||||
requestedMode,
|
||||
error: reopenError,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const persistenceEnvironment = buildPersistenceEnvironment(
|
||||
getContext(),
|
||||
resolvedLocalStore,
|
||||
);
|
||||
updateGraphPersistenceState({
|
||||
hostProfile: persistenceEnvironment.hostProfile,
|
||||
primaryStorageTier: persistenceEnvironment.primaryStorageTier,
|
||||
cacheStorageTier: persistenceEnvironment.cacheStorageTier,
|
||||
storagePrimary: resolvedLocalStore.storagePrimary,
|
||||
storageMode: resolvedLocalStore.storageMode,
|
||||
resolvedLocalStore: localStoreDiagnostics.resolvedLocalStore,
|
||||
localStoreFormatVersion: localStoreDiagnostics.localStoreFormatVersion,
|
||||
localStoreMigrationState: localStoreDiagnostics.localStoreMigrationState,
|
||||
opfsWriteLockState,
|
||||
opfsWalDepth: localStoreDiagnostics.opfsWalDepth,
|
||||
opfsPendingBytes: localStoreDiagnostics.opfsPendingBytes,
|
||||
opfsCompactionState: localStoreDiagnostics.opfsCompactionState,
|
||||
indexedDbLastError: reopenError ? reopenError : "",
|
||||
});
|
||||
|
||||
return {
|
||||
capability: cloneRuntimeDebugValue(bmeLocalStoreCapabilitySnapshot, null),
|
||||
requestedMode,
|
||||
resolvedLocalStore,
|
||||
localStoreDiagnostics,
|
||||
reopenError,
|
||||
};
|
||||
}
|
||||
|
||||
function buildPanelOpenLocalStoreRefreshPlan(
|
||||
context = getContext(),
|
||||
settings = getSettings(),
|
||||
) {
|
||||
const requestedMode = getRequestedGraphLocalStorageMode(settings);
|
||||
const usesOpfsPreference =
|
||||
requestedMode === "auto" || isGraphLocalStorageModeOpfs(requestedMode);
|
||||
const activeChatId = normalizeChatIdCandidate(getCurrentChatId(context));
|
||||
const preferredLocalStore = getPreferredGraphLocalStorePresentationSync(settings);
|
||||
const resolvedLocalStoreKey = String(
|
||||
graphPersistenceState.resolvedLocalStore ||
|
||||
buildGraphLocalStoreSelectorKey(preferredLocalStore),
|
||||
).trim();
|
||||
const resolvedIsOpfs = resolvedLocalStoreKey.startsWith("opfs:");
|
||||
const preferredIsOpfs = preferredLocalStore.storagePrimary === "opfs";
|
||||
const capabilityUnchecked = bmeLocalStoreCapabilitySnapshot.checked !== true;
|
||||
const pendingPersist = graphPersistenceState.pendingPersist === true;
|
||||
const writesBlocked = graphPersistenceState.writesBlocked === true;
|
||||
const loadState = String(graphPersistenceState.loadState || "");
|
||||
const loadingWithoutDb =
|
||||
loadState === GRAPH_LOAD_STATES.LOADING && graphPersistenceState.dbReady !== true;
|
||||
const blocked = loadState === GRAPH_LOAD_STATES.BLOCKED;
|
||||
const persistError = String(graphPersistenceState.indexedDbLastError || "").trim();
|
||||
const localStoreMismatch =
|
||||
Boolean(activeChatId) &&
|
||||
preferredIsOpfs &&
|
||||
Boolean(resolvedLocalStoreKey) &&
|
||||
!resolvedIsOpfs;
|
||||
const shouldRefresh =
|
||||
usesOpfsPreference &&
|
||||
(capabilityUnchecked ||
|
||||
pendingPersist ||
|
||||
writesBlocked ||
|
||||
blocked ||
|
||||
loadingWithoutDb ||
|
||||
Boolean(persistError) ||
|
||||
localStoreMismatch);
|
||||
const forceCapabilityRefresh =
|
||||
capabilityUnchecked ||
|
||||
pendingPersist ||
|
||||
blocked ||
|
||||
loadingWithoutDb ||
|
||||
Boolean(persistError) ||
|
||||
localStoreMismatch;
|
||||
const reopenCurrentDb =
|
||||
Boolean(activeChatId) &&
|
||||
(pendingPersist || writesBlocked || blocked || Boolean(persistError) || localStoreMismatch);
|
||||
const reasons = [];
|
||||
if (capabilityUnchecked) reasons.push("capability-unchecked");
|
||||
if (pendingPersist) reasons.push("pending-persist");
|
||||
if (writesBlocked) reasons.push("writes-blocked");
|
||||
if (blocked) reasons.push("load-blocked");
|
||||
if (loadingWithoutDb) reasons.push("loading-without-db");
|
||||
if (persistError) reasons.push("local-store-error");
|
||||
if (localStoreMismatch) reasons.push("resolved-store-mismatch");
|
||||
|
||||
return {
|
||||
shouldRefresh,
|
||||
forceCapabilityRefresh,
|
||||
reopenCurrentDb,
|
||||
requestedMode,
|
||||
resolvedLocalStoreKey,
|
||||
preferredLocalStore,
|
||||
reasons,
|
||||
};
|
||||
}
|
||||
|
||||
function getMessageHideSettings(settings = null) {
|
||||
let sourceSettings = settings;
|
||||
if (!sourceSettings || typeof sourceSettings !== "object") {
|
||||
@@ -8631,6 +8948,21 @@ async function retryPendingGraphPersist({
|
||||
});
|
||||
}
|
||||
|
||||
const requestedLocalStoreMode = getRequestedGraphLocalStorageMode(
|
||||
getSettings(),
|
||||
);
|
||||
if (
|
||||
requestedLocalStoreMode === "auto" ||
|
||||
isGraphLocalStorageModeOpfs(requestedLocalStoreMode)
|
||||
) {
|
||||
await refreshCurrentChatLocalStoreBinding({
|
||||
chatId: activeChatId,
|
||||
forceCapabilityRefresh: true,
|
||||
reopenCurrentDb: true,
|
||||
source: reason,
|
||||
});
|
||||
}
|
||||
|
||||
const pendingPersistGraphSource = resolvePendingPersistGraphSource(
|
||||
queuedChatId,
|
||||
);
|
||||
@@ -11082,6 +11414,18 @@ async function saveGraphToIndexedDb(
|
||||
typeof db?.getWriteLockSnapshot === "function"
|
||||
? cloneRuntimeDebugValue(db.getWriteLockSnapshot(), null)
|
||||
: null;
|
||||
const localStoreDiagnostics =
|
||||
typeof readLocalStoreDiagnosticsSync === "function"
|
||||
? readLocalStoreDiagnosticsSync(db, localStore)
|
||||
: {
|
||||
resolvedLocalStore: `${localStore?.storagePrimary || "indexeddb"}:${localStore?.storageMode || "indexeddb"}`,
|
||||
localStoreFormatVersion:
|
||||
localStore?.storagePrimary === "opfs" ? 2 : 1,
|
||||
localStoreMigrationState: "idle",
|
||||
opfsWalDepth: 0,
|
||||
opfsPendingBytes: 0,
|
||||
opfsCompactionState: null,
|
||||
};
|
||||
updateGraphPersistenceState({
|
||||
hostProfile: persistenceEnvironment.hostProfile,
|
||||
primaryStorageTier: persistenceEnvironment.primaryStorageTier,
|
||||
@@ -11092,8 +11436,14 @@ async function saveGraphToIndexedDb(
|
||||
: graphPersistenceState.cacheMirrorState,
|
||||
storagePrimary: localStore.storagePrimary,
|
||||
storageMode: localStore.storageMode,
|
||||
resolvedLocalStore: localStoreDiagnostics.resolvedLocalStore,
|
||||
localStoreFormatVersion: localStoreDiagnostics.localStoreFormatVersion,
|
||||
localStoreMigrationState: localStoreDiagnostics.localStoreMigrationState,
|
||||
indexedDbLastError: error?.message || String(error),
|
||||
opfsWriteLockState,
|
||||
opfsWalDepth: localStoreDiagnostics.opfsWalDepth,
|
||||
opfsPendingBytes: localStoreDiagnostics.opfsPendingBytes,
|
||||
opfsCompactionState: localStoreDiagnostics.opfsCompactionState,
|
||||
dualWriteLastResult: {
|
||||
action: persistRole === "cache-mirror" ? "cache-mirror" : "save",
|
||||
target: localStore.storagePrimary,
|
||||
@@ -11112,7 +11462,7 @@ async function saveGraphToIndexedDb(
|
||||
reason:
|
||||
persistRole === "cache-mirror"
|
||||
? "cache-mirror-write-failed"
|
||||
: "indexeddb-write-failed",
|
||||
: `${String(localStore?.reasonPrefix || "indexeddb")}-write-failed`,
|
||||
error,
|
||||
};
|
||||
}
|
||||
@@ -15306,6 +15656,8 @@ const _cleanupRuntime = () => ({
|
||||
clearCachedIndexedDbSnapshot,
|
||||
clearAllCachedIndexedDbSnapshots,
|
||||
clearCurrentChatCommitMarker,
|
||||
clearCurrentChatRecoveryAnchors,
|
||||
refreshCurrentChatLocalStoreBinding,
|
||||
deleteCurrentChatOpfsStorage: async (chatId) =>
|
||||
await deleteOpfsChatStorage(chatId),
|
||||
deleteAllOpfsStorage: async () =>
|
||||
@@ -15564,6 +15916,11 @@ async function onRollbackLastRestore() {
|
||||
}
|
||||
|
||||
async function onRetryPendingPersist() {
|
||||
await refreshCurrentChatLocalStoreBinding({
|
||||
forceCapabilityRefresh: true,
|
||||
reopenCurrentDb: true,
|
||||
source: "panel-manual-persist-retry",
|
||||
});
|
||||
const hadPending = graphPersistenceState.pendingPersist === true;
|
||||
const result = await retryPendingGraphPersist({
|
||||
reason: "panel-manual-persist-retry",
|
||||
@@ -15589,6 +15946,11 @@ async function onRetryPendingPersist() {
|
||||
}
|
||||
|
||||
async function onProbeGraphLoad() {
|
||||
await refreshCurrentChatLocalStoreBinding({
|
||||
forceCapabilityRefresh: true,
|
||||
reopenCurrentDb: true,
|
||||
source: "panel-manual-graph-probe",
|
||||
});
|
||||
const result = syncGraphLoadFromLiveContext({
|
||||
source: "panel-manual-graph-probe",
|
||||
force: true,
|
||||
@@ -15617,10 +15979,19 @@ async function onProbeGraphLoad() {
|
||||
await initializePanelBridgeController({
|
||||
$,
|
||||
actions: {
|
||||
syncGraphLoad: () =>
|
||||
syncGraphLoadFromLiveContext({
|
||||
syncGraphLoad: async () => {
|
||||
const refreshPlan = buildPanelOpenLocalStoreRefreshPlan();
|
||||
if (refreshPlan.shouldRefresh) {
|
||||
await refreshCurrentChatLocalStoreBinding({
|
||||
forceCapabilityRefresh: refreshPlan.forceCapabilityRefresh,
|
||||
reopenCurrentDb: refreshPlan.reopenCurrentDb,
|
||||
source: `panel-open-sync:${refreshPlan.reasons.join(",") || "refresh"}`,
|
||||
});
|
||||
}
|
||||
return syncGraphLoadFromLiveContext({
|
||||
source: "panel-open-sync",
|
||||
}),
|
||||
});
|
||||
},
|
||||
extractTask: onExtractionTask,
|
||||
extract: onManualExtract,
|
||||
compress: onManualCompress,
|
||||
|
||||
@@ -883,7 +883,9 @@ async function createGraphPersistenceHarness({
|
||||
buildSnapshotFromGraph,
|
||||
evaluatePersistNativeDeltaGate,
|
||||
buildBmeDbName,
|
||||
BME_GRAPH_LOCAL_STORAGE_MODE_AUTO: "auto",
|
||||
BME_GRAPH_LOCAL_STORAGE_MODE_INDEXEDDB: "indexeddb",
|
||||
BME_GRAPH_LOCAL_STORAGE_MODE_OPFS_PRIMARY: "opfs-primary",
|
||||
BME_GRAPH_LOCAL_STORAGE_MODE_OPFS_SHADOW: "opfs-shadow",
|
||||
detectOpfsSupport: async () => ({
|
||||
available: false,
|
||||
@@ -1089,6 +1091,7 @@ result = {
|
||||
writeGraphShadowSnapshot,
|
||||
removeGraphShadowSnapshot,
|
||||
maybeCaptureGraphShadowSnapshot,
|
||||
buildPanelOpenLocalStoreRefreshPlan,
|
||||
loadGraphFromChat,
|
||||
loadGraphFromIndexedDb,
|
||||
saveGraphToChat,
|
||||
@@ -1129,6 +1132,13 @@ result = {
|
||||
getGraphPersistenceState() {
|
||||
return graphPersistenceState;
|
||||
},
|
||||
setLocalStoreCapabilitySnapshot(patch = {}) {
|
||||
bmeLocalStoreCapabilitySnapshot = {
|
||||
...bmeLocalStoreCapabilitySnapshot,
|
||||
...(patch || {}),
|
||||
};
|
||||
return bmeLocalStoreCapabilitySnapshot;
|
||||
},
|
||||
setChatContext(nextContext) {
|
||||
globalThis.__chatContext = nextContext;
|
||||
return globalThis.__chatContext;
|
||||
@@ -1848,6 +1858,84 @@ result = {
|
||||
assert.equal(result.reason, "no-sync-needed");
|
||||
}
|
||||
|
||||
{
|
||||
const harness = await createGraphPersistenceHarness({
|
||||
chatId: "chat-panel-open-healthy",
|
||||
globalChatId: "chat-panel-open-healthy",
|
||||
chatMetadata: {
|
||||
integrity: "chat-panel-open-healthy-integrity",
|
||||
},
|
||||
});
|
||||
harness.runtimeContext.extension_settings[MODULE_NAME] = {
|
||||
graphLocalStorageMode: "auto",
|
||||
};
|
||||
harness.api.setLocalStoreCapabilitySnapshot({
|
||||
checked: true,
|
||||
opfsAvailable: true,
|
||||
reason: "ok",
|
||||
});
|
||||
harness.api.setGraphPersistenceState({
|
||||
loadState: "loaded",
|
||||
chatId: "chat-panel-open-healthy",
|
||||
reason: "healthy",
|
||||
dbReady: true,
|
||||
writesBlocked: false,
|
||||
pendingPersist: false,
|
||||
indexedDbLastError: "",
|
||||
resolvedLocalStore: "opfs:opfs-primary",
|
||||
storagePrimary: "opfs",
|
||||
storageMode: "opfs-primary",
|
||||
});
|
||||
|
||||
const plan = harness.api.buildPanelOpenLocalStoreRefreshPlan();
|
||||
|
||||
assert.equal(
|
||||
plan.shouldRefresh,
|
||||
false,
|
||||
"健康态的面板打开不应每次都强刷本地引擎绑定",
|
||||
);
|
||||
assert.equal(Array.isArray(plan.reasons), true);
|
||||
assert.equal(plan.reasons.length, 0);
|
||||
}
|
||||
|
||||
{
|
||||
const harness = await createGraphPersistenceHarness({
|
||||
chatId: "chat-panel-open-pending",
|
||||
globalChatId: "chat-panel-open-pending",
|
||||
chatMetadata: {
|
||||
integrity: "chat-panel-open-pending-integrity",
|
||||
},
|
||||
});
|
||||
harness.runtimeContext.extension_settings[MODULE_NAME] = {
|
||||
graphLocalStorageMode: "auto",
|
||||
};
|
||||
harness.api.setLocalStoreCapabilitySnapshot({
|
||||
checked: true,
|
||||
opfsAvailable: true,
|
||||
reason: "ok",
|
||||
});
|
||||
harness.api.setGraphPersistenceState({
|
||||
loadState: "blocked",
|
||||
chatId: "chat-panel-open-pending",
|
||||
reason: "persist-queued",
|
||||
dbReady: false,
|
||||
writesBlocked: true,
|
||||
pendingPersist: true,
|
||||
indexedDbLastError: "opfs-write-failed",
|
||||
resolvedLocalStore: "indexeddb:indexeddb",
|
||||
storagePrimary: "indexeddb",
|
||||
storageMode: "indexeddb",
|
||||
});
|
||||
|
||||
const plan = harness.api.buildPanelOpenLocalStoreRefreshPlan();
|
||||
|
||||
assert.equal(plan.shouldRefresh, true);
|
||||
assert.equal(plan.forceCapabilityRefresh, true);
|
||||
assert.equal(plan.reopenCurrentDb, true);
|
||||
assert.equal(plan.reasons.includes("pending-persist"), true);
|
||||
assert.equal(plan.reasons.includes("resolved-store-mismatch"), true);
|
||||
}
|
||||
|
||||
{
|
||||
const harness = await createGraphPersistenceHarness({
|
||||
chatId: "chat-luker-panel-open",
|
||||
|
||||
@@ -1216,10 +1216,37 @@ export async function onDeleteCurrentIdbController(runtime) {
|
||||
: null;
|
||||
runtime.clearCachedIndexedDbSnapshot?.(chatId);
|
||||
runtime.clearCachedIndexedDbSnapshot?.(restoreSafetyChatId);
|
||||
runtime.clearCurrentChatCommitMarker?.({
|
||||
reason: "manual-delete-current-local-storage",
|
||||
immediate: true,
|
||||
resetAcceptedRevision: true,
|
||||
if (typeof runtime.clearCurrentChatRecoveryAnchors === "function") {
|
||||
runtime.clearCurrentChatRecoveryAnchors({
|
||||
chatId,
|
||||
reason: "manual-delete-current-local-storage",
|
||||
immediate: true,
|
||||
clearMetadataFull: true,
|
||||
clearCommitMarker: true,
|
||||
clearPendingPersist: true,
|
||||
});
|
||||
if (restoreSafetyChatId && restoreSafetyChatId !== chatId) {
|
||||
runtime.clearCurrentChatRecoveryAnchors({
|
||||
chatId: restoreSafetyChatId,
|
||||
reason: "manual-delete-current-local-storage:restore-safety",
|
||||
immediate: true,
|
||||
clearMetadataFull: false,
|
||||
clearCommitMarker: false,
|
||||
clearPendingPersist: false,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
runtime.clearCurrentChatCommitMarker?.({
|
||||
reason: "manual-delete-current-local-storage",
|
||||
immediate: true,
|
||||
resetAcceptedRevision: true,
|
||||
});
|
||||
}
|
||||
await runtime.refreshCurrentChatLocalStoreBinding?.({
|
||||
chatId,
|
||||
forceCapabilityRefresh: true,
|
||||
reopenCurrentDb: true,
|
||||
source: "manual-delete-current-local-storage",
|
||||
});
|
||||
runtime.syncGraphLoadFromLiveContext?.({
|
||||
source: "manual-delete-current-local-storage",
|
||||
@@ -1279,10 +1306,27 @@ export async function onDeleteAllIdbController(runtime) {
|
||||
runtime.clearAllCachedIndexedDbSnapshots?.();
|
||||
const activeChatId = runtime.getCurrentChatId?.();
|
||||
if (activeChatId) {
|
||||
runtime.clearCurrentChatCommitMarker?.({
|
||||
reason: "manual-delete-all-local-storage",
|
||||
immediate: true,
|
||||
resetAcceptedRevision: true,
|
||||
if (typeof runtime.clearCurrentChatRecoveryAnchors === "function") {
|
||||
runtime.clearCurrentChatRecoveryAnchors({
|
||||
chatId: activeChatId,
|
||||
reason: "manual-delete-all-local-storage",
|
||||
immediate: true,
|
||||
clearMetadataFull: true,
|
||||
clearCommitMarker: true,
|
||||
clearPendingPersist: true,
|
||||
});
|
||||
} else {
|
||||
runtime.clearCurrentChatCommitMarker?.({
|
||||
reason: "manual-delete-all-local-storage",
|
||||
immediate: true,
|
||||
resetAcceptedRevision: true,
|
||||
});
|
||||
}
|
||||
await runtime.refreshCurrentChatLocalStoreBinding?.({
|
||||
chatId: activeChatId,
|
||||
forceCapabilityRefresh: true,
|
||||
reopenCurrentDb: true,
|
||||
source: "manual-delete-all-local-storage",
|
||||
});
|
||||
runtime.syncGraphLoadFromLiveContext?.({
|
||||
source: "manual-delete-all-local-storage",
|
||||
|
||||
Reference in New Issue
Block a user