mirror of
https://github.com/Youzini-afk/ST-Bionic-Memory-Ecology.git
synced 2026-06-14 02:40:45 +08:00
refactor(rebirth): extract identity resolver core
This commit is contained in:
333
runtime/identity-resolver.js
Normal file
333
runtime/identity-resolver.js
Normal file
@@ -0,0 +1,333 @@
|
||||
// ST-BME identity resolver core.
|
||||
//
|
||||
// Phase 1 keeps this module pure: callers provide context-like objects,
|
||||
// graph-owned metadata, alias callbacks, and persistence state snapshots.
|
||||
// The module separates active identity, graph-owner identity, queued/runtime
|
||||
// fallback identity, marker identity, and equivalence checks so later phases
|
||||
// can stop promoting recovery evidence into the active chat identity.
|
||||
|
||||
export function normalizeIdentityValue(value = "") {
|
||||
return String(value ?? "").trim();
|
||||
}
|
||||
|
||||
export function hasLikelySelectedChatContextCore(context = null) {
|
||||
if (!context || typeof context !== "object") return false;
|
||||
const metadata = context.chatMetadata;
|
||||
const hasMeaningfulChatMetadata = Boolean(
|
||||
metadata &&
|
||||
typeof metadata === "object" &&
|
||||
Object.keys(metadata).some((key) => metadata[key] != null && metadata[key] !== ""),
|
||||
);
|
||||
const hasChatMessages = Array.isArray(context.chat) && context.chat.length > 0;
|
||||
const hasCharacterId =
|
||||
context.characterId !== undefined &&
|
||||
context.characterId !== null &&
|
||||
String(context.characterId).trim() !== "";
|
||||
const hasGroupId =
|
||||
context.groupId !== undefined &&
|
||||
context.groupId !== null &&
|
||||
String(context.groupId).trim() !== "";
|
||||
return hasMeaningfulChatMetadata || hasChatMessages || hasCharacterId || hasGroupId;
|
||||
}
|
||||
|
||||
export function resolveActiveHostChatIdCore({
|
||||
context = null,
|
||||
readGlobalCurrentChatId = null,
|
||||
} = {}) {
|
||||
const candidates = [
|
||||
context?.chatId,
|
||||
typeof context?.getCurrentChatId === "function" ? context.getCurrentChatId() : "",
|
||||
typeof readGlobalCurrentChatId === "function" ? readGlobalCurrentChatId() : "",
|
||||
context?.chatMetadata?.chat_id,
|
||||
context?.chatMetadata?.chatId,
|
||||
context?.chatMetadata?.session_id,
|
||||
context?.chatMetadata?.sessionId,
|
||||
];
|
||||
|
||||
return candidates.map((candidate) => normalizeIdentityValue(candidate)).find(Boolean) || "";
|
||||
}
|
||||
|
||||
export function getContextIntegrityCore(context = null) {
|
||||
return normalizeIdentityValue(context?.chatMetadata?.integrity);
|
||||
}
|
||||
|
||||
export function resolveActiveChatIdentityCore({
|
||||
context = null,
|
||||
hostChatId = "",
|
||||
integrity = "",
|
||||
resolveAliasByHostChatId = null,
|
||||
hasLikelySelectedChat = null,
|
||||
} = {}) {
|
||||
const normalizedHostChatId = normalizeIdentityValue(hostChatId);
|
||||
const normalizedIntegrity = normalizeIdentityValue(integrity);
|
||||
const aliasedChatId =
|
||||
!normalizedIntegrity &&
|
||||
normalizedHostChatId &&
|
||||
typeof resolveAliasByHostChatId === "function"
|
||||
? normalizeIdentityValue(resolveAliasByHostChatId(normalizedHostChatId))
|
||||
: "";
|
||||
const chatId = normalizedIntegrity || aliasedChatId || normalizedHostChatId;
|
||||
const hasLikely =
|
||||
typeof hasLikelySelectedChat === "function"
|
||||
? hasLikelySelectedChat(context)
|
||||
: hasLikelySelectedChatContextCore(context);
|
||||
|
||||
return {
|
||||
chatId,
|
||||
hostChatId: normalizedHostChatId,
|
||||
integrity: normalizedIntegrity,
|
||||
identitySource: normalizedIntegrity
|
||||
? "integrity"
|
||||
: aliasedChatId
|
||||
? "alias"
|
||||
: normalizedHostChatId
|
||||
? "host-chat-id"
|
||||
: "",
|
||||
hasLikelySelectedChat: hasLikely,
|
||||
};
|
||||
}
|
||||
|
||||
export function resolveCurrentChatIdentityCore({
|
||||
context = null,
|
||||
readGlobalCurrentChatId = null,
|
||||
resolveAliasByHostChatId = null,
|
||||
resolveIntegrity = null,
|
||||
hasLikelySelectedChat = null,
|
||||
} = {}) {
|
||||
const hostChatId = resolveActiveHostChatIdCore({ context, readGlobalCurrentChatId });
|
||||
const integrity =
|
||||
typeof resolveIntegrity === "function"
|
||||
? normalizeIdentityValue(resolveIntegrity(context))
|
||||
: getContextIntegrityCore(context) ||
|
||||
normalizeIdentityValue(
|
||||
context?.chatMetadata?.chat_id || context?.chatMetadata?.chatId || "",
|
||||
);
|
||||
return resolveActiveChatIdentityCore({
|
||||
context,
|
||||
hostChatId,
|
||||
integrity,
|
||||
resolveAliasByHostChatId,
|
||||
hasLikelySelectedChat,
|
||||
});
|
||||
}
|
||||
|
||||
export function resolveGraphOwnerIdentityCore({ graph = null, graphMeta = null } = {}) {
|
||||
const ownedCandidates = [graph?.historyState?.chatId, graphMeta?.chatId];
|
||||
const chatId = ownedCandidates.map((candidate) => normalizeIdentityValue(candidate)).find(Boolean) || "";
|
||||
return {
|
||||
chatId,
|
||||
source: normalizeIdentityValue(graph?.historyState?.chatId)
|
||||
? "history-state"
|
||||
: normalizeIdentityValue(graphMeta?.chatId)
|
||||
? "graph-meta"
|
||||
: "",
|
||||
integrity: normalizeIdentityValue(graphMeta?.integrity),
|
||||
};
|
||||
}
|
||||
|
||||
export function resolveRuntimeGraphFallbackIdentityCore({
|
||||
graph = null,
|
||||
graphMeta = null,
|
||||
persistenceState = null,
|
||||
} = {}) {
|
||||
const fallbackCandidates = [
|
||||
graph?.historyState?.chatId,
|
||||
graphMeta?.chatId,
|
||||
persistenceState?.chatId,
|
||||
persistenceState?.queuedPersistChatId,
|
||||
persistenceState?.commitMarker?.chatId,
|
||||
];
|
||||
const chatId = fallbackCandidates.map((candidate) => normalizeIdentityValue(candidate)).find(Boolean) || "";
|
||||
return {
|
||||
chatId,
|
||||
source: chatId ? "runtime-fallback" : "",
|
||||
};
|
||||
}
|
||||
|
||||
export function resolvePersistenceChatIdCore({
|
||||
explicitChatId = "",
|
||||
activeIdentity = null,
|
||||
graph = null,
|
||||
graphMeta = null,
|
||||
currentGraph = null,
|
||||
currentGraphMeta = null,
|
||||
persistenceState = null,
|
||||
context = null,
|
||||
} = {}) {
|
||||
const directChatId = normalizeIdentityValue(explicitChatId);
|
||||
if (directChatId) return directChatId;
|
||||
|
||||
const resolvedChatId = normalizeIdentityValue(activeIdentity?.chatId);
|
||||
if (resolvedChatId) return resolvedChatId;
|
||||
|
||||
const fallbackCandidates = [
|
||||
graph?.historyState?.chatId,
|
||||
graphMeta?.chatId,
|
||||
currentGraph?.historyState?.chatId,
|
||||
currentGraphMeta?.chatId,
|
||||
persistenceState?.chatId,
|
||||
persistenceState?.queuedPersistChatId,
|
||||
persistenceState?.commitMarker?.chatId,
|
||||
context?.chatMetadata?.integrity,
|
||||
context?.chatMetadata?.chat_id,
|
||||
context?.chatMetadata?.chatId,
|
||||
context?.chatMetadata?.session_id,
|
||||
context?.chatMetadata?.sessionId,
|
||||
];
|
||||
|
||||
return fallbackCandidates.map((candidate) => normalizeIdentityValue(candidate)).find(Boolean) || "";
|
||||
}
|
||||
|
||||
export function getKnownChatIdsForIdentityCore({ identity = null, aliasCandidates = [] } = {}) {
|
||||
const knownChatIds = new Set();
|
||||
const addKnownChatId = (value) => {
|
||||
const normalized = normalizeIdentityValue(value);
|
||||
if (normalized) knownChatIds.add(normalized);
|
||||
};
|
||||
addKnownChatId(identity?.chatId);
|
||||
addKnownChatId(identity?.hostChatId);
|
||||
addKnownChatId(identity?.integrity);
|
||||
for (const aliasCandidate of Array.isArray(aliasCandidates) ? aliasCandidates : []) {
|
||||
addKnownChatId(aliasCandidate);
|
||||
}
|
||||
return knownChatIds;
|
||||
}
|
||||
|
||||
export function doesChatIdMatchIdentityCore(candidateChatId, { identity = null, aliasCandidates = [] } = {}) {
|
||||
const normalizedCandidate = normalizeIdentityValue(candidateChatId);
|
||||
if (!normalizedCandidate || !identity || typeof identity !== "object") return false;
|
||||
return getKnownChatIdsForIdentityCore({ identity, aliasCandidates }).has(normalizedCandidate);
|
||||
}
|
||||
|
||||
export function areChatIdsEquivalentForIdentityCore(
|
||||
candidateChatId,
|
||||
referenceChatId,
|
||||
{ identity = null, aliasCandidates = [] } = {},
|
||||
) {
|
||||
const normalizedCandidate = normalizeIdentityValue(candidateChatId);
|
||||
const normalizedReference = normalizeIdentityValue(referenceChatId);
|
||||
if (!normalizedCandidate || !normalizedReference) {
|
||||
return normalizedCandidate === normalizedReference;
|
||||
}
|
||||
if (normalizedCandidate === normalizedReference) return true;
|
||||
return (
|
||||
doesChatIdMatchIdentityCore(normalizedCandidate, { identity, aliasCandidates }) &&
|
||||
doesChatIdMatchIdentityCore(normalizedReference, { identity, aliasCandidates })
|
||||
);
|
||||
}
|
||||
|
||||
export function canMutateRuntimeGraphForIdentityCore({
|
||||
graph = null,
|
||||
activeIdentity = null,
|
||||
graphOwnedChatId = "",
|
||||
persistenceState = null,
|
||||
aliasCandidates = [],
|
||||
loadedStates = ["loaded", "empty-confirmed"],
|
||||
allowNoChatState = false,
|
||||
noChatState = "no-chat",
|
||||
} = {}) {
|
||||
if (
|
||||
!graph ||
|
||||
typeof graph !== "object" ||
|
||||
!graph.historyState ||
|
||||
typeof graph.historyState !== "object" ||
|
||||
Array.isArray(graph.historyState)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const ownedChatId = normalizeIdentityValue(graphOwnedChatId);
|
||||
if (!ownedChatId) return false;
|
||||
|
||||
const liveChatId = normalizeIdentityValue(activeIdentity?.chatId);
|
||||
if (liveChatId) {
|
||||
return (
|
||||
areChatIdsEquivalentForIdentityCore(ownedChatId, liveChatId, {
|
||||
identity: activeIdentity,
|
||||
aliasCandidates,
|
||||
}) ||
|
||||
areChatIdsEquivalentForIdentityCore(liveChatId, ownedChatId, {
|
||||
identity: activeIdentity,
|
||||
aliasCandidates,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
const stateChatId = normalizeIdentityValue(persistenceState?.chatId);
|
||||
if (!stateChatId || stateChatId !== ownedChatId) return false;
|
||||
|
||||
const markerChatId = normalizeIdentityValue(persistenceState?.commitMarker?.chatId);
|
||||
if (markerChatId && markerChatId !== ownedChatId) return false;
|
||||
|
||||
const loadState = String(persistenceState?.loadState || "");
|
||||
if (
|
||||
loadedStates.includes(loadState) ||
|
||||
persistenceState?.dbReady === true
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return allowNoChatState === true && loadState === noChatState;
|
||||
}
|
||||
|
||||
export function planRuntimeGraphIdentityRepairCore({
|
||||
graph = null,
|
||||
graphOwnedChatId = "",
|
||||
stateChatId = "",
|
||||
activeIdentity = null,
|
||||
markerChatId = "",
|
||||
aliasCandidates = [],
|
||||
} = {}) {
|
||||
if (
|
||||
!graph ||
|
||||
typeof graph !== "object" ||
|
||||
Array.isArray(graph) ||
|
||||
!graph.historyState ||
|
||||
typeof graph.historyState !== "object" ||
|
||||
Array.isArray(graph.historyState)
|
||||
) {
|
||||
return { shouldRepair: false, reason: "missing-runtime-graph" };
|
||||
}
|
||||
|
||||
const ownedChatId = normalizeIdentityValue(graphOwnedChatId);
|
||||
if (ownedChatId) {
|
||||
return { shouldRepair: false, reason: "graph-identity-present", chatId: ownedChatId };
|
||||
}
|
||||
|
||||
const normalizedStateChatId = normalizeIdentityValue(stateChatId);
|
||||
if (!normalizedStateChatId) {
|
||||
return { shouldRepair: false, reason: "missing-persistence-chat-id" };
|
||||
}
|
||||
|
||||
const liveChatId = normalizeIdentityValue(activeIdentity?.chatId);
|
||||
if (
|
||||
liveChatId &&
|
||||
!areChatIdsEquivalentForIdentityCore(normalizedStateChatId, liveChatId, {
|
||||
identity: activeIdentity,
|
||||
aliasCandidates,
|
||||
}) &&
|
||||
!areChatIdsEquivalentForIdentityCore(liveChatId, normalizedStateChatId, {
|
||||
identity: activeIdentity,
|
||||
aliasCandidates,
|
||||
})
|
||||
) {
|
||||
return {
|
||||
shouldRepair: false,
|
||||
reason: "live-chat-mismatch",
|
||||
chatId: normalizedStateChatId,
|
||||
liveChatId,
|
||||
};
|
||||
}
|
||||
|
||||
const normalizedMarkerChatId = normalizeIdentityValue(markerChatId);
|
||||
if (normalizedMarkerChatId && normalizedMarkerChatId !== normalizedStateChatId) {
|
||||
return {
|
||||
shouldRepair: false,
|
||||
reason: "commit-marker-chat-mismatch",
|
||||
chatId: normalizedStateChatId,
|
||||
markerChatId: normalizedMarkerChatId,
|
||||
};
|
||||
}
|
||||
|
||||
return { shouldRepair: true, reason: "repair", chatId: normalizedStateChatId };
|
||||
}
|
||||
Reference in New Issue
Block a user