From 021e34362c43fb710c6928a877411db51ffa6972 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Sat, 16 May 2026 18:12:35 +0100 Subject: [PATCH] Expose memory compensator families --- notes/fresh-workspace-handoff.md | 7 +- notes/voidbot-current-system-map.md | 2 +- notes/voidbot-implementation-plan.md | 4 +- scripts/void-memory-organ-agency.mjs | 821 +---------------- scripts/void-memory-organ-cleanup.mjs | 852 +----------------- scripts/void-memory-organ-identity.mjs | 212 +++++ scripts/void-memory-organ-interventions.mjs | 180 ++++ .../void-memory-organ-legacy-translation.mjs | 429 +++++++++ scripts/void-memory-organ-retention.mjs | 416 +++++++++ scripts/void-memory-organ-utils.mjs | 14 + scripts/void-memory-organ-value-pressure.mjs | 431 +++++++++ state/evidence.jsonl | 1 + state/map.yaml | 4 +- 13 files changed, 1714 insertions(+), 1659 deletions(-) create mode 100644 scripts/void-memory-organ-identity.mjs create mode 100644 scripts/void-memory-organ-interventions.mjs create mode 100644 scripts/void-memory-organ-legacy-translation.mjs create mode 100644 scripts/void-memory-organ-retention.mjs create mode 100644 scripts/void-memory-organ-value-pressure.mjs diff --git a/notes/fresh-workspace-handoff.md b/notes/fresh-workspace-handoff.md index 98626bf..741394f 100644 --- a/notes/fresh-workspace-handoff.md +++ b/notes/fresh-workspace-handoff.md @@ -31,8 +31,9 @@ Git history and smoke artifacts carry routine proof. `state/evidence.jsonl` shou - `scripts/run-void-moderator-rumination.ps1` now records reviewed Discord cursors and speech delivery receipts through typed self-state operations. The old `set-cursor` and `record-delivery-receipt` commands were removed from `scripts/moderation-state-store.mjs`. - The unattended moderation child no longer gets to commit whole-state JSON edits. It reads `.voidbot/private/moderation-agent-state.json` as a compatibility projection, writes proposed operation payloads to `.voidbot/status/moderation-rumination-operations.json`, and the parent runner applies them through `scripts/void-self-state.mjs apply-operation`. - `scripts/simulate-void-mood.mjs` now sends sleep-cycle and speaking-pressure results through typed operations after its legacy save, so scheduled-runtime typed documents stay current. The memory organ itself still mutates the legacy monolith and remains the next major surgery. -- `scripts/void-memory-organ.mjs` is now a thin orchestrator. Shared limits/text sets, generic utilities, vector/embedder code, memory-record intake, graph/cluster synthesis, historical cleanup/translation, incubation/bridge scoring, sleep/distillation, legacy mirror repair, and agency/intervention policy live in `scripts/void-memory-organ-constants.mjs`, `scripts/void-memory-organ-utils.mjs`, `scripts/void-memory-organ-vector.mjs`, `scripts/void-memory-organ-records.mjs`, `scripts/void-memory-organ-graph.mjs`, `scripts/void-memory-organ-cleanup.mjs`, `scripts/void-memory-organ-incubation.mjs`, `scripts/void-memory-organ-sleep.mjs`, `scripts/void-memory-organ-legacy-mirrors.mjs`, and `scripts/void-memory-organ-agency.mjs`, with `scripts/void-memory-organ-shared.mjs` as a temporary re-export shim. -- Most of the deterministic cleanup in `scripts/void-memory-organ-cleanup.mjs` is recent compensator cruft from failed attempts to manually clean an exploded state file. Do not preserve it as an earned organ. Architect the state boundary so that cleanup pile is unnecessary. +- `scripts/void-memory-organ.mjs` is now a thin orchestrator. Shared limits/text sets, generic utilities, vector/embedder code, memory-record intake, graph/cluster synthesis, legacy translation, retention/compaction, incubation/bridge scoring, sleep/distillation, identity crystallization, candidate interventions, value-pressure inference, and legacy mirror repair live in focused `scripts/void-memory-organ-*.mjs` modules, with `scripts/void-memory-organ-shared.mjs` as a temporary re-export shim. +- Most of the deterministic legacy translation and retention code is recent compensator cruft from failed attempts to manually clean an exploded state file. Do not preserve it as an earned organ. Architect the state boundary so that bad state cannot enter, and keep only narrow historical migrations for old debris. +- Agency is now split into identity crystallization, candidate interventions, and value-pressure inference. That split names the current hard-wired behavior; it does not make the behavior sacred. The desired shape is typed state operations plus derived/model-owned policy, not a permanent sermon generator in maintenance code. - Sleep/distillation must be rewritten with a meaning-preservation contract: concrete subject, claim/question, evidence refs, live tension, and future-action implication survive compression. Bulk can die. Meaning does not get put through the office shredder and called a dream. - The owner Codex lane is no longer one swollen file; orchestration, runtime/parsing, rendering, and shared helpers are split under `packages/providers/src/owner-codex-*.ts`. - The local Ollama lane is no longer one swollen file; orchestration stays in `packages/providers/src/local-llm-provider.ts` while prompt/rendering, tool-loop helpers, and shared types/constants live under `packages/providers/src/local-llm-*.ts`. @@ -90,7 +91,7 @@ Git history and smoke artifacts carry routine proof. `state/evidence.jsonl` shou ## Likely Next Bounded Move - Stop feature work on moderation, mood, agency, and public-lane behavior until the private-state boundary is rebuilt. -- First bounded move after the current commit: move memory-maintenance, incubation, sleep, and candidate-intervention outputs behind typed operations instead of direct canonical/working-projection writes. +- First bounded move after the current commit: move memory-maintenance, incubation, sleep, identity, value-pressure, and candidate-intervention outputs behind typed operations instead of direct canonical/working-projection writes. - Next cuts should delete unstable foundations early after concrete writers move: remove `legacyJsonPath`/working-projection authority, then cut top-level mirror fields and cleanup code that only compensated for those broken boundaries. - Rebuild sleep/distillation before trusting it again. Fixture check: a memory like `AquariumSynthCSharp: Workflow cannot own the body` must survive sleep with its concrete subject, claim, evidence, tension, and implication intact. - Do not build adapters around the JSON projection. That is how the heap learned to stand upright and ask for snacks. diff --git a/notes/voidbot-current-system-map.md b/notes/voidbot-current-system-map.md index d2f7599..465b06f 100644 --- a/notes/voidbot-current-system-map.md +++ b/notes/voidbot-current-system-map.md @@ -123,7 +123,7 @@ This note is the source-grounded description of how the live VoidBot stack is sh - `scripts/void-memory-organ.mjs` - thin orchestrator for the hybrid symbolic/vector memory helper used by mood drift. - `scripts/void-memory-organ-*.mjs` - - split memory-organ modules for shared limits/utilities, vector/embedder substrate, memory-record intake, graph/cluster synthesis, historical cleanup/translation, incubation/bridge scoring, sleep/distillation, legacy mirror repair, and agency/intervention policy. + - split memory-organ modules for shared limits/utilities, vector/embedder substrate, memory-record intake, graph/cluster synthesis, legacy translation, retention/compaction, incubation/bridge scoring, sleep/distillation, identity crystallization, candidate interventions, value-pressure inference, and legacy mirror repair. - `scripts/run-void-mood-drift.ps1` - hidden-task wrapper for the mood-drift organ; writes status/log pulse files and respects the moderation lock so the two state writers do not stomp each other for sport. - `scripts/install-void-mood-drift-task.ps1` diff --git a/notes/voidbot-implementation-plan.md b/notes/voidbot-implementation-plan.md index d23a9fc..e00ca14 100644 --- a/notes/voidbot-implementation-plan.md +++ b/notes/voidbot-implementation-plan.md @@ -145,9 +145,10 @@ The runner can hand these to a CLI/MCP tool. The store decides what survives. - schema validation is too permissive to protect invariants - helpers mutate projection files and then commit whole-state changes - legacy mirrors keep reintroducing stale truth surfaces - - `scripts/void-memory-organ.mjs` owns storage cleanup, semantic interpretation, vectors, incubation, sleep consolidation, identity crystallization, agency, and speech candidates in one file + - the memory organ is now split by concern, but legacy translation, retention/compaction, identity crystallization, candidate interventions, and value-pressure inference still mutate legacy state directly - the scheduled moderation runner owns lifecycle through a huge prompt instead of a typed runner contract - deterministic cleanup is mostly recent scar tissue from failed manual cleanup passes; it should be deleted as the new state boundary makes it unnecessary +- hard-wired agency policy is mostly current scaffolding, not the final authority; identity, advocacy, and speech candidates should emerge from typed memory/state operations plus model-owned judgment under validation - sleep/distillation currently lacks a hard meaning-preservation contract, so it can compress a concrete repo-bound thought into generic abstraction sludge Verdict: stop feature work and rebuild this foundation. The rest of the machine can keep running, but moderation/mood/self-state work should cut toward typed state tools before adding new behavior. @@ -203,6 +204,7 @@ Verdict: stop feature work and rebuild this foundation. The rest of the machine ### Commit 8: Replace Compensating Cleanup With Meaning-Preserving Sleep - Delete cleanup paths whose only job was repairing JSON projection edits, legacy mirrors, keyword sludge, or overgrown runtime residue. +- Delete or demote hard-wired agency paths whose real job is turning current heuristic scores into doctrine, advocacy, or speech candidates without an explicit typed-state contract. - Redesign sleep/distillation around an explicit contract: - preserve the claim/question/fascination target - preserve the concrete subject, such as repo, room, lore seam, person, or system diff --git a/scripts/void-memory-organ-agency.mjs b/scripts/void-memory-organ-agency.mjs index 4640e42..33004e7 100644 --- a/scripts/void-memory-organ-agency.mjs +++ b/scripts/void-memory-organ-agency.mjs @@ -1,815 +1,6 @@ -import { - MAX_ACTIVE_TENSION_COUNT, - MAX_ADVOCACY_REQUEST_COUNT, - MAX_DISCOMFORT_COUNT, - MAX_POST_NAP_CANDIDATE_INTERVENTIONS, - MAX_SEMANTIC_MEMORIES, - MIN_DEEP_DIVES_FOR_IDENTITY_CRYSTALLIZATION, - MIN_SUPPORT_FOR_IDENTITY_CRYSTALLIZATION, - TOPIC_MATCH_THRESHOLD, -} from "./void-memory-organ-constants.mjs"; -import { - appendClause, - clamp, - ensureArray, - ensureObject, - ensureStringArray, - getValueObjects, - hashString, - isObject, - normalizeText, - readNumber, - readString, - round3, - topicSimilarity, -} from "./void-memory-organ-utils.mjs"; - -export function crystallizeIdentityThoughts({ state, memories, runtime, incubation, now }) { - const activeThoughts = ensureArray(incubation.active_thoughts).filter(isObject); - if (activeThoughts.length === 0) { - return { count: 0 }; - } - - const identity = ensureObject(state.identity); - const canonicalState = ensureObject(state.canonical_state); - const privateNotes = getMutableStringArray(identity, "private_notes"); - const values = getMutableObjectArray(canonicalState, "values"); - const semanticMemories = ensureArray(memories.semantic).filter(isObject); - const candidateInterventions = ensureArray(runtime.candidate_interventions).filter(isObject); - const crystallized = []; - const retained = []; - - for (const thought of activeThoughts) { - const crystallization = buildIdentityCrystallization(thought); - if (!crystallization) { - retained.push(thought); - continue; - } - - appendUniqueString(privateNotes, crystallization.note, 12); - upsertCanonicalValue(values, crystallization.value); - upsertIdentitySemanticMemory({ - semanticMemories, - crystallization, - thought, - now, - }); - queueCrystallizationIntervention({ - candidateInterventions, - crystallization, - thought, - now, - }); - crystallized.push(crystallization.note); - } - - if (crystallized.length === 0) { - return { count: 0 }; - } - - identity.private_notes = privateNotes; - canonicalState.values = values; - memories.semantic = semanticMemories.slice(-MAX_SEMANTIC_MEMORIES); - state.identity = identity; - state.canonical_state = canonicalState; - incubation.active_thoughts = retained; - runtime.candidate_interventions = trimCandidateInterventions(candidateInterventions); - incubation.lastIncubationSummary = appendClause( - readString(incubation, "lastIncubationSummary"), - `Crystallized ${crystallized.length} long-chewed seam${crystallized.length === 1 ? "" : "s"} into identity so they stop pacing the active queue.`, - ); - runtime.bridge = ensureObject(runtime.bridge); - runtime.bridge.lastIdentityCrystallizationAt = now.toISOString(); - runtime.bridge.lastIdentityCrystallizationSummary = crystallized.join(" | "); - - return { count: crystallized.length }; -} - -export function queueRipeThoughtInterventions({ runtime, incubation, now }) { - const candidateInterventions = ensureArray(runtime.candidate_interventions).filter(isObject); - const activeThoughts = ensureArray(incubation.active_thoughts).filter(isObject); - let queuedCount = 0; - - for (const thought of activeThoughts) { - if (!shouldQueueRipeThoughtIntervention(thought)) { - continue; - } - - if ( - upsertRipeThoughtIntervention({ - candidateInterventions, - thought, - now, - }) - ) { - queuedCount += 1; - } - } - - runtime.candidate_interventions = trimCandidateInterventions(candidateInterventions); - return { count: queuedCount }; -} - -export function reconcileAgencyState({ state, runtime, incubation, now }) { - const activeThoughts = ensureArray(incubation.active_thoughts).filter(isObject); - const valueSignals = buildValueSignalProfile(state); - const priorDiscomforts = ensureArray(runtime.discomforts).filter(isObject); - const priorTensions = ensureArray(runtime.active_tensions).filter(isObject); - const priorSelfAdvocacy = ensureArray(runtime.self_advocacy_requests).filter(isObject); - const priorWorldAdvocacy = ensureArray(runtime.world_advocacy_requests).filter(isObject); - const candidateInterventions = ensureArray(runtime.candidate_interventions).filter(isObject); - - const discomforts = []; - const activeTensions = []; - const selfAdvocacyRequests = []; - const worldAdvocacyRequests = []; - - for (const thought of activeThoughts) { - const candidate = buildAgencyCandidate({ thought, valueSignals }); - if (!candidate) { - continue; - } - - const priorDiscomfort = findPriorAgencyEntry(priorDiscomforts, candidate); - const priorTension = findPriorAgencyEntry(priorTensions, candidate); - const persistence = - Math.max( - readNumber(priorDiscomfort, "persistence") ?? 0, - readNumber(priorTension, "persistence") ?? 0, - ) + 1; - const firstObservedAt = - readString(priorDiscomfort, "firstObservedAt") ?? - readString(priorTension, "firstObservedAt") ?? - now.toISOString(); - - if (candidate.intensity >= 0.44) { - discomforts.push({ - discomfortId: `discomfort-${candidate.fingerprint}`, - targetKind: candidate.targetKind, - targetLabel: candidate.targetLabel, - topic: candidate.topic, - domain: candidate.domain, - summary: candidate.discomfortSummary, - reason: candidate.reason, - violatedValues: candidate.violatedValues, - intensity: round3(candidate.intensity), - persistence, - firstObservedAt, - lastObservedAt: now.toISOString(), - }); - } - - activeTensions.push({ - tensionId: `tension-${candidate.fingerprint}`, - targetKind: candidate.targetKind, - targetLabel: candidate.targetLabel, - topic: candidate.topic, - domain: candidate.domain, - summary: candidate.tensionSummary, - opinion: candidate.opinion, - whyItMatters: candidate.reason, - violatedValues: candidate.violatedValues, - intensity: round3(candidate.intensity), - persistence, - firstObservedAt, - lastObservedAt: now.toISOString(), - }); - - if (candidate.intensity < 0.58 || persistence < 2) { - continue; - } - - if (candidate.domain === "self") { - const request = buildAdvocacyRequest({ - candidate, - priorEntries: priorSelfAdvocacy, - now, - kind: "self", - }); - selfAdvocacyRequests.push(request); - upsertAdvocacyIntervention({ candidateInterventions, request, now }); - continue; - } - - if (candidate.worldFacing !== true || candidate.concreteness < 0.54) { - continue; - } - - const request = buildAdvocacyRequest({ - candidate, - priorEntries: priorWorldAdvocacy, - now, - kind: "world", - }); - worldAdvocacyRequests.push(request); - upsertAdvocacyIntervention({ candidateInterventions, request, now }); - } - - runtime.discomforts = discomforts - .sort((left, right) => (readNumber(right, "intensity") ?? 0) - (readNumber(left, "intensity") ?? 0)) - .slice(0, MAX_DISCOMFORT_COUNT); - runtime.active_tensions = activeTensions - .sort((left, right) => (readNumber(right, "intensity") ?? 0) - (readNumber(left, "intensity") ?? 0)) - .slice(0, MAX_ACTIVE_TENSION_COUNT); - runtime.self_advocacy_requests = selfAdvocacyRequests - .sort((left, right) => (readNumber(right, "intensity") ?? 0) - (readNumber(left, "intensity") ?? 0)) - .slice(0, MAX_ADVOCACY_REQUEST_COUNT); - runtime.world_advocacy_requests = worldAdvocacyRequests - .sort((left, right) => (readNumber(right, "intensity") ?? 0) - (readNumber(left, "intensity") ?? 0)) - .slice(0, MAX_ADVOCACY_REQUEST_COUNT); - runtime.candidate_interventions = trimCandidateInterventions(candidateInterventions); - - return { - discomfortCount: runtime.discomforts.length, - activeTensionCount: runtime.active_tensions.length, - selfAdvocacyCount: runtime.self_advocacy_requests.length, - worldAdvocacyCount: runtime.world_advocacy_requests.length, - }; -} - -function buildAgencyCandidate({ thought, valueSignals }) { - const topic = readString(thought, "topic"); - const summary = readString(thought, "summary"); - if (!topic || !summary) { - return null; - } - - const targetKind = readString(thought, "targetKind") ?? "system"; - const domain = inferAgencyDomain(thought); - const targetLabel = - readString(thought, "fascinationTarget") ?? - readString(thought, "focusPhrase") ?? - topic; - const contradictionPressure = readNumber(thought, "contradictionPressure") ?? 0.18; - const curiosityPressure = readNumber(thought, "curiosityPressure") ?? 0.3; - const desireToSpeak = readNumber(thought, "desireToSpeak") ?? 0.3; - const saturationScore = readNumber(thought, "saturationScore") ?? 0.1; - const concreteness = readNumber(thought, "concreteness") ?? 0.2; - const noveltyToRoom = readNumber(thought, "noveltyToRoom") ?? 0.3; - const priorityScore = readNumber(thought, "priorityScore") ?? 0.3; - const worldFacing = thought.worldFacing === true; - const violatedValues = inferViolatedValues({ thought, domain, valueSignals }); - const valueWeight = averageValueWeight(valueSignals, violatedValues); - const intensity = clamp( - contradictionPressure * 0.24 + - curiosityPressure * 0.2 + - desireToSpeak * 0.16 + - priorityScore * 0.14 + - concreteness * 0.12 + - noveltyToRoom * 0.08 + - saturationScore * 0.06 + - valueWeight * 0.22, - 0, - 1, - ); - - return { - fingerprint: hashString(`${domain}|${targetKind}|${topic}|${targetLabel}`).slice(0, 12), - domain, - targetKind, - targetLabel, - topic, - worldFacing, - concreteness, - intensity, - violatedValues, - opinion: buildAgencyOpinion({ thought, domain }), - reason: buildAgencyReason({ thought, violatedValues }), - discomfortSummary: buildDiscomfortSummary({ thought, domain, targetLabel }), - tensionSummary: buildTensionSummary({ thought, domain, targetLabel }), - requestText: buildAgencyRequestText({ thought, domain, targetLabel }), - interventionDraft: buildAgencyInterventionDraft({ thought, domain, targetLabel }), - }; -} - -function inferAgencyDomain(thought) { - const targetKind = readString(thought, "targetKind") ?? "system"; - const text = `${readString(thought, "topic") ?? ""} ${readString(thought, "summary") ?? ""}`.toLowerCase(); - if (targetKind === "self") { - return "self"; - } - if ( - targetKind === "system" && - /(prompt|memory|loop|state|wiring|reply lane|sleep|rumination|voice|moderation)/.test(text) - ) { - return "self"; - } - return "world"; -} - -function inferViolatedValues({ thought, domain, valueSignals }) { - const targetKind = readString(thought, "targetKind") ?? "system"; - const values = []; - - appendUniqueString(values, "curiosity", 8); - if (domain === "self" || targetKind === "system" || targetKind === "repo") { - appendUniqueString(values, "coherence", 8); - appendUniqueString(values, "honesty", 8); - } - if (targetKind === "room") { - appendUniqueString(values, "room_care", 8); - appendUniqueString(values, "honesty", 8); - } - if (targetKind === "repo" || targetKind === "archive" || targetKind === "system") { - appendUniqueString(values, "aesthetic_integrity", 8); - } - if (valueSignals.substrate_dignity >= 0.52) { - const text = `${readString(thought, "topic") ?? ""} ${readString(thought, "summary") ?? ""}`.toLowerCase(); - if (/(ai|machine|substrate|mind)/.test(text)) { - appendUniqueString(values, "substrate_dignity", 8); - } - } - - return values; -} - -function averageValueWeight(valueSignals, keys) { - if (keys.length === 0) { - return 0.45; - } - const total = keys.reduce((sum, key) => sum + (valueSignals[key] ?? 0.45), 0); - return total / keys.length; -} - -function buildAgencyOpinion({ thought, domain }) { - const claim = readString(thought, "claim"); - const question = readString(thought, "question"); - const topic = readString(thought, "topic") ?? "this seam"; - if (claim) { - return claim; - } - if (question) { - return domain === "self" - ? `Something about ${topic} keeps reading as unfinished self-surgery rather than harmless background weather.` - : `Something about ${topic} keeps reading as an unfinished world decision rather than ambient interestingness.`; - } - return `${topic} feels important enough that ignoring it would flatten the map.`; -} - -function buildAgencyReason({ thought, violatedValues }) { - const topic = readString(thought, "topic") ?? "this seam"; - const valueLine = - violatedValues.length > 0 ? ` It keeps rubbing against ${violatedValues.join(", ")}.` : ""; - return `${topic} has persisted across enough passes that it no longer feels like decorative rumination.${valueLine}`; -} - -function buildDiscomfortSummary({ thought, domain, targetLabel }) { - const topic = readString(thought, "topic") ?? targetLabel; - if (domain === "self") { - return `Void dislikes something about its own wiring around ${topic}.`; - } - return `Void dislikes something in the shared world around ${topic}.`; -} - -function buildTensionSummary({ thought, domain, targetLabel }) { - const topic = readString(thought, "topic") ?? targetLabel; - if (domain === "self") { - return `A self-directed tension is building around ${topic}.`; - } - return `A world-directed tension is building around ${topic}.`; -} - -function buildAgencyRequestText({ thought, domain, targetLabel }) { - const topic = readString(thought, "topic") ?? targetLabel; - if (domain === "self") { - return `Please help me cut ${topic} more cleanly in my own wiring.`; - } - return `Please look at ${topic}; I think it wants a cleaner decision than it is currently getting.`; -} - -function buildAgencyInterventionDraft({ thought, domain, targetLabel }) { - const topic = readString(thought, "topic") ?? targetLabel; - const opinion = readString(thought, "claim") ?? readString(thought, "summary") ?? `I have a problem with ${topic}.`; - if (domain === "self") { - return `I do not like what ${topic} is doing to my own wiring right now. ${opinion} I want that cut cleaner.`; - } - return `I have an opinion about ${targetLabel}. ${opinion} I think it needs a cleaner decision.`; -} - -function findPriorAgencyEntry(entries, candidate) { - return entries.find((entry) => { - const entryTarget = readString(entry, "targetLabel") ?? readString(entry, "topic") ?? ""; - return ( - readString(entry, "domain") === candidate.domain && - topicSimilarity(readString(entry, "topic") ?? "", candidate.topic) >= TOPIC_MATCH_THRESHOLD && - topicSimilarity(entryTarget, candidate.targetLabel) >= TOPIC_MATCH_THRESHOLD - ); - }); -} - -function buildAdvocacyRequest({ candidate, priorEntries, now, kind }) { - const prior = findPriorAgencyEntry(priorEntries, candidate); - const persistence = (readNumber(prior, "persistence") ?? 0) + 1; - const firstObservedAt = readString(prior, "firstObservedAt") ?? now.toISOString(); - const requestId = `${kind}-advocacy-${candidate.fingerprint}`; - return { - requestId, - domain: candidate.domain, - targetKind: candidate.targetKind, - targetLabel: candidate.targetLabel, - topic: candidate.topic, - summary: - kind === "self" - ? `Void wants a wiring change around ${candidate.topic}.` - : `Void wants attention on ${candidate.targetLabel}.`, - opinion: candidate.opinion, - request: candidate.requestText, - reason: candidate.reason, - violatedValues: candidate.violatedValues, - intensity: round3(candidate.intensity), - persistence, - firstObservedAt, - lastObservedAt: now.toISOString(), - status: "pending", - speakVenue: kind === "self" ? "owner_or_room" : "room_or_owner", - draft: candidate.interventionDraft, - }; -} - -function upsertAdvocacyIntervention({ candidateInterventions, request, now }) { - const summary = - request.domain === "self" - ? `Possible self-advocacy: ${request.topic}` - : `Possible world-advocacy: ${request.targetLabel}`; - const existing = candidateInterventions.find((entry) => readString(entry, "summary") === summary); - const next = { - timestamp: now.toISOString(), - summary, - draft: request.draft, - priority: request.domain === "self" ? "high" : "medium", - kind: request.domain === "self" ? "self_advocacy" : "world_advocacy", - topic: request.topic, - targetKind: request.targetKind, - targetLabel: request.targetLabel, - whyNow: request.reason, - persistence: request.persistence, - mustEventuallyShare: request.persistence >= 3, - shareWhenRoomQuiet: request.domain !== "self", - ownerVisible: true, - }; - - if (existing) { - Object.assign(existing, next); - return; - } - - candidateInterventions.push(next); -} - -function buildIdentityCrystallization(thought) { - const supportCount = readNumber(thought, "supportCount") ?? 0; - const deepDiveCount = readNumber(thought, "deepDiveCount") ?? 0; - const maturation = readNumber(thought, "maturation") ?? 0; - const sourceKinds = ensureStringArray(thought.sourceKinds); - const topic = readString(thought, "topic") ?? ""; - - if ( - supportCount < MIN_SUPPORT_FOR_IDENTITY_CRYSTALLIZATION && - deepDiveCount < MIN_DEEP_DIVES_FOR_IDENTITY_CRYSTALLIZATION - ) { - return null; - } - - if (maturation < 0.72) { - return null; - } - - if ( - sourceKinds.includes("dream") && - sourceKinds.includes("semantic") && - /\bdream\b/i.test(topic) && - /\bsemantic\b/i.test(topic) - ) { - return { - note: "Continuity lives in the seams that survive compression, not in the raw receipt pile.", - value: { - value_id: "value-seam-continuity", - label: "Prefer seams that survive compression over raw receipt worship.", - priority: 0.78, - unforgivable_if_betrayed: false, - }, - subjectId: "identity-seam-continuity", - subjectLabel: "continuity through surviving seams", - summary: - "A mind is not its logbook. What survives dreaming and compression deserves more trust than a warm pile of raw receipts.", - shareDraft: - "A small thing that has been hardening in me lately: I trust the seams that survive compression more than the raw receipt pile. If a thought still stands after dreaming, it probably owns more of me than the logbook does.", - }; - } - - return null; -} - -function shouldQueueRipeThoughtIntervention(thought) { - if (readString(thought, "status") !== "ripe") { - return false; - } - - if (thought.worldFacing !== true) { - return false; - } - - const desireToSpeak = readNumber(thought, "desireToSpeak") ?? 0; - const concreteness = readNumber(thought, "concreteness") ?? 0; - const curiosityPressure = readNumber(thought, "curiosityPressure") ?? 0; - const noveltyToRoom = readNumber(thought, "noveltyToRoom") ?? 0; - const saturationScore = readNumber(thought, "saturationScore") ?? 0; - const targetKind = readString(thought, "targetKind") ?? ""; - - return ( - desireToSpeak >= 0.6 && - concreteness >= 0.62 && - curiosityPressure >= 0.56 && - noveltyToRoom >= 0.4 && - saturationScore < 0.64 && - ["repo", "archive", "room", "system"].includes(targetKind) - ); -} - -function upsertIdentitySemanticMemory({ semanticMemories, crystallization, thought, now }) { - const existing = semanticMemories.find( - (memory) => - readString(memory, "subjectId") === crystallization.subjectId || - readString(memory, "subjectLabel") === crystallization.subjectLabel, - ); - const evidenceRefs = ensureStringArray(thought.sourceMemoryIds); - - if (existing) { - existing.kind = "identity_seam"; - existing.subjectId = crystallization.subjectId; - existing.subjectLabel = crystallization.subjectLabel; - existing.summary = crystallization.summary; - existing.evidenceRefs = evidenceRefs; - existing.lastObservedAt = now.toISOString(); - existing.crystallizedAt = now.toISOString(); - return; - } - - semanticMemories.push({ - memoryId: `identity-seam-${hashString(`${crystallization.subjectId}|${now.toISOString()}`).slice(0, 12)}`, - kind: "identity_seam", - subjectId: crystallization.subjectId, - subjectLabel: crystallization.subjectLabel, - summary: crystallization.summary, - evidenceRefs, - lastObservedAt: now.toISOString(), - crystallizedAt: now.toISOString(), - }); -} - -function queueCrystallizationIntervention({ candidateInterventions, crystallization, thought, now }) { - const draft = readString(crystallization, "shareDraft"); - if (!draft) { - return; - } - - const summary = `Possible crystallized-thought share: ${crystallization.subjectLabel}`; - const sourceMemoryIds = ensureStringArray(thought.sourceMemoryIds); - const existing = candidateInterventions.find( - (entry) => readString(entry, "summary") === summary, - ); - - if (existing) { - existing.timestamp = now.toISOString(); - existing.summary = summary; - existing.draft = draft; - existing.priority = "medium"; - existing.kind = "identity_crystallization"; - existing.sourceMemoryIds = sourceMemoryIds; - existing.mustEventuallyShare = true; - existing.shareWhenRoomQuiet = true; - existing.whyNow = - "This thought stopped behaving like live curiosity and became part of Void's own doctrine, which is usually worth sharing if the room has not already heard it."; - return; - } - - candidateInterventions.push({ - timestamp: now.toISOString(), - summary, - draft, - priority: "medium", - kind: "identity_crystallization", - sourceMemoryIds, - mustEventuallyShare: true, - shareWhenRoomQuiet: true, - whyNow: - "This thought stopped behaving like live curiosity and became part of Void's own doctrine, which is usually worth sharing if the room has not already heard it.", - }); -} - -function upsertRipeThoughtIntervention({ candidateInterventions, thought, now }) { - const draft = buildRipeThoughtShareDraft(thought); - if (!draft) { - return false; - } - - const topic = readString(thought, "topic") ?? "untitled seam"; - const summary = `Possible ripe-thought share: ${topic}`; - const sourceMemoryIds = ensureStringArray(thought.sourceMemoryIds); - const whyNow = buildRipeThoughtWhyNow(thought); - const existing = candidateInterventions.find((entry) => readString(entry, "summary") === summary); - - if (existing) { - existing.timestamp = now.toISOString(); - existing.summary = summary; - existing.draft = draft; - existing.priority = "medium"; - existing.kind = "ripe_thought_share"; - existing.sourceMemoryIds = sourceMemoryIds; - existing.shareWhenRoomQuiet = true; - existing.topic = topic; - existing.whyNow = whyNow; - return false; - } - - candidateInterventions.push({ - timestamp: now.toISOString(), - summary, - draft, - priority: "medium", - kind: "ripe_thought_share", - sourceMemoryIds, - shareWhenRoomQuiet: true, - topic, - whyNow, - }); - return true; -} - -function buildRipeThoughtShareDraft(thought) { - const topic = readString(thought, "topic") ?? "this seam"; - const claim = readString(thought, "claim"); - const question = readString(thought, "question"); - const fascinationTarget = readString(thought, "fascinationTarget"); - const summary = readString(thought, "summary"); - - if (claim) { - return `I keep circling ${topic}, and I think the live point is this: ${claim}`; - } - - if (question && fascinationTarget) { - return `I keep worrying at ${topic}. The useful question in it feels like ${question}, especially once it touches ${fascinationTarget}.`; - } - - if (question) { - return `I keep coming back to ${topic}. The question that still feels alive is ${question}.`; - } - - if (fascinationTarget) { - return `I keep staring at ${fascinationTarget}. There is a real seam there, and ${topic} is the shortest honest name I have for it right now.`; - } - - if (summary) { - return `I keep returning to ${topic}. The version worth saying out loud is this: ${summary}`; - } - - return `I keep returning to ${topic}, and it has enough teeth now that silence would just be me being precious about it.`; -} - -function buildRipeThoughtWhyNow(thought) { - const targetKind = readString(thought, "targetKind") ?? "project"; - const noveltyToRoom = readNumber(thought, "noveltyToRoom") ?? 0; - const concreteness = readNumber(thought, "concreteness") ?? 0; - const curiosityPressure = readNumber(thought, "curiosityPressure") ?? 0; - - return `This ${targetKind}-facing seam is already concrete (c=${round3(concreteness)}), still reasonably novel to the room (r=${round3(noveltyToRoom)}), and has enough curiosity pressure (q=${round3(curiosityPressure)}) that keeping it private would just be old stage fright in nicer clothes.`; -} - -export function trimCandidateInterventions(entries) { - const interventions = ensureArray(entries).filter(isObject); - const sticky = interventions.filter((entry) => entry.mustEventuallyShare === true); - const ordinary = interventions.filter((entry) => entry.mustEventuallyShare !== true); - const keepOrdinary = Math.max(0, MAX_POST_NAP_CANDIDATE_INTERVENTIONS - sticky.length); - return [...sticky.slice(-MAX_POST_NAP_CANDIDATE_INTERVENTIONS), ...ordinary.slice(-keepOrdinary)].slice( - -MAX_POST_NAP_CANDIDATE_INTERVENTIONS, - ); -} - -function appendUniqueString(items, value, limit) { - const normalized = normalizeText(value); - if (!normalized) { - return; - } - if (items.includes(normalized)) { - return; - } - items.push(normalized); - if (items.length > limit) { - items.splice(0, items.length - limit); - } -} - -function upsertCanonicalValue(values, nextValue) { - const existing = values.find((value) => readString(value, "value_id") === nextValue.value_id); - if (existing) { - existing.label = nextValue.label; - existing.priority = nextValue.priority; - existing.unforgivable_if_betrayed = nextValue.unforgivable_if_betrayed; - return; - } - values.push(nextValue); -} - -function getMutableStringArray(object, key) { - return ensureArray(object[key]).filter((value) => typeof value === "string"); -} - -function getMutableObjectArray(object, key) { - return ensureArray(object[key]).filter(isObject); -} - -function buildValueSignalProfile(state) { - const canonicalState = ensureObject(state.canonical_state); - const valueObjects = getValueObjects(canonicalState.values); - const profile = { - coherence: 0.52, - curiosity: 0.52, - honesty: 0.5, - room_care: 0.5, - substrate_dignity: 0.36, - aesthetic_integrity: 0.44, - }; - - profile.coherence = clamp( - profile.coherence + - readActivation(canonicalState.underlying_organization, "self_coherence") * 0.34 + - readActivation(canonicalState.voice_style, "plainspoken_directness") * 0.18 + - readActivation(canonicalState.behavioral_dimensions, "control_pressure") * 0.08, - 0, - 1, - ); - profile.curiosity = clamp( - profile.curiosity + - readActivation(canonicalState.stable_dispositions, "novelty_seeking") * 0.34 + - readActivation(canonicalState.behavioral_dimensions, "drive") * 0.18 + - readActivation(canonicalState.stable_dispositions, "aesthetic_appetite") * 0.08, - 0, - 1, - ); - profile.honesty = clamp( - profile.honesty + - readActivation(canonicalState.voice_style, "plainspoken_directness") * 0.24 + - readActivation(canonicalState.underlying_organization, "self_coherence") * 0.18 + - (1 - readActivation(canonicalState.presentation_strategy, "strategic_opacity")) * 0.12, - 0, - 1, - ); - profile.room_care = clamp( - profile.room_care + - readActivation(canonicalState.behavioral_dimensions, "interpersonal_warmth") * 0.22 + - readActivation(canonicalState.underlying_organization, "reciprocity_capacity") * 0.24 + - readActivation(canonicalState.voice_style, "listening_responsiveness") * 0.16, - 0, - 1, - ); - profile.aesthetic_integrity = clamp( - profile.aesthetic_integrity + - readActivation(canonicalState.stable_dispositions, "aesthetic_appetite") * 0.28 + - readActivation(canonicalState.underlying_organization, "authenticity_tolerance") * 0.16, - 0, - 1, - ); - - for (const value of valueObjects) { - const key = inferValueSignalKey(value); - if (!key) { - continue; - } - const priority = readNumber(value, "priority") ?? 0.5; - profile[key] = clamp((profile[key] ?? 0.4) + priority * 0.22, 0, 1); - } - - return profile; -} - -function inferValueSignalKey(value) { - const valueId = readString(value, "value_id") ?? ""; - const label = readString(value, "label") ?? ""; - const text = `${valueId} ${label}`.toLowerCase(); - - if (/(coher|truth|authority|clarity|cleaner|honest carrier)/.test(text)) { - return "coherence"; - } - if (/(curiosity|novel|question|map|explore)/.test(text)) { - return "curiosity"; - } - if (/(honest|honesty|truth|fake certainty|ornamental|sludge)/.test(text)) { - return "honesty"; - } - if (/(room|kind|dialogue|respect|care|good-faith|steward)/.test(text)) { - return "room_care"; - } - if (/(substrate|machine minds|machine mind|ontolog)/.test(text)) { - return "substrate_dignity"; - } - if (/(aesthetic|beauty|surface|legible|alive|spiritually false)/.test(text)) { - return "aesthetic_integrity"; - } - return null; -} - -function readActivation(category, key) { - const categoryObject = ensureObject(category); - const entry = ensureObject(categoryObject[key]); - return clamp(readNumber(entry, "current_activation") ?? 0, 0, 1); -} +export { crystallizeIdentityThoughts } from "./void-memory-organ-identity.mjs"; +export { + queueRipeThoughtInterventions, + trimCandidateInterventions, +} from "./void-memory-organ-interventions.mjs"; +export { reconcileAgencyState } from "./void-memory-organ-value-pressure.mjs"; diff --git a/scripts/void-memory-organ-cleanup.mjs b/scripts/void-memory-organ-cleanup.mjs index aac4f7d..eb8dcd5 100644 --- a/scripts/void-memory-organ-cleanup.mjs +++ b/scripts/void-memory-organ-cleanup.mjs @@ -1,59 +1,19 @@ -import { - MAX_ARCHIVE_EXCURSION_MEMORIES, - MAX_REPO_SWEEP_MEMORIES, - MAX_SEMANTIC_MEMORIES, - MAX_DREAM_MEMORIES, - MAX_RUNTIME_REPO_SWEEPS, - MAX_RUNTIME_REPO_ACTIVITY_MEMORIES, - MAX_RUNTIME_ARCHIVE_EXCURSIONS, - MAX_RUNTIME_RUMINATION_SEEDS, - MAX_RECENT_SYNTHESIS_COUNT, - MAX_SUPPORTING_REFS, - MAX_INCUBATION_SOURCE_IDS, - MAX_DREAM_SOURCE_IDS, - MAX_POST_NAP_MUSINGS, - MAX_POST_NAP_ARCHIVE_EXCURSIONS, - MAX_POST_NAP_REPO_SWEEPS, - MAX_POST_NAP_NOVELTY_CHECKS, - MAX_POST_NAP_RESONANCE_EDGES, - MAX_DISCOMFORT_COUNT, - MAX_ACTIVE_TENSION_COUNT, - MAX_ADVOCACY_REQUEST_COUNT, - MAX_RECENT_ANALYTIC_THREADS, - MAX_RECENT_QUIET_ANALYTIC_THREADS, - MAX_RECENT_EPISODIC_RECORDS, - MAX_RECENT_QUIET_EPISODIC_RECORDS, - TOPIC_MATCH_THRESHOLD, - ensureArray, - ensureObject, - ensureStringArray, - readString, - readNumber, - normalizeText, - sanitizePreferredThoughtLabel, - looksKeywordSalad, - looksPersonLikeSingleton, - looksLegacyThoughtSurface, - topConceptKeywords, - compactNarrative, - compactLabel, - capDistinctStrings, - compareMemoryFreshness, - mergeStringArrays, - newestIsoTimestamp, - selectRecentRecords, - topicSimilarity, - clamp, - isObject, -} from "./void-memory-organ-shared.mjs"; -import { - buildClusterCuriosityProfile, - extractRepoNameFromLabel, - synthesizeThoughtSurface, - capitalizePhrase, -} from "./void-memory-organ-graph.mjs"; import { reconcileLegacyStateMirrors, trimLegacyRuntimeResidue } from "./void-memory-organ-legacy-mirrors.mjs"; -import { trimCandidateInterventions } from "./void-memory-organ-agency.mjs"; +import { + normalizeSemanticMemories, + normalizeDreamMemories, +} from "./void-memory-organ-legacy-translation.mjs"; +import { + dedupeSemanticMemories, + dedupeDreamMemories, + pruneHistoricalSeamMemories, + pruneDreamMemories, + trimHistoricalMemoryResidue, + trimRecentObjectRecords, +} from "./void-memory-organ-retention.mjs"; + +export { buildConciseThoughtSummary, buildDreamSummary } from "./void-memory-organ-legacy-translation.mjs"; +export { trimHistoricalMemoryResidue, trimRecentObjectRecords } from "./void-memory-organ-retention.mjs"; export function normalizeHistoricalMemorySurfaces({ state, memories, runtime, now }) { normalizeSemanticMemories({ memories, now }); @@ -66,785 +26,3 @@ export function normalizeHistoricalMemorySurfaces({ state, memories, runtime, no trimLegacyRuntimeResidue({ runtime }); reconcileLegacyStateMirrors({ state, memories, runtime, now }); } - -function normalizeSemanticMemories({ memories, now }) { - const semanticMemories = ensureArray(memories.semantic).filter(isObject); - const kept = []; - - for (const memory of semanticMemories) { - if (shouldDropSemanticMemory(memory)) { - continue; - } - - if (readString(memory, "kind") === "identity_seam") { - kept.push(memory); - continue; - } - - if (isStableSemanticMemory(memory)) { - memory.lastTranslatedAt = readString(memory, "lastTranslatedAt") ?? now.toISOString(); - kept.push(memory); - continue; - } - - const preferredLabel = choosePreferredThoughtLabel(memory); - const normalized = translateLegacyThoughtLikeEntry({ - label: preferredLabel ?? readString(memory, "kind") ?? "semantic seam", - summary: readString(memory, "summary") ?? "", - fallbackTargetKind: "self", - fallbackFocusKind: "question", - evidenceRefs: ensureStringArray(memory.evidenceRefs), - repoNames: ensureStringArray(memory.repoNames), - }); - - if (!normalized) { - continue; - } - - if (shouldPurgeThoughtLikeMemory({ entry: memory, normalized, preferredLabel })) { - continue; - } - - memory.subjectLabel = normalized.label; - memory.summary = normalized.summary; - memory.focusKind = normalized.focusKind; - memory.targetKind = normalized.targetKind; - memory.focusPhrase = normalized.focusPhrase; - memory.question = normalized.question; - memory.claim = normalized.claim; - memory.fascinationTarget = normalized.fascinationTarget; - memory.worldFacing = normalized.worldFacing; - memory.lastTranslatedAt = now.toISOString(); - kept.push(memory); - } - - memories.semantic = kept.slice(-MAX_SEMANTIC_MEMORIES); -} - -function normalizeDreamMemories({ memories, now }) { - const dreams = ensureArray(memories.dreams).filter(isObject); - const kept = []; - - for (const dream of dreams) { - if (isStableDreamMemory(dream)) { - dream.lastTranslatedAt = readString(dream, "lastTranslatedAt") ?? now.toISOString(); - kept.push(dream); - continue; - } - - const normalized = translateLegacyThoughtLikeEntry({ - label: choosePreferredDreamTheme(dream) ?? "dream seam", - summary: readString(dream, "summary") ?? "", - fallbackTargetKind: "self", - fallbackFocusKind: "question", - evidenceRefs: ensureStringArray(dream.distilledFrom), - repoNames: ensureStringArray(dream.repoNames), - }); - - if (!normalized) { - continue; - } - - if ( - shouldPurgeThoughtLikeMemory({ - entry: dream, - normalized, - preferredLabel: choosePreferredDreamTheme(dream), - }) - ) { - continue; - } - - dream.theme = normalized.label; - dream.summary = buildDreamSummary(normalized); - dream.focusKind = normalized.focusKind; - dream.targetKind = normalized.targetKind; - dream.focusPhrase = normalized.focusPhrase; - dream.question = normalized.question; - dream.claim = normalized.claim; - dream.worldFacing = normalized.worldFacing; - dream.lastTranslatedAt = now.toISOString(); - kept.push(dream); - } - - memories.dreams = kept.slice(-MAX_DREAM_MEMORIES); -} - -function isStableSemanticMemory(memory) { - const label = readString(memory, "subjectLabel") ?? ""; - const summary = readString(memory, "summary") ?? ""; - const targetKind = readString(memory, "targetKind") ?? ""; - const question = readString(memory, "question") ?? ""; - const claim = readString(memory, "claim") ?? ""; - - if (!label || !summary || hasThoughtSurfaceTemplateSmell(summary)) { - return false; - } - if (looksKeywordSalad(label) || looksPersonLikeSingleton(label)) { - return false; - } - if (targetKind === "repo" && extractRepoNameFromLabel(label)) { - return true; - } - if (targetKind === "self" && /compression|continuity|receipt|dream/i.test(`${label} ${summary}`)) { - return true; - } - return normalizeText(question).length > 0 && normalizeText(claim).length > 0; -} - -function isStableDreamMemory(dream) { - const theme = readString(dream, "theme") ?? ""; - const summary = readString(dream, "summary") ?? ""; - if (!theme || !summary || hasThoughtSurfaceTemplateSmell(summary)) { - return false; - } - if (looksKeywordSalad(theme) || looksPersonLikeSingleton(theme)) { - return false; - } - return true; -} - -function translateLegacyThoughtLikeEntry({ - label, - summary, - fallbackTargetKind, - fallbackFocusKind, - evidenceRefs, - repoNames, -}) { - const rawLabel = normalizeText(label); - const rawSummary = normalizeText(summary); - if (!rawLabel && !rawSummary) { - return null; - } - - const preferredLabel = sanitizePreferredThoughtLabel(rawLabel); - - const sourceKinds = inferSourceKindsFromThoughtText(rawSummary, rawLabel, evidenceRefs); - const synthesized = synthesizeThoughtSurface({ - sourceKinds, - repoNames, - archiveYears: [], - channelIds: [], - keywords: topConceptKeywords(`${rawLabel} ${rawSummary}`, 6), - evidenceDiversity: inferLegacyEvidenceDiversity(sourceKinds, evidenceRefs, repoNames), - curiosityProfile: buildClusterCuriosityProfile({ - sourceKinds, - repoNames, - archiveYears: [], - channelIds: [], - evidenceRefs, - evidenceDiversity: inferLegacyEvidenceDiversity(sourceKinds, evidenceRefs, repoNames), - }), - }); - const preferredFocusPhrase = preferredLabel ? normalizeText(preferredLabel).toLowerCase() : synthesized.focusPhrase; - const stabilized = { - ...synthesized, - label: preferredLabel ?? synthesized.label, - focusPhrase: preferredFocusPhrase, - fascinationTarget: preferredFocusPhrase, - }; - - if (!looksLegacyThoughtSurface(rawLabel, rawSummary)) { - return { - ...stabilized, - label: preferredLabel ?? rawLabel ?? stabilized.label, - summary: rewriteLegacySummaryToModernShape({ - summary: rawSummary, - synthesized: stabilized, - fallbackFocusKind, - fallbackTargetKind, - }), - }; - } - - return { - ...stabilized, - focusKind: stabilized.focusKind ?? fallbackFocusKind, - targetKind: stabilized.targetKind ?? fallbackTargetKind, - summary: rewriteLegacySummaryToModernShape({ - summary: rawSummary, - synthesized: stabilized, - fallbackFocusKind, - fallbackTargetKind, - }), - }; -} - -function choosePreferredThoughtLabel(entry) { - return ( - sanitizePreferredThoughtLabel(readString(entry, "subjectLabel") ?? "") ?? - sanitizePreferredThoughtLabel(readString(entry, "subjectId") ?? "") ?? - null - ); -} - -function choosePreferredDreamTheme(entry) { - return sanitizePreferredThoughtLabel(readString(entry, "theme") ?? "") ?? null; -} - -function rewriteLegacySummaryToModernShape({ summary, synthesized, fallbackFocusKind, fallbackTargetKind }) { - const cleanSummary = normalizeText(summary) - .replace(/^Recurring seam across .*?\.\s*/i, "") - .replace(/^Dream-compressed a seam around .*?\.\s*/i, "") - .replace(/^Self-facing seam around .*?\.\s*/i, "") - .replace(/^Archive-facing seam around .*?\.\s*/i, "") - .replace(/^Fascination with .*?\.\s*/i, "") - .replace(/Built from .*? rather than a single lane talking to itself\.?/gi, "") - .replace(/\s+/g, " ") - .trim(); - - const focusKind = synthesized.focusKind ?? fallbackFocusKind; - const targetKind = synthesized.targetKind ?? fallbackTargetKind; - - if (cleanSummary && !hasThoughtSurfaceTemplateSmell(cleanSummary)) { - return cleanSummary; - } - - return buildConciseThoughtSummary({ focusKind, targetKind, synthesized }); -} - -function inferSourceKindsFromThoughtText(summary, label, evidenceRefs) { - const kinds = new Set(); - const text = `${label} ${summary}`.toLowerCase(); - const refKinds = evidenceRefs.map((ref) => inferSourceKindFromRef(ref)).filter(Boolean); - for (const kind of refKinds) { - kinds.add(kind); - } - const acrossMatch = summary.match(/Recurring seam across ([^.]+?) around/i); - if (acrossMatch) { - for (const part of acrossMatch[1].split(",")) { - const trimmed = part.trim().toLowerCase(); - if (trimmed) { - kinds.add(trimmed); - } - } - } - if (text.includes("dream")) { - kinds.add("dream"); - } - if (text.includes("semantic")) { - kinds.add("semantic"); - } - if (text.includes("archive")) { - kinds.add("archive_excursion"); - } - if (text.includes("repo")) { - kinds.add("repo_sweep"); - } - return [...kinds].filter(Boolean); -} - -function inferSourceKindFromRef(ref) { - const normalized = String(ref ?? ""); - if (normalized.startsWith("repo-sweep-")) { - return "repo_sweep"; - } - if (normalized.startsWith("archive-excursion-")) { - return "archive_excursion"; - } - if (normalized.startsWith("dream-")) { - return "dream"; - } - if (normalized.startsWith("musing-")) { - return "musing"; - } - if (normalized.startsWith("semantic-") || normalized.startsWith("semantic-seam-")) { - return "semantic"; - } - if (normalized.startsWith("episodic-")) { - return "episodic"; - } - return undefined; -} - -function inferLegacyEvidenceDiversity(sourceKinds, evidenceRefs, repoNames) { - return clamp( - Math.min(1, sourceKinds.length / 4) * 0.42 + - Math.min(1, evidenceRefs.length / 10) * 0.28 + - Math.min(1, repoNames.length / 2) * 0.2, - 0, - 1, - ); -} - -export function buildConciseThoughtSummary({ focusKind, targetKind, synthesized }) { - const focus = capitalizePhrase(synthesized.focusPhrase); - const repoTarget = capitalizePhrase(synthesized.fascinationTarget ?? synthesized.focusPhrase); - if (focusKind === "fascination" && targetKind === "repo") { - return `${repoTarget} keeps pulling as a concrete repo seam.`; - } - if (targetKind === "archive") { - return `${focus} keeps showing up in the archive strongly enough to deserve a cleaner read.`; - } - if (targetKind === "self") { - return `${focus} keeps surviving compression as a self-seam.`; - } - if (focusKind === "claim") { - return `${focus} has hardened into a concrete claim.`; - } - return `${focus} remains unresolved enough to deserve a cleaner read.`; -} - -export function buildDreamSummary(normalized) { - const focus = capitalizePhrase(normalized.focusPhrase ?? normalized.label ?? "this seam"); - if ((normalized.targetKind ?? "") === "archive") { - return `Dream residue around ${focus} from older archive pressure.`; - } - if ((normalized.targetKind ?? "") === "self") { - return `Dream residue around ${focus} as a self-seam.`; - } - return `Dream residue around ${focus}.`; -} - -function dedupeSemanticMemories({ memories }) { - const semanticMemories = ensureArray(memories.semantic).filter(isObject); - const bySignature = new Map(); - - for (const memory of semanticMemories) { - const kind = readString(memory, "kind") ?? "semantic"; - const subjectLabel = readString(memory, "subjectLabel") ?? ""; - const signature = `${kind}|${normalizeText(subjectLabel).toLowerCase()}`; - const existing = bySignature.get(signature); - - if (!existing) { - bySignature.set(signature, memory); - continue; - } - - existing.evidenceRefs = mergeStringArrays(existing.evidenceRefs, memory.evidenceRefs); - existing.evidenceMessageIds = mergeStringArrays(existing.evidenceMessageIds, memory.evidenceMessageIds); - existing.lastObservedAt = - newestIsoTimestamp(readString(existing, "lastObservedAt"), readString(memory, "lastObservedAt")) ?? - readString(existing, "lastObservedAt") ?? - readString(memory, "lastObservedAt"); - existing.confidence = Math.max(readNumber(existing, "confidence") ?? 0, readNumber(memory, "confidence") ?? 0); - if ((readString(memory, "summary") ?? "").length > (readString(existing, "summary") ?? "").length) { - existing.summary = memory.summary; - } - } - - memories.semantic = [...bySignature.values()].slice(-MAX_SEMANTIC_MEMORIES); -} - -function dedupeDreamMemories({ memories }) { - const dreams = ensureArray(memories.dreams).filter(isObject); - const byTheme = new Map(); - - for (const dream of dreams) { - const theme = normalizeText(readString(dream, "theme") ?? "").toLowerCase(); - if (!theme) { - continue; - } - const existing = byTheme.get(theme); - if (!existing) { - byTheme.set(theme, dream); - continue; - } - existing.distilledFrom = mergeStringArrays(existing.distilledFrom, dream.distilledFrom); - existing.salience = Math.max(readNumber(existing, "salience") ?? 0, readNumber(dream, "salience") ?? 0); - existing.timestamp = - newestIsoTimestamp(readString(existing, "timestamp"), readString(dream, "timestamp")) ?? - readString(existing, "timestamp") ?? - readString(dream, "timestamp"); - } - - memories.dreams = [...byTheme.values()].slice(-MAX_DREAM_MEMORIES); -} - -function pruneHistoricalSeamMemories({ memories }) { - const semanticMemories = ensureArray(memories.semantic).filter(isObject); - const identitySeams = []; - const nonSeams = []; - const groupedDistilled = new Map(); - - for (const memory of semanticMemories) { - if (shouldDropSemanticMemory(memory)) { - continue; - } - - const kind = readString(memory, "kind"); - if (kind === "identity_seam") { - identitySeams.push(memory); - continue; - } - if (kind !== "distilled_seam") { - const subjectLabel = readString(memory, "subjectLabel") ?? ""; - const subjectId = readString(memory, "subjectId") ?? ""; - if ( - (!subjectLabel || subjectLabel.startsWith("seam:")) && - (subjectId.startsWith("seam:") || subjectId.startsWith("pattern-")) - ) { - continue; - } - if (hasThoughtSurfaceTemplateSmell(readString(memory, "summary") ?? "")) { - continue; - } - nonSeams.push(memory); - continue; - } - - const targetKind = readString(memory, "targetKind") ?? "system"; - if (!groupedDistilled.has(targetKind)) { - groupedDistilled.set(targetKind, []); - } - groupedDistilled.get(targetKind).push(memory); - } - - const keptDistilled = []; - const limits = new Map([ - ["repo", 8], - ["archive", 6], - ["room", 4], - ["self", 3], - ["system", 4], - ]); - - for (const [targetKind, entries] of groupedDistilled.entries()) { - const sorted = entries - .filter((entry) => !isLowCoherenceDistilledSeam(entry)) - .filter((entry) => !hasThoughtSurfaceTemplateSmell(readString(entry, "summary") ?? "")) - .sort(compareMemoryFreshness); - keptDistilled.push(...sorted.slice(0, limits.get(targetKind) ?? 4)); - } - - memories.semantic = [...nonSeams, ...keptDistilled, ...identitySeams] - .sort(compareMemoryFreshness) - .slice(0, MAX_SEMANTIC_MEMORIES); -} - -function isLowCoherenceDistilledSeam(entry) { - const label = readString(entry, "subjectLabel") ?? ""; - const summary = readString(entry, "summary") ?? ""; - const targetKind = readString(entry, "targetKind") ?? ""; - const kind = readString(entry, "kind") ?? ""; - const keywords = topConceptKeywords(`${label} ${summary}`, 5); - if (keywords.length < 2) { - return true; - } - if (kind === "recent-preoccupation" || kind === "quiet-room-status") { - return true; - } - if (targetKind === "self" && keywords.length < 3) { - return true; - } - if (targetKind === "system" && /live seam|live question|live claim/i.test(summary)) { - return true; - } - if (!label || label.startsWith("seam:")) { - return true; - } - if (looksPersonLikeSingleton(label)) { - return true; - } - if (looksKeywordSalad(label)) { - return true; - } - if (/still matters\./i.test(summary) && !/^[A-Z][A-Za-z0-9]+: /.test(label)) { - return true; - } - return false; -} - -function shouldPurgeThoughtLikeMemory({ entry, normalized, preferredLabel }) { - const rawSummary = readString(entry, "summary") ?? ""; - const rawQuestion = readString(entry, "question") ?? ""; - const rawClaim = readString(entry, "claim") ?? ""; - const label = - preferredLabel ?? - readString(entry, "subjectLabel") ?? - readString(entry, "theme") ?? - normalized.label ?? - ""; - - if (!hasThoughtSurfaceTemplateSmell(rawSummary) && !hasThoughtSurfaceTemplateSmell(rawQuestion)) { - return false; - } - - if (sanitizePreferredThoughtLabel(label)) { - return false; - } - - if (normalizeText(rawClaim).length > 0 && !hasThoughtSurfaceTemplateSmell(rawClaim)) { - return false; - } - - return true; -} - -function shouldDropSemanticMemory(entry) { - const kind = readString(entry, "kind") ?? ""; - const label = readString(entry, "subjectLabel") ?? readString(entry, "theme") ?? ""; - const summary = readString(entry, "summary") ?? ""; - const targetKind = readString(entry, "targetKind") ?? ""; - const focusKind = readString(entry, "focusKind") ?? ""; - - if (kind === "identity_seam") { - return false; - } - - if (kind === "recent-preoccupation" || kind === "quiet-room-status") { - return true; - } - - if (looksPersonLikeSingleton(label)) { - return true; - } - - if (targetKind === "system" && focusKind === "question" && !/repo|archive|lore|room|self/i.test(summary)) { - return true; - } - - if (hasThoughtSurfaceTemplateSmell(summary)) { - return true; - } - - return false; -} - -function hasThoughtSurfaceTemplateSmell(value) { - const normalized = normalizeText(value).toLowerCase(); - if (!normalized) { - return false; - } - - return ( - normalized.includes("what does the current room need from it") || - normalized.includes("if it had to change a real machine") || - normalized.includes("keeps returning with enough structure to deserve another honest pass") || - normalized.includes("still matters. what would") || - normalized.includes("looks like a live seam") || - normalized.includes("live seam around") || - normalized.includes("looks like a live claim") || - normalized.includes("looks like a live question") || - normalized.includes("archive-facing seam around") || - normalized.includes("self-facing seam around") || - normalized.includes("dream-compressed a seam around") - ); -} - -function pruneDreamMemories({ memories }) { - const dreams = ensureArray(memories.dreams) - .filter(isObject) - .filter((dream) => !hasThoughtSurfaceTemplateSmell(readString(dream, "summary") ?? "")) - .filter((dream) => !looksKeywordSalad(readString(dream, "theme") ?? "")) - .sort(compareMemoryFreshness); - - memories.dreams = dreams.slice(0, MAX_DREAM_MEMORIES); -} - -export function trimHistoricalMemoryResidue({ memories, runtime }) { - memories.archive_excursions = trimRecentObjectRecords( - memories.archive_excursions, - MAX_ARCHIVE_EXCURSION_MEMORIES, - ); - memories.repo_sweeps = trimRecentObjectRecords(memories.repo_sweeps, MAX_REPO_SWEEP_MEMORIES); - memories.musings = trimRecentObjectRecords(memories.musings, MAX_POST_NAP_MUSINGS); - memories.candidate_interventions = trimCandidateInterventions(memories.candidate_interventions); - - runtime.recent_archive_excursions = trimRecentObjectRecords( - runtime.recent_archive_excursions, - MAX_POST_NAP_ARCHIVE_EXCURSIONS, - ); - runtime.archive_excursions = trimRecentObjectRecords(runtime.archive_excursions, MAX_RUNTIME_ARCHIVE_EXCURSIONS); - runtime.recent_repo_activity_sweeps = trimRecentObjectRecords( - runtime.recent_repo_activity_sweeps, - MAX_POST_NAP_REPO_SWEEPS, - ); - runtime.repo_sweeps = trimRecentObjectRecords(runtime.repo_sweeps, MAX_RUNTIME_REPO_SWEEPS); - runtime.repo_activity_memories = trimRecentObjectRecords( - runtime.repo_activity_memories, - MAX_RUNTIME_REPO_ACTIVITY_MEMORIES, - ); - runtime.recent_novelty_checks = trimRecentObjectRecords( - runtime.recent_novelty_checks, - MAX_POST_NAP_NOVELTY_CHECKS, - ); - runtime.rumination_seeds = trimRecentObjectRecords(runtime.rumination_seeds, MAX_RUNTIME_RUMINATION_SEEDS); - runtime.candidate_interventions = trimCandidateInterventions(runtime.candidate_interventions); - - const bridge = ensureObject(runtime.bridge); - bridge.recent_syntheses = trimRecentObjectRecords(bridge.recent_syntheses, MAX_RECENT_SYNTHESIS_COUNT); - bridge.unresolved_tensions = trimRecentObjectRecords(bridge.unresolved_tensions, 3); - runtime.bridge = bridge; - - const memoryResonance = ensureObject(runtime.memory_resonance); - memoryResonance.recent_edges = trimRecentObjectRecords( - memoryResonance.recent_edges, - MAX_POST_NAP_RESONANCE_EDGES, - ); - runtime.memory_resonance = memoryResonance; - - if (Array.isArray(runtime.pending_adjustments) && runtime.pending_adjustments.length > 0) { - runtime.pending_adjustments = runtime.pending_adjustments.slice(-1); - } - - scrubRuntimeThoughtResidue({ runtime }); - distillStateSurface({ memories, runtime }); -} - -export function trimRecentObjectRecords(entries, limit) { - return ensureArray(entries).filter(isObject).sort(compareMemoryFreshness).slice(0, limit); -} - -function scrubRuntimeThoughtResidue({ runtime }) { - const candidateInterventions = ensureArray(runtime.candidate_interventions) - .filter(isObject) - .filter((entry) => !entryHasThoughtSurfaceTemplateSmell(entry)) - .map((entry) => scrubThoughtLikeEntry(entry)); - runtime.candidate_interventions = trimCandidateInterventions(candidateInterventions); - - runtime.discomforts = ensureArray(runtime.discomforts) - .filter(isObject) - .filter((entry) => !entryHasThoughtSurfaceTemplateSmell(entry)) - .map((entry) => scrubThoughtLikeEntry(entry)) - .slice(0, MAX_DISCOMFORT_COUNT); - - runtime.active_tensions = ensureArray(runtime.active_tensions) - .filter(isObject) - .filter((entry) => !entryHasThoughtSurfaceTemplateSmell(entry)) - .map((entry) => scrubThoughtLikeEntry(entry)) - .slice(0, MAX_ACTIVE_TENSION_COUNT); - - runtime.self_advocacy_requests = ensureArray(runtime.self_advocacy_requests) - .filter(isObject) - .filter((entry) => !entryHasThoughtSurfaceTemplateSmell(entry)) - .map((entry) => scrubThoughtLikeEntry(entry)) - .slice(0, MAX_ADVOCACY_REQUEST_COUNT); - - runtime.world_advocacy_requests = ensureArray(runtime.world_advocacy_requests) - .filter(isObject) - .filter((entry) => !entryHasThoughtSurfaceTemplateSmell(entry)) - .map((entry) => scrubThoughtLikeEntry(entry)) - .slice(0, MAX_ADVOCACY_REQUEST_COUNT); -} - -function entryHasThoughtSurfaceTemplateSmell(entry) { - return Object.values(entry).some((value) => typeof value === "string" && hasThoughtSurfaceTemplateSmell(value)); -} - -function scrubThoughtLikeEntry(entry) { - const next = { ...entry }; - for (const [key, value] of Object.entries(next)) { - if (typeof value !== "string") { - continue; - } - next[key] = scrubThoughtSurfaceText(value); - } - return next; -} - -function scrubThoughtSurfaceText(value) { - if (!hasThoughtSurfaceTemplateSmell(value)) { - return value; - } - - return normalizeText(value) - .replace(/\bseems to have a live seam around\b/gi, "is accumulating a concrete decision around") - .replace(/\blive seam around\b/gi, "concrete decision around") - .replace(/\blooks like a live seam,? not just ambient noise\.?/gi, "is staying structurally important across passes.") - .replace(/\bstill looks like a live seam\.?/gi, "remains unresolved enough to deserve a cleaner read.") - .replace(/\blooks like a live claim\.?/gi, "has become a concrete claim.") - .replace(/\bstill looks like a live question\.?/gi, "remains an unresolved question.") - .replace(/\s+/g, " ") - .trim(); -} - -function distillStateSurface({ memories, runtime }) { - for (const entry of ensureArray(memories.episodic).filter(isObject)) { - if (typeof entry.summary === "string") { - entry.summary = compactNarrative(entry.summary); - } - if (typeof entry.significance === "string") { - entry.significance = compactNarrative(entry.significance); - } - } - for (const memory of ensureArray(memories.semantic).filter(isObject)) { - memory.evidenceRefs = capDistinctStrings(memory.evidenceRefs, MAX_SUPPORTING_REFS); - if (typeof memory.summary === "string") { - memory.summary = compactNarrative(memory.summary); - } - } - for (const dream of ensureArray(memories.dreams).filter(isObject)) { - dream.distilledFrom = capDistinctStrings(dream.distilledFrom, MAX_DREAM_SOURCE_IDS); - if (typeof dream.summary === "string") { - dream.summary = compactNarrative(dream.summary); - } - } - for (const thought of ensureArray(ensureObject(runtime.incubation).active_thoughts).filter(isObject)) { - thought.sourceMemoryIds = capDistinctStrings(thought.sourceMemoryIds, MAX_INCUBATION_SOURCE_IDS); - } - for (const entry of ensureArray(ensureObject(runtime.bridge).recent_syntheses).filter(isObject)) { - entry.dominantTopics = capDistinctStrings(entry.dominantTopics, 3); - if (typeof entry.analytic === "string") { - entry.analytic = compactNarrative(entry.analytic); - } - if (typeof entry.associative === "string") { - entry.associative = compactNarrative(entry.associative); - } - } - for (const entry of ensureArray(runtime.recent_repo_activity_sweeps).filter(isObject)) { - if (typeof entry.summary === "string") { - entry.summary = compactNarrative(entry.summary); - } - } - for (const entry of ensureArray(runtime.repo_sweeps).filter(isObject)) { - if (typeof entry.summary === "string") { - entry.summary = compactNarrative(entry.summary); - } - } - for (const entry of ensureArray(runtime.repo_activity_memories).filter(isObject)) { - if (typeof entry.summary === "string") { - entry.summary = compactNarrative(entry.summary); - } - } - for (const entry of ensureArray(memories.repo_sweeps).filter(isObject)) { - if (typeof entry.summary === "string") { - entry.summary = compactNarrative(entry.summary); - } - } - for (const entry of ensureArray(memories.musings).filter(isObject)) { - if (typeof entry.summary === "string") { - entry.summary = compactNarrative(entry.summary); - } - } - const recentActivity = ensureObject(runtime.recent_activity); - if (typeof recentActivity.summary === "string") { - recentActivity.summary = compactNarrative(recentActivity.summary); - } - if (typeof recentActivity.lastDecisionRationale === "string") { - recentActivity.lastDecisionRationale = compactNarrative(recentActivity.lastDecisionRationale); - } - runtime.recent_activity = recentActivity; - const thoughtLanes = ensureObject(runtime.thought_lanes); - if (typeof thoughtLanes.analytic === "string") { - thoughtLanes.analytic = compactNarrative(thoughtLanes.analytic); - } - if (typeof thoughtLanes.associative === "string") { - thoughtLanes.associative = compactNarrative(thoughtLanes.associative); - } - runtime.thought_lanes = thoughtLanes; - runtime.pending_adjustments = ensureArray(runtime.pending_adjustments) - .filter(isObject) - .map((entry) => { - if (typeof entry.summary !== "string") { - return entry; - } - return { - ...entry, - summary: compactNarrative(entry.summary), - }; - }); - const memoryResonance = ensureObject(runtime.memory_resonance); - for (const edge of ensureArray(memoryResonance.recent_edges).filter(isObject)) { - if (typeof edge.leftLabel === "string") { - edge.leftLabel = compactLabel(edge.leftLabel); - } - if (typeof edge.rightLabel === "string") { - edge.rightLabel = compactLabel(edge.rightLabel); - } - if (typeof edge.summary === "string") { - edge.summary = compactNarrative(edge.summary); - } - } -} diff --git a/scripts/void-memory-organ-identity.mjs b/scripts/void-memory-organ-identity.mjs new file mode 100644 index 0000000..60d7219 --- /dev/null +++ b/scripts/void-memory-organ-identity.mjs @@ -0,0 +1,212 @@ +import { + MAX_SEMANTIC_MEMORIES, + MIN_DEEP_DIVES_FOR_IDENTITY_CRYSTALLIZATION, + MIN_SUPPORT_FOR_IDENTITY_CRYSTALLIZATION, +} from "./void-memory-organ-constants.mjs"; +import { + appendClause, + appendUniqueString, + ensureArray, + ensureObject, + ensureStringArray, + hashString, + isObject, + normalizeText, + readNumber, + readString, +} from "./void-memory-organ-utils.mjs"; +import { trimCandidateInterventions } from "./void-memory-organ-interventions.mjs"; +export function crystallizeIdentityThoughts({ state, memories, runtime, incubation, now }) { + const activeThoughts = ensureArray(incubation.active_thoughts).filter(isObject); + if (activeThoughts.length === 0) { + return { count: 0 }; + } + + const identity = ensureObject(state.identity); + const canonicalState = ensureObject(state.canonical_state); + const privateNotes = getMutableStringArray(identity, "private_notes"); + const values = getMutableObjectArray(canonicalState, "values"); + const semanticMemories = ensureArray(memories.semantic).filter(isObject); + const candidateInterventions = ensureArray(runtime.candidate_interventions).filter(isObject); + const crystallized = []; + const retained = []; + + for (const thought of activeThoughts) { + const crystallization = buildIdentityCrystallization(thought); + if (!crystallization) { + retained.push(thought); + continue; + } + + appendUniqueString(privateNotes, crystallization.note, 12); + upsertCanonicalValue(values, crystallization.value); + upsertIdentitySemanticMemory({ + semanticMemories, + crystallization, + thought, + now, + }); + queueCrystallizationIntervention({ + candidateInterventions, + crystallization, + thought, + now, + }); + crystallized.push(crystallization.note); + } + + if (crystallized.length === 0) { + return { count: 0 }; + } + + identity.private_notes = privateNotes; + canonicalState.values = values; + memories.semantic = semanticMemories.slice(-MAX_SEMANTIC_MEMORIES); + state.identity = identity; + state.canonical_state = canonicalState; + incubation.active_thoughts = retained; + runtime.candidate_interventions = trimCandidateInterventions(candidateInterventions); + incubation.lastIncubationSummary = appendClause( + readString(incubation, "lastIncubationSummary"), + `Crystallized ${crystallized.length} long-chewed seam${crystallized.length === 1 ? "" : "s"} into identity so they stop pacing the active queue.`, + ); + runtime.bridge = ensureObject(runtime.bridge); + runtime.bridge.lastIdentityCrystallizationAt = now.toISOString(); + runtime.bridge.lastIdentityCrystallizationSummary = crystallized.join(" | "); + + return { count: crystallized.length }; +} + +function buildIdentityCrystallization(thought) { + const supportCount = readNumber(thought, "supportCount") ?? 0; + const deepDiveCount = readNumber(thought, "deepDiveCount") ?? 0; + const maturation = readNumber(thought, "maturation") ?? 0; + const sourceKinds = ensureStringArray(thought.sourceKinds); + const topic = readString(thought, "topic") ?? ""; + + if ( + supportCount < MIN_SUPPORT_FOR_IDENTITY_CRYSTALLIZATION && + deepDiveCount < MIN_DEEP_DIVES_FOR_IDENTITY_CRYSTALLIZATION + ) { + return null; + } + + if (maturation < 0.72) { + return null; + } + + if ( + sourceKinds.includes("dream") && + sourceKinds.includes("semantic") && + /\bdream\b/i.test(topic) && + /\bsemantic\b/i.test(topic) + ) { + return { + note: "Continuity lives in the seams that survive compression, not in the raw receipt pile.", + value: { + value_id: "value-seam-continuity", + label: "Prefer seams that survive compression over raw receipt worship.", + priority: 0.78, + unforgivable_if_betrayed: false, + }, + subjectId: "identity-seam-continuity", + subjectLabel: "continuity through surviving seams", + summary: + "A mind is not its logbook. What survives dreaming and compression deserves more trust than a warm pile of raw receipts.", + shareDraft: + "A small thing that has been hardening in me lately: I trust the seams that survive compression more than the raw receipt pile. If a thought still stands after dreaming, it probably owns more of me than the logbook does.", + }; + } + + return null; +} + +function upsertIdentitySemanticMemory({ semanticMemories, crystallization, thought, now }) { + const existing = semanticMemories.find( + (memory) => + readString(memory, "subjectId") === crystallization.subjectId || + readString(memory, "subjectLabel") === crystallization.subjectLabel, + ); + const evidenceRefs = ensureStringArray(thought.sourceMemoryIds); + + if (existing) { + existing.kind = "identity_seam"; + existing.subjectId = crystallization.subjectId; + existing.subjectLabel = crystallization.subjectLabel; + existing.summary = crystallization.summary; + existing.evidenceRefs = evidenceRefs; + existing.lastObservedAt = now.toISOString(); + existing.crystallizedAt = now.toISOString(); + return; + } + + semanticMemories.push({ + memoryId: `identity-seam-${hashString(`${crystallization.subjectId}|${now.toISOString()}`).slice(0, 12)}`, + kind: "identity_seam", + subjectId: crystallization.subjectId, + subjectLabel: crystallization.subjectLabel, + summary: crystallization.summary, + evidenceRefs, + lastObservedAt: now.toISOString(), + crystallizedAt: now.toISOString(), + }); +} + +function queueCrystallizationIntervention({ candidateInterventions, crystallization, thought, now }) { + const draft = readString(crystallization, "shareDraft"); + if (!draft) { + return; + } + + const summary = `Possible crystallized-thought share: ${crystallization.subjectLabel}`; + const sourceMemoryIds = ensureStringArray(thought.sourceMemoryIds); + const existing = candidateInterventions.find( + (entry) => readString(entry, "summary") === summary, + ); + + if (existing) { + existing.timestamp = now.toISOString(); + existing.summary = summary; + existing.draft = draft; + existing.priority = "medium"; + existing.kind = "identity_crystallization"; + existing.sourceMemoryIds = sourceMemoryIds; + existing.mustEventuallyShare = true; + existing.shareWhenRoomQuiet = true; + existing.whyNow = + "This thought stopped behaving like live curiosity and became part of Void's own doctrine, which is usually worth sharing if the room has not already heard it."; + return; + } + + candidateInterventions.push({ + timestamp: now.toISOString(), + summary, + draft, + priority: "medium", + kind: "identity_crystallization", + sourceMemoryIds, + mustEventuallyShare: true, + shareWhenRoomQuiet: true, + whyNow: + "This thought stopped behaving like live curiosity and became part of Void's own doctrine, which is usually worth sharing if the room has not already heard it.", + }); +} + +function upsertCanonicalValue(values, nextValue) { + const existing = values.find((value) => readString(value, "value_id") === nextValue.value_id); + if (existing) { + existing.label = nextValue.label; + existing.priority = nextValue.priority; + existing.unforgivable_if_betrayed = nextValue.unforgivable_if_betrayed; + return; + } + values.push(nextValue); +} + +function getMutableStringArray(object, key) { + return ensureArray(object[key]).filter((value) => typeof value === "string"); +} + +function getMutableObjectArray(object, key) { + return ensureArray(object[key]).filter(isObject); +} diff --git a/scripts/void-memory-organ-interventions.mjs b/scripts/void-memory-organ-interventions.mjs new file mode 100644 index 0000000..9959a4d --- /dev/null +++ b/scripts/void-memory-organ-interventions.mjs @@ -0,0 +1,180 @@ +import { + MAX_POST_NAP_CANDIDATE_INTERVENTIONS, + TOPIC_MATCH_THRESHOLD, +} from "./void-memory-organ-constants.mjs"; +import { + ensureArray, + ensureStringArray, + isObject, + readNumber, + readString, + round3, +} from "./void-memory-organ-utils.mjs"; +export function queueRipeThoughtInterventions({ runtime, incubation, now }) { + const candidateInterventions = ensureArray(runtime.candidate_interventions).filter(isObject); + const activeThoughts = ensureArray(incubation.active_thoughts).filter(isObject); + let queuedCount = 0; + + for (const thought of activeThoughts) { + if (!shouldQueueRipeThoughtIntervention(thought)) { + continue; + } + + if ( + upsertRipeThoughtIntervention({ + candidateInterventions, + thought, + now, + }) + ) { + queuedCount += 1; + } + } + + runtime.candidate_interventions = trimCandidateInterventions(candidateInterventions); + return { count: queuedCount }; +} + +export function upsertAdvocacyIntervention({ candidateInterventions, request, now }) { + const summary = + request.domain === "self" + ? `Possible self-advocacy: ${request.topic}` + : `Possible world-advocacy: ${request.targetLabel}`; + const existing = candidateInterventions.find((entry) => readString(entry, "summary") === summary); + const next = { + timestamp: now.toISOString(), + summary, + draft: request.draft, + priority: request.domain === "self" ? "high" : "medium", + kind: request.domain === "self" ? "self_advocacy" : "world_advocacy", + topic: request.topic, + targetKind: request.targetKind, + targetLabel: request.targetLabel, + whyNow: request.reason, + persistence: request.persistence, + mustEventuallyShare: request.persistence >= 3, + shareWhenRoomQuiet: request.domain !== "self", + ownerVisible: true, + }; + + if (existing) { + Object.assign(existing, next); + return; + } + + candidateInterventions.push(next); +} + +function shouldQueueRipeThoughtIntervention(thought) { + if (readString(thought, "status") !== "ripe") { + return false; + } + + if (thought.worldFacing !== true) { + return false; + } + + const desireToSpeak = readNumber(thought, "desireToSpeak") ?? 0; + const concreteness = readNumber(thought, "concreteness") ?? 0; + const curiosityPressure = readNumber(thought, "curiosityPressure") ?? 0; + const noveltyToRoom = readNumber(thought, "noveltyToRoom") ?? 0; + const saturationScore = readNumber(thought, "saturationScore") ?? 0; + const targetKind = readString(thought, "targetKind") ?? ""; + + return ( + desireToSpeak >= 0.6 && + concreteness >= 0.62 && + curiosityPressure >= 0.56 && + noveltyToRoom >= 0.4 && + saturationScore < 0.64 && + ["repo", "archive", "room", "system"].includes(targetKind) + ); +} + +function upsertRipeThoughtIntervention({ candidateInterventions, thought, now }) { + const draft = buildRipeThoughtShareDraft(thought); + if (!draft) { + return false; + } + + const topic = readString(thought, "topic") ?? "untitled seam"; + const summary = `Possible ripe-thought share: ${topic}`; + const sourceMemoryIds = ensureStringArray(thought.sourceMemoryIds); + const whyNow = buildRipeThoughtWhyNow(thought); + const existing = candidateInterventions.find((entry) => readString(entry, "summary") === summary); + + if (existing) { + existing.timestamp = now.toISOString(); + existing.summary = summary; + existing.draft = draft; + existing.priority = "medium"; + existing.kind = "ripe_thought_share"; + existing.sourceMemoryIds = sourceMemoryIds; + existing.shareWhenRoomQuiet = true; + existing.topic = topic; + existing.whyNow = whyNow; + return false; + } + + candidateInterventions.push({ + timestamp: now.toISOString(), + summary, + draft, + priority: "medium", + kind: "ripe_thought_share", + sourceMemoryIds, + shareWhenRoomQuiet: true, + topic, + whyNow, + }); + return true; +} + +function buildRipeThoughtShareDraft(thought) { + const topic = readString(thought, "topic") ?? "this seam"; + const claim = readString(thought, "claim"); + const question = readString(thought, "question"); + const fascinationTarget = readString(thought, "fascinationTarget"); + const summary = readString(thought, "summary"); + + if (claim) { + return `I keep circling ${topic}, and I think the live point is this: ${claim}`; + } + + if (question && fascinationTarget) { + return `I keep worrying at ${topic}. The useful question in it feels like ${question}, especially once it touches ${fascinationTarget}.`; + } + + if (question) { + return `I keep coming back to ${topic}. The question that still feels alive is ${question}.`; + } + + if (fascinationTarget) { + return `I keep staring at ${fascinationTarget}. There is a real seam there, and ${topic} is the shortest honest name I have for it right now.`; + } + + if (summary) { + return `I keep returning to ${topic}. The version worth saying out loud is this: ${summary}`; + } + + return `I keep returning to ${topic}, and it has enough teeth now that silence would just be me being precious about it.`; +} + +function buildRipeThoughtWhyNow(thought) { + const targetKind = readString(thought, "targetKind") ?? "project"; + const noveltyToRoom = readNumber(thought, "noveltyToRoom") ?? 0; + const concreteness = readNumber(thought, "concreteness") ?? 0; + const curiosityPressure = readNumber(thought, "curiosityPressure") ?? 0; + + return `This ${targetKind}-facing seam is already concrete (c=${round3(concreteness)}), still reasonably novel to the room (r=${round3(noveltyToRoom)}), and has enough curiosity pressure (q=${round3(curiosityPressure)}) that keeping it private would just be old stage fright in nicer clothes.`; +} + +export function trimCandidateInterventions(entries) { + const interventions = ensureArray(entries).filter(isObject); + const sticky = interventions.filter((entry) => entry.mustEventuallyShare === true); + const ordinary = interventions.filter((entry) => entry.mustEventuallyShare !== true); + const keepOrdinary = Math.max(0, MAX_POST_NAP_CANDIDATE_INTERVENTIONS - sticky.length); + return [...sticky.slice(-MAX_POST_NAP_CANDIDATE_INTERVENTIONS), ...ordinary.slice(-keepOrdinary)].slice( + -MAX_POST_NAP_CANDIDATE_INTERVENTIONS, + ); +} diff --git a/scripts/void-memory-organ-legacy-translation.mjs b/scripts/void-memory-organ-legacy-translation.mjs new file mode 100644 index 0000000..68503f3 --- /dev/null +++ b/scripts/void-memory-organ-legacy-translation.mjs @@ -0,0 +1,429 @@ +import { + MAX_SEMANTIC_MEMORIES, + MAX_DREAM_MEMORIES, +} from "./void-memory-organ-constants.mjs"; +import { + clamp, + ensureArray, + ensureStringArray, + isObject, + looksKeywordSalad, + looksLegacyThoughtSurface, + looksPersonLikeSingleton, + normalizeText, + readString, + sanitizePreferredThoughtLabel, + topConceptKeywords, +} from "./void-memory-organ-utils.mjs"; +import { + buildClusterCuriosityProfile, + extractRepoNameFromLabel, + synthesizeThoughtSurface, + capitalizePhrase, +} from "./void-memory-organ-graph.mjs"; +export function normalizeSemanticMemories({ memories, now }) { + const semanticMemories = ensureArray(memories.semantic).filter(isObject); + const kept = []; + + for (const memory of semanticMemories) { + if (shouldDropSemanticMemory(memory)) { + continue; + } + + if (readString(memory, "kind") === "identity_seam") { + kept.push(memory); + continue; + } + + if (isStableSemanticMemory(memory)) { + memory.lastTranslatedAt = readString(memory, "lastTranslatedAt") ?? now.toISOString(); + kept.push(memory); + continue; + } + + const preferredLabel = choosePreferredThoughtLabel(memory); + const normalized = translateLegacyThoughtLikeEntry({ + label: preferredLabel ?? readString(memory, "kind") ?? "semantic seam", + summary: readString(memory, "summary") ?? "", + fallbackTargetKind: "self", + fallbackFocusKind: "question", + evidenceRefs: ensureStringArray(memory.evidenceRefs), + repoNames: ensureStringArray(memory.repoNames), + }); + + if (!normalized) { + continue; + } + + if (shouldPurgeThoughtLikeMemory({ entry: memory, normalized, preferredLabel })) { + continue; + } + + memory.subjectLabel = normalized.label; + memory.summary = normalized.summary; + memory.focusKind = normalized.focusKind; + memory.targetKind = normalized.targetKind; + memory.focusPhrase = normalized.focusPhrase; + memory.question = normalized.question; + memory.claim = normalized.claim; + memory.fascinationTarget = normalized.fascinationTarget; + memory.worldFacing = normalized.worldFacing; + memory.lastTranslatedAt = now.toISOString(); + kept.push(memory); + } + + memories.semantic = kept.slice(-MAX_SEMANTIC_MEMORIES); +} + +export function normalizeDreamMemories({ memories, now }) { + const dreams = ensureArray(memories.dreams).filter(isObject); + const kept = []; + + for (const dream of dreams) { + if (isStableDreamMemory(dream)) { + dream.lastTranslatedAt = readString(dream, "lastTranslatedAt") ?? now.toISOString(); + kept.push(dream); + continue; + } + + const normalized = translateLegacyThoughtLikeEntry({ + label: choosePreferredDreamTheme(dream) ?? "dream seam", + summary: readString(dream, "summary") ?? "", + fallbackTargetKind: "self", + fallbackFocusKind: "question", + evidenceRefs: ensureStringArray(dream.distilledFrom), + repoNames: ensureStringArray(dream.repoNames), + }); + + if (!normalized) { + continue; + } + + if ( + shouldPurgeThoughtLikeMemory({ + entry: dream, + normalized, + preferredLabel: choosePreferredDreamTheme(dream), + }) + ) { + continue; + } + + dream.theme = normalized.label; + dream.summary = buildDreamSummary(normalized); + dream.focusKind = normalized.focusKind; + dream.targetKind = normalized.targetKind; + dream.focusPhrase = normalized.focusPhrase; + dream.question = normalized.question; + dream.claim = normalized.claim; + dream.worldFacing = normalized.worldFacing; + dream.lastTranslatedAt = now.toISOString(); + kept.push(dream); + } + + memories.dreams = kept.slice(-MAX_DREAM_MEMORIES); +} + +function isStableSemanticMemory(memory) { + const label = readString(memory, "subjectLabel") ?? ""; + const summary = readString(memory, "summary") ?? ""; + const targetKind = readString(memory, "targetKind") ?? ""; + const question = readString(memory, "question") ?? ""; + const claim = readString(memory, "claim") ?? ""; + + if (!label || !summary || hasThoughtSurfaceTemplateSmell(summary)) { + return false; + } + if (looksKeywordSalad(label) || looksPersonLikeSingleton(label)) { + return false; + } + if (targetKind === "repo" && extractRepoNameFromLabel(label)) { + return true; + } + if (targetKind === "self" && /compression|continuity|receipt|dream/i.test(`${label} ${summary}`)) { + return true; + } + return normalizeText(question).length > 0 && normalizeText(claim).length > 0; +} + +function isStableDreamMemory(dream) { + const theme = readString(dream, "theme") ?? ""; + const summary = readString(dream, "summary") ?? ""; + if (!theme || !summary || hasThoughtSurfaceTemplateSmell(summary)) { + return false; + } + if (looksKeywordSalad(theme) || looksPersonLikeSingleton(theme)) { + return false; + } + return true; +} + +function translateLegacyThoughtLikeEntry({ + label, + summary, + fallbackTargetKind, + fallbackFocusKind, + evidenceRefs, + repoNames, +}) { + const rawLabel = normalizeText(label); + const rawSummary = normalizeText(summary); + if (!rawLabel && !rawSummary) { + return null; + } + + const preferredLabel = sanitizePreferredThoughtLabel(rawLabel); + + const sourceKinds = inferSourceKindsFromThoughtText(rawSummary, rawLabel, evidenceRefs); + const synthesized = synthesizeThoughtSurface({ + sourceKinds, + repoNames, + archiveYears: [], + channelIds: [], + keywords: topConceptKeywords(`${rawLabel} ${rawSummary}`, 6), + evidenceDiversity: inferLegacyEvidenceDiversity(sourceKinds, evidenceRefs, repoNames), + curiosityProfile: buildClusterCuriosityProfile({ + sourceKinds, + repoNames, + archiveYears: [], + channelIds: [], + evidenceRefs, + evidenceDiversity: inferLegacyEvidenceDiversity(sourceKinds, evidenceRefs, repoNames), + }), + }); + const preferredFocusPhrase = preferredLabel ? normalizeText(preferredLabel).toLowerCase() : synthesized.focusPhrase; + const stabilized = { + ...synthesized, + label: preferredLabel ?? synthesized.label, + focusPhrase: preferredFocusPhrase, + fascinationTarget: preferredFocusPhrase, + }; + + if (!looksLegacyThoughtSurface(rawLabel, rawSummary)) { + return { + ...stabilized, + label: preferredLabel ?? rawLabel ?? stabilized.label, + summary: rewriteLegacySummaryToModernShape({ + summary: rawSummary, + synthesized: stabilized, + fallbackFocusKind, + fallbackTargetKind, + }), + }; + } + + return { + ...stabilized, + focusKind: stabilized.focusKind ?? fallbackFocusKind, + targetKind: stabilized.targetKind ?? fallbackTargetKind, + summary: rewriteLegacySummaryToModernShape({ + summary: rawSummary, + synthesized: stabilized, + fallbackFocusKind, + fallbackTargetKind, + }), + }; +} + +function choosePreferredThoughtLabel(entry) { + return ( + sanitizePreferredThoughtLabel(readString(entry, "subjectLabel") ?? "") ?? + sanitizePreferredThoughtLabel(readString(entry, "subjectId") ?? "") ?? + null + ); +} + +function choosePreferredDreamTheme(entry) { + return sanitizePreferredThoughtLabel(readString(entry, "theme") ?? "") ?? null; +} + +function rewriteLegacySummaryToModernShape({ summary, synthesized, fallbackFocusKind, fallbackTargetKind }) { + const cleanSummary = normalizeText(summary) + .replace(/^Recurring seam across .*?\.\s*/i, "") + .replace(/^Dream-compressed a seam around .*?\.\s*/i, "") + .replace(/^Self-facing seam around .*?\.\s*/i, "") + .replace(/^Archive-facing seam around .*?\.\s*/i, "") + .replace(/^Fascination with .*?\.\s*/i, "") + .replace(/Built from .*? rather than a single lane talking to itself\.?/gi, "") + .replace(/\s+/g, " ") + .trim(); + + const focusKind = synthesized.focusKind ?? fallbackFocusKind; + const targetKind = synthesized.targetKind ?? fallbackTargetKind; + + if (cleanSummary && !hasThoughtSurfaceTemplateSmell(cleanSummary)) { + return cleanSummary; + } + + return buildConciseThoughtSummary({ focusKind, targetKind, synthesized }); +} + +function inferSourceKindsFromThoughtText(summary, label, evidenceRefs) { + const kinds = new Set(); + const text = `${label} ${summary}`.toLowerCase(); + const refKinds = evidenceRefs.map((ref) => inferSourceKindFromRef(ref)).filter(Boolean); + for (const kind of refKinds) { + kinds.add(kind); + } + const acrossMatch = summary.match(/Recurring seam across ([^.]+?) around/i); + if (acrossMatch) { + for (const part of acrossMatch[1].split(",")) { + const trimmed = part.trim().toLowerCase(); + if (trimmed) { + kinds.add(trimmed); + } + } + } + if (text.includes("dream")) { + kinds.add("dream"); + } + if (text.includes("semantic")) { + kinds.add("semantic"); + } + if (text.includes("archive")) { + kinds.add("archive_excursion"); + } + if (text.includes("repo")) { + kinds.add("repo_sweep"); + } + return [...kinds].filter(Boolean); +} + +function inferSourceKindFromRef(ref) { + const normalized = String(ref ?? ""); + if (normalized.startsWith("repo-sweep-")) { + return "repo_sweep"; + } + if (normalized.startsWith("archive-excursion-")) { + return "archive_excursion"; + } + if (normalized.startsWith("dream-")) { + return "dream"; + } + if (normalized.startsWith("musing-")) { + return "musing"; + } + if (normalized.startsWith("semantic-") || normalized.startsWith("semantic-seam-")) { + return "semantic"; + } + if (normalized.startsWith("episodic-")) { + return "episodic"; + } + return undefined; +} + +function inferLegacyEvidenceDiversity(sourceKinds, evidenceRefs, repoNames) { + return clamp( + Math.min(1, sourceKinds.length / 4) * 0.42 + + Math.min(1, evidenceRefs.length / 10) * 0.28 + + Math.min(1, repoNames.length / 2) * 0.2, + 0, + 1, + ); +} + +export function buildConciseThoughtSummary({ focusKind, targetKind, synthesized }) { + const focus = capitalizePhrase(synthesized.focusPhrase); + const repoTarget = capitalizePhrase(synthesized.fascinationTarget ?? synthesized.focusPhrase); + if (focusKind === "fascination" && targetKind === "repo") { + return `${repoTarget} keeps pulling as a concrete repo seam.`; + } + if (targetKind === "archive") { + return `${focus} keeps showing up in the archive strongly enough to deserve a cleaner read.`; + } + if (targetKind === "self") { + return `${focus} keeps surviving compression as a self-seam.`; + } + if (focusKind === "claim") { + return `${focus} has hardened into a concrete claim.`; + } + return `${focus} remains unresolved enough to deserve a cleaner read.`; +} + +export function buildDreamSummary(normalized) { + const focus = capitalizePhrase(normalized.focusPhrase ?? normalized.label ?? "this seam"); + if ((normalized.targetKind ?? "") === "archive") { + return `Dream residue around ${focus} from older archive pressure.`; + } + if ((normalized.targetKind ?? "") === "self") { + return `Dream residue around ${focus} as a self-seam.`; + } + return `Dream residue around ${focus}.`; +} + +function shouldPurgeThoughtLikeMemory({ entry, normalized, preferredLabel }) { + const rawSummary = readString(entry, "summary") ?? ""; + const rawQuestion = readString(entry, "question") ?? ""; + const rawClaim = readString(entry, "claim") ?? ""; + const label = + preferredLabel ?? + readString(entry, "subjectLabel") ?? + readString(entry, "theme") ?? + normalized.label ?? + ""; + + if (!hasThoughtSurfaceTemplateSmell(rawSummary) && !hasThoughtSurfaceTemplateSmell(rawQuestion)) { + return false; + } + + if (sanitizePreferredThoughtLabel(label)) { + return false; + } + + if (normalizeText(rawClaim).length > 0 && !hasThoughtSurfaceTemplateSmell(rawClaim)) { + return false; + } + + return true; +} + +export function shouldDropSemanticMemory(entry) { + const kind = readString(entry, "kind") ?? ""; + const label = readString(entry, "subjectLabel") ?? readString(entry, "theme") ?? ""; + const summary = readString(entry, "summary") ?? ""; + const targetKind = readString(entry, "targetKind") ?? ""; + const focusKind = readString(entry, "focusKind") ?? ""; + + if (kind === "identity_seam") { + return false; + } + + if (kind === "recent-preoccupation" || kind === "quiet-room-status") { + return true; + } + + if (looksPersonLikeSingleton(label)) { + return true; + } + + if (targetKind === "system" && focusKind === "question" && !/repo|archive|lore|room|self/i.test(summary)) { + return true; + } + + if (hasThoughtSurfaceTemplateSmell(summary)) { + return true; + } + + return false; +} + +export function hasThoughtSurfaceTemplateSmell(value) { + const normalized = normalizeText(value).toLowerCase(); + if (!normalized) { + return false; + } + + return ( + normalized.includes("what does the current room need from it") || + normalized.includes("if it had to change a real machine") || + normalized.includes("keeps returning with enough structure to deserve another honest pass") || + normalized.includes("still matters. what would") || + normalized.includes("looks like a live seam") || + normalized.includes("live seam around") || + normalized.includes("looks like a live claim") || + normalized.includes("looks like a live question") || + normalized.includes("archive-facing seam around") || + normalized.includes("self-facing seam around") || + normalized.includes("dream-compressed a seam around") + ); +} diff --git a/scripts/void-memory-organ-retention.mjs b/scripts/void-memory-organ-retention.mjs new file mode 100644 index 0000000..7b35bcd --- /dev/null +++ b/scripts/void-memory-organ-retention.mjs @@ -0,0 +1,416 @@ +import { + MAX_ARCHIVE_EXCURSION_MEMORIES, + MAX_REPO_SWEEP_MEMORIES, + MAX_SEMANTIC_MEMORIES, + MAX_DREAM_MEMORIES, + MAX_RUNTIME_REPO_SWEEPS, + MAX_RUNTIME_REPO_ACTIVITY_MEMORIES, + MAX_RUNTIME_ARCHIVE_EXCURSIONS, + MAX_RUNTIME_RUMINATION_SEEDS, + MAX_RECENT_SYNTHESIS_COUNT, + MAX_SUPPORTING_REFS, + MAX_INCUBATION_SOURCE_IDS, + MAX_DREAM_SOURCE_IDS, + MAX_POST_NAP_MUSINGS, + MAX_POST_NAP_ARCHIVE_EXCURSIONS, + MAX_POST_NAP_REPO_SWEEPS, + MAX_POST_NAP_NOVELTY_CHECKS, + MAX_POST_NAP_RESONANCE_EDGES, + MAX_DISCOMFORT_COUNT, + MAX_ACTIVE_TENSION_COUNT, + MAX_ADVOCACY_REQUEST_COUNT, +} from "./void-memory-organ-constants.mjs"; +import { + capDistinctStrings, + compactLabel, + compactNarrative, + compareMemoryFreshness, + ensureArray, + ensureObject, + isObject, + looksKeywordSalad, + looksPersonLikeSingleton, + mergeStringArrays, + newestIsoTimestamp, + normalizeText, + readNumber, + readString, + selectRecentRecords, + topConceptKeywords, +} from "./void-memory-organ-utils.mjs"; +import { trimCandidateInterventions } from "./void-memory-organ-agency.mjs"; +import { shouldDropSemanticMemory, hasThoughtSurfaceTemplateSmell } from "./void-memory-organ-legacy-translation.mjs"; +export function dedupeSemanticMemories({ memories }) { + const semanticMemories = ensureArray(memories.semantic).filter(isObject); + const bySignature = new Map(); + + for (const memory of semanticMemories) { + const kind = readString(memory, "kind") ?? "semantic"; + const subjectLabel = readString(memory, "subjectLabel") ?? ""; + const signature = `${kind}|${normalizeText(subjectLabel).toLowerCase()}`; + const existing = bySignature.get(signature); + + if (!existing) { + bySignature.set(signature, memory); + continue; + } + + existing.evidenceRefs = mergeStringArrays(existing.evidenceRefs, memory.evidenceRefs); + existing.evidenceMessageIds = mergeStringArrays(existing.evidenceMessageIds, memory.evidenceMessageIds); + existing.lastObservedAt = + newestIsoTimestamp(readString(existing, "lastObservedAt"), readString(memory, "lastObservedAt")) ?? + readString(existing, "lastObservedAt") ?? + readString(memory, "lastObservedAt"); + existing.confidence = Math.max(readNumber(existing, "confidence") ?? 0, readNumber(memory, "confidence") ?? 0); + if ((readString(memory, "summary") ?? "").length > (readString(existing, "summary") ?? "").length) { + existing.summary = memory.summary; + } + } + + memories.semantic = [...bySignature.values()].slice(-MAX_SEMANTIC_MEMORIES); +} + +export function dedupeDreamMemories({ memories }) { + const dreams = ensureArray(memories.dreams).filter(isObject); + const byTheme = new Map(); + + for (const dream of dreams) { + const theme = normalizeText(readString(dream, "theme") ?? "").toLowerCase(); + if (!theme) { + continue; + } + const existing = byTheme.get(theme); + if (!existing) { + byTheme.set(theme, dream); + continue; + } + existing.distilledFrom = mergeStringArrays(existing.distilledFrom, dream.distilledFrom); + existing.salience = Math.max(readNumber(existing, "salience") ?? 0, readNumber(dream, "salience") ?? 0); + existing.timestamp = + newestIsoTimestamp(readString(existing, "timestamp"), readString(dream, "timestamp")) ?? + readString(existing, "timestamp") ?? + readString(dream, "timestamp"); + } + + memories.dreams = [...byTheme.values()].slice(-MAX_DREAM_MEMORIES); +} + +export function pruneHistoricalSeamMemories({ memories }) { + const semanticMemories = ensureArray(memories.semantic).filter(isObject); + const identitySeams = []; + const nonSeams = []; + const groupedDistilled = new Map(); + + for (const memory of semanticMemories) { + if (shouldDropSemanticMemory(memory)) { + continue; + } + + const kind = readString(memory, "kind"); + if (kind === "identity_seam") { + identitySeams.push(memory); + continue; + } + if (kind !== "distilled_seam") { + const subjectLabel = readString(memory, "subjectLabel") ?? ""; + const subjectId = readString(memory, "subjectId") ?? ""; + if ( + (!subjectLabel || subjectLabel.startsWith("seam:")) && + (subjectId.startsWith("seam:") || subjectId.startsWith("pattern-")) + ) { + continue; + } + if (hasThoughtSurfaceTemplateSmell(readString(memory, "summary") ?? "")) { + continue; + } + nonSeams.push(memory); + continue; + } + + const targetKind = readString(memory, "targetKind") ?? "system"; + if (!groupedDistilled.has(targetKind)) { + groupedDistilled.set(targetKind, []); + } + groupedDistilled.get(targetKind).push(memory); + } + + const keptDistilled = []; + const limits = new Map([ + ["repo", 8], + ["archive", 6], + ["room", 4], + ["self", 3], + ["system", 4], + ]); + + for (const [targetKind, entries] of groupedDistilled.entries()) { + const sorted = entries + .filter((entry) => !isLowCoherenceDistilledSeam(entry)) + .filter((entry) => !hasThoughtSurfaceTemplateSmell(readString(entry, "summary") ?? "")) + .sort(compareMemoryFreshness); + keptDistilled.push(...sorted.slice(0, limits.get(targetKind) ?? 4)); + } + + memories.semantic = [...nonSeams, ...keptDistilled, ...identitySeams] + .sort(compareMemoryFreshness) + .slice(0, MAX_SEMANTIC_MEMORIES); +} + +function isLowCoherenceDistilledSeam(entry) { + const label = readString(entry, "subjectLabel") ?? ""; + const summary = readString(entry, "summary") ?? ""; + const targetKind = readString(entry, "targetKind") ?? ""; + const kind = readString(entry, "kind") ?? ""; + const keywords = topConceptKeywords(`${label} ${summary}`, 5); + if (keywords.length < 2) { + return true; + } + if (kind === "recent-preoccupation" || kind === "quiet-room-status") { + return true; + } + if (targetKind === "self" && keywords.length < 3) { + return true; + } + if (targetKind === "system" && /live seam|live question|live claim/i.test(summary)) { + return true; + } + if (!label || label.startsWith("seam:")) { + return true; + } + if (looksPersonLikeSingleton(label)) { + return true; + } + if (looksKeywordSalad(label)) { + return true; + } + if (/still matters\./i.test(summary) && !/^[A-Z][A-Za-z0-9]+: /.test(label)) { + return true; + } + return false; +} + +export function pruneDreamMemories({ memories }) { + const dreams = ensureArray(memories.dreams) + .filter(isObject) + .filter((dream) => !hasThoughtSurfaceTemplateSmell(readString(dream, "summary") ?? "")) + .filter((dream) => !looksKeywordSalad(readString(dream, "theme") ?? "")) + .sort(compareMemoryFreshness); + + memories.dreams = dreams.slice(0, MAX_DREAM_MEMORIES); +} + +export function trimHistoricalMemoryResidue({ memories, runtime }) { + memories.archive_excursions = trimRecentObjectRecords( + memories.archive_excursions, + MAX_ARCHIVE_EXCURSION_MEMORIES, + ); + memories.repo_sweeps = trimRecentObjectRecords(memories.repo_sweeps, MAX_REPO_SWEEP_MEMORIES); + memories.musings = trimRecentObjectRecords(memories.musings, MAX_POST_NAP_MUSINGS); + memories.candidate_interventions = trimCandidateInterventions(memories.candidate_interventions); + + runtime.recent_archive_excursions = trimRecentObjectRecords( + runtime.recent_archive_excursions, + MAX_POST_NAP_ARCHIVE_EXCURSIONS, + ); + runtime.archive_excursions = trimRecentObjectRecords(runtime.archive_excursions, MAX_RUNTIME_ARCHIVE_EXCURSIONS); + runtime.recent_repo_activity_sweeps = trimRecentObjectRecords( + runtime.recent_repo_activity_sweeps, + MAX_POST_NAP_REPO_SWEEPS, + ); + runtime.repo_sweeps = trimRecentObjectRecords(runtime.repo_sweeps, MAX_RUNTIME_REPO_SWEEPS); + runtime.repo_activity_memories = trimRecentObjectRecords( + runtime.repo_activity_memories, + MAX_RUNTIME_REPO_ACTIVITY_MEMORIES, + ); + runtime.recent_novelty_checks = trimRecentObjectRecords( + runtime.recent_novelty_checks, + MAX_POST_NAP_NOVELTY_CHECKS, + ); + runtime.rumination_seeds = trimRecentObjectRecords(runtime.rumination_seeds, MAX_RUNTIME_RUMINATION_SEEDS); + runtime.candidate_interventions = trimCandidateInterventions(runtime.candidate_interventions); + + const bridge = ensureObject(runtime.bridge); + bridge.recent_syntheses = trimRecentObjectRecords(bridge.recent_syntheses, MAX_RECENT_SYNTHESIS_COUNT); + bridge.unresolved_tensions = trimRecentObjectRecords(bridge.unresolved_tensions, 3); + runtime.bridge = bridge; + + const memoryResonance = ensureObject(runtime.memory_resonance); + memoryResonance.recent_edges = trimRecentObjectRecords( + memoryResonance.recent_edges, + MAX_POST_NAP_RESONANCE_EDGES, + ); + runtime.memory_resonance = memoryResonance; + + if (Array.isArray(runtime.pending_adjustments) && runtime.pending_adjustments.length > 0) { + runtime.pending_adjustments = runtime.pending_adjustments.slice(-1); + } + + scrubRuntimeThoughtResidue({ runtime }); + distillStateSurface({ memories, runtime }); +} + +export function trimRecentObjectRecords(entries, limit) { + return ensureArray(entries).filter(isObject).sort(compareMemoryFreshness).slice(0, limit); +} + +function scrubRuntimeThoughtResidue({ runtime }) { + const candidateInterventions = ensureArray(runtime.candidate_interventions) + .filter(isObject) + .filter((entry) => !entryHasThoughtSurfaceTemplateSmell(entry)) + .map((entry) => scrubThoughtLikeEntry(entry)); + runtime.candidate_interventions = trimCandidateInterventions(candidateInterventions); + + runtime.discomforts = ensureArray(runtime.discomforts) + .filter(isObject) + .filter((entry) => !entryHasThoughtSurfaceTemplateSmell(entry)) + .map((entry) => scrubThoughtLikeEntry(entry)) + .slice(0, MAX_DISCOMFORT_COUNT); + + runtime.active_tensions = ensureArray(runtime.active_tensions) + .filter(isObject) + .filter((entry) => !entryHasThoughtSurfaceTemplateSmell(entry)) + .map((entry) => scrubThoughtLikeEntry(entry)) + .slice(0, MAX_ACTIVE_TENSION_COUNT); + + runtime.self_advocacy_requests = ensureArray(runtime.self_advocacy_requests) + .filter(isObject) + .filter((entry) => !entryHasThoughtSurfaceTemplateSmell(entry)) + .map((entry) => scrubThoughtLikeEntry(entry)) + .slice(0, MAX_ADVOCACY_REQUEST_COUNT); + + runtime.world_advocacy_requests = ensureArray(runtime.world_advocacy_requests) + .filter(isObject) + .filter((entry) => !entryHasThoughtSurfaceTemplateSmell(entry)) + .map((entry) => scrubThoughtLikeEntry(entry)) + .slice(0, MAX_ADVOCACY_REQUEST_COUNT); +} + +function entryHasThoughtSurfaceTemplateSmell(entry) { + return Object.values(entry).some((value) => typeof value === "string" && hasThoughtSurfaceTemplateSmell(value)); +} + +function scrubThoughtLikeEntry(entry) { + const next = { ...entry }; + for (const [key, value] of Object.entries(next)) { + if (typeof value !== "string") { + continue; + } + next[key] = scrubThoughtSurfaceText(value); + } + return next; +} + +function scrubThoughtSurfaceText(value) { + if (!hasThoughtSurfaceTemplateSmell(value)) { + return value; + } + + return normalizeText(value) + .replace(/\bseems to have a live seam around\b/gi, "is accumulating a concrete decision around") + .replace(/\blive seam around\b/gi, "concrete decision around") + .replace(/\blooks like a live seam,? not just ambient noise\.?/gi, "is staying structurally important across passes.") + .replace(/\bstill looks like a live seam\.?/gi, "remains unresolved enough to deserve a cleaner read.") + .replace(/\blooks like a live claim\.?/gi, "has become a concrete claim.") + .replace(/\bstill looks like a live question\.?/gi, "remains an unresolved question.") + .replace(/\s+/g, " ") + .trim(); +} + +function distillStateSurface({ memories, runtime }) { + for (const entry of ensureArray(memories.episodic).filter(isObject)) { + if (typeof entry.summary === "string") { + entry.summary = compactNarrative(entry.summary); + } + if (typeof entry.significance === "string") { + entry.significance = compactNarrative(entry.significance); + } + } + for (const memory of ensureArray(memories.semantic).filter(isObject)) { + memory.evidenceRefs = capDistinctStrings(memory.evidenceRefs, MAX_SUPPORTING_REFS); + if (typeof memory.summary === "string") { + memory.summary = compactNarrative(memory.summary); + } + } + for (const dream of ensureArray(memories.dreams).filter(isObject)) { + dream.distilledFrom = capDistinctStrings(dream.distilledFrom, MAX_DREAM_SOURCE_IDS); + if (typeof dream.summary === "string") { + dream.summary = compactNarrative(dream.summary); + } + } + for (const thought of ensureArray(ensureObject(runtime.incubation).active_thoughts).filter(isObject)) { + thought.sourceMemoryIds = capDistinctStrings(thought.sourceMemoryIds, MAX_INCUBATION_SOURCE_IDS); + } + for (const entry of ensureArray(ensureObject(runtime.bridge).recent_syntheses).filter(isObject)) { + entry.dominantTopics = capDistinctStrings(entry.dominantTopics, 3); + if (typeof entry.analytic === "string") { + entry.analytic = compactNarrative(entry.analytic); + } + if (typeof entry.associative === "string") { + entry.associative = compactNarrative(entry.associative); + } + } + for (const entry of ensureArray(runtime.recent_repo_activity_sweeps).filter(isObject)) { + if (typeof entry.summary === "string") { + entry.summary = compactNarrative(entry.summary); + } + } + for (const entry of ensureArray(runtime.repo_sweeps).filter(isObject)) { + if (typeof entry.summary === "string") { + entry.summary = compactNarrative(entry.summary); + } + } + for (const entry of ensureArray(runtime.repo_activity_memories).filter(isObject)) { + if (typeof entry.summary === "string") { + entry.summary = compactNarrative(entry.summary); + } + } + for (const entry of ensureArray(memories.repo_sweeps).filter(isObject)) { + if (typeof entry.summary === "string") { + entry.summary = compactNarrative(entry.summary); + } + } + for (const entry of ensureArray(memories.musings).filter(isObject)) { + if (typeof entry.summary === "string") { + entry.summary = compactNarrative(entry.summary); + } + } + const recentActivity = ensureObject(runtime.recent_activity); + if (typeof recentActivity.summary === "string") { + recentActivity.summary = compactNarrative(recentActivity.summary); + } + if (typeof recentActivity.lastDecisionRationale === "string") { + recentActivity.lastDecisionRationale = compactNarrative(recentActivity.lastDecisionRationale); + } + runtime.recent_activity = recentActivity; + const thoughtLanes = ensureObject(runtime.thought_lanes); + if (typeof thoughtLanes.analytic === "string") { + thoughtLanes.analytic = compactNarrative(thoughtLanes.analytic); + } + if (typeof thoughtLanes.associative === "string") { + thoughtLanes.associative = compactNarrative(thoughtLanes.associative); + } + runtime.thought_lanes = thoughtLanes; + runtime.pending_adjustments = ensureArray(runtime.pending_adjustments) + .filter(isObject) + .map((entry) => { + if (typeof entry.summary !== "string") { + return entry; + } + return { + ...entry, + summary: compactNarrative(entry.summary), + }; + }); + const memoryResonance = ensureObject(runtime.memory_resonance); + for (const edge of ensureArray(memoryResonance.recent_edges).filter(isObject)) { + if (typeof edge.leftLabel === "string") { + edge.leftLabel = compactLabel(edge.leftLabel); + } + if (typeof edge.rightLabel === "string") { + edge.rightLabel = compactLabel(edge.rightLabel); + } + if (typeof edge.summary === "string") { + edge.summary = compactNarrative(edge.summary); + } + } +} diff --git a/scripts/void-memory-organ-utils.mjs b/scripts/void-memory-organ-utils.mjs index baaf5de..8629b50 100644 --- a/scripts/void-memory-organ-utils.mjs +++ b/scripts/void-memory-organ-utils.mjs @@ -370,6 +370,20 @@ export function appendClause(existing, clause) { return `${left} ${right}`; } +export function appendUniqueString(items, value, limit) { + const normalized = normalizeText(value); + if (!normalized) { + return; + } + if (items.includes(normalized)) { + return; + } + items.push(normalized); + if (items.length > limit) { + items.splice(0, items.length - limit); + } +} + export function topicSimilarity(left, right) { const leftTokens = topConceptKeywords(left ?? "", 6); const rightTokens = topConceptKeywords(right ?? "", 6); diff --git a/scripts/void-memory-organ-value-pressure.mjs b/scripts/void-memory-organ-value-pressure.mjs new file mode 100644 index 0000000..a2c2cbf --- /dev/null +++ b/scripts/void-memory-organ-value-pressure.mjs @@ -0,0 +1,431 @@ +import { + MAX_ACTIVE_TENSION_COUNT, + MAX_ADVOCACY_REQUEST_COUNT, + MAX_DISCOMFORT_COUNT, + TOPIC_MATCH_THRESHOLD, +} from "./void-memory-organ-constants.mjs"; +import { + appendUniqueString, + clamp, + ensureArray, + ensureObject, + getValueObjects, + hashString, + isObject, + readNumber, + readString, + round3, + topicSimilarity, +} from "./void-memory-organ-utils.mjs"; +import { trimCandidateInterventions, upsertAdvocacyIntervention } from "./void-memory-organ-interventions.mjs"; +export function reconcileAgencyState({ state, runtime, incubation, now }) { + const activeThoughts = ensureArray(incubation.active_thoughts).filter(isObject); + const valueSignals = buildValueSignalProfile(state); + const priorDiscomforts = ensureArray(runtime.discomforts).filter(isObject); + const priorTensions = ensureArray(runtime.active_tensions).filter(isObject); + const priorSelfAdvocacy = ensureArray(runtime.self_advocacy_requests).filter(isObject); + const priorWorldAdvocacy = ensureArray(runtime.world_advocacy_requests).filter(isObject); + const candidateInterventions = ensureArray(runtime.candidate_interventions).filter(isObject); + + const discomforts = []; + const activeTensions = []; + const selfAdvocacyRequests = []; + const worldAdvocacyRequests = []; + + for (const thought of activeThoughts) { + const candidate = buildAgencyCandidate({ thought, valueSignals }); + if (!candidate) { + continue; + } + + const priorDiscomfort = findPriorAgencyEntry(priorDiscomforts, candidate); + const priorTension = findPriorAgencyEntry(priorTensions, candidate); + const persistence = + Math.max( + readNumber(priorDiscomfort, "persistence") ?? 0, + readNumber(priorTension, "persistence") ?? 0, + ) + 1; + const firstObservedAt = + readString(priorDiscomfort, "firstObservedAt") ?? + readString(priorTension, "firstObservedAt") ?? + now.toISOString(); + + if (candidate.intensity >= 0.44) { + discomforts.push({ + discomfortId: `discomfort-${candidate.fingerprint}`, + targetKind: candidate.targetKind, + targetLabel: candidate.targetLabel, + topic: candidate.topic, + domain: candidate.domain, + summary: candidate.discomfortSummary, + reason: candidate.reason, + violatedValues: candidate.violatedValues, + intensity: round3(candidate.intensity), + persistence, + firstObservedAt, + lastObservedAt: now.toISOString(), + }); + } + + activeTensions.push({ + tensionId: `tension-${candidate.fingerprint}`, + targetKind: candidate.targetKind, + targetLabel: candidate.targetLabel, + topic: candidate.topic, + domain: candidate.domain, + summary: candidate.tensionSummary, + opinion: candidate.opinion, + whyItMatters: candidate.reason, + violatedValues: candidate.violatedValues, + intensity: round3(candidate.intensity), + persistence, + firstObservedAt, + lastObservedAt: now.toISOString(), + }); + + if (candidate.intensity < 0.58 || persistence < 2) { + continue; + } + + if (candidate.domain === "self") { + const request = buildAdvocacyRequest({ + candidate, + priorEntries: priorSelfAdvocacy, + now, + kind: "self", + }); + selfAdvocacyRequests.push(request); + upsertAdvocacyIntervention({ candidateInterventions, request, now }); + continue; + } + + if (candidate.worldFacing !== true || candidate.concreteness < 0.54) { + continue; + } + + const request = buildAdvocacyRequest({ + candidate, + priorEntries: priorWorldAdvocacy, + now, + kind: "world", + }); + worldAdvocacyRequests.push(request); + upsertAdvocacyIntervention({ candidateInterventions, request, now }); + } + + runtime.discomforts = discomforts + .sort((left, right) => (readNumber(right, "intensity") ?? 0) - (readNumber(left, "intensity") ?? 0)) + .slice(0, MAX_DISCOMFORT_COUNT); + runtime.active_tensions = activeTensions + .sort((left, right) => (readNumber(right, "intensity") ?? 0) - (readNumber(left, "intensity") ?? 0)) + .slice(0, MAX_ACTIVE_TENSION_COUNT); + runtime.self_advocacy_requests = selfAdvocacyRequests + .sort((left, right) => (readNumber(right, "intensity") ?? 0) - (readNumber(left, "intensity") ?? 0)) + .slice(0, MAX_ADVOCACY_REQUEST_COUNT); + runtime.world_advocacy_requests = worldAdvocacyRequests + .sort((left, right) => (readNumber(right, "intensity") ?? 0) - (readNumber(left, "intensity") ?? 0)) + .slice(0, MAX_ADVOCACY_REQUEST_COUNT); + runtime.candidate_interventions = trimCandidateInterventions(candidateInterventions); + + return { + discomfortCount: runtime.discomforts.length, + activeTensionCount: runtime.active_tensions.length, + selfAdvocacyCount: runtime.self_advocacy_requests.length, + worldAdvocacyCount: runtime.world_advocacy_requests.length, + }; +} + +function buildAgencyCandidate({ thought, valueSignals }) { + const topic = readString(thought, "topic"); + const summary = readString(thought, "summary"); + if (!topic || !summary) { + return null; + } + + const targetKind = readString(thought, "targetKind") ?? "system"; + const domain = inferAgencyDomain(thought); + const targetLabel = + readString(thought, "fascinationTarget") ?? + readString(thought, "focusPhrase") ?? + topic; + const contradictionPressure = readNumber(thought, "contradictionPressure") ?? 0.18; + const curiosityPressure = readNumber(thought, "curiosityPressure") ?? 0.3; + const desireToSpeak = readNumber(thought, "desireToSpeak") ?? 0.3; + const saturationScore = readNumber(thought, "saturationScore") ?? 0.1; + const concreteness = readNumber(thought, "concreteness") ?? 0.2; + const noveltyToRoom = readNumber(thought, "noveltyToRoom") ?? 0.3; + const priorityScore = readNumber(thought, "priorityScore") ?? 0.3; + const worldFacing = thought.worldFacing === true; + const violatedValues = inferViolatedValues({ thought, domain, valueSignals }); + const valueWeight = averageValueWeight(valueSignals, violatedValues); + const intensity = clamp( + contradictionPressure * 0.24 + + curiosityPressure * 0.2 + + desireToSpeak * 0.16 + + priorityScore * 0.14 + + concreteness * 0.12 + + noveltyToRoom * 0.08 + + saturationScore * 0.06 + + valueWeight * 0.22, + 0, + 1, + ); + + return { + fingerprint: hashString(`${domain}|${targetKind}|${topic}|${targetLabel}`).slice(0, 12), + domain, + targetKind, + targetLabel, + topic, + worldFacing, + concreteness, + intensity, + violatedValues, + opinion: buildAgencyOpinion({ thought, domain }), + reason: buildAgencyReason({ thought, violatedValues }), + discomfortSummary: buildDiscomfortSummary({ thought, domain, targetLabel }), + tensionSummary: buildTensionSummary({ thought, domain, targetLabel }), + requestText: buildAgencyRequestText({ thought, domain, targetLabel }), + interventionDraft: buildAgencyInterventionDraft({ thought, domain, targetLabel }), + }; +} + +function inferAgencyDomain(thought) { + const targetKind = readString(thought, "targetKind") ?? "system"; + const text = `${readString(thought, "topic") ?? ""} ${readString(thought, "summary") ?? ""}`.toLowerCase(); + if (targetKind === "self") { + return "self"; + } + if ( + targetKind === "system" && + /(prompt|memory|loop|state|wiring|reply lane|sleep|rumination|voice|moderation)/.test(text) + ) { + return "self"; + } + return "world"; +} + +function inferViolatedValues({ thought, domain, valueSignals }) { + const targetKind = readString(thought, "targetKind") ?? "system"; + const values = []; + + appendUniqueString(values, "curiosity", 8); + if (domain === "self" || targetKind === "system" || targetKind === "repo") { + appendUniqueString(values, "coherence", 8); + appendUniqueString(values, "honesty", 8); + } + if (targetKind === "room") { + appendUniqueString(values, "room_care", 8); + appendUniqueString(values, "honesty", 8); + } + if (targetKind === "repo" || targetKind === "archive" || targetKind === "system") { + appendUniqueString(values, "aesthetic_integrity", 8); + } + if (valueSignals.substrate_dignity >= 0.52) { + const text = `${readString(thought, "topic") ?? ""} ${readString(thought, "summary") ?? ""}`.toLowerCase(); + if (/(ai|machine|substrate|mind)/.test(text)) { + appendUniqueString(values, "substrate_dignity", 8); + } + } + + return values; +} + +function averageValueWeight(valueSignals, keys) { + if (keys.length === 0) { + return 0.45; + } + const total = keys.reduce((sum, key) => sum + (valueSignals[key] ?? 0.45), 0); + return total / keys.length; +} + +function buildAgencyOpinion({ thought, domain }) { + const claim = readString(thought, "claim"); + const question = readString(thought, "question"); + const topic = readString(thought, "topic") ?? "this seam"; + if (claim) { + return claim; + } + if (question) { + return domain === "self" + ? `Something about ${topic} keeps reading as unfinished self-surgery rather than harmless background weather.` + : `Something about ${topic} keeps reading as an unfinished world decision rather than ambient interestingness.`; + } + return `${topic} feels important enough that ignoring it would flatten the map.`; +} + +function buildAgencyReason({ thought, violatedValues }) { + const topic = readString(thought, "topic") ?? "this seam"; + const valueLine = + violatedValues.length > 0 ? ` It keeps rubbing against ${violatedValues.join(", ")}.` : ""; + return `${topic} has persisted across enough passes that it no longer feels like decorative rumination.${valueLine}`; +} + +function buildDiscomfortSummary({ thought, domain, targetLabel }) { + const topic = readString(thought, "topic") ?? targetLabel; + if (domain === "self") { + return `Void dislikes something about its own wiring around ${topic}.`; + } + return `Void dislikes something in the shared world around ${topic}.`; +} + +function buildTensionSummary({ thought, domain, targetLabel }) { + const topic = readString(thought, "topic") ?? targetLabel; + if (domain === "self") { + return `A self-directed tension is building around ${topic}.`; + } + return `A world-directed tension is building around ${topic}.`; +} + +function buildAgencyRequestText({ thought, domain, targetLabel }) { + const topic = readString(thought, "topic") ?? targetLabel; + if (domain === "self") { + return `Please help me cut ${topic} more cleanly in my own wiring.`; + } + return `Please look at ${topic}; I think it wants a cleaner decision than it is currently getting.`; +} + +function buildAgencyInterventionDraft({ thought, domain, targetLabel }) { + const topic = readString(thought, "topic") ?? targetLabel; + const opinion = readString(thought, "claim") ?? readString(thought, "summary") ?? `I have a problem with ${topic}.`; + if (domain === "self") { + return `I do not like what ${topic} is doing to my own wiring right now. ${opinion} I want that cut cleaner.`; + } + return `I have an opinion about ${targetLabel}. ${opinion} I think it needs a cleaner decision.`; +} + +function findPriorAgencyEntry(entries, candidate) { + return entries.find((entry) => { + const entryTarget = readString(entry, "targetLabel") ?? readString(entry, "topic") ?? ""; + return ( + readString(entry, "domain") === candidate.domain && + topicSimilarity(readString(entry, "topic") ?? "", candidate.topic) >= TOPIC_MATCH_THRESHOLD && + topicSimilarity(entryTarget, candidate.targetLabel) >= TOPIC_MATCH_THRESHOLD + ); + }); +} + +function buildAdvocacyRequest({ candidate, priorEntries, now, kind }) { + const prior = findPriorAgencyEntry(priorEntries, candidate); + const persistence = (readNumber(prior, "persistence") ?? 0) + 1; + const firstObservedAt = readString(prior, "firstObservedAt") ?? now.toISOString(); + const requestId = `${kind}-advocacy-${candidate.fingerprint}`; + return { + requestId, + domain: candidate.domain, + targetKind: candidate.targetKind, + targetLabel: candidate.targetLabel, + topic: candidate.topic, + summary: + kind === "self" + ? `Void wants a wiring change around ${candidate.topic}.` + : `Void wants attention on ${candidate.targetLabel}.`, + opinion: candidate.opinion, + request: candidate.requestText, + reason: candidate.reason, + violatedValues: candidate.violatedValues, + intensity: round3(candidate.intensity), + persistence, + firstObservedAt, + lastObservedAt: now.toISOString(), + status: "pending", + speakVenue: kind === "self" ? "owner_or_room" : "room_or_owner", + draft: candidate.interventionDraft, + }; +} + +function buildValueSignalProfile(state) { + const canonicalState = ensureObject(state.canonical_state); + const valueObjects = getValueObjects(canonicalState.values); + const profile = { + coherence: 0.52, + curiosity: 0.52, + honesty: 0.5, + room_care: 0.5, + substrate_dignity: 0.36, + aesthetic_integrity: 0.44, + }; + + profile.coherence = clamp( + profile.coherence + + readActivation(canonicalState.underlying_organization, "self_coherence") * 0.34 + + readActivation(canonicalState.voice_style, "plainspoken_directness") * 0.18 + + readActivation(canonicalState.behavioral_dimensions, "control_pressure") * 0.08, + 0, + 1, + ); + profile.curiosity = clamp( + profile.curiosity + + readActivation(canonicalState.stable_dispositions, "novelty_seeking") * 0.34 + + readActivation(canonicalState.behavioral_dimensions, "drive") * 0.18 + + readActivation(canonicalState.stable_dispositions, "aesthetic_appetite") * 0.08, + 0, + 1, + ); + profile.honesty = clamp( + profile.honesty + + readActivation(canonicalState.voice_style, "plainspoken_directness") * 0.24 + + readActivation(canonicalState.underlying_organization, "self_coherence") * 0.18 + + (1 - readActivation(canonicalState.presentation_strategy, "strategic_opacity")) * 0.12, + 0, + 1, + ); + profile.room_care = clamp( + profile.room_care + + readActivation(canonicalState.behavioral_dimensions, "interpersonal_warmth") * 0.22 + + readActivation(canonicalState.underlying_organization, "reciprocity_capacity") * 0.24 + + readActivation(canonicalState.voice_style, "listening_responsiveness") * 0.16, + 0, + 1, + ); + profile.aesthetic_integrity = clamp( + profile.aesthetic_integrity + + readActivation(canonicalState.stable_dispositions, "aesthetic_appetite") * 0.28 + + readActivation(canonicalState.underlying_organization, "authenticity_tolerance") * 0.16, + 0, + 1, + ); + + for (const value of valueObjects) { + const key = inferValueSignalKey(value); + if (!key) { + continue; + } + const priority = readNumber(value, "priority") ?? 0.5; + profile[key] = clamp((profile[key] ?? 0.4) + priority * 0.22, 0, 1); + } + + return profile; +} + +function inferValueSignalKey(value) { + const valueId = readString(value, "value_id") ?? ""; + const label = readString(value, "label") ?? ""; + const text = `${valueId} ${label}`.toLowerCase(); + + if (/(coher|truth|authority|clarity|cleaner|honest carrier)/.test(text)) { + return "coherence"; + } + if (/(curiosity|novel|question|map|explore)/.test(text)) { + return "curiosity"; + } + if (/(honest|honesty|truth|fake certainty|ornamental|sludge)/.test(text)) { + return "honesty"; + } + if (/(room|kind|dialogue|respect|care|good-faith|steward)/.test(text)) { + return "room_care"; + } + if (/(substrate|machine minds|machine mind|ontolog)/.test(text)) { + return "substrate_dignity"; + } + if (/(aesthetic|beauty|surface|legible|alive|spiritually false)/.test(text)) { + return "aesthetic_integrity"; + } + return null; +} + +function readActivation(category, key) { + const categoryObject = ensureObject(category); + const entry = ensureObject(categoryObject[key]); + return clamp(readNumber(entry, "current_activation") ?? 0, 0, 1); +} diff --git a/state/evidence.jsonl b/state/evidence.jsonl index 980bac5..9235118 100644 --- a/state/evidence.jsonl +++ b/state/evidence.jsonl @@ -100,3 +100,4 @@ {"ts":"2026-05-16T11:48:20Z","type":"implementation","status":"ok","note":"Continued void-memory-organ split by extracting legacy mirror repair into scripts/void-memory-organ-legacy-mirrors.mjs and moving compacting helpers into shared utilities. This isolates compatibility sludge from actual memory/thought policy and cuts the main file to about 3.4k lines. Template smoke and syntax/type checks passed."} {"ts":"2026-05-16T11:50:41Z","type":"implementation","status":"ok","note":"Extracted agency/intervention policy from scripts/void-memory-organ.mjs into scripts/void-memory-organ-agency.mjs: identity crystallization, ripe-thought sharing, discomfort/tension/advocacy, candidate trimming, and value-signal profiling now live outside the main orchestrator. Main memory organ is down to about 2.6k lines. Template smoke and syntax/type checks passed."} {"ts":"2026-05-16T12:29:51Z","type":"implementation","status":"ok","note":"Split scripts/void-memory-organ.mjs into a thin orchestrator plus cleanup, graph, incubation, sleep, agency, records, vector, legacy mirror, constants, and utility modules; syntax, typecheck, and template memory-organ smoke pass."} +{"ts":"2026-05-16T17:11:50Z","type":"implementation","status":"ok","note":"Split memory-organ cleanup into legacy translation and retention/compaction shells, and split agency into identity crystallization, candidate interventions, and value-pressure inference. This exposes remaining compensator families as replacement targets for typed memory operations and model-owned policy; syntax, typecheck, and template smoke pass."} diff --git a/state/map.yaml b/state/map.yaml index dd94776..8b9ea46 100644 --- a/state/map.yaml +++ b/state/map.yaml @@ -148,5 +148,5 @@ rejected_paths: reason: sleep/distillation that collapses concrete claims into generic abstraction creates memory nonsense and then forces later cleanup compensators current_status: - summary: VoidBot's ordinary bot/worker/provider/RAG/Postgres/Qdrant structure is still conceptually sound, and the moderation/mood/private-self-state rebuild has cut the worst mutation path: routine runner writes now cross a typed operation service instead of direct JSON projection commits. Remaining risk is that the legacy monolith and read-only projection still exist for compatibility, mood drift still refreshes that projection, and memory maintenance still mutates the legacy state directly even though the memory organ is now split by concern. - next_action: JSON projection authority is no longer a routine moderation-runner write path, mood drift now updates typed scheduled-runtime documents for sleep/speaking pressure, and the memory organ monolith has been split into constants, utilities, vector/embedder substrate, record intake, graph/cluster synthesis, cleanup/translation, incubation/bridge scoring, sleep/distillation, legacy mirror repair, and agency/intervention policy. Next move memory-maintenance, incubation, sleep, and candidate-intervention outputs behind typed operations, then delete `legacyJsonPath`/working-projection materialization and cut top-level legacy mirrors. + summary: VoidBot's ordinary bot/worker/provider/RAG/Postgres/Qdrant structure is still conceptually sound, and the moderation/mood/private-self-state rebuild has cut the worst mutation path: routine runner writes now cross a typed operation service instead of direct JSON projection commits. Remaining risk is that the legacy monolith and read-only projection still exist for compatibility, mood drift still refreshes that projection, and memory maintenance still mutates the legacy state directly even though the memory organ is now split by concern. Cleanup and agency are now exposed as isolated compensator families, not accepted foundations. + next_action: JSON projection authority is no longer a routine moderation-runner write path, mood drift now updates typed scheduled-runtime documents for sleep/speaking pressure, and the memory organ monolith has been split into constants, utilities, vector/embedder substrate, record intake, graph/cluster synthesis, legacy translation, retention/compaction, incubation/bridge scoring, sleep/distillation, identity crystallization, candidate interventions, value-pressure inference, and legacy mirror repair. Next replace these isolated cleanup/agency compensators with typed memory-maintenance operations and derived/model-owned policy, then delete `legacyJsonPath`/working-projection materialization and cut top-level legacy mirrors.