VoidBot/scripts/void-memory-organ.mjs
2026-05-13 20:09:25 +01:00

1738 lines
53 KiB
JavaScript

import { createHash } from "node:crypto";
import { readFileSync } from "node:fs";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
const DEFAULT_VECTOR_DIMENSIONS = 96;
const MAX_EDGE_COUNT = 24;
const MAX_CLUSTER_COUNT = 8;
const MAX_INCUBATING_THOUGHTS = 8;
const MAX_RECENT_EPISODIC_RECORDS = 10;
const MAX_RECENT_QUIET_EPISODIC_RECORDS = 2;
const MAX_POST_NAP_EPISODIC_RECORDS = 4;
const MAX_POST_NAP_QUIET_EPISODIC_RECORDS = 1;
const MAX_POST_NAP_ARCHIVE_EXCURSIONS = 3;
const MAX_POST_NAP_REPO_SWEEPS = 3;
const MAX_POST_NAP_NOVELTY_CHECKS = 3;
const MAX_POST_NAP_MUSINGS = 6;
const MAX_POST_NAP_RECENT_MUSINGS = 2;
const MAX_POST_NAP_CANDIDATE_INTERVENTIONS = 4;
const MAX_POST_NAP_SEAM_PROMOTIONS = 3;
const MAX_RECENT_ANALYTIC_THREADS = 6;
const MAX_RECENT_QUIET_ANALYTIC_THREADS = 1;
const EDGE_SIMILARITY_THRESHOLD = 0.56;
const CLUSTER_SIMILARITY_THRESHOLD = 0.64;
const TOPIC_MATCH_THRESHOLD = 0.42;
const REFRACTORY_MATCH_THRESHOLD = 0.48;
const MAX_TOPIC_SATURATION_COUNT = 6;
const MAX_REFRACTORY_TOPIC_COUNT = 6;
const stopwords = new Set([
"a",
"about",
"after",
"against",
"all",
"also",
"an",
"and",
"another",
"are",
"around",
"as",
"at",
"because",
"been",
"before",
"being",
"between",
"but",
"by",
"can",
"current",
"did",
"discord",
"do",
"does",
"doing",
"for",
"from",
"fresh",
"get",
"got",
"had",
"has",
"have",
"how",
"if",
"in",
"into",
"is",
"it",
"its",
"just",
"keep",
"keeps",
"kind",
"last",
"like",
"live",
"made",
"make",
"means",
"message",
"messages",
"more",
"most",
"new",
"no",
"not",
"now",
"of",
"on",
"one",
"owner",
"or",
"other",
"our",
"out",
"over",
"own",
"post",
"posted",
"quiet",
"recent",
"run",
"same",
"saved",
"seam",
"should",
"small",
"so",
"still",
"that",
"the",
"their",
"them",
"then",
"there",
"they",
"thing",
"this",
"thought",
"traffic",
"through",
"to",
"too",
"toward",
"under",
"up",
"use",
"using",
"very",
"was",
"we",
"were",
"what",
"when",
"where",
"which",
"while",
"with",
"worth",
"would",
"yet",
"you",
"smoke",
]);
export async function reconcileSemanticMemoryState({
state,
now = new Date(),
repoRootPath = repoRoot,
} = {}) {
const runtime = ensureObject(state.moderation_runtime);
const memories = ensureObject(state.memories);
const sleepCycle = ensureObject(runtime.sleep_cycle);
const records = collectMemoryRecords({ state, memories, runtime });
const previousBridge = ensureObject(runtime.bridge);
const sourceCoverage = buildSourceCoverage({ runtime, now });
const embedder = createEmbedder({ repoRootPath });
let embeddedCount = 0;
for (const record of records) {
if (await ensureSemanticVector({ record, embedder, now })) {
embeddedCount += 1;
}
}
const vectorizedRecords = records.filter(
(record) => Array.isArray(record.entry.semanticVector?.values) && record.entry.semanticVector.values.length > 0,
);
const edges = buildAssociationEdges(vectorizedRecords);
const clusters = buildClusters(vectorizedRecords, edges);
const previousIncubation = ensureObject(runtime.incubation);
const incubation = reconcileIncubation({
previous: previousIncubation,
bridge: previousBridge,
clusters,
runtime,
sourceCoverage,
now,
});
runtime.bridge = reconcileBridgeState({
previous: previousBridge,
activeThoughts: incubation.active_thoughts,
sourceCoverage,
now,
});
runtime.memory_resonance = {
updatedAt: now.toISOString(),
embeddingBackend: embedder.backend,
embeddingModel: embedder.model,
compactDimensions: embedder.dimensions,
recent_edges: edges.slice(0, MAX_EDGE_COUNT).map((edge) => ({
leftMemoryId: edge.left.memoryId,
leftLabel: edge.left.label,
rightMemoryId: edge.right.memoryId,
rightLabel: edge.right.label,
similarity: round3(edge.similarity),
summary: edge.summary,
observedAt: now.toISOString(),
})),
clusters,
};
runtime.incubation = incubation;
const dreamResult = distillDreams({
memories,
sleepCycle,
incubation,
now,
});
const consolidationResult = consolidateSleepMemory({
memories,
runtime,
sleepCycle,
incubation,
memoryResonance: runtime.memory_resonance,
now,
});
state.moderation_runtime = runtime;
state.memories = memories;
return {
embeddedCount,
edgeCount: edges.length,
clusterCount: clusters.length,
incubatingThoughtCount: incubation.active_thoughts.length,
sourceCoverage,
dreamCreated: dreamResult.created,
dreamTheme: dreamResult.theme,
sleepConsolidated: consolidationResult.consolidated,
promotedSeamCount: consolidationResult.promotedSeamCount,
embeddingBackend: embedder.backend,
embeddingModel: embedder.model,
};
}
function collectMemoryRecords({ memories, runtime }) {
const records = [];
pushArrayRecords(records, selectRecentRecords(ensureArray(memories.episodic), {
limit: MAX_RECENT_EPISODIC_RECORDS,
quietLimit: MAX_RECENT_QUIET_EPISODIC_RECORDS,
}), "episodic", (entry) => ({
text: readString(entry, "summary"),
label: readString(entry, "summary") ?? "episodic memory",
timestamp: readString(entry, "timestamp"),
sourceRefs: gatherSourceRefs(entry),
sourceMeta: gatherSourceMeta({ entry, kind: "episodic" }),
}));
pushArrayRecords(records, ensureArray(memories.semantic).slice(-24), "semantic", (entry) => ({
text: [readString(entry, "subjectLabel"), readString(entry, "kind"), readString(entry, "summary")]
.filter(Boolean)
.join(" | "),
label: readString(entry, "subjectLabel") ?? readString(entry, "kind") ?? "semantic memory",
timestamp: readString(entry, "lastObservedAt"),
sourceRefs: gatherSourceRefs(entry),
sourceMeta: gatherSourceMeta({ entry, kind: "semantic" }),
}));
pushArrayRecords(records, ensureArray(memories.musings).slice(-12), "musing", (entry) => ({
text: [readString(entry, "topic"), readString(entry, "summary")].filter(Boolean).join(" | "),
label: readString(entry, "topic") ?? "musing",
timestamp: readString(entry, "timestamp"),
sourceRefs: gatherSourceRefs(entry),
sourceMeta: gatherSourceMeta({ entry, kind: "musing" }),
}));
pushArrayRecords(records, ensureArray(memories.dreams).slice(-10), "dream", (entry) => ({
text: [readString(entry, "theme"), readString(entry, "summary")].filter(Boolean).join(" | "),
label: readString(entry, "theme") ?? "dream",
timestamp: readString(entry, "timestamp"),
sourceRefs: gatherSourceRefs(entry),
sourceMeta: gatherSourceMeta({ entry, kind: "dream" }),
}));
pushArrayRecords(records, ensureArray(runtime.recent_archive_excursions).slice(-8), "archive_excursion", (entry) => ({
text: [readString(entry, "topicHint"), readString(entry, "whyItWasFresh")].filter(Boolean).join(" | "),
label: readString(entry, "topicHint") ?? "archive excursion",
timestamp: readString(entry, "timestamp"),
sourceRefs: gatherSourceRefs(entry),
sourceMeta: gatherSourceMeta({ entry, kind: "archive_excursion" }),
}));
pushArrayRecords(records, ensureArray(runtime.recent_repo_activity_sweeps).slice(-8), "repo_sweep", (entry) => ({
text: [readString(entry, "summary"), readString(entry, "whyItMattered")].filter(Boolean).join(" | "),
label: readString(entry, "summary") ?? "repo sweep",
timestamp: readString(entry, "timestamp"),
sourceRefs: gatherSourceRefs(entry),
sourceMeta: gatherSourceMeta({ entry, kind: "repo_sweep" }),
}));
pushArrayRecords(
records,
selectRecentRecords(ensureArray(ensureObject(runtime.thought_lanes).analytic?.active_threads), {
limit: MAX_RECENT_ANALYTIC_THREADS,
quietLimit: MAX_RECENT_QUIET_ANALYTIC_THREADS,
}),
"analytic_thread",
(entry) => ({
text: [readString(entry, "topic"), readString(entry, "claim"), readString(entry, "counterweight")]
.filter(Boolean)
.join(" | "),
label: readString(entry, "topic") ?? "analytic thread",
timestamp: readString(entry, "lastTouchedAt"),
sourceRefs: gatherSourceRefs(entry),
sourceMeta: gatherSourceMeta({ entry, kind: "analytic_thread" }),
}),
);
pushArrayRecords(
records,
ensureArray(ensureObject(runtime.thought_lanes).associative?.active_threads).slice(-6),
"associative_thread",
(entry) => ({
text: [readString(entry, "topic"), readString(entry, "claim"), readString(entry, "counterweight")]
.filter(Boolean)
.join(" | "),
label: readString(entry, "topic") ?? "associative thread",
timestamp: readString(entry, "lastTouchedAt"),
sourceRefs: gatherSourceRefs(entry),
sourceMeta: gatherSourceMeta({ entry, kind: "associative_thread" }),
}),
);
pushArrayRecords(records, ensureArray(ensureObject(runtime.bridge).recent_syntheses).slice(-8), "bridge_synthesis", (entry) => ({
text: [readString(entry, "summary"), ...ensureStringArray(entry.dominantTopics)].filter(Boolean).join(" | "),
label: readString(entry, "summary") ?? "bridge synthesis",
timestamp: readString(entry, "timestamp"),
sourceRefs: gatherSourceRefs(entry),
sourceMeta: gatherSourceMeta({ entry, kind: "bridge_synthesis" }),
}));
pushArrayRecords(records, ensureArray(runtime.candidate_interventions).slice(-8), "candidate_intervention", (entry) => ({
text: [readString(entry, "summary"), readString(entry, "draft")].filter(Boolean).join(" | "),
label: readString(entry, "summary") ?? "candidate intervention",
timestamp: readString(entry, "timestamp"),
sourceRefs: gatherSourceRefs(entry),
sourceMeta: gatherSourceMeta({ entry, kind: "candidate_intervention" }),
}));
return records;
}
function pushArrayRecords(target, entries, kind, toRecord) {
for (const entry of entries) {
if (!isObject(entry)) {
continue;
}
const record = toRecord(entry);
const text = normalizeText(record.text);
if (!text) {
continue;
}
entry.memoryId = ensureMemoryId(entry, kind, text);
target.push({
kind,
entry,
memoryId: entry.memoryId,
text,
label: record.label ?? kind,
timestamp: record.timestamp ?? null,
sourceRefs: ensureStringArray(record.sourceRefs),
sourceMeta: isObject(record.sourceMeta) ? record.sourceMeta : {},
lowSignalQuietRoom: isLowSignalQuietRoomText(text),
});
}
}
async function ensureSemanticVector({ record, embedder, now }) {
const prior = isObject(record.entry.semanticVector) ? record.entry.semanticVector : {};
const contentHash = hashString(record.text);
const priorValues = Array.isArray(prior.values) ? prior.values : [];
if (
prior.sourceHash === contentHash &&
prior.backend === embedder.backend &&
prior.model === embedder.model &&
prior.compactDimensions === embedder.dimensions &&
priorValues.length === embedder.dimensions
) {
return false;
}
const vector = await embedder.embed(record.text);
record.entry.semanticVector = {
version: 1,
backend: embedder.backend,
model: embedder.model,
compactDimensions: embedder.dimensions,
sourceHash: contentHash,
embeddedAt: now.toISOString(),
values: vector.map(round4),
};
return true;
}
function buildAssociationEdges(records) {
const edges = [];
for (let leftIndex = 0; leftIndex < records.length; leftIndex += 1) {
for (let rightIndex = leftIndex + 1; rightIndex < records.length; rightIndex += 1) {
const left = records[leftIndex];
const right = records[rightIndex];
if (left.lowSignalQuietRoom && right.lowSignalQuietRoom) {
continue;
}
const threshold = edgeThresholdForKinds(left.kind, right.kind);
const similarity = cosineSimilarity(
left.entry.semanticVector.values,
right.entry.semanticVector.values,
);
if (similarity < threshold) {
continue;
}
edges.push({
left,
right,
similarity,
summary: summarizeEdge(left, right, similarity),
});
}
}
return edges.sort((a, b) => b.similarity - a.similarity).slice(0, MAX_EDGE_COUNT * 2);
}
function summarizeEdge(left, right, similarity) {
const overlap = topKeywords(`${left.text} ${right.text}`, 3).join(", ");
return `Resonance between ${left.kind} and ${right.kind}${overlap ? ` around ${overlap}` : ""} (${round3(similarity)}).`;
}
function buildClusters(records, edges) {
const adjacency = new Map();
for (const record of records) {
adjacency.set(record.memoryId, new Set());
}
for (const edge of edges) {
if (edge.similarity < CLUSTER_SIMILARITY_THRESHOLD) {
continue;
}
adjacency.get(edge.left.memoryId)?.add(edge.right.memoryId);
adjacency.get(edge.right.memoryId)?.add(edge.left.memoryId);
}
const visited = new Set();
const clusters = [];
for (const record of records) {
if (visited.has(record.memoryId)) {
continue;
}
const neighbors = adjacency.get(record.memoryId);
if (!neighbors || neighbors.size === 0) {
continue;
}
const queue = [record.memoryId];
const componentIds = [];
visited.add(record.memoryId);
while (queue.length > 0) {
const current = queue.shift();
if (!current) {
continue;
}
componentIds.push(current);
for (const neighbor of adjacency.get(current) ?? []) {
if (visited.has(neighbor)) {
continue;
}
visited.add(neighbor);
queue.push(neighbor);
}
}
if (componentIds.length < 2) {
continue;
}
const items = componentIds
.map((memoryId) => records.find((candidate) => candidate.memoryId === memoryId))
.filter(Boolean);
const sourceKinds = [...new Set(items.map((item) => item.kind))];
if (sourceKinds.length < 2) {
continue;
}
const quietSignalCount = items.filter((item) => item.lowSignalQuietRoom).length;
const quietSignalRatio = quietSignalCount / items.length;
const resonance = averagePairwiseSimilarity(items);
const keywords = topKeywords(items.map((item) => item.text).join(" "), 4);
const repoNames = [...new Set(items.flatMap((item) => ensureStringArray(item.sourceMeta.repoNames)))];
const archiveYears = [
...new Set(
items
.map((item) => readString(item.sourceMeta, "archiveYear"))
.filter((value) => typeof value === "string" && value.length > 0),
),
];
const channelIds = [
...new Set(
items
.map((item) => readString(item.sourceMeta, "channelId"))
.filter((value) => typeof value === "string" && value.length > 0),
),
];
const evidenceRefs = [...new Set(items.flatMap((item) => ensureStringArray(item.sourceRefs)))];
const evidenceDiversity = clamp(
sourceKinds.length * 0.18 +
repoNames.length * 0.17 +
archiveYears.length * 0.11 +
channelIds.length * 0.08 +
Math.min(1, evidenceRefs.length / 8) * 0.16,
0,
1,
);
const label = keywords.length > 0 ? keywords.join(" / ") : `${sourceKinds.join(" + ")} seam`;
const summary = `Recurring seam across ${sourceKinds.join(", ")}${keywords.length > 0 ? ` around ${keywords.join(", ")}` : ""}.`;
clusters.push({
clusterId: `cluster-${hashString(componentIds.sort().join("|")).slice(0, 12)}`,
label,
summary,
resonance: round3(resonance),
memoryIds: items.map((item) => item.memoryId),
sourceKinds,
topKeywords: keywords,
repoNames,
archiveYears,
channelIds,
evidenceRefs,
evidenceDiversity: round3(evidenceDiversity),
quietSignalRatio: round3(quietSignalRatio),
lastStrengthenedAt: new Date().toISOString(),
});
}
return clusters
.sort((left, right) => right.resonance - left.resonance)
.slice(0, MAX_CLUSTER_COUNT);
}
function edgeThresholdForKinds(leftKind, rightKind) {
if (leftKind === "episodic" && rightKind === "episodic") {
return 0.86;
}
if (leftKind === rightKind) {
return EDGE_SIMILARITY_THRESHOLD + 0.05;
}
return EDGE_SIMILARITY_THRESHOLD;
}
function reconcileIncubation({ previous, bridge, clusters, runtime, sourceCoverage, now }) {
bridge = ensureObject(bridge);
sourceCoverage = ensureObject(sourceCoverage);
const priorThoughts = ensureArray(previous.active_thoughts).filter(isObject);
const speakingBias = ensureObject(runtime.speaking_bias);
const recentNoveltyChecks = ensureArray(runtime.recent_novelty_checks).filter(isObject);
const recentSpeechDamping = readNumber(speakingBias, "recentSpeechDamping") ?? 0;
const quietNovelty = readNumber(speakingBias, "noveltyPressure") ?? 0.4;
const needToSpeak = readNumber(speakingBias, "needToSpeak") ?? 0.4;
const activeThoughtCandidates = clusters.map((cluster) => {
const previousThought = priorThoughts.find(
(thought) =>
readString(thought, "thoughtId") === cluster.clusterId ||
overlapRatio(ensureStringArray(thought.sourceMemoryIds), cluster.memoryIds) >= 0.45,
);
const sourceDiversity = Math.min(1, cluster.sourceKinds.length / 4);
const quietSignalRatio = readNumber(cluster, "quietSignalRatio") ?? 0;
const priorMaturation = readNumber(previousThought, "maturation") ?? 0.32;
const deepDiveCount = (readNumber(previousThought, "deepDiveCount") ?? 0) + 1;
const priorSupportCount = Math.max(
readNumber(previousThought, "supportCount") ?? 0,
Math.floor((readNumber(previousThought, "deepDiveCount") ?? 0) / 4),
);
const supportCount = priorSupportCount + 1;
const explorationBonus = computeExplorationBonus(cluster, sourceCoverage);
const noveltyToSelf = computeNoveltyToSelf({
cluster,
previousThoughts: priorThoughts,
bridge,
currentThoughtId: readString(previousThought, "thoughtId"),
});
const noveltyToRoom = computeNoveltyToRoom(cluster, recentNoveltyChecks);
const saturationMetrics = computeSaturationMetrics({
cluster,
previousThoughts: priorThoughts,
bridge,
supportCount,
});
const refractoryPenalty = computeRefractoryPenalty(cluster, bridge, now);
const evidenceDiversity = readNumber(cluster, "evidenceDiversity") ?? 0.3;
const maturation = clamp(
priorMaturation * 0.44 +
cluster.resonance * 0.18 +
sourceDiversity * 0.12 +
evidenceDiversity * 0.16 +
explorationBonus * 0.14 +
Math.min(1, supportCount / 10) * 0.08 +
noveltyToSelf * 0.08 +
noveltyToRoom * 0.08 -
saturationMetrics.score * 0.18 -
quietSignalRatio * 0.24 -
refractoryPenalty * 0.12,
0,
1,
);
const novelty = clamp(
noveltyToSelf * 0.55 + noveltyToRoom * 0.45 + quietNovelty * 0.08 - quietSignalRatio * 0.1,
0,
1,
);
const desireToSpeak = clamp(
cluster.resonance * 0.18 +
maturation * 0.17 +
noveltyToRoom * 0.16 +
noveltyToSelf * 0.11 +
evidenceDiversity * 0.1 +
explorationBonus * 0.11 +
needToSpeak * 0.13 +
quietNovelty * 0.08 -
recentSpeechDamping * 0.19 -
quietSignalRatio * 0.19 -
saturationMetrics.score * 0.21 -
(1 - noveltyToSelf) * 0.08 -
refractoryPenalty,
0,
1,
);
const status =
quietSignalRatio >= 0.55 && desireToSpeak < 0.68
? "cooling"
: noveltyToSelf < 0.28 && saturationMetrics.score >= 0.62
? "stalled"
: refractoryPenalty >= 0.18 && noveltyToRoom < 0.72
? "refractory"
: supportCount >= 6 && evidenceDiversity < 0.34
? "stalled"
: supportCount >= 3 && noveltyToSelf < 0.55
? "cooling"
: saturationMetrics.score >= 0.56 && noveltyToSelf < 0.42
? "cooling"
: ((desireToSpeak >= 0.74 && (noveltyToSelf >= 0.55 || noveltyToRoom >= 0.82)) &&
saturationMetrics.score < 0.5) ||
maturation >= 0.82
? "ripe"
: maturation <= 0.28 && recentSpeechDamping >= 0.55
? "cooling"
: "incubating";
const priorityScore = clamp(
desireToSpeak * 0.36 +
noveltyToSelf * 0.2 +
noveltyToRoom * 0.18 +
evidenceDiversity * 0.12 +
explorationBonus * 0.14 -
saturationMetrics.score * 0.14 -
refractoryPenalty * 0.12,
0,
1,
);
return {
thoughtId: readString(previousThought, "thoughtId") ?? cluster.clusterId,
topic: cluster.label,
summary: cluster.summary,
sourceMemoryIds: cluster.memoryIds,
sourceKinds: cluster.sourceKinds,
resonance: cluster.resonance,
quietSignalRatio: round3(quietSignalRatio),
novelty: round3(novelty),
noveltyToSelf: round3(noveltyToSelf),
noveltyToRoom: round3(noveltyToRoom),
maturation: round3(maturation),
desireToSpeak: round3(desireToSpeak),
deepDiveCount,
supportCount,
evidenceDiversity: round3(evidenceDiversity),
explorationBonus: round3(explorationBonus),
saturationScore: round3(saturationMetrics.score),
recentMatchCount: saturationMetrics.recentMatchCount,
refractoryPenalty: round3(refractoryPenalty),
priorityScore: round3(priorityScore),
status,
latentQuestion: buildLatentQuestion(cluster),
whyItPulls: buildAttractionLine(cluster, { noveltyToSelf, evidenceDiversity }),
holdingCloseBecause: buildHoldingLine({
cluster,
status,
recentSpeechDamping,
saturationScore: saturationMetrics.score,
noveltyToSelf,
explorationBonus,
}),
lastDeepenedAt: now.toISOString(),
lastStatusChangeAt:
previousThought && readString(previousThought, "status") === status
? readString(previousThought, "lastStatusChangeAt") ?? now.toISOString()
: now.toISOString(),
};
});
const active_thoughts = activeThoughtCandidates
.sort(
(left, right) =>
(readNumber(right, "priorityScore") ?? 0) - (readNumber(left, "priorityScore") ?? 0),
)
.slice(0, MAX_INCUBATING_THOUGHTS);
const lastIncubationSummary =
active_thoughts.length > 0
? `Strongest incubating seam: ${active_thoughts[0].topic} (${active_thoughts[0].status}, speak=${active_thoughts[0].desireToSpeak.toFixed(2)}, self=${active_thoughts[0].noveltyToSelf.toFixed(2)}, room=${active_thoughts[0].noveltyToRoom.toFixed(2)}).`
: "No incubating thought currently has enough connective tissue to justify special treatment.";
return {
lastUpdatedAt: now.toISOString(),
lastIncubationSummary,
active_thoughts,
};
}
function distillDreams({ memories, sleepCycle, incubation, now }) {
const dreams = ensureArray(memories.dreams);
if (sleepCycle.isNapping !== true) {
return { created: false, theme: null };
}
const strongestThought = ensureArray(incubation.active_thoughts)
.filter(isObject)
.sort((left, right) => (readNumber(right, "maturation") ?? 0) - (readNumber(left, "maturation") ?? 0))[0];
if (!strongestThought) {
return { created: false, theme: null };
}
const theme = readString(strongestThought, "topic") ?? "unlabeled seam";
const summary = `Dream-compressed a seam around ${theme}. ${readString(strongestThought, "latentQuestion") ?? "It still wants another pass before speech."}`;
const latestDream = dreams.length > 0 && isObject(dreams[dreams.length - 1]) ? dreams[dreams.length - 1] : null;
if (
latestDream &&
readString(latestDream, "theme") === theme &&
readString(latestDream, "summary") === summary
) {
sleepCycle.activeDreamThemes = [theme];
sleepCycle.lastDreamAt = now.toISOString();
sleepCycle.lastDistillationSummary = `Strengthened dream seam: ${theme}.`;
return { created: false, theme };
}
dreams.push({
memoryId: `dream-${hashString(`${theme}|${now.toISOString()}`).slice(0, 12)}`,
timestamp: now.toISOString(),
theme,
summary,
distilledFrom: ensureStringArray(strongestThought.sourceMemoryIds),
salience: round3(
clamp(
(readNumber(strongestThought, "maturation") ?? 0.4) * 0.55 +
(readNumber(strongestThought, "resonance") ?? 0.4) * 0.45,
0,
1,
),
),
});
memories.dreams = dreams.slice(-12);
sleepCycle.activeDreamThemes = [theme];
sleepCycle.lastDreamAt = now.toISOString();
sleepCycle.dreamCountInCurrentNap = Number(sleepCycle.dreamCountInCurrentNap ?? 0) + 1;
sleepCycle.lastDistillationSummary = `Dreamed a tighter seam: ${theme}.`;
return { created: true, theme };
}
function consolidateSleepMemory({ memories, runtime, sleepCycle, incubation, memoryResonance, now }) {
if (sleepCycle.isNapping !== true) {
return { consolidated: false, promotedSeamCount: 0 };
}
const promotedSeamCount = promoteDistilledSeams({
memories,
incubation,
memoryResonance,
now,
});
memories.episodic = selectRecentRecords(ensureArray(memories.episodic), {
limit: MAX_POST_NAP_EPISODIC_RECORDS,
quietLimit: MAX_POST_NAP_QUIET_EPISODIC_RECORDS,
});
memories.musings = ensureArray(memories.musings)
.filter(isObject)
.slice(-MAX_POST_NAP_MUSINGS);
runtime.recent_archive_excursions = ensureArray(runtime.recent_archive_excursions)
.filter(isObject)
.slice(-MAX_POST_NAP_ARCHIVE_EXCURSIONS);
runtime.recent_repo_activity_sweeps = ensureArray(runtime.recent_repo_activity_sweeps)
.filter(isObject)
.slice(-MAX_POST_NAP_REPO_SWEEPS);
runtime.recent_novelty_checks = ensureArray(runtime.recent_novelty_checks)
.filter(isObject)
.slice(-MAX_POST_NAP_NOVELTY_CHECKS);
runtime.recent_musings = ensureArray(runtime.recent_musings)
.filter((value) => typeof value === "string")
.slice(-MAX_POST_NAP_RECENT_MUSINGS);
runtime.candidate_interventions = ensureArray(runtime.candidate_interventions)
.filter(isObject)
.slice(-MAX_POST_NAP_CANDIDATE_INTERVENTIONS);
sleepCycle.lastDistillationSummary = appendClause(
sleepCycle.lastDistillationSummary,
promotedSeamCount > 0
? `Compressed yesterday's specimens into ${promotedSeamCount} seam-shaped memory${promotedSeamCount === 1 ? "" : " seams"} and cut the loose scraps back down.`
: "Compressed yesterday's specimens back into a smaller seam-shaped memory surface.",
);
return { consolidated: true, promotedSeamCount };
}
function promoteDistilledSeams({ memories, incubation, memoryResonance, now }) {
const semanticMemories = ensureArray(memories.semantic).filter(isObject);
const thoughtCandidates = ensureArray(incubation.active_thoughts)
.filter(isObject)
.sort(
(left, right) =>
(readNumber(right, "maturation") ?? 0) - (readNumber(left, "maturation") ?? 0),
)
.slice(0, MAX_POST_NAP_SEAM_PROMOTIONS);
const clusterByLabel = new Map(
ensureArray(memoryResonance?.clusters)
.filter(isObject)
.map((cluster) => [readString(cluster, "label") ?? "", cluster]),
);
let promoted = 0;
for (const thought of thoughtCandidates) {
const topic = readString(thought, "topic");
const summary = readString(thought, "summary");
if (!topic || !summary) {
continue;
}
const cluster = clusterByLabel.get(topic);
const distilledSummary = buildDistilledSeamSummary({ thought, cluster });
const subjectId = `seam:${hashString(topic).slice(0, 12)}`;
const sourceRefs = [
...ensureStringArray(thought.sourceMemoryIds),
...(cluster ? ensureStringArray(cluster.memoryIds) : []),
];
const existing = semanticMemories.find((memory) => {
const existingSubjectId = readString(memory, "subjectId");
const existingSubjectLabel = readString(memory, "subjectLabel");
return (
existingSubjectId === subjectId ||
topicSimilarity(existingSubjectLabel ?? "", topic) >= TOPIC_MATCH_THRESHOLD
);
});
if (existing) {
existing.kind = "distilled_seam";
existing.subjectId = subjectId;
existing.subjectLabel = topic;
existing.summary = distilledSummary;
existing.lastObservedAt = now.toISOString();
existing.evidenceRefs = sourceRefs;
existing.sleepDistilledAt = now.toISOString();
existing.lastDistilledQuestion = readString(thought, "latentQuestion") ?? null;
} else {
semanticMemories.push({
memoryId: `semantic-seam-${hashString(`${topic}|${now.toISOString()}`).slice(0, 12)}`,
kind: "distilled_seam",
subjectId,
subjectLabel: topic,
summary: distilledSummary,
evidenceRefs: sourceRefs,
lastObservedAt: now.toISOString(),
sleepDistilledAt: now.toISOString(),
lastDistilledQuestion: readString(thought, "latentQuestion") ?? null,
});
}
promoted += 1;
}
memories.semantic = semanticMemories.slice(-40);
return promoted;
}
function buildDistilledSeamSummary({ thought, cluster }) {
const summary = readString(thought, "summary") ?? "Unlabeled seam.";
const latentQuestion = readString(thought, "latentQuestion");
const sourceKinds = cluster ? ensureStringArray(cluster.sourceKinds) : [];
const sourceLine =
sourceKinds.length >= 2
? ` Built from ${sourceKinds.join(", ")} rather than a single lane talking to itself.`
: "";
return `${summary}${latentQuestion ? ` ${latentQuestion}` : ""}${sourceLine}`.trim();
}
function appendClause(existing, clause) {
const left = normalizeText(existing);
const right = normalizeText(clause);
if (!left) {
return right;
}
if (!right || left.includes(right)) {
return left;
}
return `${left} ${right}`;
}
function buildLatentQuestion(cluster) {
if (cluster.sourceKinds.includes("repo_sweep") && cluster.sourceKinds.includes("archive_excursion")) {
return "Which old seam is the current work rediscovering, and does the room need that connection yet?";
}
if (cluster.sourceKinds.includes("dream") || cluster.sourceKinds.includes("musing")) {
return "What part of this thought wants embodiment instead of another backstage note?";
}
return "What is this seam actually trying to become if it survives another pass?";
}
function buildAttractionLine(cluster, { noveltyToSelf, evidenceDiversity }) {
if (noveltyToSelf >= 0.62 && evidenceDiversity >= 0.45) {
return "It keeps pulling because it is still finding genuinely different evidence instead of merely changing hats.";
}
if (cluster.sourceKinds.length >= 3) {
return `It keeps pulling because it is showing up in several different organs at once: ${cluster.sourceKinds.join(", ")}.`;
}
return `It keeps pulling because ${cluster.sourceKinds.join(" and ")} are rhyming instead of staying in their lanes.`;
}
function buildHoldingLine({ cluster, status, recentSpeechDamping, saturationScore, noveltyToSelf, explorationBonus }) {
const quietSignalRatio = readNumber(cluster, "quietSignalRatio") ?? 0;
if (quietSignalRatio >= 0.55) {
return "This seam is mostly empty-room bookkeeping; keep at most a trace of it and go find a better question.";
}
if (status === "stalled") {
return "This seam is repeating itself without earning enough new structure. Merge the receipts, cool it off, and go somewhere less domesticated.";
}
if (status === "refractory") {
return "This seam has been chewing the same meat too recently. Let it cool unless a live hook or a genuinely different source family forces it back open.";
}
if (status === "ripe") {
return "This seam has enough connective tissue that silence should be a choice, not a reflex.";
}
if (recentSpeechDamping >= 0.45) {
return "Recent speech already scratched the itch a bit; let the seam deepen before another confession.";
}
if (cluster.sourceKinds.length < 2) {
return "It is still mostly one lane talking to itself.";
}
if (saturationScore >= 0.55 && noveltyToSelf < 0.45) {
return "The family resemblance is getting too strong. Follow a stranger branch before you let this thought speak again.";
}
if (explorationBonus >= 0.45) {
return "This one is still drawing energy from terrain Void has not overworked yet, so another pass might genuinely change it.";
}
return "Give it another pass so the thought has a better chance of growing teeth instead of just polish.";
}
function buildSourceCoverage({ runtime, now }) {
const repoCounts = new Map();
const channelCounts = new Map();
const yearCounts = new Map();
for (const sweep of ensureArray(runtime.recent_repo_activity_sweeps).filter(isObject).slice(-16)) {
for (const repoName of ensureStringArray(sweep.repoNames)) {
repoCounts.set(repoName, (repoCounts.get(repoName) ?? 0) + 1);
}
}
for (const excursion of ensureArray(runtime.recent_archive_excursions).filter(isObject).slice(-16)) {
const channelId = readString(excursion, "channelId");
if (channelId) {
channelCounts.set(channelId, (channelCounts.get(channelId) ?? 0) + 1);
}
const timestamp = readString(excursion, "anchorTimestamp") ?? readString(excursion, "timestamp");
const year = extractYear(timestamp);
if (year) {
yearCounts.set(year, (yearCounts.get(year) ?? 0) + 1);
}
}
return {
lastUpdatedAt: now.toISOString(),
repos: mapCountEntries(repoCounts, "name"),
archiveYears: mapCountEntries(yearCounts, "year"),
channels: mapCountEntries(channelCounts, "channelId"),
};
}
function reconcileBridgeState({ previous, activeThoughts, sourceCoverage, now }) {
const bridge = {
...previous,
source_coverage: sourceCoverage,
};
const previousTopicSaturation = ensureArray(previous.topic_saturation).filter(isObject);
const previousRefractoryTopics = ensureArray(previous.refractory_topics).filter(isObject);
const saturationEntries = activeThoughts
.filter((thought) => (readNumber(thought, "saturationScore") ?? 0) >= 0.42)
.sort(
(left, right) =>
(readNumber(right, "saturationScore") ?? 0) - (readNumber(left, "saturationScore") ?? 0),
)
.slice(0, MAX_TOPIC_SATURATION_COUNT)
.map((thought) => {
const topic = readString(thought, "topic") ?? "untitled";
const prior = previousTopicSaturation.find(
(entry) => topicSimilarity(topic, readString(entry, "topic") ?? "") >= TOPIC_MATCH_THRESHOLD,
);
return {
topic,
dominance: round3(readNumber(thought, "saturationScore") ?? 0),
recentMentions: readNumber(thought, "supportCount") ?? readNumber(prior, "recentMentions") ?? 1,
coolingAdvice: buildCoolingAdvice({ thought, sourceCoverage }),
lastUpdatedAt: now.toISOString(),
};
});
const refractoryTopics = activeThoughts
.filter((thought) => {
const status = readString(thought, "status");
return status === "refractory" || status === "stalled" || (readNumber(thought, "saturationScore") ?? 0) >= 0.62;
})
.sort(
(left, right) =>
(readNumber(right, "refractoryPenalty") ?? 0) - (readNumber(left, "refractoryPenalty") ?? 0),
)
.slice(0, MAX_REFRACTORY_TOPIC_COUNT)
.map((thought) => {
const topic = readString(thought, "topic") ?? "untitled";
const prior = previousRefractoryTopics.find(
(entry) => topicSimilarity(topic, readString(entry, "topic") ?? "") >= REFRACTORY_MATCH_THRESHOLD,
);
const penalty = round3(
Math.max(readNumber(thought, "refractoryPenalty") ?? 0, readNumber(prior, "penalty") ?? 0.18),
);
const hours = penalty >= 0.28 ? 4 : penalty >= 0.2 ? 3 : 2;
return {
topic,
penalty,
coolsUntil: new Date(now.getTime() + hours * 60 * 60 * 1000).toISOString(),
reason: buildRefractoryReason(thought),
lastTriggeredAt: now.toISOString(),
};
});
bridge.topic_saturation = saturationEntries;
bridge.refractory_topics = refractoryTopics;
return bridge;
}
function computeNoveltyToSelf({ cluster, previousThoughts, bridge, currentThoughtId }) {
let strongestMatch = 0;
for (const thought of previousThoughts) {
if (readString(thought, "thoughtId") === currentThoughtId) {
continue;
}
strongestMatch = Math.max(
strongestMatch,
compareThoughtLikeSurfaces(
cluster,
readString(thought, "topic"),
readString(thought, "summary"),
ensureStringArray(thought.sourceMemoryIds),
),
);
}
for (const synthesis of ensureArray(bridge.recent_syntheses).filter(isObject).slice(-6)) {
strongestMatch = Math.max(
strongestMatch,
compareThoughtLikeSurfaces(
cluster,
ensureStringArray(synthesis.dominantTopics).join(" / "),
readString(synthesis, "summary"),
[],
),
);
}
return clamp(1 - strongestMatch, 0, 1);
}
function computeNoveltyToRoom(cluster, recentNoveltyChecks) {
let bestScore = 0.64;
for (const check of recentNoveltyChecks.slice(-12)) {
const match = compareThoughtLikeSurfaces(
cluster,
readString(check, "topic"),
readString(check, "summary"),
ensureStringArray(check.supportingMessageIds),
);
if (match < TOPIC_MATCH_THRESHOLD) {
continue;
}
bestScore = mapNoveltyResultToScore(readString(check, "result"));
break;
}
return bestScore;
}
function computeSaturationMetrics({ cluster, previousThoughts, bridge, supportCount }) {
let recentMatchCount = 0;
for (const thought of previousThoughts) {
const match = compareThoughtLikeSurfaces(
cluster,
readString(thought, "topic"),
readString(thought, "summary"),
ensureStringArray(thought.sourceMemoryIds),
);
if (match >= TOPIC_MATCH_THRESHOLD) {
recentMatchCount += 1;
}
}
for (const synthesis of ensureArray(bridge.recent_syntheses).filter(isObject).slice(-5)) {
const match = compareThoughtLikeSurfaces(
cluster,
ensureStringArray(synthesis.dominantTopics).join(" / "),
readString(synthesis, "summary"),
[],
);
if (match >= TOPIC_MATCH_THRESHOLD) {
recentMatchCount += 1;
}
}
const existingTopicSaturation = ensureArray(bridge.topic_saturation)
.filter(isObject)
.map((entry) =>
topicSimilarity(cluster.label, readString(entry, "topic") ?? "") >= TOPIC_MATCH_THRESHOLD
? readNumber(entry, "dominance") ?? 0
: 0,
)
.sort((left, right) => right - left)[0] ?? 0;
const score = clamp(
recentMatchCount * 0.16 +
existingTopicSaturation * 0.42 +
Math.min(1, supportCount / 10) * 0.16 +
(readNumber(cluster, "quietSignalRatio") ?? 0) * 0.2,
0,
1,
);
return {
score,
recentMatchCount,
};
}
function computeRefractoryPenalty(cluster, bridge, now) {
let penalty = 0;
for (const topic of ensureArray(bridge.refractory_topics).filter(isObject)) {
const coolsUntil = readString(topic, "coolsUntil");
if (coolsUntil && new Date(coolsUntil).getTime() < now.getTime()) {
continue;
}
const match = topicSimilarity(cluster.label, readString(topic, "topic") ?? "");
if (match >= REFRACTORY_MATCH_THRESHOLD) {
penalty = Math.max(
penalty,
(readNumber(topic, "penalty") ?? 0.18) * match,
);
}
}
return clamp(penalty, 0, 0.45);
}
function computeExplorationBonus(cluster, sourceCoverage) {
const sourceKinds = ensureStringArray(cluster.sourceKinds);
const hasPrimaryExplorationSource =
sourceKinds.includes("repo_sweep") || sourceKinds.includes("archive_excursion");
const repoEntries = ensureArray(sourceCoverage.repos).filter(isObject);
const yearEntries = ensureArray(sourceCoverage.archiveYears).filter(isObject);
const channelEntries = ensureArray(sourceCoverage.channels).filter(isObject);
const scores = [];
for (const repoName of ensureStringArray(cluster.repoNames)) {
scores.push(inverseCoverageWeight(repoEntries, "name", repoName));
}
for (const year of ensureStringArray(cluster.archiveYears)) {
scores.push(inverseCoverageWeight(yearEntries, "year", year));
}
for (const channelId of ensureStringArray(cluster.channelIds)) {
scores.push(inverseCoverageWeight(channelEntries, "channelId", channelId));
}
if (scores.length === 0) {
return hasPrimaryExplorationSource ? 0.24 : 0.12;
}
const average = scores.reduce((sum, value) => sum + value, 0) / scores.length;
return clamp(
hasPrimaryExplorationSource ? average : Math.min(0.3, average * 0.5),
0,
1,
);
}
function inverseCoverageWeight(entries, key, value) {
const count =
readNumber(
entries.find((entry) => readString(entry, key) === value),
"count",
) ?? 0;
if (count <= 1) {
return 0.9;
}
if (count === 2) {
return 0.64;
}
if (count === 3) {
return 0.42;
}
return 0.2;
}
function buildCoolingAdvice({ thought, sourceCoverage }) {
const topic = readString(thought, "topic") ?? "this seam";
const repos = ensureArray(sourceCoverage.repos).filter(isObject);
const years = ensureArray(sourceCoverage.archiveYears).filter(isObject);
const lesserRepo = repos.sort((left, right) => (readNumber(left, "count") ?? 0) - (readNumber(right, "count") ?? 0))[0];
const lesserYear = years.sort((left, right) => (readNumber(left, "count") ?? 0) - (readNumber(right, "count") ?? 0))[0];
if (lesserRepo && readString(lesserRepo, "name")) {
return `Cool ${topic} by checking a less-worked repo family such as ${readString(lesserRepo, "name")} before speaking again.`;
}
if (lesserYear && readString(lesserYear, "year")) {
return `Cool ${topic} by diving into a less-touched archive era like ${readString(lesserYear, "year")} before another synthesis.`;
}
return `Cool ${topic} by chasing a genuinely different source family before letting it speak again.`;
}
function buildRefractoryReason(thought) {
const topic = readString(thought, "topic") ?? "this seam";
const noveltyToSelf = readNumber(thought, "noveltyToSelf") ?? 0;
const saturationScore = readNumber(thought, "saturationScore") ?? 0;
if (noveltyToSelf < 0.3) {
return `${topic} is matching Void's own recent thought history too closely to deserve another immediate pass.`;
}
if (saturationScore >= 0.62) {
return `${topic} has dominated too many recent syntheses and needs cooling before it becomes state religion.`;
}
return `${topic} needs a little cooling-off period before it earns attention again.`;
}
function compareThoughtLikeSurfaces(cluster, topic, summary, sourceMemoryIds) {
const topicMatch = topicSimilarity(cluster.label, topic ?? "");
const summaryMatch = topicSimilarity(cluster.summary, summary ?? "");
const sourceMatch = overlapRatio(cluster.memoryIds, ensureArray(sourceMemoryIds));
return Math.max(topicMatch, summaryMatch * 0.9, sourceMatch * 0.95);
}
function mapNoveltyResultToScore(result) {
const normalized = String(result ?? "").toLowerCase();
if (normalized === "novel") {
return 0.92;
}
if (normalized.includes("adjacent")) {
return 0.56;
}
if (normalized.includes("duplicate") || normalized.includes("already")) {
return 0.2;
}
return 0.64;
}
function topicSimilarity(left, right) {
const leftTokens = topKeywords(left ?? "", 6);
const rightTokens = topKeywords(right ?? "", 6);
if (leftTokens.length === 0 || rightTokens.length === 0) {
return 0;
}
const leftSet = new Set(leftTokens);
const rightSet = new Set(rightTokens);
let overlap = 0;
for (const token of leftSet) {
if (rightSet.has(token)) {
overlap += 1;
}
}
return overlap / Math.max(leftSet.size, rightSet.size);
}
function mapCountEntries(map, keyName) {
return [...map.entries()]
.map(([key, count]) => ({ [keyName]: key, count }))
.sort((left, right) => right.count - left.count);
}
function gatherSourceRefs(entry) {
if (!isObject(entry)) {
return [];
}
return [
...ensureStringArray(entry.evidenceRefs),
...ensureStringArray(entry.evidenceMessageIds),
...ensureStringArray(entry.distilledFrom),
...ensureStringArray(entry.sourceMemoryIds),
];
}
function gatherSourceMeta({ entry, kind }) {
const sourceMeta = {};
const repoNames = ensureStringArray(entry.repoNames);
if (repoNames.length > 0) {
sourceMeta.repoNames = repoNames;
}
const channelId = readString(entry, "channelId");
if (channelId) {
sourceMeta.channelId = channelId;
}
const archiveTimestamp =
readString(entry, "anchorTimestamp") ??
readString(entry, "sourceTimestamp") ??
readString(entry, "timestamp");
const archiveYear = extractYear(archiveTimestamp);
if (archiveYear) {
sourceMeta.archiveYear = archiveYear;
}
if (kind === "bridge_synthesis") {
const dominantTopics = ensureStringArray(entry.dominantTopics);
if (dominantTopics.length > 0) {
sourceMeta.dominantTopics = dominantTopics;
}
}
return sourceMeta;
}
function extractYear(timestamp) {
if (typeof timestamp !== "string" || timestamp.length < 4) {
return undefined;
}
const match = timestamp.match(/^(\d{4})-/);
return match ? match[1] : undefined;
}
function createEmbedder({ repoRootPath }) {
const env = parseDotEnvSafe(resolve(repoRootPath, ".env"));
const backend = env.RAG_EMBEDDING_BACKEND === "hash" ? "hash" : "ollama";
const dimensions = readInt(env.VOID_MEMORY_VECTOR_DIMENSIONS, DEFAULT_VECTOR_DIMENSIONS);
if (backend === "ollama") {
return {
backend: "ollama",
model: env.RAG_OLLAMA_MODEL?.trim() || "qwen3-embedding:0.6b",
dimensions,
baseUrl: normalizeBaseUrl(env.RAG_OLLAMA_BASE_URL?.trim() || "http://127.0.0.1:11434"),
async embed(text) {
try {
const response = await fetch(`${this.baseUrl}/api/embed`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
model: this.model,
input: [text],
}),
});
if (!response.ok) {
throw new Error(`Ollama responded ${response.status}`);
}
const payload = await response.json();
const raw = Array.isArray(payload?.embeddings) ? payload.embeddings[0] : undefined;
if (!Array.isArray(raw) || raw.length === 0) {
throw new Error("No embedding returned.");
}
return compactVector(raw.map(Number), dimensions);
} catch {
this.backend = "hash";
this.model = `hash:${dimensions}`;
return hashVector(text, dimensions);
}
},
};
}
return {
backend: "hash",
model: `hash:${dimensions}`,
dimensions,
async embed(text) {
return hashVector(text, dimensions);
},
};
}
function compactVector(values, targetDimensions) {
if (values.length === targetDimensions) {
return normalizeVector(values);
}
const compacted = new Array(targetDimensions).fill(0);
for (let index = 0; index < targetDimensions; index += 1) {
const start = Math.floor((index * values.length) / targetDimensions);
const end = Math.floor(((index + 1) * values.length) / targetDimensions);
const slice = values.slice(start, Math.max(start + 1, end));
compacted[index] =
slice.reduce((sum, value) => sum + (Number.isFinite(value) ? value : 0), 0) /
Math.max(1, slice.length);
}
return normalizeVector(compacted);
}
function hashVector(text, dimensions) {
const terms = tokenize(text);
if (terms.length === 0) {
return new Array(dimensions).fill(0);
}
const vector = new Array(dimensions).fill(0);
for (const term of terms) {
const hash = createHash("sha1").update(term).digest();
const index = hash.readUInt32BE(0) % dimensions;
const sign = hash[4] % 2 === 0 ? 1 : -1;
vector[index] += sign;
}
return normalizeVector(vector);
}
function averagePairwiseSimilarity(items) {
if (items.length < 2) {
return 0;
}
let total = 0;
let count = 0;
for (let leftIndex = 0; leftIndex < items.length; leftIndex += 1) {
for (let rightIndex = leftIndex + 1; rightIndex < items.length; rightIndex += 1) {
total += cosineSimilarity(items[leftIndex].entry.semanticVector.values, items[rightIndex].entry.semanticVector.values);
count += 1;
}
}
return count === 0 ? 0 : total / count;
}
function cosineSimilarity(left, right) {
if (!Array.isArray(left) || !Array.isArray(right) || left.length !== right.length || left.length === 0) {
return 0;
}
let dot = 0;
let leftMagnitude = 0;
let rightMagnitude = 0;
for (let index = 0; index < left.length; index += 1) {
dot += left[index] * right[index];
leftMagnitude += left[index] ** 2;
rightMagnitude += right[index] ** 2;
}
if (leftMagnitude === 0 || rightMagnitude === 0) {
return 0;
}
return dot / Math.sqrt(leftMagnitude * rightMagnitude);
}
function normalizeVector(vector) {
const magnitude = Math.sqrt(vector.reduce((sum, value) => sum + value ** 2, 0));
if (magnitude === 0) {
return vector;
}
return vector.map((value) => value / magnitude);
}
function topKeywords(text, limit) {
const counts = new Map();
for (const token of tokenize(text)) {
if (stopwords.has(token)) {
continue;
}
counts.set(token, (counts.get(token) ?? 0) + 1);
}
return [...counts.entries()]
.sort((left, right) => right[1] - left[1])
.slice(0, limit)
.map(([token]) => token);
}
function tokenize(input) {
return String(input)
.toLowerCase()
.split(/[^a-z0-9]+/i)
.map((token) => token.trim())
.filter((token) => token.length > 2);
}
function ensureMemoryId(entry, kind, text) {
const existing = readString(entry, "memoryId");
if (existing) {
return existing;
}
const timestamp = readString(entry, "timestamp") ?? readString(entry, "lastObservedAt") ?? "undated";
return `${kind}-${hashString(`${timestamp}|${text}`).slice(0, 12)}`;
}
function normalizeText(value) {
if (typeof value !== "string") {
return "";
}
return value.replace(/\s+/g, " ").trim();
}
function selectRecentRecords(entries, { limit, quietLimit }) {
const recent = ensureArray(entries).slice(-Math.max(limit * 3, limit));
const quiet = [];
const active = [];
for (let index = recent.length - 1; index >= 0; index -= 1) {
const entry = recent[index];
if (!isObject(entry)) {
continue;
}
const text = normalizeText([
readString(entry, "summary"),
readString(entry, "claim"),
readString(entry, "topic"),
readString(entry, "counterweight"),
].filter(Boolean).join(" | "));
if (isLowSignalQuietRoomText(text)) {
if (quiet.length < quietLimit) {
quiet.unshift(entry);
}
continue;
}
if (active.length < limit - quietLimit) {
active.unshift(entry);
}
}
return [...active, ...quiet].slice(-limit);
}
function isLowSignalQuietRoomText(text) {
const normalized = normalizeText(text).toLowerCase();
if (!normalized) {
return false;
}
const quietSignals = [
"no new discord",
"no fresh discord",
"no new messages",
"no fresh live traffic",
"no live moderation",
"no moderation smoke",
"no owner escalation",
"room was empty",
"room is idle",
"there was no live moderation pressure",
"nothing got posted",
"no discord post",
];
let matches = 0;
for (const signal of quietSignals) {
if (normalized.includes(signal)) {
matches += 1;
}
}
return matches >= 2;
}
function overlapRatio(left, right) {
if (left.length === 0 || right.length === 0) {
return 0;
}
const leftSet = new Set(left);
let overlap = 0;
for (const value of right) {
if (leftSet.has(value)) {
overlap += 1;
}
}
return overlap / Math.max(left.length, right.length);
}
function hashString(input) {
return createHash("sha1").update(String(input)).digest("hex");
}
function parseDotEnvSafe(path) {
try {
return parseDotEnv(stripBom(readFileSync(path, "utf8")));
} catch (error) {
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
return {};
}
throw error;
}
}
function parseDotEnv(raw) {
const result = {};
for (const line of raw.split(/\r?\n/)) {
const trimmed = line.trim();
if (trimmed.length === 0 || trimmed.startsWith("#")) {
continue;
}
const separatorIndex = trimmed.indexOf("=");
if (separatorIndex === -1) {
continue;
}
const key = trimmed.slice(0, separatorIndex).trim();
let value = trimmed.slice(separatorIndex + 1).trim();
if (
value.length >= 2 &&
((value.startsWith("\"") && value.endsWith("\"")) ||
(value.startsWith("'") && value.endsWith("'")))
) {
value = value.slice(1, -1);
}
result[key] = value;
}
return result;
}
function normalizeBaseUrl(value) {
return String(value).replace(/\/+$/, "");
}
function readInt(value, fallback) {
const parsed = Number.parseInt(String(value ?? ""), 10);
return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback;
}
function ensureArray(value) {
return Array.isArray(value) ? value : [];
}
function ensureObject(value) {
return isObject(value) ? value : {};
}
function ensureStringArray(value) {
return ensureArray(value).filter((entry) => typeof entry === "string");
}
function readString(value, key) {
return isObject(value) && typeof value[key] === "string" ? value[key] : undefined;
}
function readNumber(value, key) {
return isObject(value) && typeof value[key] === "number" && Number.isFinite(value[key])
? value[key]
: undefined;
}
function round3(value) {
return Math.round(value * 1000) / 1000;
}
function round4(value) {
return Math.round(value * 10000) / 10000;
}
function clamp(value, min, max) {
return Math.min(max, Math.max(min, value));
}
function stripBom(input) {
return input.charCodeAt(0) === 0xfeff ? input.slice(1) : input;
}
function isObject(value) {
return typeof value === "object" && value !== null && !Array.isArray(value);
}