Merge branch 'Youzini-afk:main' into main

This commit is contained in:
Hao19911125
2026-04-02 16:12:32 +08:00
committed by GitHub
7 changed files with 703 additions and 6 deletions

View File

@@ -129,6 +129,9 @@ export function registerCoreEventHooksController(runtime) {
if (eventTypes.GENERATION_STARTED) {
bind(eventTypes.GENERATION_STARTED, handlers.onGenerationStarted);
}
if (eventTypes.GENERATION_ENDED) {
bind(eventTypes.GENERATION_ENDED, handlers.onGenerationEnded);
}
const beforeCombineCleanup = runtime.registerBeforeCombinePrompts(
handlers.onBeforeCombinePrompts,

303
hide-engine.js Normal file
View File

@@ -0,0 +1,303 @@
// ST-BME: 隐藏旧楼层引擎
// 通过临时把旧楼层标记为 is_system=true让宿主主回复与 ST-BME 自己的聊天读取一起跳过这些楼层。
const hideState = {
managedChatRef: null,
hiddenIndices: new Set(),
lastProcessedLength: 0,
scheduledTimer: null,
};
function getTimerApi(runtime = {}) {
return {
setTimeout:
typeof runtime.setTimeout === "function"
? runtime.setTimeout.bind(runtime)
: globalThis.setTimeout.bind(globalThis),
clearTimeout:
typeof runtime.clearTimeout === "function"
? runtime.clearTimeout.bind(runtime)
: globalThis.clearTimeout.bind(globalThis),
};
}
function getJquery(runtime = {}) {
if (typeof runtime.$ === "function") return runtime.$;
if (typeof globalThis.$ === "function") return globalThis.$;
return null;
}
function getCurrentChat(runtime = {}) {
try {
const context =
typeof runtime.getContext === "function" ? runtime.getContext() : null;
return Array.isArray(context?.chat) ? context.chat : null;
} catch {
return null;
}
}
function normalizeHideSettings(settings = {}) {
return {
enabled: Boolean(settings.enabled),
hideLastN: Math.max(
0,
Math.trunc(
Number(
settings.hideLastN ??
settings.hide_last_n ??
settings.keepLastN ??
settings.keep_last_n ??
0,
) || 0,
),
),
};
}
function syncSystemAttribute(chat, indices = [], value = "true", runtime = {}) {
if (!Array.isArray(indices) || indices.length === 0) return;
if (getCurrentChat(runtime) !== chat) return;
const jq = getJquery(runtime);
if (!jq) return;
const selector = indices.map((index) => `.mes[mesid="${index}"]`).join(",");
if (!selector) return;
jq(selector).attr("is_system", value);
}
function unhideTrackedChat(chat, runtime = {}) {
if (!Array.isArray(chat) || hideState.hiddenIndices.size === 0) {
return { shownCount: 0 };
}
const toShow = [];
for (const index of hideState.hiddenIndices) {
const message = chat[index];
if (!message || message.is_system !== true) continue;
message.is_system = false;
toShow.push(index);
}
syncSystemAttribute(chat, toShow, "false", runtime);
return { shownCount: toShow.length };
}
function swapManagedChat(nextChat, runtime = {}) {
const previousChat = hideState.managedChatRef;
if (previousChat && previousChat !== nextChat) {
unhideTrackedChat(previousChat, runtime);
hideState.hiddenIndices.clear();
hideState.lastProcessedLength = 0;
}
hideState.managedChatRef = nextChat;
}
export function runFullHideCheck(settings = {}, runtime = {}) {
const normalized = normalizeHideSettings(settings);
const chat = getCurrentChat(runtime);
if (!chat || chat.length === 0) {
resetHideState(runtime);
return {
active: false,
hiddenCount: 0,
shownCount: 0,
managedCount: 0,
chatLength: 0,
};
}
swapManagedChat(chat, runtime);
if (!normalized.enabled || normalized.hideLastN <= 0) {
const { shownCount } = unhideTrackedChat(chat, runtime);
hideState.hiddenIndices.clear();
hideState.lastProcessedLength = chat.length;
return {
active: false,
hiddenCount: 0,
shownCount,
managedCount: 0,
chatLength: chat.length,
};
}
const visibleStart =
normalized.hideLastN >= chat.length
? 0
: Math.max(0, chat.length - normalized.hideLastN);
const desiredHiddenIndices = new Set();
const managedHiddenIndices = new Set();
const toHide = [];
const toShow = [];
for (let index = 0; index < chat.length; index++) {
const message = chat[index];
if (!message) continue;
const shouldHide = index < visibleStart;
const isHidden = message.is_system === true;
const wasHiddenByBme = hideState.hiddenIndices.has(index);
if (shouldHide) {
desiredHiddenIndices.add(index);
if (wasHiddenByBme || !isHidden) {
managedHiddenIndices.add(index);
}
if (!isHidden) {
message.is_system = true;
toHide.push(index);
}
continue;
}
if (isHidden && wasHiddenByBme) {
message.is_system = false;
toShow.push(index);
}
}
syncSystemAttribute(chat, [...desiredHiddenIndices], "true", runtime);
syncSystemAttribute(chat, toShow, "false", runtime);
hideState.hiddenIndices = managedHiddenIndices;
hideState.lastProcessedLength = chat.length;
return {
active: true,
hiddenCount: toHide.length,
shownCount: toShow.length,
managedCount: managedHiddenIndices.size,
chatLength: chat.length,
};
}
export function runIncrementalHideCheck(settings = {}, runtime = {}) {
const normalized = normalizeHideSettings(settings);
const chat = getCurrentChat(runtime);
if (!chat || chat.length === 0) {
resetHideState(runtime);
return {
active: false,
hiddenCount: 0,
shownCount: 0,
managedCount: 0,
chatLength: 0,
incremental: false,
};
}
if (
hideState.managedChatRef !== chat ||
!normalized.enabled ||
normalized.hideLastN <= 0
) {
return {
...runFullHideCheck(normalized, runtime),
incremental: false,
};
}
const chatLength = chat.length;
const previousLength = hideState.lastProcessedLength;
if (chatLength <= previousLength) {
return {
...runFullHideCheck(normalized, runtime),
incremental: false,
};
}
const previousVisibleStart =
previousLength > 0 ? Math.max(0, previousLength - normalized.hideLastN) : 0;
const nextVisibleStart = Math.max(0, chatLength - normalized.hideLastN);
const toHide = [];
if (nextVisibleStart > previousVisibleStart) {
for (let index = previousVisibleStart; index < nextVisibleStart; index++) {
const message = chat[index];
if (!message || message.is_system === true) continue;
message.is_system = true;
hideState.hiddenIndices.add(index);
toHide.push(index);
}
}
syncSystemAttribute(chat, toHide, "true", runtime);
hideState.lastProcessedLength = chatLength;
return {
active: true,
hiddenCount: toHide.length,
shownCount: 0,
managedCount: hideState.hiddenIndices.size,
chatLength,
incremental: true,
};
}
export function applyHideSettings(settings = {}, runtime = {}) {
return runFullHideCheck(settings, runtime);
}
export function scheduleHideSettingsApply(
settings = {},
runtime = {},
delayMs = 120,
) {
const timers = getTimerApi(runtime);
if (hideState.scheduledTimer) {
timers.clearTimeout(hideState.scheduledTimer);
hideState.scheduledTimer = null;
}
const snapshot = normalizeHideSettings(settings);
hideState.scheduledTimer = timers.setTimeout(() => {
hideState.scheduledTimer = null;
applyHideSettings(snapshot, runtime);
}, Math.max(0, Math.trunc(Number(delayMs) || 0)));
}
export function unhideAll(runtime = {}) {
const timers = getTimerApi(runtime);
if (hideState.scheduledTimer) {
timers.clearTimeout(hideState.scheduledTimer);
hideState.scheduledTimer = null;
}
const managedChat = hideState.managedChatRef;
const { shownCount } = unhideTrackedChat(managedChat, runtime);
hideState.hiddenIndices.clear();
hideState.lastProcessedLength = Array.isArray(managedChat)
? managedChat.length
: 0;
return {
active: false,
shownCount,
managedCount: 0,
};
}
export function resetHideState(runtime = {}) {
const timers = getTimerApi(runtime);
if (hideState.scheduledTimer) {
timers.clearTimeout(hideState.scheduledTimer);
hideState.scheduledTimer = null;
}
const managedChat = hideState.managedChatRef;
unhideTrackedChat(managedChat, runtime);
hideState.managedChatRef = null;
hideState.hiddenIndices.clear();
hideState.lastProcessedLength = 0;
}
export function getHideStateSnapshot() {
return {
hasManagedChat: Boolean(hideState.managedChatRef),
managedHiddenCount: hideState.hiddenIndices.size,
lastProcessedLength: hideState.lastProcessedLength,
scheduled: Boolean(hideState.scheduledTimer),
};
}

182
index.js
View File

@@ -81,6 +81,14 @@ import {
writeChatMetadataPatch,
writeGraphShadowSnapshot,
} from "./graph-persistence.js";
import {
applyHideSettings,
getHideStateSnapshot,
resetHideState,
runIncrementalHideCheck,
scheduleHideSettingsApply,
unhideAll,
} from "./hide-engine.js";
import {
createEmptyGraph,
deserializeGraph,
@@ -333,6 +341,8 @@ function readRuntimeDebugSnapshot() {
const defaultSettings = {
enabled: true,
timeoutMs: 300000,
hideOldMessagesEnabled: false,
hideOldMessagesKeepLastN: 12,
// 提取设置
extractEvery: 1, // 每 N 条 assistant 回复提取一次
@@ -2246,6 +2256,105 @@ function getSettings() {
return mergedSettings;
}
function getMessageHideSettings(settings = null) {
let sourceSettings = settings;
if (!sourceSettings || typeof sourceSettings !== "object") {
try {
sourceSettings =
typeof getSettings === "function" ? getSettings() : {};
} catch {
sourceSettings = {};
}
}
return {
enabled: Boolean(sourceSettings.hideOldMessagesEnabled),
hide_last_n: Math.max(
0,
Math.trunc(Number(sourceSettings.hideOldMessagesKeepLastN ?? 0) || 0),
),
};
}
function getHideRuntimeAdapters() {
return {
$,
clearTimeout,
getContext,
setTimeout,
};
}
function applyMessageHideNow(reason = "manual-apply") {
try {
const result = applyHideSettings(
getMessageHideSettings(),
getHideRuntimeAdapters(),
);
console.log("[ST-BME] 已应用旧楼层隐藏:", reason, result);
return result;
} catch (error) {
console.warn("[ST-BME] 应用旧楼层隐藏失败:", reason, error);
return {
active: false,
error: error instanceof Error ? error.message : String(error || "未知错误"),
};
}
}
function scheduleMessageHideApply(reason = "scheduled", delayMs = 120) {
try {
scheduleHideSettingsApply(
getMessageHideSettings(),
getHideRuntimeAdapters(),
delayMs,
);
} catch (error) {
console.warn("[ST-BME] 调度旧楼层隐藏失败:", reason, error);
}
}
function runIncrementalMessageHide(reason = "incremental") {
try {
const result = runIncrementalHideCheck(
getMessageHideSettings(),
getHideRuntimeAdapters(),
);
if (result?.active) {
console.log("[ST-BME] 已增量更新旧楼层隐藏:", reason, result);
}
return result;
} catch (error) {
console.warn("[ST-BME] 增量更新旧楼层隐藏失败:", reason, error);
return {
active: false,
error: error instanceof Error ? error.message : String(error || "未知错误"),
};
}
}
function clearMessageHideState(reason = "reset") {
try {
resetHideState(getHideRuntimeAdapters());
console.log("[ST-BME] 已重置旧楼层隐藏状态:", reason);
} catch (error) {
console.warn("[ST-BME] 重置旧楼层隐藏状态失败:", reason, error);
}
}
function clearAllHiddenMessages(reason = "manual-clear") {
try {
const result = unhideAll(getHideRuntimeAdapters());
console.log("[ST-BME] 已取消全部旧楼层隐藏:", reason, result);
return result;
} catch (error) {
console.warn("[ST-BME] 取消全部旧楼层隐藏失败:", reason, error);
return {
active: false,
error: error instanceof Error ? error.message : String(error || "未知错误"),
};
}
}
function initializeHostCapabilityBridge(options = {}) {
try {
initializeHostAdapter({
@@ -2324,6 +2433,7 @@ export function getPanelRuntimeDebugSnapshot(options = {}) {
return {
hostCapabilities,
messageHiding: getHideStateSnapshot(),
runtimeDebug: readRuntimeDebugSnapshot(),
};
}
@@ -4732,6 +4842,10 @@ function updateModuleSettings(patch = {}) {
"embeddingBackendApiUrl",
"embeddingAutoSuffix",
]);
const messageHideKeys = new Set([
"hideOldMessagesEnabled",
"hideOldMessagesKeepLastN",
]);
const settings = getSettings();
Object.assign(settings, patch);
extension_settings[MODULE_NAME] = settings;
@@ -4779,6 +4893,15 @@ function updateModuleSettings(patch = {}) {
);
}
if (Object.keys(patch).some((key) => messageHideKeys.has(key))) {
const hideSettings = getMessageHideSettings(settings);
if (!hideSettings.enabled || hideSettings.hide_last_n <= 0) {
clearAllHiddenMessages("settings-updated-disable");
} else {
scheduleMessageHideApply("settings-updated", 30);
}
}
scheduleServerSettingsSave();
return settings;
}
@@ -7752,6 +7875,9 @@ async function runRecall(options = {}) {
// ==================== 事件钩子 ====================
function onChatChanged() {
if (typeof clearMessageHideState === "function") {
clearMessageHideState("chat-changed");
}
const result = onChatChangedController({
abortAllRunningStages,
clearCoreEventBindingState,
@@ -7796,6 +7922,10 @@ function onChatChanged() {
}
});
if (typeof scheduleMessageHideApply === "function") {
scheduleMessageHideApply("chat-changed", 220);
}
return result;
}
@@ -7817,11 +7947,15 @@ function onChatLoaded() {
}
});
if (typeof scheduleMessageHideApply === "function") {
scheduleMessageHideApply("chat-loaded", 180);
}
return result;
}
function onMessageSent(messageId) {
return onMessageSentController(
const result = onMessageSentController(
{
getContext,
recordRecallSentUserMessage,
@@ -7829,10 +7963,14 @@ function onMessageSent(messageId) {
},
messageId,
);
if (typeof scheduleMessageHideApply === "function") {
scheduleMessageHideApply("message-sent", 40);
}
return result;
}
function onMessageDeleted(chatLengthOrMessageId, meta = null) {
return onMessageDeletedController(
const result = onMessageDeletedController(
{
invalidateRecallAfterHistoryMutation,
refreshPersistedRecallMessageUi: schedulePersistedRecallMessageUiRefresh,
@@ -7841,10 +7979,14 @@ function onMessageDeleted(chatLengthOrMessageId, meta = null) {
chatLengthOrMessageId,
meta,
);
if (typeof scheduleMessageHideApply === "function") {
scheduleMessageHideApply("message-deleted", 80);
}
return result;
}
function onMessageEdited(messageId, meta = null) {
return onMessageEditedController(
const result = onMessageEditedController(
{
invalidateRecallAfterHistoryMutation,
refreshPersistedRecallMessageUi: schedulePersistedRecallMessageUiRefresh,
@@ -7853,10 +7995,14 @@ function onMessageEdited(messageId, meta = null) {
messageId,
meta,
);
if (typeof scheduleMessageHideApply === "function") {
scheduleMessageHideApply("message-edited", 80);
}
return result;
}
function onMessageSwiped(messageId, meta = null) {
return onMessageSwipedController(
const result = onMessageSwipedController(
{
invalidateRecallAfterHistoryMutation,
refreshPersistedRecallMessageUi: schedulePersistedRecallMessageUiRefresh,
@@ -7865,6 +8011,10 @@ function onMessageSwiped(messageId, meta = null) {
messageId,
meta,
);
if (typeof scheduleMessageHideApply === "function") {
scheduleMessageHideApply("message-swiped", 80);
}
return result;
}
function onGenerationStarted(type, params = {}, dryRun = false) {
@@ -7882,6 +8032,12 @@ function onGenerationStarted(type, params = {}, dryRun = false) {
);
}
function onGenerationEnded(_chatLength = null) {
if (typeof scheduleMessageHideApply === "function") {
scheduleMessageHideApply("generation-ended", 180);
}
}
async function onGenerationAfterCommands(type, params = {}, dryRun = false) {
return await onGenerationAfterCommandsController(
{
@@ -7926,7 +8082,7 @@ async function onBeforeCombinePrompts(promptData = null) {
}
function onMessageReceived(messageId = null, type = "") {
return onMessageReceivedController({
const result = onMessageReceivedController({
console,
createRecallInputRecord,
getContext,
@@ -7951,6 +8107,20 @@ function onMessageReceived(messageId = null, type = "") {
pendingRecallSendIntent = record;
},
}, messageId, type);
const hideSettings =
typeof getMessageHideSettings === "function"
? getMessageHideSettings()
: null;
if (
hideSettings?.enabled &&
hideSettings?.hide_last_n > 0 &&
typeof runIncrementalMessageHide === "function"
) {
runIncrementalMessageHide("message-received");
}
return result;
}
// ==================== UI 操作 ====================
@@ -8245,6 +8415,7 @@ async function onReembedDirect() {
initializeHostCapabilityBridge();
installSendIntentHooks();
autoSyncOnVisibility(buildBmeSyncRuntimeOptions());
scheduleMessageHideApply("init", 180);
// 注册事件钩子
registerCoreEventHooksController({
@@ -8257,6 +8428,7 @@ async function onReembedDirect() {
onChatChanged,
onChatLoaded,
onGenerationAfterCommands,
onGenerationEnded,
onGenerationStarted,
onMessageDeleted,
onMessageEdited,

View File

@@ -3,13 +3,14 @@
"test:p0": "node tests/p0-regressions.mjs",
"test:runtime-history": "node tests/runtime-history.mjs",
"test:graph-persistence": "node tests/graph-persistence.mjs",
"test:hide-engine": "node tests/hide-engine.mjs",
"test:indexeddb-persistence": "node tests/indexeddb-persistence.mjs",
"test:indexeddb-sync": "node tests/indexeddb-sync.mjs",
"test:indexeddb-migration": "node tests/indexeddb-migration.mjs",
"test:indexeddb": "npm run test:indexeddb-persistence && npm run test:indexeddb-sync && npm run test:indexeddb-migration",
"test:persistence-matrix": "npm run test:p0 && npm run test:runtime-history && npm run test:graph-persistence && npm run test:indexeddb",
"test:all": "npm run test:persistence-matrix",
"check": "node --check index.js && node --check bme-db.js && node --check panel.js && node --check ui-status.js && node --check event-binding.js"
"check": "node --check index.js && node --check bme-db.js && node --check hide-engine.js && node --check panel.js && node --check ui-status.js && node --check event-binding.js"
},
"dependencies": {
"triviumdb": "^0.4.41"

View File

@@ -952,6 +952,66 @@
</label>
</div>
</div>
<div class="bme-config-card">
<div class="bme-config-card-head">
<div>
<div class="bme-config-card-title">隐藏旧楼层</div>
<div class="bme-config-card-subtitle">
不删除聊天内容,只是把较早楼层临时标成系统消息,让主回复和 ST-BME 自己读取聊天时一起跳过它们。
</div>
</div>
</div>
<label
class="bme-toggle-item"
for="bme-setting-hide-old-messages-enabled"
>
<span class="bme-toggle-copy">
<span class="bme-toggle-title">启用旧楼层隐藏</span>
<span class="bme-toggle-desc">
适合长聊天控 token。切聊天会自动重新应用不会删除原楼层。
</span>
</span>
<input
id="bme-setting-hide-old-messages-enabled"
type="checkbox"
/>
</label>
<div class="bme-config-row">
<label for="bme-setting-hide-old-messages-keep-last-n">
保留最近 N 条消息
</label>
<input
id="bme-setting-hide-old-messages-keep-last-n"
class="bme-config-input"
type="number"
min="0"
max="200"
placeholder="0 = 不隐藏"
/>
</div>
<div class="bme-config-help">
设置修改后会自动生效。`0` 表示不隐藏;“取消全部隐藏”会立即把当前聊天里由 ST-BME 隐藏的楼层恢复。
</div>
<div class="bme-config-actions">
<button
class="bme-config-secondary-btn"
id="bme-apply-hide-settings"
type="button"
>
<i class="fa-solid fa-eye-slash"></i>
<span>重新应用当前隐藏</span>
</button>
<button
class="bme-config-secondary-btn"
id="bme-clear-hide-settings"
type="button"
>
<i class="fa-solid fa-eye"></i>
<span>取消全部隐藏</span>
</button>
</div>
</div>
</div>
</section>

View File

@@ -1420,6 +1420,10 @@ function _refreshConfigTab() {
const settings = _getSettings?.() || {};
_setCheckboxValue("bme-setting-enabled", settings.enabled ?? true);
_setCheckboxValue(
"bme-setting-hide-old-messages-enabled",
settings.hideOldMessagesEnabled ?? false,
);
_setCheckboxValue(
"bme-setting-recall-enabled",
settings.recallEnabled ?? true,
@@ -1495,6 +1499,10 @@ function _refreshConfigTab() {
);
_setInputValue("bme-setting-extract-every", settings.extractEvery ?? 1);
_setInputValue(
"bme-setting-hide-old-messages-keep-last-n",
settings.hideOldMessagesKeepLastN ?? 12,
);
_setInputValue(
"bme-setting-extract-context-turns",
settings.extractContextTurns ?? 2,
@@ -1695,6 +1703,9 @@ function _bindConfigControls() {
_patchSettings({ enabled: checked });
_refreshGuardedConfigStates();
});
bindCheckbox("bme-setting-hide-old-messages-enabled", (checked) => {
_patchSettings({ hideOldMessagesEnabled: checked });
});
bindCheckbox("bme-setting-recall-enabled", (checked) => {
_patchSettings({ recallEnabled: checked });
_refreshGuardedConfigStates();
@@ -1768,6 +1779,13 @@ function _bindConfigControls() {
bindNumber("bme-setting-extract-every", 1, 1, 50, (value) =>
_patchSettings({ extractEvery: value }),
);
bindNumber(
"bme-setting-hide-old-messages-keep-last-n",
12,
0,
200,
(value) => _patchSettings({ hideOldMessagesKeepLastN: value }),
);
bindNumber("bme-setting-extract-context-turns", 2, 0, 20, (value) =>
_patchSettings({ extractContextTurns: value }),
);
@@ -2024,6 +2042,27 @@ function _bindConfigControls() {
card.dataset.bmeBound = "true";
});
document
.getElementById("bme-apply-hide-settings")
?.addEventListener("click", () => {
const settings = _getSettings?.() || {};
_patchSettings({
hideOldMessagesEnabled: settings.hideOldMessagesEnabled ?? false,
hideOldMessagesKeepLastN: settings.hideOldMessagesKeepLastN ?? 12,
});
toastr.success("当前聊天的隐藏设置已重新应用", "ST-BME");
});
document
.getElementById("bme-clear-hide-settings")
?.addEventListener("click", () => {
_patchSettings({
hideOldMessagesEnabled: false,
hideOldMessagesKeepLastN: 0,
});
_setCheckboxValue("bme-setting-hide-old-messages-enabled", false);
_setInputValue("bme-setting-hide-old-messages-keep-last-n", 0);
toastr.info("已取消当前聊天里由 ST-BME 应用的隐藏", "ST-BME");
});
document
.getElementById("bme-test-llm")
?.addEventListener("click", async () => {

119
tests/hide-engine.mjs Normal file
View File

@@ -0,0 +1,119 @@
import assert from "node:assert/strict";
import {
applyHideSettings,
getHideStateSnapshot,
resetHideState,
runIncrementalHideCheck,
unhideAll,
} from "../hide-engine.js";
function createRuntime(chat) {
const domWrites = [];
return {
chat,
domWrites,
getContext() {
return { chat: this.chat };
},
$(selector) {
return {
attr(name, value) {
domWrites.push({ selector, name, value });
},
};
},
};
}
function testApplyAndUnhidePreservesOriginalSystemMessages() {
const chat = [
{ mes: "原系统", is_system: true },
{ mes: "用户1", is_user: true, is_system: false },
{ mes: "助手1", is_user: false, is_system: false },
{ mes: "用户2", is_user: true, is_system: false },
{ mes: "助手2", is_user: false, is_system: false },
];
const runtime = createRuntime(chat);
const applyResult = applyHideSettings(
{ enabled: true, hide_last_n: 2 },
runtime,
);
assert.equal(applyResult.active, true);
assert.equal(chat[0].is_system, true);
assert.equal(chat[1].is_system, true);
assert.equal(chat[2].is_system, true);
assert.equal(chat[3].is_system, false);
assert.equal(chat[4].is_system, false);
assert.equal(applyResult.managedCount, 2);
const unhideResult = unhideAll(runtime);
assert.equal(unhideResult.active, false);
assert.equal(chat[0].is_system, true, "原系统消息不应被恢复");
assert.equal(chat[1].is_system, false);
assert.equal(chat[2].is_system, false);
}
function testResetRestoresPreviousManagedChat() {
const oldChat = [
{ mes: "用户1", is_user: true, is_system: false },
{ mes: "助手1", is_user: false, is_system: false },
{ mes: "用户2", is_user: true, is_system: false },
{ mes: "助手2", is_user: false, is_system: false },
];
const newChat = [
{ mes: "新用户", is_user: true, is_system: false },
{ mes: "新助手", is_user: false, is_system: false },
];
const runtime = createRuntime(oldChat);
applyHideSettings({ enabled: true, hide_last_n: 1 }, runtime);
assert.equal(oldChat[0].is_system, true);
assert.equal(oldChat[1].is_system, true);
assert.equal(oldChat[2].is_system, true);
runtime.chat = newChat;
resetHideState(runtime);
assert.equal(oldChat[0].is_system, false);
assert.equal(oldChat[1].is_system, false);
assert.equal(oldChat[2].is_system, false);
assert.deepEqual(getHideStateSnapshot(), {
hasManagedChat: false,
managedHiddenCount: 0,
lastProcessedLength: 0,
scheduled: false,
});
}
function testIncrementalHideOnlyHidesNewOverflowMessages() {
const chat = [
{ mes: "用户1", is_user: true, is_system: false },
{ mes: "助手1", is_user: false, is_system: false },
{ mes: "用户2", is_user: true, is_system: false },
];
const runtime = createRuntime(chat);
applyHideSettings({ enabled: true, hide_last_n: 2 }, runtime);
assert.equal(chat[0].is_system, true);
assert.equal(chat[1].is_system, false);
assert.equal(chat[2].is_system, false);
chat.push({ mes: "助手2", is_user: false, is_system: false });
const result = runIncrementalHideCheck(
{ enabled: true, hide_last_n: 2 },
runtime,
);
assert.equal(result.incremental, true);
assert.equal(result.hiddenCount, 1);
assert.equal(chat[1].is_system, true);
assert.equal(chat[2].is_system, false);
assert.equal(chat[3].is_system, false);
}
testApplyAndUnhidePreservesOriginalSystemMessages();
testResetRestoresPreviousManagedChat();
testIncrementalHideOnlyHidesNewOverflowMessages();
console.log("hide-engine tests passed");