mirror-mind 0.0.1 → 0.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/hooks/log-assistant-reply.sh +85 -0
- package/.claude/hooks/log-session-end.sh +85 -0
- package/.claude/hooks/log-user-prompt.sh +85 -0
- package/.claude/hooks/mirror-inject.sh +85 -0
- package/.claude/hooks/session-start.sh +85 -0
- package/.gemini/hooks/log-assistant.sh +85 -0
- package/.gemini/hooks/log-user.sh +85 -0
- package/.gemini/hooks/session-end.sh +85 -0
- package/.gemini/hooks/session-start.sh +85 -0
- package/.pi/extensions/mirror-logger.ts +646 -0
- package/.pi/skills/mm-backup/SKILL.md +21 -0
- package/.pi/skills/mm-build/SKILL.md +1206 -0
- package/.pi/skills/mm-consolidate/SKILL.md +112 -0
- package/.pi/skills/mm-consult/SKILL.md +70 -0
- package/.pi/skills/mm-conversations/SKILL.md +16 -0
- package/.pi/skills/mm-discard/SKILL.md +39 -0
- package/.pi/skills/mm-explore/SKILL.md +255 -0
- package/.pi/skills/mm-help/SKILL.md +74 -0
- package/.pi/skills/mm-identity/SKILL.md +96 -0
- package/.pi/skills/mm-journal/SKILL.md +16 -0
- package/.pi/skills/mm-journey/SKILL.md +23 -0
- package/.pi/skills/mm-journeys/SKILL.md +15 -0
- package/.pi/skills/mm-memories/SKILL.md +17 -0
- package/.pi/skills/mm-mirror/SKILL.md +98 -0
- package/.pi/skills/mm-mute/SKILL.md +18 -0
- package/.pi/skills/mm-new/SKILL.md +18 -0
- package/.pi/skills/mm-recall/SKILL.md +17 -0
- package/.pi/skills/mm-release-notes/SKILL.md +52 -0
- package/.pi/skills/mm-seed/SKILL.md +19 -0
- package/.pi/skills/mm-shadow/SKILL.md +117 -0
- package/.pi/skills/mm-soul/SKILL.md +502 -0
- package/.pi/skills/mm-tasks/SKILL.md +24 -0
- package/.pi/skills/mm-update/SKILL.md +32 -0
- package/.pi/skills/mm-week/SKILL.md +28 -0
- package/.pi/skills/mm-welcome/SKILL.md +24 -0
- package/AGENTS.md +188 -0
- package/README.md +126 -9
- package/bin/loader.js +67 -0
- package/bin/mirror-hook.js +7 -0
- package/bin/mirror.js +7 -0
- package/docs/releases/index.md +100 -0
- package/docs/releases/pending-cutoffs.md +510 -0
- package/docs/releases/v0.10.0.md +123 -0
- package/docs/releases/v0.10.1.md +66 -0
- package/docs/releases/v0.10.10.md +51 -0
- package/docs/releases/v0.10.11.md +39 -0
- package/docs/releases/v0.10.12.md +57 -0
- package/docs/releases/v0.10.13.md +35 -0
- package/docs/releases/v0.10.14.md +42 -0
- package/docs/releases/v0.10.15.md +53 -0
- package/docs/releases/v0.10.16.md +35 -0
- package/docs/releases/v0.10.17.md +43 -0
- package/docs/releases/v0.10.2.md +64 -0
- package/docs/releases/v0.10.3.md +78 -0
- package/docs/releases/v0.10.4.md +42 -0
- package/docs/releases/v0.10.5.md +57 -0
- package/docs/releases/v0.10.6.md +47 -0
- package/docs/releases/v0.10.7.md +53 -0
- package/docs/releases/v0.10.8.md +49 -0
- package/docs/releases/v0.10.9.md +37 -0
- package/docs/releases/v0.11.0.md +86 -0
- package/docs/releases/v0.12.0.md +67 -0
- package/docs/releases/v0.13.0.md +64 -0
- package/docs/releases/v0.14.0.md +68 -0
- package/docs/releases/v0.15.0.md +48 -0
- package/docs/releases/v0.16.0.md +50 -0
- package/docs/releases/v0.16.1.md +55 -0
- package/docs/releases/v0.16.2.md +36 -0
- package/docs/releases/v0.17.0.md +50 -0
- package/docs/releases/v0.17.1.md +58 -0
- package/docs/releases/v0.17.2.md +42 -0
- package/docs/releases/v0.17.3.md +58 -0
- package/docs/releases/v0.18.0.md +84 -0
- package/docs/releases/v0.18.1.md +51 -0
- package/docs/releases/v0.18.2.md +69 -0
- package/docs/releases/v0.19.0.md +85 -0
- package/docs/releases/v0.19.1.md +46 -0
- package/docs/releases/v0.20.0.md +85 -0
- package/docs/releases/v0.21.0.md +61 -0
- package/docs/releases/v0.21.1.md +47 -0
- package/docs/releases/v0.22.0.md +77 -0
- package/docs/releases/v0.22.1.md +42 -0
- package/docs/releases/v0.23.0.md +105 -0
- package/docs/releases/v0.24.0.md +125 -0
- package/docs/releases/v0.25.0.md +107 -0
- package/docs/releases/v0.26.0.md +139 -0
- package/docs/releases/v0.26.1.md +65 -0
- package/docs/releases/v0.26.2.md +43 -0
- package/docs/releases/v0.27.0.md +63 -0
- package/docs/releases/v0.28.0.md +59 -0
- package/docs/releases/v0.29.0.md +61 -0
- package/docs/releases/v0.29.1.md +59 -0
- package/docs/releases/v0.29.2.md +59 -0
- package/docs/releases/v0.29.3.md +93 -0
- package/docs/releases/v0.30.0.md +75 -0
- package/docs/releases/v0.30.1.md +93 -0
- package/docs/releases/v0.30.2.md +66 -0
- package/docs/releases/v0.31.0.md +122 -0
- package/docs/releases/v0.31.1.md +142 -0
- package/docs/releases/v0.31.10.md +118 -0
- package/docs/releases/v0.31.11.md +88 -0
- package/docs/releases/v0.31.12.md +105 -0
- package/docs/releases/v0.31.13.md +99 -0
- package/docs/releases/v0.31.14.md +97 -0
- package/docs/releases/v0.31.2.md +115 -0
- package/docs/releases/v0.31.3.md +142 -0
- package/docs/releases/v0.31.4.md +117 -0
- package/docs/releases/v0.31.5.md +155 -0
- package/docs/releases/v0.31.6.md +132 -0
- package/docs/releases/v0.31.7.md +132 -0
- package/docs/releases/v0.31.8.md +120 -0
- package/docs/releases/v0.31.9.md +120 -0
- package/docs/releases/v0.32.0.md +261 -0
- package/docs/releases/v0.32.1.md +59 -0
- package/docs/releases/v0.33.0.md +164 -0
- package/docs/releases/v0.8.0.md +118 -0
- package/docs/releases/v0.9.0.md +98 -0
- package/docs/releases/v0.9.1.md +83 -0
- package/package.json +102 -6
- package/plugins/mirror-mind/.claude-plugin/plugin.json +17 -0
- package/plugins/mirror-mind/hooks/hooks.json +62 -0
- package/plugins/mirror-mind/hooks/log-assistant-reply.sh +98 -0
- package/plugins/mirror-mind/hooks/log-session-end.sh +98 -0
- package/plugins/mirror-mind/hooks/log-user-prompt.sh +98 -0
- package/plugins/mirror-mind/hooks/mirror-inject.sh +98 -0
- package/plugins/mirror-mind/hooks/session-start.sh +98 -0
- package/plugins/mirror-mind/skills/mm-backup/SKILL.md +27 -0
- package/plugins/mirror-mind/skills/mm-build/SKILL.md +1206 -0
- package/plugins/mirror-mind/skills/mm-consolidate/SKILL.md +112 -0
- package/plugins/mirror-mind/skills/mm-consult/SKILL.md +83 -0
- package/plugins/mirror-mind/skills/mm-conversations/SKILL.md +17 -0
- package/plugins/mirror-mind/skills/mm-discard/SKILL.md +40 -0
- package/plugins/mirror-mind/skills/mm-explore/SKILL.md +241 -0
- package/plugins/mirror-mind/skills/mm-help/SKILL.md +104 -0
- package/plugins/mirror-mind/skills/mm-identity/SKILL.md +96 -0
- package/plugins/mirror-mind/skills/mm-journal/SKILL.md +21 -0
- package/plugins/mirror-mind/skills/mm-journey/SKILL.md +37 -0
- package/plugins/mirror-mind/skills/mm-journeys/SKILL.md +15 -0
- package/plugins/mirror-mind/skills/mm-memories/SKILL.md +20 -0
- package/plugins/mirror-mind/skills/mm-mirror/SKILL.md +67 -0
- package/plugins/mirror-mind/skills/mm-mute/SKILL.md +18 -0
- package/plugins/mirror-mind/skills/mm-new/SKILL.md +18 -0
- package/plugins/mirror-mind/skills/mm-recall/SKILL.md +17 -0
- package/plugins/mirror-mind/skills/mm-release-notes/SKILL.md +31 -0
- package/plugins/mirror-mind/skills/mm-seed/SKILL.md +15 -0
- package/plugins/mirror-mind/skills/mm-shadow/SKILL.md +117 -0
- package/plugins/mirror-mind/skills/mm-soul/SKILL.md +488 -0
- package/plugins/mirror-mind/skills/mm-tasks/SKILL.md +36 -0
- package/plugins/mirror-mind/skills/mm-update/SKILL.md +30 -0
- package/plugins/mirror-mind/skills/mm-week/SKILL.md +46 -0
- package/plugins/mirror-mind/skills/mm-welcome/SKILL.md +22 -0
- package/scripts/codex-hooks/session-end.sh +85 -0
- package/scripts/codex-hooks/session-start.sh +85 -0
- package/scripts/codex-mirror.sh +48 -0
- package/templates/identity/README.md +119 -0
- package/templates/identity/ego/behavior.yaml +52 -0
- package/templates/identity/ego/constraints.yaml +15 -0
- package/templates/identity/ego/identity.yaml +43 -0
- package/templates/identity/journeys/personal-growth.yaml +39 -0
- package/templates/identity/organization/identity.yaml +24 -0
- package/templates/identity/organization/principles.yaml +17 -0
- package/templates/identity/personas/coach.yaml +94 -0
- package/templates/identity/personas/designer.yaml +93 -0
- package/templates/identity/personas/doctor.yaml +96 -0
- package/templates/identity/personas/engineer.yaml +77 -0
- package/templates/identity/personas/financial.yaml +97 -0
- package/templates/identity/personas/prompt-engineer.yaml +122 -0
- package/templates/identity/personas/researcher.yaml +88 -0
- package/templates/identity/personas/strategist.yaml +91 -0
- package/templates/identity/personas/teacher.yaml +95 -0
- package/templates/identity/personas/therapist.yaml +91 -0
- package/templates/identity/personas/thinker.yaml +74 -0
- package/templates/identity/personas/writer.yaml +76 -0
- package/templates/identity/self/config.yaml +16 -0
- package/templates/identity/self/soul.yaml +105 -0
- package/templates/identity/user/identity.yaml +30 -0
- package/ts/src/backup/staging.ts +62 -0
- package/ts/src/backup/zipBackup.ts +267 -0
- package/ts/src/backup/zipReader.ts +109 -0
- package/ts/src/backup/zipWriter.ts +148 -0
- package/ts/src/builder/activeCheckpoint.ts +134 -0
- package/ts/src/builder/aggregateStatus.ts +34 -0
- package/ts/src/builder/approve.ts +149 -0
- package/ts/src/builder/argv.ts +292 -0
- package/ts/src/builder/ariadMethod.ts +536 -0
- package/ts/src/builder/artifacts/artifactSurfaces.ts +106 -0
- package/ts/src/builder/artifacts/artifactWriter.ts +113 -0
- package/ts/src/builder/artifacts/closureArtifacts.ts +199 -0
- package/ts/src/builder/artifacts/deliveryStoryArtifacts.ts +316 -0
- package/ts/src/builder/artifacts/planArtifacts.ts +242 -0
- package/ts/src/builder/artifacts/scaffoldSections.ts +273 -0
- package/ts/src/builder/artifacts/scaffoldState.ts +227 -0
- package/ts/src/builder/artifacts/storyFiles.ts +61 -0
- package/ts/src/builder/artifacts/storyIndex.ts +166 -0
- package/ts/src/builder/cadence.ts +59 -0
- package/ts/src/builder/card.ts +192 -0
- package/ts/src/builder/checkpointRefused.ts +66 -0
- package/ts/src/builder/closure.ts +964 -0
- package/ts/src/builder/commands.ts +2053 -0
- package/ts/src/builder/cursorPosition.ts +176 -0
- package/ts/src/builder/cursorTransitions.ts +77 -0
- package/ts/src/builder/deliveryCursor.ts +577 -0
- package/ts/src/builder/deliveryStoryClosure.ts +521 -0
- package/ts/src/builder/deliveryStoryPlan.ts +536 -0
- package/ts/src/builder/deliveryStoryReady.ts +160 -0
- package/ts/src/builder/deliveryStoryRoadmapClosure.ts +151 -0
- package/ts/src/builder/expand.ts +336 -0
- package/ts/src/builder/flowUnit.ts +339 -0
- package/ts/src/builder/homeSurface.ts +122 -0
- package/ts/src/builder/implementationGuard.ts +125 -0
- package/ts/src/builder/index.ts +11 -0
- package/ts/src/builder/lifecycleRefusal.ts +92 -0
- package/ts/src/builder/lifecycleRibbon.ts +159 -0
- package/ts/src/builder/load.ts +313 -0
- package/ts/src/builder/methodAdoption.ts +113 -0
- package/ts/src/builder/methodDefinition.ts +429 -0
- package/ts/src/builder/methodInspection.ts +371 -0
- package/ts/src/builder/plan.ts +408 -0
- package/ts/src/builder/planPreauthorization.ts +265 -0
- package/ts/src/builder/prepare.ts +225 -0
- package/ts/src/builder/projectPaths.ts +64 -0
- package/ts/src/builder/pull.ts +356 -0
- package/ts/src/builder/pullCandidates.ts +463 -0
- package/ts/src/builder/pullCandidatesRender.ts +238 -0
- package/ts/src/builder/refinementField.ts +93 -0
- package/ts/src/builder/releaseIntent.ts +173 -0
- package/ts/src/builder/resumeState.ts +84 -0
- package/ts/src/builder/resumeSurface.ts +149 -0
- package/ts/src/builder/roadmapGrammar.ts +300 -0
- package/ts/src/builder/roadmapScan.ts +97 -0
- package/ts/src/builder/roadmapScope.ts +210 -0
- package/ts/src/builder/scopePhrases.ts +146 -0
- package/ts/src/builder/storyPaths.ts +196 -0
- package/ts/src/builder/storyPlanPreauthorization.ts +287 -0
- package/ts/src/builder/surfaceProtocol.ts +42 -0
- package/ts/src/builder/templateGeneration.ts +148 -0
- package/ts/src/builder/transition.ts +178 -0
- package/ts/src/consult/args.ts +99 -0
- package/ts/src/consult/core.ts +122 -0
- package/ts/src/consult/modelCatalog.ts +55 -0
- package/ts/src/consult/render.ts +47 -0
- package/ts/src/conversation/append.ts +414 -0
- package/ts/src/conversation/backfill.ts +387 -0
- package/ts/src/conversation/closeTail.ts +467 -0
- package/ts/src/conversation/extraction.ts +333 -0
- package/ts/src/conversation/extractionDriver.ts +130 -0
- package/ts/src/conversation/extractionRun.ts +70 -0
- package/ts/src/conversation/hookPayload.ts +51 -0
- package/ts/src/conversation/journeyInference.ts +165 -0
- package/ts/src/conversation/journeyRepair.ts +114 -0
- package/ts/src/conversation/lifecycleFaces.ts +197 -0
- package/ts/src/conversation/lifecycleWrites.ts +313 -0
- package/ts/src/conversation/listing.ts +77 -0
- package/ts/src/conversation/logger.ts +851 -0
- package/ts/src/conversation/loggerCli.ts +321 -0
- package/ts/src/conversation/loggerRuntime.ts +259 -0
- package/ts/src/conversation/metadataLifecycle.ts +443 -0
- package/ts/src/conversation/recall.ts +66 -0
- package/ts/src/conversation/sessionComposites.ts +258 -0
- package/ts/src/conversation/sessionImport.ts +90 -0
- package/ts/src/conversation/transcriptBackfill.ts +141 -0
- package/ts/src/cultivation/applyActions.ts +241 -0
- package/ts/src/cultivation/cluster.ts +86 -0
- package/ts/src/cultivation/consolidationStore.ts +281 -0
- package/ts/src/cultivation/promptContext.ts +62 -0
- package/ts/src/cultivation/propose.ts +305 -0
- package/ts/src/cultivation/scan.ts +155 -0
- package/ts/src/db/backupGate.ts +47 -0
- package/ts/src/db/bootstrap.ts +108 -0
- package/ts/src/db/bootstrapLock.ts +223 -0
- package/ts/src/db/copyGuard.ts +60 -0
- package/ts/src/db/database.ts +397 -0
- package/ts/src/db/decode.ts +66 -0
- package/ts/src/db/ftsIntegrity.ts +41 -0
- package/ts/src/db/migrateOnOpen.ts +214 -0
- package/ts/src/db/migrations.ts +576 -0
- package/ts/src/db/readyOnOpen.ts +35 -0
- package/ts/src/db/rowDecode.ts +42 -0
- package/ts/src/db/schema.ts +432 -0
- package/ts/src/db/schemaInventory.ts +225 -0
- package/ts/src/db/schemaInventorySnapshot.ts +2107 -0
- package/ts/src/db/schemaState.ts +91 -0
- package/ts/src/db/schemaTsDivergence.ts +114 -0
- package/ts/src/descriptor/descriptorRead.ts +49 -0
- package/ts/src/docs/docsLint.ts +265 -0
- package/ts/src/explorer/handoff.ts +522 -0
- package/ts/src/explorer/render.ts +335 -0
- package/ts/src/explorer/story.ts +769 -0
- package/ts/src/explorer/transition.ts +72 -0
- package/ts/src/extensions/bindings.ts +170 -0
- package/ts/src/extensions/catalog.ts +249 -0
- package/ts/src/extensions/catalogCommands.ts +513 -0
- package/ts/src/extensions/catalogWrites.ts +652 -0
- package/ts/src/extensions/contextRuntime.ts +325 -0
- package/ts/src/extensions/dispatch.ts +374 -0
- package/ts/src/extensions/errors.ts +24 -0
- package/ts/src/extensions/manifest.ts +356 -0
- package/ts/src/extensions/migrations.ts +350 -0
- package/ts/src/extraction/conversation.ts +304 -0
- package/ts/src/extraction/conversationMetadata.ts +206 -0
- package/ts/src/extraction/fencing.ts +106 -0
- package/ts/src/extraction/json.ts +17 -0
- package/ts/src/extraction/prompts.ts +470 -0
- package/ts/src/frontDoor/args.ts +30 -0
- package/ts/src/frontDoor/argvShape.ts +106 -0
- package/ts/src/frontDoor/buildLoadRuntime.ts +139 -0
- package/ts/src/frontDoor/buildRoute.ts +398 -0
- package/ts/src/frontDoor/cli.ts +1872 -0
- package/ts/src/frontDoor/cloneRoleGuard.ts +98 -0
- package/ts/src/frontDoor/consultRoute.ts +50 -0
- package/ts/src/frontDoor/contentTailRoute.ts +239 -0
- package/ts/src/frontDoor/conversationLoggerRoute.ts +83 -0
- package/ts/src/frontDoor/cultivationRoute.ts +210 -0
- package/ts/src/frontDoor/dbPath.ts +131 -0
- package/ts/src/frontDoor/dbSafetyToolsRoute.ts +240 -0
- package/ts/src/frontDoor/exploreRoute.ts +572 -0
- package/ts/src/frontDoor/extensionCatalogRoute.ts +252 -0
- package/ts/src/frontDoor/frontDoorLog.ts +57 -0
- package/ts/src/frontDoor/identityWrite.ts +36 -0
- package/ts/src/frontDoor/journeyWriteRoute.ts +26 -0
- package/ts/src/frontDoor/lifecycleWriteRoute.ts +110 -0
- package/ts/src/frontDoor/liveBackup.ts +100 -0
- package/ts/src/frontDoor/mirrorModeRoute.ts +163 -0
- package/ts/src/frontDoor/namedRefusal.ts +35 -0
- package/ts/src/frontDoor/nodeSupport.ts +24 -0
- package/ts/src/frontDoor/render/consolidate.ts +199 -0
- package/ts/src/frontDoor/render/conversations.ts +33 -0
- package/ts/src/frontDoor/render/cultivationShared.ts +21 -0
- package/ts/src/frontDoor/render/descriptor.ts +20 -0
- package/ts/src/frontDoor/render/detectPersona.ts +50 -0
- package/ts/src/frontDoor/render/icons.ts +13 -0
- package/ts/src/frontDoor/render/identity.ts +59 -0
- package/ts/src/frontDoor/render/identityRows.ts +17 -0
- package/ts/src/frontDoor/render/inspectPersona.ts +53 -0
- package/ts/src/frontDoor/render/journeyStatus.ts +78 -0
- package/ts/src/frontDoor/render/journeys.ts +79 -0
- package/ts/src/frontDoor/render/list.ts +41 -0
- package/ts/src/frontDoor/render/memories.ts +67 -0
- package/ts/src/frontDoor/render/recall.ts +49 -0
- package/ts/src/frontDoor/render/shadow.ts +155 -0
- package/ts/src/frontDoor/render/tasks.ts +99 -0
- package/ts/src/frontDoor/render/tasksImportSync.ts +63 -0
- package/ts/src/frontDoor/render/week.ts +113 -0
- package/ts/src/frontDoor/routing.ts +1001 -0
- package/ts/src/frontDoor/runtimeRoute.ts +623 -0
- package/ts/src/frontDoor/searchRoute.ts +166 -0
- package/ts/src/frontDoor/seedPaths.ts +98 -0
- package/ts/src/frontDoor/sessionStartWelcomeRoute.ts +166 -0
- package/ts/src/frontDoor/soulRoute.ts +433 -0
- package/ts/src/frontDoor/stdinSource.ts +37 -0
- package/ts/src/frontDoor/tasksImportSyncRoute.ts +110 -0
- package/ts/src/frontDoor/tasksWriteRoute.ts +101 -0
- package/ts/src/frontDoor/usage.ts +176 -0
- package/ts/src/guards/claudePlugin.ts +298 -0
- package/ts/src/guards/packContents.ts +306 -0
- package/ts/src/guards/piSourcedSkills.ts +70 -0
- package/ts/src/guards/retiredRouteShadows.ts +57 -0
- package/ts/src/guards/retiredSurfaces.ts +564 -0
- package/ts/src/hooks/claude.ts +214 -0
- package/ts/src/hooks/codex.ts +45 -0
- package/ts/src/hooks/gemini.ts +126 -0
- package/ts/src/hooks/main.ts +109 -0
- package/ts/src/hooks/mirrorState.ts +71 -0
- package/ts/src/hooks/payload.ts +73 -0
- package/ts/src/hooks/runtime.ts +179 -0
- package/ts/src/hooks/runtimeDetection.ts +34 -0
- package/ts/src/identity/applyConsolidationIdentityUpdate.ts +67 -0
- package/ts/src/identity/descriptorGenerate.ts +162 -0
- package/ts/src/identity/identityEdit.ts +143 -0
- package/ts/src/identity/identityKey.ts +77 -0
- package/ts/src/identity/identityRead.ts +69 -0
- package/ts/src/identity/identityStore.ts +80 -0
- package/ts/src/identity/journeyListing.ts +53 -0
- package/ts/src/identity/personaInspect.ts +44 -0
- package/ts/src/identity/personaListing.ts +37 -0
- package/ts/src/identity/setIdentity.ts +64 -0
- package/ts/src/index.ts +231 -0
- package/ts/src/init/init.ts +111 -0
- package/ts/src/journey/journeyOptions.ts +131 -0
- package/ts/src/journey/journeyRemoval.ts +158 -0
- package/ts/src/journey/journeyStatus.ts +149 -0
- package/ts/src/journey/journeySyncFile.ts +87 -0
- package/ts/src/journey/journeyWrite.ts +203 -0
- package/ts/src/journey/parentJourney.ts +41 -0
- package/ts/src/journey/validateParentJourney.ts +55 -0
- package/ts/src/mcp/boundary.ts +151 -0
- package/ts/src/mcp/guards.ts +244 -0
- package/ts/src/mcp/main.ts +121 -0
- package/ts/src/mcp/payload.ts +141 -0
- package/ts/src/mcp/protocol.ts +141 -0
- package/ts/src/mcp/readModels.ts +241 -0
- package/ts/src/mcp/registry.ts +201 -0
- package/ts/src/mcp/serve.ts +120 -0
- package/ts/src/mcp/tools/deterministic.ts +137 -0
- package/ts/src/mcp/tools/providerCrossing.ts +187 -0
- package/ts/src/mcp/wire.ts +8 -0
- package/ts/src/memory/journal.ts +180 -0
- package/ts/src/memory/listing.ts +107 -0
- package/ts/src/memory/memoryWrite.ts +88 -0
- package/ts/src/memory/reinforcement.ts +35 -0
- package/ts/src/mirror/context.ts +161 -0
- package/ts/src/mirror/defaultResolution.ts +199 -0
- package/ts/src/mirror/mirrorForm.ts +43 -0
- package/ts/src/mirror/orchestration.ts +364 -0
- package/ts/src/mirror/reception.ts +194 -0
- package/ts/src/mirror/render.ts +75 -0
- package/ts/src/mirror/runtimeSession.ts +205 -0
- package/ts/src/mode/operatingMode.ts +148 -0
- package/ts/src/observability/callOutcome.ts +115 -0
- package/ts/src/observability/inspectLedger.ts +329 -0
- package/ts/src/observability/ledgerHooks.ts +126 -0
- package/ts/src/observability/llmCalls.ts +154 -0
- package/ts/src/persona/detectPersona.ts +81 -0
- package/ts/src/planning/promptAssembly.ts +111 -0
- package/ts/src/planning/weekPending.ts +81 -0
- package/ts/src/planning/weekPlan.ts +183 -0
- package/ts/src/planning/weekSave.ts +117 -0
- package/ts/src/providers/config.ts +189 -0
- package/ts/src/providers/cost.ts +64 -0
- package/ts/src/providers/credits.ts +204 -0
- package/ts/src/providers/embedding.ts +321 -0
- package/ts/src/providers/familyProviders.ts +147 -0
- package/ts/src/providers/llm.ts +334 -0
- package/ts/src/providers/openrouter.ts +244 -0
- package/ts/src/providers/redaction.ts +110 -0
- package/ts/src/providers/replay.ts +15 -0
- package/ts/src/providers/transport.ts +410 -0
- package/ts/src/repair/encodingRepair.ts +253 -0
- package/ts/src/runtime/backup.ts +151 -0
- package/ts/src/runtime/channel.ts +146 -0
- package/ts/src/runtime/config.ts +168 -0
- package/ts/src/runtime/diagnose.ts +686 -0
- package/ts/src/runtime/git.ts +510 -0
- package/ts/src/runtime/installKind.ts +220 -0
- package/ts/src/runtime/migrate.ts +154 -0
- package/ts/src/runtime/packageIdentity.ts +102 -0
- package/ts/src/runtime/releaseNotes.ts +244 -0
- package/ts/src/runtime/status.ts +557 -0
- package/ts/src/runtime/strategies/clone.ts +200 -0
- package/ts/src/runtime/strategies/package.ts +328 -0
- package/ts/src/runtime/treeRoot.ts +30 -0
- package/ts/src/runtime/update.ts +445 -0
- package/ts/src/runtime/updateGate.ts +40 -0
- package/ts/src/runtime/updatePipeline.ts +159 -0
- package/ts/src/runtime/version.ts +36 -0
- package/ts/src/runtime/wiring.ts +130 -0
- package/ts/src/search/memorySearch.ts +338 -0
- package/ts/src/search/ranker.ts +154 -0
- package/ts/src/seed/seed.ts +256 -0
- package/ts/src/seed/seedYaml.ts +133 -0
- package/ts/src/soul/apply.ts +218 -0
- package/ts/src/soul/harvest.ts +267 -0
- package/ts/src/soul/prompts/soul_beauty_voice.md +103 -0
- package/ts/src/soul/prompts/soul_self_voice.md +219 -0
- package/ts/src/soul/prompts/soul_wisdom_voice.md +121 -0
- package/ts/src/soul/prompts.ts +85 -0
- package/ts/src/soul/render.ts +358 -0
- package/ts/src/soul/state.ts +212 -0
- package/ts/src/soul/transition.ts +37 -0
- package/ts/src/tasks/journeyPathParse.ts +113 -0
- package/ts/src/tasks/taskImportSync.ts +142 -0
- package/ts/src/tasks/taskStore.ts +312 -0
- package/ts/src/tasks/weekView.ts +120 -0
- package/ts/src/util/clipCodePoints.ts +26 -0
- package/ts/src/util/paths.ts +63 -0
- package/ts/src/util/program.ts +9 -0
- package/ts/src/util/pyGenerators.ts +144 -0
- package/ts/src/util/pythonText.ts +317 -0
- package/ts/src/util/semver.ts +45 -0
- package/ts/src/util/shellWord.ts +25 -0
- package/ts/src/util/slug.ts +64 -0
- package/ts/src/welcome/card.ts +339 -0
- package/ts/src/welcome/statusLine.ts +101 -0
- package/ts/src/welcome/updateCache.ts +129 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
// Deterministic + Slice B `consolidate`/`shadow` apply/reject actions
|
|
2
|
+
// (CV22.DS7.US3).
|
|
3
|
+
//
|
|
4
|
+
// Ports `cli/consolidate_cmd.py:cmd_apply`/`cmd_reject` and
|
|
5
|
+
// `cli/shadow_cmd.py:cmd_apply`/`cmd_reject`, including the `merge` action
|
|
6
|
+
// (which needs a fresh embedding -- Slice B, gated on the caller supplying a
|
|
7
|
+
// replay/live `EmbeddingProvider`; see `propose.ts` for the sibling `scan`
|
|
8
|
+
// orchestration). Each action is its own small, individually-tested function
|
|
9
|
+
// behind the front door's dispatch, not one branching mega-handler.
|
|
10
|
+
//
|
|
11
|
+
// Security posture: `identity_update` reaches an identity write ONLY through
|
|
12
|
+
// `applyConsolidationIdentityUpdate` (the self/ego allowlist gate, AI-23) --
|
|
13
|
+
// never the ungated `upsertIdentity`/`setIdentity`. `shadow apply`'s safety is
|
|
14
|
+
// the HARDCODED constant `layer: "shadow"`; only the *key* is model-influenced,
|
|
15
|
+
// and the port must never let the proposal drive the layer.
|
|
16
|
+
|
|
17
|
+
import type { WritableDatabase } from "#db/database.ts";
|
|
18
|
+
import { embeddingToBytes } from "#db/decode.ts";
|
|
19
|
+
import { applyConsolidationIdentityUpdate } from "#identity/applyConsolidationIdentityUpdate.ts";
|
|
20
|
+
import { getIdentityContent } from "#identity/identityRead.ts";
|
|
21
|
+
import { upsertIdentity } from "#identity/identityStore.ts";
|
|
22
|
+
import { createMemoryRow, getMemorySourceForMerge } from "#memory/memoryWrite.ts";
|
|
23
|
+
import {
|
|
24
|
+
addEmbeddingProvenance,
|
|
25
|
+
type EmbeddingAttemptInfo,
|
|
26
|
+
type EmbeddingProvider,
|
|
27
|
+
generateEmbeddingSafely,
|
|
28
|
+
} from "#providers/embedding.ts";
|
|
29
|
+
import {
|
|
30
|
+
type ConsolidationRow,
|
|
31
|
+
updateConsolidationStatus,
|
|
32
|
+
updateMemoryReadinessState,
|
|
33
|
+
} from "./consolidationStore.ts";
|
|
34
|
+
|
|
35
|
+
function sourceMemoryIds(consolidation: ConsolidationRow): string[] {
|
|
36
|
+
return JSON.parse(consolidation.source_memory_ids) as string[];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// --- Reject (shared by both families) ----------------------------------------
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Port of `consolidate reject` / `shadow reject`: `status -> 'rejected'`, no
|
|
43
|
+
* source-memory or identity mutation at all. Both CLIs are byte-identical
|
|
44
|
+
* here, so one function serves both front-door commands.
|
|
45
|
+
*/
|
|
46
|
+
export function rejectConsolidation(db: WritableDatabase, id: string, nowIso: string): void {
|
|
47
|
+
updateConsolidationStatus(db, id, "rejected", null, nowIso);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// --- consolidate apply: identity_update --------------------------------------
|
|
51
|
+
|
|
52
|
+
export type IdentityUpdateOutcome =
|
|
53
|
+
| { kind: "applied"; targetLayer: string; targetKey: string; sourceMemoryIds: string[] }
|
|
54
|
+
| { kind: "missing_target" }
|
|
55
|
+
| { kind: "refused"; message: string };
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* `consolidate apply` for `action = 'identity_update'`. Routes the write
|
|
59
|
+
* exclusively through `applyConsolidationIdentityUpdate` (the allowlist gate)
|
|
60
|
+
* -- there is no other reachable path to an identity write from this
|
|
61
|
+
* function. A refused layer writes NOTHING: no identity row, no readiness
|
|
62
|
+
* advance, no consolidation status change (mirrors Python's `sys.exit(1)`
|
|
63
|
+
* before any of those side effects run).
|
|
64
|
+
*/
|
|
65
|
+
export function applyIdentityUpdate(
|
|
66
|
+
db: WritableDatabase,
|
|
67
|
+
consolidation: ConsolidationRow,
|
|
68
|
+
resultContent: string,
|
|
69
|
+
ids: { id: string; nowIso: string },
|
|
70
|
+
): IdentityUpdateOutcome {
|
|
71
|
+
if (!consolidation.target_layer || !consolidation.target_key) {
|
|
72
|
+
return { kind: "missing_target" };
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
applyConsolidationIdentityUpdate(db, {
|
|
76
|
+
targetLayer: consolidation.target_layer,
|
|
77
|
+
targetKey: consolidation.target_key,
|
|
78
|
+
content: resultContent,
|
|
79
|
+
id: ids.id,
|
|
80
|
+
nowIso: ids.nowIso,
|
|
81
|
+
});
|
|
82
|
+
} catch (error) {
|
|
83
|
+
return { kind: "refused", message: error instanceof Error ? error.message : String(error) };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const sourceIds = sourceMemoryIds(consolidation);
|
|
87
|
+
for (const memoryId of sourceIds) {
|
|
88
|
+
updateMemoryReadinessState(db, memoryId, "acknowledged");
|
|
89
|
+
}
|
|
90
|
+
updateConsolidationStatus(db, consolidation.id, "accepted", resultContent, ids.nowIso);
|
|
91
|
+
return {
|
|
92
|
+
kind: "applied",
|
|
93
|
+
targetLayer: consolidation.target_layer,
|
|
94
|
+
targetKey: consolidation.target_key,
|
|
95
|
+
sourceMemoryIds: sourceIds,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// --- consolidate apply: shadow_candidate --------------------------------------
|
|
100
|
+
|
|
101
|
+
export interface ShadowCandidateOutcome {
|
|
102
|
+
sourceMemoryIds: string[];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* `consolidate apply` for `action = 'shadow_candidate'`: advances every
|
|
107
|
+
* source memory's readiness to `'candidate'` for the next `mm-shadow` pass,
|
|
108
|
+
* then records the consolidation as accepted.
|
|
109
|
+
*/
|
|
110
|
+
export function applyShadowCandidate(
|
|
111
|
+
db: WritableDatabase,
|
|
112
|
+
consolidation: ConsolidationRow,
|
|
113
|
+
resultContent: string,
|
|
114
|
+
nowIso: string,
|
|
115
|
+
): ShadowCandidateOutcome {
|
|
116
|
+
const sourceIds = sourceMemoryIds(consolidation);
|
|
117
|
+
for (const memoryId of sourceIds) {
|
|
118
|
+
updateMemoryReadinessState(db, memoryId, "candidate");
|
|
119
|
+
}
|
|
120
|
+
updateConsolidationStatus(db, consolidation.id, "accepted", resultContent, nowIso);
|
|
121
|
+
return { sourceMemoryIds: sourceIds };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// --- shadow apply --------------------------------------------------------------
|
|
125
|
+
|
|
126
|
+
export interface ShadowApplyOutcome {
|
|
127
|
+
targetKey: string;
|
|
128
|
+
sourceMemoryIds: string[];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* `shadow apply`: append (or create) the structural `shadow` identity layer at
|
|
133
|
+
* `target_key` (default `'profile'`), advance source memories to
|
|
134
|
+
* `'acknowledged'`, and record acceptance. SAFETY: `layer` is the HARDCODED
|
|
135
|
+
* constant `"shadow"` -- never read from `consolidation.target_layer` or any
|
|
136
|
+
* other proposal field. Only `target_key` (or the `'profile'` default) is
|
|
137
|
+
* model-influenced; that is the entire allowed surface, matching Python's
|
|
138
|
+
* `mem.store.upsert_identity(Identity(layer="shadow", key=target_key, ...))`.
|
|
139
|
+
*/
|
|
140
|
+
export function applyShadowApply(
|
|
141
|
+
db: WritableDatabase,
|
|
142
|
+
consolidation: ConsolidationRow,
|
|
143
|
+
resultContent: string,
|
|
144
|
+
ids: { id: string; nowIso: string },
|
|
145
|
+
): ShadowApplyOutcome {
|
|
146
|
+
const targetKey = consolidation.target_key ?? "profile";
|
|
147
|
+
const existingContent = getIdentityContent(db, "shadow", targetKey);
|
|
148
|
+
const updatedContent =
|
|
149
|
+
existingContent !== null
|
|
150
|
+
? `${existingContent.trimEnd()}\n\n---\n\n${resultContent}`
|
|
151
|
+
: resultContent;
|
|
152
|
+
|
|
153
|
+
upsertIdentity(
|
|
154
|
+
db,
|
|
155
|
+
{
|
|
156
|
+
id: ids.id,
|
|
157
|
+
layer: "shadow",
|
|
158
|
+
key: targetKey,
|
|
159
|
+
content: updatedContent,
|
|
160
|
+
version: "1.0.0",
|
|
161
|
+
metadata: null,
|
|
162
|
+
},
|
|
163
|
+
ids.nowIso,
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
const sourceIds = sourceMemoryIds(consolidation);
|
|
167
|
+
for (const memoryId of sourceIds) {
|
|
168
|
+
updateMemoryReadinessState(db, memoryId, "acknowledged");
|
|
169
|
+
}
|
|
170
|
+
updateConsolidationStatus(db, consolidation.id, "accepted", resultContent, ids.nowIso);
|
|
171
|
+
return { targetKey, sourceMemoryIds: sourceIds };
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// --- consolidate apply: merge (Slice B -- needs a fresh embedding) -----------
|
|
175
|
+
|
|
176
|
+
export type MergeOutcome =
|
|
177
|
+
| { kind: "merged"; mergedMemoryId: string; mergedTitle: string; sourceMemoryIds: string[] }
|
|
178
|
+
| { kind: "source_not_found" };
|
|
179
|
+
|
|
180
|
+
export interface ApplyMergeOptions {
|
|
181
|
+
embeddingProvider: EmbeddingProvider;
|
|
182
|
+
/** The NEW merged memory's id, injected (DS4 determinism idiom) -- unrelated to `consolidation.id`. */
|
|
183
|
+
id: string;
|
|
184
|
+
nowIso: string;
|
|
185
|
+
onEmbeddingAttempt?: (info: EmbeddingAttemptInfo) => void;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* `consolidate apply` for `action = 'merge'`: distill the cluster into one
|
|
190
|
+
* sharper memory. Reads the FIRST source memory only for `memory_type`/
|
|
191
|
+
* `layer`/`journey` (Python's `first = mem.store.get_memory(source_ids[0])`),
|
|
192
|
+
* titles the merge `"[merged] {first.title}"`, sets `context` to the exact
|
|
193
|
+
* `"Merged from: {id, id, ...}"` provenance string (source id order, not
|
|
194
|
+
* sorted), embeds `resultContent` via the injected `EmbeddingProvider`
|
|
195
|
+
* (replay under DS7, live under DS8), stamps embedding provenance, marks
|
|
196
|
+
* every source memory `'integrated'`, and records acceptance. A missing
|
|
197
|
+
* first source memory returns `source_not_found` -- no memory row, no
|
|
198
|
+
* readiness advance, no consolidation status change, mirroring Python's
|
|
199
|
+
* `sys.exit(1)` before any side effect runs.
|
|
200
|
+
*/
|
|
201
|
+
export async function applyMerge(
|
|
202
|
+
db: WritableDatabase,
|
|
203
|
+
consolidation: ConsolidationRow,
|
|
204
|
+
resultContent: string,
|
|
205
|
+
options: ApplyMergeOptions,
|
|
206
|
+
): Promise<MergeOutcome> {
|
|
207
|
+
const sourceIds = sourceMemoryIds(consolidation);
|
|
208
|
+
const first = getMemorySourceForMerge(db, sourceIds[0] as string);
|
|
209
|
+
if (first === null) {
|
|
210
|
+
return { kind: "source_not_found" };
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const embedding = await generateEmbeddingSafely(options.embeddingProvider, resultContent, {
|
|
214
|
+
onAttempt: options.onEmbeddingAttempt,
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
createMemoryRow(db, {
|
|
218
|
+
id: options.id,
|
|
219
|
+
memoryType: first.memory_type,
|
|
220
|
+
layer: first.layer,
|
|
221
|
+
title: `[merged] ${first.title}`,
|
|
222
|
+
content: resultContent,
|
|
223
|
+
journey: first.journey,
|
|
224
|
+
context: `Merged from: ${sourceIds.join(", ")}`,
|
|
225
|
+
createdAt: options.nowIso,
|
|
226
|
+
relevanceScore: 1.0,
|
|
227
|
+
embedding: embeddingToBytes(embedding),
|
|
228
|
+
metadata: addEmbeddingProvenance(null),
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
for (const memoryId of sourceIds) {
|
|
232
|
+
updateMemoryReadinessState(db, memoryId, "integrated");
|
|
233
|
+
}
|
|
234
|
+
updateConsolidationStatus(db, consolidation.id, "accepted", resultContent, options.nowIso);
|
|
235
|
+
return {
|
|
236
|
+
kind: "merged",
|
|
237
|
+
mergedMemoryId: options.id,
|
|
238
|
+
mergedTitle: `[merged] ${first.title}`,
|
|
239
|
+
sourceMemoryIds: sourceIds,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// Port of `cluster_memories` (`src/memory/intelligence/consolidate.py`,
|
|
2
|
+
// CV22.DS7.US3). Pure function, no DB/clock/provider dependency -- graded
|
|
3
|
+
// against the Python oracle by a committed synthetic-embedding golden
|
|
4
|
+
// (`ts/parity/generate_cluster_golden.py`).
|
|
5
|
+
//
|
|
6
|
+
// Greedy single-linkage: a memory joins an existing cluster when its cosine
|
|
7
|
+
// similarity to the cluster SEED (the first member, never re-derived as a
|
|
8
|
+
// centroid) meets the threshold. A memory can only belong to one cluster
|
|
9
|
+
// (first match wins). Memories with no embedding or in a terminal readiness
|
|
10
|
+
// state are skipped up front; singleton groups (clusters of one) are dropped.
|
|
11
|
+
|
|
12
|
+
import { blobToFloat32 } from "#db/decode.ts";
|
|
13
|
+
import { cosineSimilarity } from "#search/ranker.ts";
|
|
14
|
+
import type { CultivationMemory } from "./consolidationStore.ts";
|
|
15
|
+
|
|
16
|
+
/** Mirrors Python's `DEFAULT_CLUSTER_THRESHOLD`. */
|
|
17
|
+
export const DEFAULT_CLUSTER_THRESHOLD = 0.75;
|
|
18
|
+
|
|
19
|
+
/** Mirrors Python's `MAX_CLUSTER_SIZE` -- caps memories per cluster (prevents unwieldy LLM prompts). */
|
|
20
|
+
export const MAX_CLUSTER_SIZE = 5;
|
|
21
|
+
|
|
22
|
+
/** Mirrors Python's `_TERMINAL_STATES` -- readiness states already "done", skipped for clustering. */
|
|
23
|
+
const TERMINAL_READINESS_STATES: ReadonlySet<string> = new Set(["integrated"]);
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The input shape `clusterMemories` needs: a cultivation memory whose
|
|
27
|
+
* embedding MAY be absent (`null`), mirroring Python's `Memory.embedding:
|
|
28
|
+
* bytes | None`. `CultivationMemoryWithEmbedding` (embedding always present,
|
|
29
|
+
* guaranteed by the `embedding IS NOT NULL` SQL filter) is structurally
|
|
30
|
+
* assignable here -- the real `consolidate scan` read path never actually
|
|
31
|
+
* exercises the null branch, but the defensive skip is still a faithful part
|
|
32
|
+
* of the ported function and is proven directly by the golden/unit tests.
|
|
33
|
+
*/
|
|
34
|
+
export interface ClusterableMemory extends CultivationMemory {
|
|
35
|
+
embedding_b64: string | null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Group semantically similar memories into clusters, byte-for-byte matching
|
|
40
|
+
* Python's `cluster_memories` algorithm and iteration order (both the outer
|
|
41
|
+
* seed scan and the inner candidate scan iterate the SAME `eligible` array in
|
|
42
|
+
* its original order -- not a shrinking remainder -- with `assigned` doing
|
|
43
|
+
* the exclusion).
|
|
44
|
+
*/
|
|
45
|
+
export function clusterMemories<T extends ClusterableMemory>(
|
|
46
|
+
memories: readonly T[],
|
|
47
|
+
threshold: number = DEFAULT_CLUSTER_THRESHOLD,
|
|
48
|
+
): T[][] {
|
|
49
|
+
const eligible = memories.filter(
|
|
50
|
+
(memory) =>
|
|
51
|
+
memory.embedding_b64 !== null && !TERMINAL_READINESS_STATES.has(memory.readiness_state),
|
|
52
|
+
);
|
|
53
|
+
if (eligible.length < 2) return [];
|
|
54
|
+
|
|
55
|
+
const embeddings = new Map<string, Float32Array>();
|
|
56
|
+
for (const memory of eligible) {
|
|
57
|
+
embeddings.set(memory.id, blobToFloat32(Buffer.from(memory.embedding_b64 as string, "base64")));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const assigned = new Set<string>();
|
|
61
|
+
const clusters: T[][] = [];
|
|
62
|
+
|
|
63
|
+
for (const seed of eligible) {
|
|
64
|
+
if (assigned.has(seed.id)) continue;
|
|
65
|
+
const cluster: T[] = [seed];
|
|
66
|
+
assigned.add(seed.id);
|
|
67
|
+
const seedEmbedding = embeddings.get(seed.id) as Float32Array;
|
|
68
|
+
|
|
69
|
+
for (const candidate of eligible) {
|
|
70
|
+
if (assigned.has(candidate.id)) continue;
|
|
71
|
+
if (cluster.length >= MAX_CLUSTER_SIZE) break;
|
|
72
|
+
const similarity = cosineSimilarity(
|
|
73
|
+
seedEmbedding,
|
|
74
|
+
embeddings.get(candidate.id) as Float32Array,
|
|
75
|
+
);
|
|
76
|
+
if (similarity >= threshold) {
|
|
77
|
+
cluster.push(candidate);
|
|
78
|
+
assigned.add(candidate.id);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (cluster.length >= 2) clusters.push(cluster);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return clusters;
|
|
86
|
+
}
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
// Consolidation persistence + cultivation memory reads (CV22.DS7.US3).
|
|
2
|
+
//
|
|
3
|
+
// A faithful TypeScript port of `ConsolidationStore`
|
|
4
|
+
// (`src/memory/storage/consolidations.py`) plus the two `Store` memory reads
|
|
5
|
+
// `consolidate`/`shadow` depend on: `get_all_memories_with_embeddings` (used by
|
|
6
|
+
// `consolidate scan`, filtered client-side in Python -- pushed down to SQL here,
|
|
7
|
+
// same database-seam philosophy as DS2/US2) and `get_shadow_candidate_memories`
|
|
8
|
+
// (used by `shadow scan`). The `consolidations` table and `memories.readiness_state`
|
|
9
|
+
// column already exist in the TS-authored schema (DS6) -- no new migration here.
|
|
10
|
+
|
|
11
|
+
import type { Database, SqlValue, WritableDatabase } from "#db/database.ts";
|
|
12
|
+
import { optionalString, requireString } from "#db/rowDecode.ts";
|
|
13
|
+
|
|
14
|
+
/** A consolidation row, mirroring the Python `Consolidation` model exactly. */
|
|
15
|
+
export interface ConsolidationRow {
|
|
16
|
+
id: string;
|
|
17
|
+
action: string; // 'merge' | 'identity_update' | 'shadow_candidate' | 'shadow_observation'
|
|
18
|
+
proposal: string;
|
|
19
|
+
result: string | null;
|
|
20
|
+
source_memory_ids: string; // JSON-encoded array of Memory.id values
|
|
21
|
+
target_layer: string | null;
|
|
22
|
+
target_key: string | null;
|
|
23
|
+
rationale: string | null;
|
|
24
|
+
status: string; // 'pending' | 'accepted' | 'rejected'
|
|
25
|
+
created_at: string;
|
|
26
|
+
reviewed_at: string | null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const CONSOLIDATION_COLUMNS =
|
|
30
|
+
"id, action, proposal, result, source_memory_ids, target_layer, target_key, " +
|
|
31
|
+
"rationale, status, created_at, reviewed_at";
|
|
32
|
+
|
|
33
|
+
function toConsolidation(row: Record<string, unknown>): ConsolidationRow {
|
|
34
|
+
return {
|
|
35
|
+
id: requireString(row, "id"),
|
|
36
|
+
action: requireString(row, "action"),
|
|
37
|
+
proposal: requireString(row, "proposal"),
|
|
38
|
+
result: optionalString(row, "result"),
|
|
39
|
+
source_memory_ids: requireString(row, "source_memory_ids"),
|
|
40
|
+
target_layer: optionalString(row, "target_layer"),
|
|
41
|
+
target_key: optionalString(row, "target_key"),
|
|
42
|
+
rationale: optionalString(row, "rationale"),
|
|
43
|
+
status: requireString(row, "status"),
|
|
44
|
+
created_at: requireString(row, "created_at"),
|
|
45
|
+
reviewed_at: optionalString(row, "reviewed_at"),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// --- Consolidations ----------------------------------------------------------
|
|
50
|
+
|
|
51
|
+
/** Port of `ConsolidationStore.create_consolidation`. Returns the row as-is. */
|
|
52
|
+
export function createConsolidation(
|
|
53
|
+
db: WritableDatabase,
|
|
54
|
+
consolidation: ConsolidationRow,
|
|
55
|
+
): ConsolidationRow {
|
|
56
|
+
db.prepare(
|
|
57
|
+
`INSERT INTO consolidations (${CONSOLIDATION_COLUMNS}) ` +
|
|
58
|
+
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
|
59
|
+
).run(
|
|
60
|
+
consolidation.id,
|
|
61
|
+
consolidation.action,
|
|
62
|
+
consolidation.proposal,
|
|
63
|
+
consolidation.result,
|
|
64
|
+
consolidation.source_memory_ids,
|
|
65
|
+
consolidation.target_layer,
|
|
66
|
+
consolidation.target_key,
|
|
67
|
+
consolidation.rationale,
|
|
68
|
+
consolidation.status,
|
|
69
|
+
consolidation.created_at,
|
|
70
|
+
consolidation.reviewed_at,
|
|
71
|
+
);
|
|
72
|
+
return consolidation;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Port of `ConsolidationStore.get_consolidation`. */
|
|
76
|
+
export function getConsolidation(db: Database, id: string): ConsolidationRow | null {
|
|
77
|
+
const row = db
|
|
78
|
+
.prepare(`SELECT ${CONSOLIDATION_COLUMNS} FROM consolidations WHERE id = ?`)
|
|
79
|
+
.get(id);
|
|
80
|
+
return row === undefined ? null : toConsolidation(row);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Port of `ConsolidationStore.update_consolidation_status`: `result` is
|
|
85
|
+
* `COALESCE(?, result)` -- passing `null` preserves the existing `result`
|
|
86
|
+
* (Python's default `result: str | None = None` parameter), matching a
|
|
87
|
+
* `reject` call (no result) leaving `result` untouched.
|
|
88
|
+
*/
|
|
89
|
+
export function updateConsolidationStatus(
|
|
90
|
+
db: WritableDatabase,
|
|
91
|
+
id: string,
|
|
92
|
+
status: string,
|
|
93
|
+
result: string | null,
|
|
94
|
+
nowIso: string,
|
|
95
|
+
): void {
|
|
96
|
+
db.prepare(
|
|
97
|
+
"UPDATE consolidations SET status = ?, result = COALESCE(?, result), reviewed_at = ? WHERE id = ?",
|
|
98
|
+
).run(status, result, nowIso, id);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Filters for `listConsolidations`, mirroring the Python signature. */
|
|
102
|
+
export interface ListConsolidationsFilters {
|
|
103
|
+
status?: string | null;
|
|
104
|
+
limit?: number;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Port of `ConsolidationStore.list_consolidations`: a `1=1` seed, an optional
|
|
109
|
+
* `status = ?` clause, `ORDER BY created_at DESC`, trailing `LIMIT ?`.
|
|
110
|
+
*/
|
|
111
|
+
export function listConsolidations(
|
|
112
|
+
db: Database,
|
|
113
|
+
filters: ListConsolidationsFilters = {},
|
|
114
|
+
): ConsolidationRow[] {
|
|
115
|
+
const { status = null, limit = 20 } = filters;
|
|
116
|
+
const conditions = ["1=1"];
|
|
117
|
+
const params: SqlValue[] = [];
|
|
118
|
+
if (status) {
|
|
119
|
+
conditions.push("status = ?");
|
|
120
|
+
params.push(status);
|
|
121
|
+
}
|
|
122
|
+
params.push(limit);
|
|
123
|
+
return db
|
|
124
|
+
.prepare(
|
|
125
|
+
`SELECT ${CONSOLIDATION_COLUMNS} FROM consolidations WHERE ${conditions.join(" AND ")} ` +
|
|
126
|
+
"ORDER BY created_at DESC LIMIT ?",
|
|
127
|
+
)
|
|
128
|
+
.all(...params)
|
|
129
|
+
.map(toConsolidation);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Port of the `_resolve_proposal` helper duplicated in both
|
|
134
|
+
* `cli/consolidate_cmd.py` and `cli/shadow_cmd.py`: exact id first, else a
|
|
135
|
+
* first-match-wins scan (by `created_at DESC` order) over the most recent 200
|
|
136
|
+
* consolidations. Deliberately kept SEPARATE from `resolveTaskByIdOrPrefix`
|
|
137
|
+
* (US2): Python itself has no ambiguous-match branch here (unlike tasks) --
|
|
138
|
+
* the first prefix hit in listing order simply wins, so unifying the two
|
|
139
|
+
* resolvers would invent a branch Python does not have.
|
|
140
|
+
*/
|
|
141
|
+
export function resolveProposalByIdOrPrefix(
|
|
142
|
+
db: Database,
|
|
143
|
+
idOrPrefix: string,
|
|
144
|
+
): ConsolidationRow | null {
|
|
145
|
+
const exact = getConsolidation(db, idOrPrefix);
|
|
146
|
+
if (exact) return exact;
|
|
147
|
+
const recent = listConsolidations(db, { limit: 200 });
|
|
148
|
+
return recent.find((item) => item.id.startsWith(idOrPrefix)) ?? null;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// --- Memory readiness ---------------------------------------------------------
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Port of `ConsolidationStore.update_memory_readiness_state`. Valid states:
|
|
155
|
+
* observed | candidate | surfaced | acknowledged | integrated. One statement,
|
|
156
|
+
* one implicit commit -- callers must NOT wrap a sequence of these (plus a
|
|
157
|
+
* sibling write) in an explicit transaction; Python commits each call
|
|
158
|
+
* separately and the port preserves that commit boundary exactly.
|
|
159
|
+
*/
|
|
160
|
+
export function updateMemoryReadinessState(
|
|
161
|
+
db: WritableDatabase,
|
|
162
|
+
memoryId: string,
|
|
163
|
+
state: string,
|
|
164
|
+
): void {
|
|
165
|
+
db.prepare("UPDATE memories SET readiness_state = ? WHERE id = ?").run(state, memoryId);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// --- Cultivation memory reads --------------------------------------------------
|
|
169
|
+
|
|
170
|
+
/** The memory projection cultivation formatting needs, shared by both scans. */
|
|
171
|
+
export interface CultivationMemory {
|
|
172
|
+
id: string;
|
|
173
|
+
memory_type: string;
|
|
174
|
+
layer: string;
|
|
175
|
+
title: string;
|
|
176
|
+
content: string;
|
|
177
|
+
context: string | null;
|
|
178
|
+
journey: string | null;
|
|
179
|
+
created_at: string;
|
|
180
|
+
readiness_state: string;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** A cultivation memory with its embedding, for clustering (`consolidate scan`). */
|
|
184
|
+
export interface CultivationMemoryWithEmbedding extends CultivationMemory {
|
|
185
|
+
embedding_b64: string;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const CULTIVATION_COLUMNS_BASE =
|
|
189
|
+
"id, memory_type, layer, title, content, context, journey, created_at, readiness_state";
|
|
190
|
+
|
|
191
|
+
function toCultivationMemory(row: Record<string, unknown>): CultivationMemory {
|
|
192
|
+
return {
|
|
193
|
+
id: requireString(row, "id"),
|
|
194
|
+
memory_type: requireString(row, "memory_type"),
|
|
195
|
+
layer: requireString(row, "layer"),
|
|
196
|
+
title: requireString(row, "title"),
|
|
197
|
+
content: requireString(row, "content"),
|
|
198
|
+
context: optionalString(row, "context"),
|
|
199
|
+
journey: optionalString(row, "journey"),
|
|
200
|
+
created_at: requireString(row, "created_at"),
|
|
201
|
+
readiness_state: requireString(row, "readiness_state"),
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/** Optional equality filters for the cultivation memory reads. */
|
|
206
|
+
export interface CultivationMemoryFilters {
|
|
207
|
+
journey?: string | null;
|
|
208
|
+
layer?: string | null;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Port of `Store.get_all_memories_with_embeddings`, plus the `--journey`/
|
|
213
|
+
* `--layer` filters `consolidate_cmd.cmd_scan` applies client-side in Python
|
|
214
|
+
* -- pushed down into the SQL `WHERE` here (same result set, DS2/US2's
|
|
215
|
+
* query-builder philosophy). `ORDER BY created_at DESC`, matching Python.
|
|
216
|
+
*/
|
|
217
|
+
export function getMemoriesWithEmbeddingsForCultivation(
|
|
218
|
+
db: Database,
|
|
219
|
+
filters: CultivationMemoryFilters = {},
|
|
220
|
+
): CultivationMemoryWithEmbedding[] {
|
|
221
|
+
const conditions = ["embedding IS NOT NULL"];
|
|
222
|
+
const params: SqlValue[] = [];
|
|
223
|
+
if (filters.journey) {
|
|
224
|
+
conditions.push("journey = ?");
|
|
225
|
+
params.push(filters.journey);
|
|
226
|
+
}
|
|
227
|
+
if (filters.layer) {
|
|
228
|
+
conditions.push("layer = ?");
|
|
229
|
+
params.push(filters.layer);
|
|
230
|
+
}
|
|
231
|
+
return db
|
|
232
|
+
.prepare(
|
|
233
|
+
`SELECT ${CULTIVATION_COLUMNS_BASE}, embedding FROM memories ` +
|
|
234
|
+
`WHERE ${conditions.join(" AND ")} ORDER BY created_at DESC`,
|
|
235
|
+
)
|
|
236
|
+
.all(...params)
|
|
237
|
+
.map((row) => {
|
|
238
|
+
const embedding = row.embedding;
|
|
239
|
+
if (!(embedding instanceof Uint8Array)) {
|
|
240
|
+
throw new Error("memory embedding must be a BLOB/Uint8Array");
|
|
241
|
+
}
|
|
242
|
+
return {
|
|
243
|
+
...toCultivationMemory(row),
|
|
244
|
+
embedding_b64: Buffer.from(embedding).toString("base64"),
|
|
245
|
+
};
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/** Filters for `getShadowCandidateMemories`, mirroring the Python signature. */
|
|
250
|
+
export interface ShadowCandidateFilters {
|
|
251
|
+
readinessStates?: readonly string[];
|
|
252
|
+
limit?: number;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const DEFAULT_SHADOW_READINESS_STATES: readonly string[] = ["observed", "candidate"];
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Port of `Store.get_shadow_candidate_memories`: memories with `layer =
|
|
259
|
+
* 'shadow'` OR `memory_type IN ('tension', 'pattern')`, restricted to the
|
|
260
|
+
* given readiness states (default `observed`/`candidate`), `ORDER BY
|
|
261
|
+
* created_at DESC LIMIT ?`. No embedding requirement -- shadow candidates need
|
|
262
|
+
* not have been embedded.
|
|
263
|
+
*/
|
|
264
|
+
export function getShadowCandidateMemories(
|
|
265
|
+
db: Database,
|
|
266
|
+
filters: ShadowCandidateFilters = {},
|
|
267
|
+
): CultivationMemory[] {
|
|
268
|
+
const readinessStates = filters.readinessStates ?? DEFAULT_SHADOW_READINESS_STATES;
|
|
269
|
+
const limit = filters.limit ?? 100;
|
|
270
|
+
const placeholders = readinessStates.map(() => "?").join(", ");
|
|
271
|
+
const params: SqlValue[] = [...readinessStates, limit];
|
|
272
|
+
return db
|
|
273
|
+
.prepare(
|
|
274
|
+
`SELECT ${CULTIVATION_COLUMNS_BASE} FROM memories ` +
|
|
275
|
+
"WHERE (layer = 'shadow' OR memory_type IN ('tension', 'pattern')) " +
|
|
276
|
+
`AND readiness_state IN (${placeholders}) ` +
|
|
277
|
+
"ORDER BY created_at DESC LIMIT ?",
|
|
278
|
+
)
|
|
279
|
+
.all(...params)
|
|
280
|
+
.map(toCultivationMemory);
|
|
281
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// The two inputs `cmd_scan` resolves from the database before calling the
|
|
2
|
+
// LLM (CV22.DS8.TS2): the owner's first name and the identity context the
|
|
3
|
+
// consolidation prompt shows the model. Ports `cli/shadow_cmd.py:_user_name`
|
|
4
|
+
// and `cli/consolidate_cmd.py:_identity_context`.
|
|
5
|
+
//
|
|
6
|
+
// On the name: Python has TWO resolvers for these leaves. `shadow_cmd` uses
|
|
7
|
+
// the regex below; `consolidate_cmd` first short-circuits on the literal
|
|
8
|
+
// owner's name being anywhere in the content (a distribution defect, CR014).
|
|
9
|
+
// TS ships one resolver, the regex form. The only input where the two Python
|
|
10
|
+
// forms disagree is content that names the owner without the seed template's
|
|
11
|
+
// "speaking with" line, which no shipped template produces; the golden's
|
|
12
|
+
// `resolvers` section records both columns so the deviation stays visible
|
|
13
|
+
// (plan decision D1, docs/project/decisions.md).
|
|
14
|
+
//
|
|
15
|
+
// This is NOT the close tail's `resolveUserName` (`conversation/extraction.ts`),
|
|
16
|
+
// which ports a third oracle (`You are talking to`). Unifying the three is
|
|
17
|
+
// CR014's job, not this story's.
|
|
18
|
+
|
|
19
|
+
import type { Database } from "#db/database.ts";
|
|
20
|
+
import { getIdentityContent } from "#identity/identityRead.ts";
|
|
21
|
+
import { sliceCodePoints } from "#util/pythonText.ts";
|
|
22
|
+
|
|
23
|
+
/** Python's `\w+` on a `str` pattern: Unicode letters, digits, underscore. A
|
|
24
|
+
* combining mark (Mn) is not a word character in either engine. */
|
|
25
|
+
const SPEAKING_WITH = /speaking with ([\p{L}\p{N}_]+)/u;
|
|
26
|
+
|
|
27
|
+
/** Python's fallback when the row or the marker phrase is absent. */
|
|
28
|
+
export const FALLBACK_USER_NAME = "the user";
|
|
29
|
+
|
|
30
|
+
/** Port of `shadow_cmd._user_name`: the owner's first name from `user/identity`. */
|
|
31
|
+
export function cultivationUserName(db: Database): string {
|
|
32
|
+
const content = getIdentityContent(db, "user", "identity");
|
|
33
|
+
const match = content?.match(SPEAKING_WITH);
|
|
34
|
+
return match?.[1] ?? FALLBACK_USER_NAME;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** `entry.content[:600]` -- code points, not UTF-16 units. */
|
|
38
|
+
export const IDENTITY_CONTEXT_CODE_POINTS = 600;
|
|
39
|
+
|
|
40
|
+
/** The layers `_identity_context` reads, in Python's order. */
|
|
41
|
+
const IDENTITY_CONTEXT_LAYERS: readonly (readonly [string, string])[] = [
|
|
42
|
+
["ego", "behavior"],
|
|
43
|
+
["ego", "identity"],
|
|
44
|
+
["self", "soul"],
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
/** Python's fallback when none of the three rows exists. */
|
|
48
|
+
export const FALLBACK_IDENTITY_CONTEXT = "(no identity context loaded)";
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Port of `consolidate_cmd._identity_context`: a brief, system-side identity
|
|
52
|
+
* digest for the consolidation prompt's IDENTITY_UPDATE reference.
|
|
53
|
+
*/
|
|
54
|
+
export function consolidationIdentityContext(db: Database): string {
|
|
55
|
+
const parts: string[] = [];
|
|
56
|
+
for (const [layer, key] of IDENTITY_CONTEXT_LAYERS) {
|
|
57
|
+
const content = getIdentityContent(db, layer, key);
|
|
58
|
+
if (content === null) continue;
|
|
59
|
+
parts.push(`## ${layer}/${key}\n${sliceCodePoints(content, IDENTITY_CONTEXT_CODE_POINTS)}`);
|
|
60
|
+
}
|
|
61
|
+
return parts.length > 0 ? parts.join("\n\n") : FALLBACK_IDENTITY_CONTEXT;
|
|
62
|
+
}
|