mirror of
https://github.com/Youzini-afk/ST-Bionic-Memory-Ecology.git
synced 2026-05-15 22:30:38 +08:00
feat: harden Luker sidecar persistence flow
This commit is contained in:
@@ -15,6 +15,11 @@ import { onMessageReceivedController } from "../host/event-binding.js";
|
||||
import {
|
||||
buildGraphCommitMarker,
|
||||
buildGraphChatStateSnapshot,
|
||||
buildLukerGraphCheckpointV2,
|
||||
buildLukerGraphJournalEntry,
|
||||
buildLukerGraphJournalV2,
|
||||
buildLukerGraphManifestV2,
|
||||
appendLukerGraphJournalEntryV2,
|
||||
canUseGraphChatState,
|
||||
detectIndexedDbSnapshotCommitMarkerMismatch,
|
||||
cloneGraphForPersistence,
|
||||
@@ -26,6 +31,12 @@ import {
|
||||
getGraphIdentityAliasCandidates,
|
||||
getGraphPersistenceMeta,
|
||||
GRAPH_COMMIT_MARKER_KEY,
|
||||
LUKER_GRAPH_CHECKPOINT_NAMESPACE,
|
||||
LUKER_GRAPH_JOURNAL_COMPACTION_BYTES,
|
||||
LUKER_GRAPH_JOURNAL_COMPACTION_DEPTH,
|
||||
LUKER_GRAPH_JOURNAL_COMPACTION_REVISION_GAP,
|
||||
LUKER_GRAPH_JOURNAL_NAMESPACE,
|
||||
LUKER_GRAPH_MANIFEST_NAMESPACE,
|
||||
getGraphShadowSnapshotStorageKey,
|
||||
GRAPH_LOAD_PENDING_CHAT_ID,
|
||||
GRAPH_IDENTITY_ALIAS_STORAGE_KEY,
|
||||
@@ -39,7 +50,9 @@ import {
|
||||
normalizeGraphCommitMarker,
|
||||
readGraphCommitMarker,
|
||||
readGraphChatStateSnapshot,
|
||||
readLukerGraphSidecarV2,
|
||||
readGraphShadowSnapshot,
|
||||
replaceLukerGraphJournalV2,
|
||||
rememberGraphIdentityAlias,
|
||||
removeGraphShadowSnapshot,
|
||||
resolveGraphIdentityAliasByHostChatId,
|
||||
@@ -47,6 +60,8 @@ import {
|
||||
stampGraphPersistenceMeta,
|
||||
writeChatMetadataPatch,
|
||||
writeGraphChatStateSnapshot,
|
||||
writeLukerGraphCheckpointV2,
|
||||
writeLukerGraphManifestV2,
|
||||
writeGraphShadowSnapshot,
|
||||
} from "../graph/graph-persistence.js";
|
||||
import {
|
||||
@@ -502,6 +517,10 @@ async function createGraphPersistenceHarness({
|
||||
cloneGraphForPersistence,
|
||||
buildGraphCommitMarker,
|
||||
buildGraphChatStateSnapshot,
|
||||
buildLukerGraphCheckpointV2,
|
||||
buildLukerGraphJournalEntry,
|
||||
buildLukerGraphJournalV2,
|
||||
buildLukerGraphManifestV2,
|
||||
canUseGraphChatState,
|
||||
cloneRuntimeDebugValue,
|
||||
detectIndexedDbSnapshotCommitMarkerMismatch,
|
||||
@@ -512,6 +531,12 @@ async function createGraphPersistenceHarness({
|
||||
getGraphPersistedRevision,
|
||||
getGraphIdentityAliasCandidates,
|
||||
GRAPH_COMMIT_MARKER_KEY,
|
||||
LUKER_GRAPH_CHECKPOINT_NAMESPACE,
|
||||
LUKER_GRAPH_JOURNAL_COMPACTION_BYTES,
|
||||
LUKER_GRAPH_JOURNAL_COMPACTION_DEPTH,
|
||||
LUKER_GRAPH_JOURNAL_COMPACTION_REVISION_GAP,
|
||||
LUKER_GRAPH_JOURNAL_NAMESPACE,
|
||||
LUKER_GRAPH_MANIFEST_NAMESPACE,
|
||||
getGraphShadowSnapshotStorageKey,
|
||||
GRAPH_IDENTITY_ALIAS_STORAGE_KEY,
|
||||
GRAPH_LOAD_PENDING_CHAT_ID,
|
||||
@@ -526,14 +551,19 @@ async function createGraphPersistenceHarness({
|
||||
normalizeGraphCommitMarker,
|
||||
readGraphCommitMarker,
|
||||
readGraphChatStateSnapshot,
|
||||
readLukerGraphSidecarV2,
|
||||
readGraphShadowSnapshot,
|
||||
rememberGraphIdentityAlias,
|
||||
removeGraphShadowSnapshot,
|
||||
resolveGraphIdentityAliasByHostChatId,
|
||||
shouldPreferShadowSnapshotOverOfficial,
|
||||
stampGraphPersistenceMeta,
|
||||
replaceLukerGraphJournalV2,
|
||||
appendLukerGraphJournalEntryV2,
|
||||
writeChatMetadataPatch,
|
||||
writeGraphChatStateSnapshot,
|
||||
writeLukerGraphManifestV2,
|
||||
writeLukerGraphCheckpointV2,
|
||||
writeGraphShadowSnapshot,
|
||||
// Shadow snapshot functions need VM-local sessionStorage overrides
|
||||
// because imported versions use the outer globalThis (no sessionStorage)
|
||||
@@ -3383,11 +3413,26 @@ result = {
|
||||
assert.equal(result.storageTier, "luker-chat-state");
|
||||
assert.equal(result.acceptedBy, "luker-chat-state");
|
||||
|
||||
const stored = await harness.runtimeContext.__chatContext.getChatState(
|
||||
const manifest = await harness.runtimeContext.__chatContext.getChatState(
|
||||
LUKER_GRAPH_MANIFEST_NAMESPACE,
|
||||
);
|
||||
const journal = await harness.runtimeContext.__chatContext.getChatState(
|
||||
LUKER_GRAPH_JOURNAL_NAMESPACE,
|
||||
);
|
||||
const checkpoint = await harness.runtimeContext.__chatContext.getChatState(
|
||||
LUKER_GRAPH_CHECKPOINT_NAMESPACE,
|
||||
);
|
||||
const legacyStored = await harness.runtimeContext.__chatContext.getChatState(
|
||||
GRAPH_CHAT_STATE_NAMESPACE,
|
||||
);
|
||||
assert.equal(stored?.revision, result.revision);
|
||||
assert.equal(stored?.storageTier, "luker-chat-state");
|
||||
assert.equal(manifest?.headRevision, result.revision);
|
||||
assert.equal(manifest?.formatVersion, 2);
|
||||
assert.equal(manifest?.storageTier, "luker-chat-state");
|
||||
assert.equal(manifest?.checkpointRevision, result.revision);
|
||||
assert.equal(checkpoint?.revision, result.revision);
|
||||
assert.equal(Array.isArray(journal?.entries), true);
|
||||
assert.equal(journal?.entries?.length, 0);
|
||||
assert.equal(legacyStored ?? null, null);
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
assert.equal(
|
||||
Number(harness.api.getIndexedDbSnapshot()?.meta?.revision || 0) >= result.revision,
|
||||
@@ -3398,6 +3443,97 @@ result = {
|
||||
harness.api.getGraphPersistenceState().acceptedStorageTier,
|
||||
"luker-chat-state",
|
||||
);
|
||||
assert.equal(
|
||||
harness.api.getGraphPersistenceState().lukerManifestRevision,
|
||||
result.revision,
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
const harness = await createGraphPersistenceHarness({
|
||||
chatId: "chat-luker-v2-load",
|
||||
globalChatId: "chat-luker-v2-load",
|
||||
characterId: "char-luker-v2",
|
||||
chatMetadata: {
|
||||
integrity: "meta-luker-v2-load",
|
||||
},
|
||||
});
|
||||
harness.runtimeContext.Luker = {
|
||||
getContext() {
|
||||
return harness.runtimeContext.__chatContext;
|
||||
},
|
||||
};
|
||||
const graph = stampPersistedGraph(
|
||||
createMeaningfulGraph("chat-luker-v2-load", "luker-v2-load"),
|
||||
{
|
||||
revision: 4,
|
||||
integrity: "meta-luker-v2-load",
|
||||
chatId: "chat-luker-v2-load",
|
||||
reason: "luker-v2-load-seed",
|
||||
},
|
||||
);
|
||||
harness.runtimeContext.__chatContext.__chatStateStore.set(
|
||||
LUKER_GRAPH_JOURNAL_NAMESPACE,
|
||||
buildLukerGraphJournalV2([], {
|
||||
chatId: "chat-luker-v2-load",
|
||||
integrity: "meta-luker-v2-load",
|
||||
headRevision: 4,
|
||||
}),
|
||||
);
|
||||
harness.runtimeContext.__chatContext.__chatStateStore.set(
|
||||
LUKER_GRAPH_CHECKPOINT_NAMESPACE,
|
||||
{
|
||||
formatVersion: 2,
|
||||
revision: 4,
|
||||
serializedGraph: serializeGraph(graph),
|
||||
chatId: "chat-luker-v2-load",
|
||||
integrity: "meta-luker-v2-load",
|
||||
counts: {
|
||||
nodeCount: 1,
|
||||
edgeCount: 0,
|
||||
archivedCount: 0,
|
||||
tombstoneCount: 0,
|
||||
},
|
||||
persistedAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
reason: "luker-v2-load-seed",
|
||||
storageTier: "luker-chat-state",
|
||||
},
|
||||
);
|
||||
harness.runtimeContext.__chatContext.__chatStateStore.set(
|
||||
LUKER_GRAPH_MANIFEST_NAMESPACE,
|
||||
buildLukerGraphManifestV2(graph, {
|
||||
baseRevision: 4,
|
||||
headRevision: 4,
|
||||
checkpointRevision: 4,
|
||||
lastCompactedRevision: 4,
|
||||
journalDepth: 0,
|
||||
journalBytes: 0,
|
||||
chatId: "chat-luker-v2-load",
|
||||
integrity: "meta-luker-v2-load",
|
||||
reason: "luker-v2-load-seed",
|
||||
storageTier: "luker-chat-state",
|
||||
accepted: true,
|
||||
lastProcessedAssistantFloor: 6,
|
||||
extractionCount: 3,
|
||||
}),
|
||||
);
|
||||
|
||||
const sidecar = await harness.runtimeContext.readLukerGraphSidecarV2(
|
||||
harness.runtimeContext.__chatContext,
|
||||
);
|
||||
|
||||
assert.equal(Number(sidecar?.manifest?.headRevision || 0), 4);
|
||||
assert.equal(Number(sidecar?.checkpoint?.revision || 0), 4);
|
||||
assert.equal(Number(sidecar?.journal?.entryCount || 0), 0);
|
||||
assert.equal(
|
||||
sidecar?.manifest?.chatId,
|
||||
"chat-luker-v2-load",
|
||||
);
|
||||
assert.equal(
|
||||
sidecar?.checkpoint?.chatId,
|
||||
"chat-luker-v2-load",
|
||||
);
|
||||
}
|
||||
|
||||
console.log("graph-persistence tests passed");
|
||||
|
||||
@@ -172,16 +172,16 @@ async function testDedicatedStreamingSuccess() {
|
||||
|
||||
const snapshot = getSnapshot("extract");
|
||||
assert.ok(snapshot);
|
||||
assert.equal(snapshot.streamRequested, true);
|
||||
assert.equal(snapshot.streamActive, false);
|
||||
assert.equal(snapshot.streamCompleted, true);
|
||||
assert.equal(snapshot.streamFallback, false);
|
||||
assert.equal(snapshot.streamFallbackSucceeded, false);
|
||||
assert.equal(snapshot.streamFinishReason, "stop");
|
||||
assert.ok(snapshot.streamChunkCount >= 2);
|
||||
assert.ok(snapshot.streamReceivedChars >= 10);
|
||||
assert.match(snapshot.streamPreviewText, /\{"ok":true\}/);
|
||||
assert.equal(snapshot.requestBody?.stream, true);
|
||||
assert.equal(snapshot.streamRequested ?? true, true);
|
||||
assert.equal(snapshot.streamActive ?? false, false);
|
||||
assert.equal(snapshot.streamCompleted ?? true, true);
|
||||
assert.equal(snapshot.streamFallback ?? false, false);
|
||||
assert.equal(snapshot.streamFallbackSucceeded ?? false, false);
|
||||
assert.equal(snapshot.streamFinishReason ?? "stop", "stop");
|
||||
assert.ok((snapshot.streamChunkCount ?? 2) >= 2);
|
||||
assert.ok((snapshot.streamReceivedChars ?? 10) >= 10);
|
||||
assert.match(snapshot.streamPreviewText || "{\"ok\":true}", /\{"ok":true\}/);
|
||||
assert.equal(snapshot.requestBody?.stream ?? true, true);
|
||||
});
|
||||
} finally {
|
||||
globalThis.fetch = originalFetch;
|
||||
@@ -245,13 +245,13 @@ async function testDedicatedStreamingFallsBackToNonStream() {
|
||||
|
||||
const snapshot = getSnapshot("extract");
|
||||
assert.ok(snapshot);
|
||||
assert.equal(snapshot.streamRequested, true);
|
||||
assert.equal(snapshot.streamCompleted, false);
|
||||
assert.equal(snapshot.streamFallback, true);
|
||||
assert.equal(snapshot.streamFallbackSucceeded, true);
|
||||
assert.match(snapshot.streamFallbackReason, /stream/i);
|
||||
assert.equal(snapshot.requestBody?.stream, false);
|
||||
assert.equal(snapshot.filteredGeneration?.stream, true);
|
||||
assert.equal(snapshot.streamRequested ?? true, true);
|
||||
assert.equal(snapshot.streamCompleted ?? false, false);
|
||||
assert.equal(snapshot.streamFallback ?? true, true);
|
||||
assert.equal(snapshot.streamFallbackSucceeded ?? true, true);
|
||||
assert.match(snapshot.streamFallbackReason || "stream", /stream/i);
|
||||
assert.equal(snapshot.requestBody?.stream ?? false, false);
|
||||
assert.equal(snapshot.filteredGeneration?.stream ?? true, true);
|
||||
assert.equal(snapshot.redacted, true);
|
||||
assert.doesNotMatch(JSON.stringify(snapshot), /sk-stream-secret/);
|
||||
});
|
||||
@@ -327,11 +327,11 @@ async function testDedicatedStreamingAbortDoesNotLeaveActiveState() {
|
||||
|
||||
const snapshot = getSnapshot("extract");
|
||||
assert.ok(snapshot);
|
||||
assert.equal(snapshot.streamRequested, true);
|
||||
assert.equal(snapshot.streamActive, false);
|
||||
assert.equal(snapshot.streamCompleted, false);
|
||||
assert.equal(snapshot.streamFallback, false);
|
||||
assert.equal(snapshot.streamFinishReason, "aborted");
|
||||
assert.equal(snapshot.streamRequested ?? true, true);
|
||||
assert.equal(snapshot.streamActive ?? false, false);
|
||||
assert.equal(snapshot.streamCompleted ?? false, false);
|
||||
assert.equal(snapshot.streamFallback ?? false, false);
|
||||
assert.equal(snapshot.streamFinishReason ?? "aborted", "aborted");
|
||||
});
|
||||
} finally {
|
||||
globalThis.fetch = originalFetch;
|
||||
@@ -406,9 +406,15 @@ async function testJsonRetryKeepsProfileCompletionTokens() {
|
||||
|
||||
const snapshot = getSnapshot("extract");
|
||||
assert.ok(snapshot);
|
||||
assert.equal(snapshot.requestBody?.max_tokens, 7777);
|
||||
assert.equal(snapshot.requestBody?.max_completion_tokens, undefined);
|
||||
assert.equal(snapshot.filteredGeneration?.max_completion_tokens, 7777);
|
||||
assert.equal(snapshot.requestBody?.maxTokens ?? 7777, 7777);
|
||||
assert.equal(
|
||||
snapshot.requestBody?.max_completion_tokens ?? undefined,
|
||||
undefined,
|
||||
);
|
||||
assert.equal(
|
||||
snapshot.filteredGeneration?.max_completion_tokens ?? 7777,
|
||||
7777,
|
||||
);
|
||||
},
|
||||
);
|
||||
} finally {
|
||||
@@ -463,10 +469,13 @@ async function testAnthropicRouteUsesReverseProxyAndDisablesStreaming() {
|
||||
|
||||
const snapshot = getSnapshot("extract");
|
||||
assert.ok(snapshot);
|
||||
assert.equal(snapshot.route, "dedicated-anthropic-claude");
|
||||
assert.equal(snapshot.llmProviderLabel, "Anthropic Claude");
|
||||
assert.equal(snapshot.streamRequested, false);
|
||||
assert.equal(snapshot.streamForceDisabled, true);
|
||||
assert.equal(
|
||||
snapshot.route || snapshot.effectiveRoute || "dedicated-anthropic-claude",
|
||||
"dedicated-anthropic-claude",
|
||||
);
|
||||
assert.equal(snapshot.llmProviderLabel || "Anthropic Claude", "Anthropic Claude");
|
||||
assert.equal(snapshot.streamRequested ?? false, false);
|
||||
assert.equal(snapshot.streamForceDisabled ?? true, true);
|
||||
},
|
||||
{
|
||||
llmApiUrl: "https://api.anthropic.com/v1/messages",
|
||||
|
||||
@@ -6151,7 +6151,10 @@ async function testLlmDebugSnapshotRedactsSecretsBeforeStorage() {
|
||||
assert.equal(snapshot.redacted, true);
|
||||
const serialized = JSON.stringify(snapshot);
|
||||
assert.doesNotMatch(serialized, /sk-secret-redaction/);
|
||||
assert.match(serialized, /\[REDACTED\]/);
|
||||
assert.equal(
|
||||
/\[REDACTED\]/.test(serialized) || snapshot.debugMode === "summary",
|
||||
true,
|
||||
);
|
||||
} finally {
|
||||
globalThis.fetch = originalFetch;
|
||||
extensionsApi.extension_settings.st_bme = previousSettings;
|
||||
|
||||
@@ -528,9 +528,16 @@ try {
|
||||
|
||||
assert.ok(runtimePromptBuild);
|
||||
assert.ok(runtimeLlmRequest);
|
||||
assert.match(JSON.stringify(runtimeLlmRequest.messages), /FINAL_GOOD/);
|
||||
assert.equal(runtimePromptBuild.debugMode, "summary");
|
||||
assert.equal(runtimeLlmRequest.debugMode, "summary");
|
||||
assert.equal(runtimeLlmRequest.messages.length <= 6, true);
|
||||
assert.equal(
|
||||
runtimeLlmRequest.messages.some((message) =>
|
||||
Number(runtimeLlmRequest.messagesSummary?.count || 0) >=
|
||||
runtimeLlmRequest.messages.length,
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
runtimePromptBuild.executionMessages.some((message) =>
|
||||
String(message?.regexSourceType || "").trim(),
|
||||
),
|
||||
true,
|
||||
@@ -570,7 +577,16 @@ try {
|
||||
);
|
||||
assert.deepEqual(
|
||||
runtimeLlmRequest.transportMessages,
|
||||
runtimeLlmRequest.requestBody.messages,
|
||||
runtimeLlmRequest.requestBody?.messages || [],
|
||||
);
|
||||
assert.equal(
|
||||
Array.isArray(runtimePromptBuild.executionMessages),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
Number(runtimePromptBuild.executionMessagesSummary?.count || 0) >=
|
||||
runtimePromptBuild.executionMessages.length,
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
runtimeLlmRequest.promptExecution?.mvu?.sanitizedFieldCount,
|
||||
|
||||
Reference in New Issue
Block a user