mirror-mind 0.0.1 → 0.32.1
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-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 +561 -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 +99 -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.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 +13 -0
- package/plugins/mirror-mind/hooks/hooks.json +50 -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/mcp/launch.sh +52 -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 +65 -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/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 +576 -0
- package/ts/src/conversation/loggerCli.ts +282 -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 +1865 -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 +73 -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 +999 -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/soulRoute.ts +433 -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 +252 -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 +176 -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 +68 -0
- package/ts/src/hooks/runtime.ts +159 -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/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,166 @@
|
|
|
1
|
+
import type { WritableDatabase } from "#db/database.ts";
|
|
2
|
+
import { requireString } from "#db/rowDecode.ts";
|
|
3
|
+
import type { EmbeddingProvider } from "#providers/embedding.ts";
|
|
4
|
+
import { resolveFamilyProviders } from "#providers/familyProviders.ts";
|
|
5
|
+
import { SEARCH_TRANSPORT } from "#providers/transport.ts";
|
|
6
|
+
import type { FreshSearchResult } from "#search/memorySearch.ts";
|
|
7
|
+
import { searchMemoriesWithStatus } from "#search/memorySearch.ts";
|
|
8
|
+
import { optionValue } from "./args.ts";
|
|
9
|
+
import { ICONS } from "./render/icons.ts";
|
|
10
|
+
import { tagsText } from "./render/memories.ts";
|
|
11
|
+
|
|
12
|
+
// Mirrors Python's cli/memories.py degraded_note wording exactly (AI-04),
|
|
13
|
+
// including under a degraded search with zero results.
|
|
14
|
+
const DEGRADED_NOTE =
|
|
15
|
+
"⚠ Degraded: lexical-only search (embedding unavailable — offline or no API key).";
|
|
16
|
+
|
|
17
|
+
export interface SearchMemoryRow {
|
|
18
|
+
id: string;
|
|
19
|
+
memory_type: string;
|
|
20
|
+
layer: string;
|
|
21
|
+
title: string;
|
|
22
|
+
content: string;
|
|
23
|
+
created_at: string;
|
|
24
|
+
journey: string | null;
|
|
25
|
+
tags: string | null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Resolve the embedding provider by the same precedence the router used
|
|
30
|
+
* (CV22.DS8.US1), so the route can never disagree with the routing decision
|
|
31
|
+
* about which transport is in play.
|
|
32
|
+
*/
|
|
33
|
+
export async function resolveSearchEmbeddingProvider(
|
|
34
|
+
env: NodeJS.ProcessEnv = process.env,
|
|
35
|
+
): Promise<EmbeddingProvider> {
|
|
36
|
+
// The live provider resolves its config lazily, so a missing key surfaces
|
|
37
|
+
// inside searchMemoriesWithStatus and degrades to lexical-only rather than
|
|
38
|
+
// failing the command -- Python's behavior for an unconfigured install.
|
|
39
|
+
const family = await resolveFamilyProviders(env, SEARCH_TRANSPORT);
|
|
40
|
+
if (!family.embedding) {
|
|
41
|
+
// The search family declares an embedding fixture, so replay and live both
|
|
42
|
+
// build one; this is defense in depth, not a reachable state.
|
|
43
|
+
throw new Error("memories --search requires an embedding provider");
|
|
44
|
+
}
|
|
45
|
+
return family.embedding;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface MemorySearchRouteOptions {
|
|
49
|
+
/** Receives a content-free degraded category for the front-door log. */
|
|
50
|
+
onDegraded?: (detail: string) => void;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export async function runMemorySearchRoute(
|
|
54
|
+
db: WritableDatabase,
|
|
55
|
+
args: readonly string[],
|
|
56
|
+
options: MemorySearchRouteOptions = {},
|
|
57
|
+
): Promise<string> {
|
|
58
|
+
const query = optionValue(args, "--search");
|
|
59
|
+
if (!query) throw new Error("memories --search requires a query");
|
|
60
|
+
const provider = await resolveSearchEmbeddingProvider();
|
|
61
|
+
const limit = Number(optionValue(args, "--limit") ?? 20);
|
|
62
|
+
const { results, degraded, degradedKind } = await searchMemoriesWithStatus(db, {
|
|
63
|
+
query,
|
|
64
|
+
limit,
|
|
65
|
+
memoryType: optionValue(args, "--type"),
|
|
66
|
+
layer: optionValue(args, "--layer"),
|
|
67
|
+
journey: optionValue(args, "--journey"),
|
|
68
|
+
provider,
|
|
69
|
+
// An exploratory CLI search is not a genuine context load, so it must not
|
|
70
|
+
// reinforce: `access_count` feeds `reinforcement_score` and the hybrid
|
|
71
|
+
// ranker, and a ranker fed by its own exhaust drifts toward whatever was
|
|
72
|
+
// searched for rather than whatever was used (AI-12). Python's
|
|
73
|
+
// `cli/memories.py` has passed `log_access=False` since 2026-07-16; this
|
|
74
|
+
// port did not, and reinforced on every `memories --search` from the flip
|
|
75
|
+
// until CV22.DS9.US2 measured it. Builder context load deliberately keeps
|
|
76
|
+
// the default and still reinforces -- that one IS a genuine load.
|
|
77
|
+
logAccess: false,
|
|
78
|
+
});
|
|
79
|
+
// A degraded live search is the one failure an operator has to diagnose
|
|
80
|
+
// without a re-run, so the taxonomy class reaches the front-door log rather
|
|
81
|
+
// than dying inside the degrade path. Category only -- never a message,
|
|
82
|
+
// never the query (RS005/CR026).
|
|
83
|
+
if (degraded && degradedKind) {
|
|
84
|
+
options.onDegraded?.(`embedding_degraded kind=${degradedKind}`);
|
|
85
|
+
}
|
|
86
|
+
const rows = memoriesById(
|
|
87
|
+
db,
|
|
88
|
+
results.map((result) => result.id),
|
|
89
|
+
);
|
|
90
|
+
return formatSearchResults(query, results, rows, degraded);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Pure rendering of a fresh-search outcome (AI-04). Split out from
|
|
95
|
+
* `runMemorySearchRoute` so the degraded/normal render branches are directly
|
|
96
|
+
* unit-testable without a database or a live-failing embedding provider (none
|
|
97
|
+
* exists yet -- see CR037 plan).
|
|
98
|
+
*/
|
|
99
|
+
export function formatSearchResults(
|
|
100
|
+
query: string,
|
|
101
|
+
results: readonly FreshSearchResult[],
|
|
102
|
+
rows: ReadonlyMap<string, SearchMemoryRow>,
|
|
103
|
+
degraded: boolean,
|
|
104
|
+
): string {
|
|
105
|
+
if (results.length === 0) {
|
|
106
|
+
return degraded ? `${DEGRADED_NOTE}\n` : "No memories found.\n";
|
|
107
|
+
}
|
|
108
|
+
const lines: string[] = [];
|
|
109
|
+
if (degraded) {
|
|
110
|
+
lines.push(`${DEGRADED_NOTE} Ranked by keyword match.`, "");
|
|
111
|
+
}
|
|
112
|
+
lines.push(`🔍 Search: "${query}" (${results.length} results)`, "");
|
|
113
|
+
for (const result of results) {
|
|
114
|
+
const memory = rows.get(result.id);
|
|
115
|
+
if (!memory) continue;
|
|
116
|
+
const icon = ICONS[memory.memory_type] ?? "•";
|
|
117
|
+
const date = memory.created_at ? memory.created_at.slice(0, 10) : "?";
|
|
118
|
+
const journey = memory.journey ? ` 🧭 ${memory.journey}` : "";
|
|
119
|
+
const tags = tagsText(memory.tags);
|
|
120
|
+
lines.push(`${icon} **${memory.title}** (score: ${result.score.toFixed(3)})`);
|
|
121
|
+
lines.push(
|
|
122
|
+
` ${date} | \`${memory.id.slice(0, 8)}\` | ${memory.memory_type} [${memory.layer}]${journey}`,
|
|
123
|
+
);
|
|
124
|
+
lines.push(` ${memory.content.slice(0, 200)}`);
|
|
125
|
+
if (tags) lines.push(` 🏷 ${tags}`);
|
|
126
|
+
lines.push("");
|
|
127
|
+
}
|
|
128
|
+
return `${lines.join("\n")}\n`;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* The row reader both search faces share.
|
|
133
|
+
*
|
|
134
|
+
* Exported for `build load` (CV22.DS7.US8 plateau 7), which renders the same
|
|
135
|
+
* memories from the same ranked ids: the ranker returns `{id, score}` only, so a
|
|
136
|
+
* second copy of this query is a second chance for the two blocks to disagree
|
|
137
|
+
* about what a memory says.
|
|
138
|
+
*/
|
|
139
|
+
export function memoriesById(
|
|
140
|
+
db: WritableDatabase,
|
|
141
|
+
ids: readonly string[],
|
|
142
|
+
): Map<string, SearchMemoryRow> {
|
|
143
|
+
if (ids.length === 0) return new Map();
|
|
144
|
+
const placeholders = ids.map(() => "?").join(", ");
|
|
145
|
+
const rows = db
|
|
146
|
+
.prepare(
|
|
147
|
+
`SELECT id, memory_type, layer, title, content, created_at, journey, tags FROM memories ` +
|
|
148
|
+
`WHERE id IN (${placeholders})`,
|
|
149
|
+
)
|
|
150
|
+
.all(...ids);
|
|
151
|
+
return new Map(
|
|
152
|
+
rows.map((row) => [
|
|
153
|
+
requireString(row, "id"),
|
|
154
|
+
{
|
|
155
|
+
id: requireString(row, "id"),
|
|
156
|
+
memory_type: requireString(row, "memory_type"),
|
|
157
|
+
layer: requireString(row, "layer"),
|
|
158
|
+
title: requireString(row, "title"),
|
|
159
|
+
content: requireString(row, "content"),
|
|
160
|
+
created_at: requireString(row, "created_at"),
|
|
161
|
+
journey: typeof row.journey === "string" ? row.journey : null,
|
|
162
|
+
tags: typeof row.tags === "string" ? row.tags : null,
|
|
163
|
+
},
|
|
164
|
+
]),
|
|
165
|
+
);
|
|
166
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// `seed` path resolution — the port of the bespoke resolution inside
|
|
2
|
+
// memory.cli.seed.seed(), which is NOT the same precedence chain
|
|
3
|
+
// `resolveDbPath` (dbPath.ts) uses for every other command. Verified against
|
|
4
|
+
// the live oracle (never assumed) because it diverges in a genuinely
|
|
5
|
+
// surprising way:
|
|
6
|
+
//
|
|
7
|
+
// - `identity_root`/`mirror_home` resolve via `--mirror-home` or the plain
|
|
8
|
+
// MIRROR_HOME/MIRROR_USER chain (`resolveMirrorHome`) -- completely
|
|
9
|
+
// independent of `--env`, `MEMORY_DIR`, or `DB_PATH`.
|
|
10
|
+
// - When `--mirror-home` IS given, the database path is ALWAYS
|
|
11
|
+
// `<mirror_home>/memory.db` -- `--env` is silently ignored for the
|
|
12
|
+
// filename (`default_db_path_for_home` hardcodes "memory.db"). Confirmed
|
|
13
|
+
// live: `seed --mirror-home X --env test` writes `X/memory.db`, not
|
|
14
|
+
// `X/memory_test.db`.
|
|
15
|
+
// - When `--mirror-home` is absent and `--env` differs from the ambient
|
|
16
|
+
// `MEMORY_ENV` (or ambient is unset, defaulting "production" like Python's
|
|
17
|
+
// module-level MEMORY_ENV constant), the database path follows
|
|
18
|
+
// `db_path_for_env`: MEMORY_PROD_DIR (production only) > MEMORY_DIR > the
|
|
19
|
+
// resolved mirror home, with the env-appropriate filename -- but WITHOUT
|
|
20
|
+
// checking `DB_PATH` (that env var is only wired to the *ambient*
|
|
21
|
+
// environment in Python, cached once at import time).
|
|
22
|
+
// - When `--mirror-home` is absent and `--env` matches the ambient
|
|
23
|
+
// `MEMORY_ENV` (or is omitted), the database path follows the SAME full
|
|
24
|
+
// generic chain every other command uses (`require_db_path()` ==
|
|
25
|
+
// `resolveDbPath`).
|
|
26
|
+
//
|
|
27
|
+
// `--db-path` is a TS-front-door-only convenience (Python's `seed` CLI has no
|
|
28
|
+
// such flag), kept here for consistency with every other ported command --
|
|
29
|
+
// it wins over everything, as documented in dbPath.ts.
|
|
30
|
+
|
|
31
|
+
import { join } from "node:path";
|
|
32
|
+
import { expandHome } from "#util/paths.ts";
|
|
33
|
+
import { optionValue } from "./args.ts";
|
|
34
|
+
import {
|
|
35
|
+
type DbPathEnv,
|
|
36
|
+
type DbPathIo,
|
|
37
|
+
dbNameForEnv,
|
|
38
|
+
MirrorHomeNotConfiguredError,
|
|
39
|
+
resolveDbPath,
|
|
40
|
+
resolveMirrorHome,
|
|
41
|
+
} from "./dbPath.ts";
|
|
42
|
+
|
|
43
|
+
export interface SeedPaths {
|
|
44
|
+
dbPath: string;
|
|
45
|
+
identityRoot: string;
|
|
46
|
+
mirrorHome: string;
|
|
47
|
+
env: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Resolve `seed`'s database path, identity root, mirror home, and effective
|
|
52
|
+
* env, mirroring the Python branches documented above exactly. Throws
|
|
53
|
+
* MirrorHomeNotConfiguredError when nothing resolves (mapped to exit 2 by the
|
|
54
|
+
* caller, matching every other command's unconfigured-home contract).
|
|
55
|
+
*/
|
|
56
|
+
export function resolveSeedPaths(
|
|
57
|
+
args: readonly string[],
|
|
58
|
+
env: DbPathEnv = process.env,
|
|
59
|
+
io: DbPathIo = {},
|
|
60
|
+
): SeedPaths {
|
|
61
|
+
const explicitDbPath = optionValue(args, "--db-path");
|
|
62
|
+
const explicitMirrorHome = optionValue(args, "--mirror-home");
|
|
63
|
+
const envFlag = optionValue(args, "--env");
|
|
64
|
+
const ambientEnv = env.MEMORY_ENV || "production";
|
|
65
|
+
const effectiveEnv = envFlag || ambientEnv;
|
|
66
|
+
|
|
67
|
+
const mirrorHome = explicitMirrorHome
|
|
68
|
+
? expandHome(explicitMirrorHome)
|
|
69
|
+
: resolveMirrorHome(env, io);
|
|
70
|
+
const identityRoot = join(mirrorHome, "identity");
|
|
71
|
+
|
|
72
|
+
if (explicitDbPath) {
|
|
73
|
+
return { dbPath: expandHome(explicitDbPath), identityRoot, mirrorHome, env: effectiveEnv };
|
|
74
|
+
}
|
|
75
|
+
if (explicitMirrorHome) {
|
|
76
|
+
// default_db_path_for_home: ALWAYS "memory.db", --env is not consulted.
|
|
77
|
+
return { dbPath: join(mirrorHome, "memory.db"), identityRoot, mirrorHome, env: effectiveEnv };
|
|
78
|
+
}
|
|
79
|
+
if (envFlag && envFlag !== ambientEnv) {
|
|
80
|
+
const dbName = dbNameForEnv(envFlag);
|
|
81
|
+
let dir: string;
|
|
82
|
+
if (envFlag === "production" && env.MEMORY_PROD_DIR) {
|
|
83
|
+
dir = expandHome(env.MEMORY_PROD_DIR);
|
|
84
|
+
} else if (env.MEMORY_DIR) {
|
|
85
|
+
dir = expandHome(env.MEMORY_DIR);
|
|
86
|
+
} else {
|
|
87
|
+
dir = mirrorHome;
|
|
88
|
+
}
|
|
89
|
+
return { dbPath: join(dir, dbName), identityRoot, mirrorHome, env: effectiveEnv };
|
|
90
|
+
}
|
|
91
|
+
// require_db_path(): by construction neither --db-path nor --mirror-home is
|
|
92
|
+
// present here, so this is exactly resolveDbPath's own DB_PATH ->
|
|
93
|
+
// MEMORY_PROD_DIR -> MEMORY_DIR -> resolved-mirror-home chain -- the same
|
|
94
|
+
// one every other command uses. Reused directly rather than re-implemented.
|
|
95
|
+
return { dbPath: resolveDbPath(args, env, io), identityRoot, mirrorHome, env: effectiveEnv };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export { MirrorHomeNotConfiguredError };
|
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
// CV22.DS7.US6 plateau 6 — the `soul` front-door route.
|
|
2
|
+
//
|
|
3
|
+
// Dispatch only: every behavior it reaches was ported and graded in plateaus
|
|
4
|
+
// 1-5. What this module owns is the argv shape, the exit codes, and the
|
|
5
|
+
// refusals — Python's `cmd_*` wrappers print `Error: <message>` to stderr and
|
|
6
|
+
// exit 1 for a ValueError, and argparse exits 2 for an unknown subcommand or a
|
|
7
|
+
// missing required argument.
|
|
8
|
+
//
|
|
9
|
+
// The subcommands are allowlisted BY NAME by `routing.ts`, so a subcommand
|
|
10
|
+
// Python grows later reaches Python instead of inheriting this route. That is
|
|
11
|
+
// the `conversations append` lesson (RS009/CR055): a family claimed wholesale
|
|
12
|
+
// answered an argv shape it had never implemented, exited 0, and discarded the
|
|
13
|
+
// caller's data.
|
|
14
|
+
|
|
15
|
+
import type { Database, WritableDatabase } from "#db/database.ts";
|
|
16
|
+
import { getIdentityContent } from "#identity/identityRead.ts";
|
|
17
|
+
import { persistStickyDefaults } from "#mirror/orchestration.ts";
|
|
18
|
+
import { resolveRuntimeSessionId } from "#mirror/runtimeSession.ts";
|
|
19
|
+
import { activateOperatingMode } from "#mode/operatingMode.ts";
|
|
20
|
+
import { embeddingLedgerHook } from "#observability/ledgerHooks.ts";
|
|
21
|
+
import { generateEmbeddingSafely } from "#providers/embedding.ts";
|
|
22
|
+
import { resolveFamilyProviders } from "#providers/familyProviders.ts";
|
|
23
|
+
import { SOUL_HARVEST_TRANSPORT } from "#providers/transport.ts";
|
|
24
|
+
import { applyIdentityIntegration, SoulApplyError } from "#soul/apply.ts";
|
|
25
|
+
import {
|
|
26
|
+
persistHarvestSave,
|
|
27
|
+
planHarvestSave,
|
|
28
|
+
SoulHarvestError,
|
|
29
|
+
type TranscriptMessage,
|
|
30
|
+
} from "#soul/harvest.ts";
|
|
31
|
+
import {
|
|
32
|
+
composeSoulBeautyVoicePrompt,
|
|
33
|
+
composeSoulSelfVoicePrompt,
|
|
34
|
+
composeSoulWisdomVoicePrompt,
|
|
35
|
+
} from "#soul/prompts.ts";
|
|
36
|
+
import {
|
|
37
|
+
renderActiveRite,
|
|
38
|
+
renderClosingRite,
|
|
39
|
+
renderEnrichmentProposal,
|
|
40
|
+
renderFruitInMaturation,
|
|
41
|
+
renderHarvestedFruit,
|
|
42
|
+
renderIdentityChangeApplied,
|
|
43
|
+
renderIntegrationReview,
|
|
44
|
+
renderPossibleListenings,
|
|
45
|
+
type SoulListeningOption,
|
|
46
|
+
SoulSurfaceError,
|
|
47
|
+
} from "#soul/render.ts";
|
|
48
|
+
import {
|
|
49
|
+
clearFruitInMaturation,
|
|
50
|
+
clearHarvestedFruit,
|
|
51
|
+
getFruitInMaturation,
|
|
52
|
+
getHarvestedFruit,
|
|
53
|
+
harvestFruit,
|
|
54
|
+
resolveCliSoulSessionId,
|
|
55
|
+
SoulStateError,
|
|
56
|
+
setFruitInMaturation,
|
|
57
|
+
} from "#soul/state.ts";
|
|
58
|
+
import { renderSoulModeTransition } from "#soul/transition.ts";
|
|
59
|
+
import { newId, nowIso } from "#util/pyGenerators.ts";
|
|
60
|
+
import { pyStrip } from "#util/pythonText.ts";
|
|
61
|
+
import { optionValue, stripOptionWithValue } from "./args.ts";
|
|
62
|
+
|
|
63
|
+
/** Python's argparse subcommands, by name. Order is irrelevant; membership is not. */
|
|
64
|
+
export const SOUL_SUBCOMMANDS = [
|
|
65
|
+
"load",
|
|
66
|
+
"listen",
|
|
67
|
+
"rite",
|
|
68
|
+
"close",
|
|
69
|
+
"review",
|
|
70
|
+
"propose",
|
|
71
|
+
"apply",
|
|
72
|
+
"fruit",
|
|
73
|
+
"harvest",
|
|
74
|
+
"prompt",
|
|
75
|
+
] as const;
|
|
76
|
+
|
|
77
|
+
const OPTIONS_WITH_VALUES = [
|
|
78
|
+
"--mirror-home",
|
|
79
|
+
"--db-path",
|
|
80
|
+
"--session-id",
|
|
81
|
+
"--matter",
|
|
82
|
+
"--self",
|
|
83
|
+
"--shadow",
|
|
84
|
+
"--wisdom",
|
|
85
|
+
"--beauty",
|
|
86
|
+
"--says",
|
|
87
|
+
"--listening-for",
|
|
88
|
+
"--question",
|
|
89
|
+
"--harvested",
|
|
90
|
+
"--echoes",
|
|
91
|
+
"--open",
|
|
92
|
+
"--integration",
|
|
93
|
+
"--origin",
|
|
94
|
+
"--journal",
|
|
95
|
+
"--ego",
|
|
96
|
+
"--persona",
|
|
97
|
+
"--key",
|
|
98
|
+
"--current",
|
|
99
|
+
"--proposed",
|
|
100
|
+
"--why",
|
|
101
|
+
"--confirm",
|
|
102
|
+
"--conversation-id",
|
|
103
|
+
"--journal-id",
|
|
104
|
+
"--journey",
|
|
105
|
+
];
|
|
106
|
+
|
|
107
|
+
function positionals(args: readonly string[]): string[] {
|
|
108
|
+
return OPTIONS_WITH_VALUES.reduce<string[]>(
|
|
109
|
+
(remaining, option) => stripOptionWithValue(remaining, option),
|
|
110
|
+
[...args],
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function fail(message: string): number {
|
|
115
|
+
process.stderr.write(`Error: ${message}\n`);
|
|
116
|
+
return 1;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** argparse's own exit for an unknown subcommand or a missing required argument. */
|
|
120
|
+
function usageError(message: string): number {
|
|
121
|
+
process.stderr.write(`${message}\n`);
|
|
122
|
+
return 2;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export interface SoulRouteDeps {
|
|
126
|
+
readMessages: (db: Database, conversationId: string) => TranscriptMessage[];
|
|
127
|
+
/**
|
|
128
|
+
* The harvest journal's embedding (CV22.DS8.US3).
|
|
129
|
+
*
|
|
130
|
+
* Async because it is a real, billable provider round trip: `routing.ts`
|
|
131
|
+
* used to keep `harvest save` on Python unless a replay fixture was set, and
|
|
132
|
+
* nothing ever supplied one through the front door -- so the only leaf of
|
|
133
|
+
* the Soul command that crosses the provider seam could not complete on
|
|
134
|
+
* TypeScript at all. The route now resolves the family's provider and
|
|
135
|
+
* embeds through `generateEmbeddingSafely`, with the ledger hook attached.
|
|
136
|
+
*/
|
|
137
|
+
embed: (db: WritableDatabase, text: string) => Promise<Uint8Array>;
|
|
138
|
+
newId: () => string;
|
|
139
|
+
nowIso: () => string;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export const defaultSoulRouteDeps: SoulRouteDeps = {
|
|
143
|
+
readMessages: (db, conversationId) =>
|
|
144
|
+
db
|
|
145
|
+
.prepare("SELECT role, content FROM messages WHERE conversation_id = ? ORDER BY rowid")
|
|
146
|
+
.all(conversationId) as unknown as TranscriptMessage[],
|
|
147
|
+
embed: async (db, text) => {
|
|
148
|
+
const family = await resolveFamilyProviders(process.env, SOUL_HARVEST_TRANSPORT);
|
|
149
|
+
if (!family?.embedding) {
|
|
150
|
+
// `routing.ts` reverts the family before the route is reached, so this
|
|
151
|
+
// is defense in depth rather than the operator-facing message.
|
|
152
|
+
throw new SoulHarvestError("soul harvest save requires an embedding provider");
|
|
153
|
+
}
|
|
154
|
+
// Through the wrapper, like every other embedding path: bounded retry of a
|
|
155
|
+
// transient empty payload, the AI-07 dimension guard, and one ledger row
|
|
156
|
+
// per round trip, priced (CR075's adjacent case).
|
|
157
|
+
const vector = await generateEmbeddingSafely(family.embedding, text, {
|
|
158
|
+
onAttempt: embeddingLedgerHook(db),
|
|
159
|
+
});
|
|
160
|
+
return new Uint8Array(new Float32Array(vector).buffer);
|
|
161
|
+
},
|
|
162
|
+
newId,
|
|
163
|
+
nowIso,
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
export async function runSoulRoute(
|
|
167
|
+
db: WritableDatabase,
|
|
168
|
+
argv: readonly string[],
|
|
169
|
+
deps: SoulRouteDeps = defaultSoulRouteDeps,
|
|
170
|
+
): Promise<number> {
|
|
171
|
+
const rawArgs = argv.slice(1);
|
|
172
|
+
const args = positionals(rawArgs);
|
|
173
|
+
const sub = args[0];
|
|
174
|
+
const option = (name: string) => optionValue(rawArgs, name);
|
|
175
|
+
|
|
176
|
+
try {
|
|
177
|
+
switch (sub) {
|
|
178
|
+
case "load":
|
|
179
|
+
return runLoad(db, args[1] ?? null, option("--session-id"), deps);
|
|
180
|
+
case "listen":
|
|
181
|
+
return write(renderPossibleListenings(listeningOptions(option)));
|
|
182
|
+
case "rite": {
|
|
183
|
+
const voice = args[1];
|
|
184
|
+
if (!voice) return usageError("soul rite requires a voice");
|
|
185
|
+
return write(
|
|
186
|
+
renderActiveRite(voice, {
|
|
187
|
+
utterance: option("--says"),
|
|
188
|
+
listeningFor: option("--listening-for"),
|
|
189
|
+
question: option("--question"),
|
|
190
|
+
}),
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
case "close":
|
|
194
|
+
return write(
|
|
195
|
+
renderClosingRite({
|
|
196
|
+
harvested: option("--harvested"),
|
|
197
|
+
echoes: option("--echoes"),
|
|
198
|
+
remainsOpen: option("--open"),
|
|
199
|
+
integration: option("--integration"),
|
|
200
|
+
}),
|
|
201
|
+
);
|
|
202
|
+
case "review":
|
|
203
|
+
return write(
|
|
204
|
+
renderIntegrationReview({
|
|
205
|
+
// `--origin` and its legacy alias `--journal` share one argparse
|
|
206
|
+
// destination, so the LAST one on the command line wins -- not a
|
|
207
|
+
// fixed precedence between them.
|
|
208
|
+
journal: lastOptionValue(rawArgs, ["--origin", "--journal"]),
|
|
209
|
+
selfMaterial: option("--self"),
|
|
210
|
+
shadow: option("--shadow"),
|
|
211
|
+
ego: option("--ego"),
|
|
212
|
+
persona: option("--persona"),
|
|
213
|
+
leaveOpen: option("--open"),
|
|
214
|
+
}),
|
|
215
|
+
);
|
|
216
|
+
case "propose": {
|
|
217
|
+
const layer = args[1];
|
|
218
|
+
if (!layer) return usageError("soul propose requires a layer");
|
|
219
|
+
const key = resolveIdentityKey(layer, option("--key"));
|
|
220
|
+
return write(
|
|
221
|
+
renderEnrichmentProposal(layer, {
|
|
222
|
+
key,
|
|
223
|
+
origin: option("--origin") ?? "",
|
|
224
|
+
current: option("--current"),
|
|
225
|
+
proposed: option("--proposed") ?? "",
|
|
226
|
+
why: option("--why") ?? "",
|
|
227
|
+
}),
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
case "apply":
|
|
231
|
+
return runApply(db, args[1], option, deps);
|
|
232
|
+
case "fruit":
|
|
233
|
+
return runFruit(db, args[1], args[2] ?? null, option("--session-id"), deps);
|
|
234
|
+
case "harvest":
|
|
235
|
+
return await runHarvest(db, args[1], args[2] ?? null, option, deps);
|
|
236
|
+
case "prompt":
|
|
237
|
+
return runPrompt(db, args[1]);
|
|
238
|
+
default:
|
|
239
|
+
return usageError(`soul: unknown subcommand ${sub ?? ""}`);
|
|
240
|
+
}
|
|
241
|
+
} catch (error) {
|
|
242
|
+
if (
|
|
243
|
+
error instanceof SoulSurfaceError ||
|
|
244
|
+
error instanceof SoulStateError ||
|
|
245
|
+
error instanceof SoulApplyError ||
|
|
246
|
+
error instanceof SoulHarvestError
|
|
247
|
+
) {
|
|
248
|
+
return fail(error.message);
|
|
249
|
+
}
|
|
250
|
+
throw error;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/** The value of whichever of `names` appears LAST in argv, as argparse resolves a shared dest. */
|
|
255
|
+
function lastOptionValue(args: readonly string[], names: readonly string[]): string | null {
|
|
256
|
+
let value: string | null = null;
|
|
257
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
258
|
+
if (names.includes(args[index] as string)) value = args[index + 1] ?? null;
|
|
259
|
+
}
|
|
260
|
+
return value;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function write(rendered: string): number {
|
|
264
|
+
process.stdout.write(`${rendered}\n`);
|
|
265
|
+
return 0;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function listeningOptions(option: (name: string) => string | null): SoulListeningOption[] {
|
|
269
|
+
const raw: [string, string | null][] = [
|
|
270
|
+
["self", option("--self")],
|
|
271
|
+
["shadow", option("--shadow")],
|
|
272
|
+
["wisdom", option("--wisdom")],
|
|
273
|
+
["beauty", option("--beauty")],
|
|
274
|
+
];
|
|
275
|
+
return raw
|
|
276
|
+
.filter(([, description]) => description !== null && description.trim() !== "")
|
|
277
|
+
.map(([voice, description]) => ({ voice, description: (description as string).trim() }));
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/** Port of `_resolve_identity_key`. */
|
|
281
|
+
function resolveIdentityKey(layer: string, key: string | null): string {
|
|
282
|
+
if (layer === "self") return key || "soul";
|
|
283
|
+
if (layer === "shadow") return key || "profile";
|
|
284
|
+
if (layer === "ego") return key || "behavior";
|
|
285
|
+
if (layer === "persona") {
|
|
286
|
+
if (!key) throw new SoulApplyError("persona proposals require --key");
|
|
287
|
+
return key;
|
|
288
|
+
}
|
|
289
|
+
throw new SoulApplyError(`unsupported psyche layer: ${layer}`);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function runLoad(
|
|
293
|
+
db: WritableDatabase,
|
|
294
|
+
slug: string | null,
|
|
295
|
+
sessionId: string | null,
|
|
296
|
+
deps: SoulRouteDeps,
|
|
297
|
+
): number {
|
|
298
|
+
const now = deps.nowIso();
|
|
299
|
+
if (slug) {
|
|
300
|
+
if (!getIdentityContent(db, "journey", slug)) {
|
|
301
|
+
process.stderr.write(`Error: journey '${slug}' not found.\n`);
|
|
302
|
+
return 1;
|
|
303
|
+
}
|
|
304
|
+
persistStickyDefaults(db, null, slug, now);
|
|
305
|
+
}
|
|
306
|
+
// `cmd_load` uses the OPERATING-MODE resolver, not the Soul one, and that
|
|
307
|
+
// resolver may return null -- in which case `activate_mode` writes the GLOBAL
|
|
308
|
+
// mode row instead of a session row. Falling back to `__global_soul_mode__`
|
|
309
|
+
// here would silently write to a different row than Python does.
|
|
310
|
+
const resolvedSessionId = resolveRuntimeSessionId(
|
|
311
|
+
db,
|
|
312
|
+
sessionId,
|
|
313
|
+
process.env.MIRROR_SESSION_ID ?? null,
|
|
314
|
+
);
|
|
315
|
+
activateOperatingMode(
|
|
316
|
+
db,
|
|
317
|
+
{ mode: "Soul Mode", journey: slug, sessionId: resolvedSessionId },
|
|
318
|
+
now,
|
|
319
|
+
);
|
|
320
|
+
return write(renderSoulModeTransition(slug));
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function runApply(
|
|
324
|
+
db: WritableDatabase,
|
|
325
|
+
layer: string | undefined,
|
|
326
|
+
option: (name: string) => string | null,
|
|
327
|
+
deps: SoulRouteDeps,
|
|
328
|
+
): number {
|
|
329
|
+
if (!layer) return usageError("soul apply requires a layer");
|
|
330
|
+
const key = resolveIdentityKey(layer, option("--key"));
|
|
331
|
+
if (option("--confirm") !== "APPLY") {
|
|
332
|
+
return fail("identity update requires --confirm APPLY");
|
|
333
|
+
}
|
|
334
|
+
const content = pyStrip(option("--proposed") ?? "");
|
|
335
|
+
if (!content) return fail("identity content must not be empty");
|
|
336
|
+
applyIdentityIntegration(
|
|
337
|
+
db,
|
|
338
|
+
{
|
|
339
|
+
layer,
|
|
340
|
+
key,
|
|
341
|
+
content,
|
|
342
|
+
origin: option("--origin"),
|
|
343
|
+
conversationId: option("--conversation-id"),
|
|
344
|
+
journalId: option("--journal-id"),
|
|
345
|
+
},
|
|
346
|
+
{ newId: deps.newId, nowIso: deps.nowIso() },
|
|
347
|
+
);
|
|
348
|
+
return write(renderIdentityChangeApplied(layer, { key, content }));
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function runFruit(
|
|
352
|
+
db: WritableDatabase,
|
|
353
|
+
action: string | undefined,
|
|
354
|
+
fruit: string | null,
|
|
355
|
+
sessionId: string | null,
|
|
356
|
+
deps: SoulRouteDeps,
|
|
357
|
+
): number {
|
|
358
|
+
const resolved = resolveCliSoulSessionId(db, sessionId, process.env.MIRROR_SESSION_ID ?? null);
|
|
359
|
+
if (action === "set") {
|
|
360
|
+
if (fruit === null) return usageError("soul fruit set requires a fruit");
|
|
361
|
+
const state = setFruitInMaturation(db, fruit, resolved, deps.nowIso());
|
|
362
|
+
return write(renderFruitInMaturation(state.fruit ?? ""));
|
|
363
|
+
}
|
|
364
|
+
if (action === "show") {
|
|
365
|
+
const state = getFruitInMaturation(db, resolved);
|
|
366
|
+
if (!state.fruit) return fail("No fruit in maturation.");
|
|
367
|
+
return write(renderFruitInMaturation(state.fruit));
|
|
368
|
+
}
|
|
369
|
+
if (action === "clear") {
|
|
370
|
+
clearFruitInMaturation(db, resolved, deps.nowIso());
|
|
371
|
+
process.stdout.write("Fruit in maturation cleared.\n");
|
|
372
|
+
return 0;
|
|
373
|
+
}
|
|
374
|
+
return usageError(`soul fruit: unknown action ${action ?? ""}`);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
async function runHarvest(
|
|
378
|
+
db: WritableDatabase,
|
|
379
|
+
action: string | undefined,
|
|
380
|
+
fruit: string | null,
|
|
381
|
+
option: (name: string) => string | null,
|
|
382
|
+
deps: SoulRouteDeps,
|
|
383
|
+
): Promise<number> {
|
|
384
|
+
const resolved = resolveCliSoulSessionId(
|
|
385
|
+
db,
|
|
386
|
+
option("--session-id"),
|
|
387
|
+
process.env.MIRROR_SESSION_ID ?? null,
|
|
388
|
+
);
|
|
389
|
+
if (action === "set") {
|
|
390
|
+
const state = harvestFruit(db, { fruit, sessionId: resolved }, deps.nowIso());
|
|
391
|
+
return write(renderHarvestedFruit(state.fruit ?? ""));
|
|
392
|
+
}
|
|
393
|
+
if (action === "show") {
|
|
394
|
+
const state = getHarvestedFruit(db, resolved);
|
|
395
|
+
if (!state.fruit) return fail("No harvested fruit.");
|
|
396
|
+
return write(renderHarvestedFruit(state.fruit));
|
|
397
|
+
}
|
|
398
|
+
if (action === "save") {
|
|
399
|
+
const state = getHarvestedFruit(db, resolved);
|
|
400
|
+
if (!state.fruit) return fail("No harvested fruit.");
|
|
401
|
+
// Compose, then embed, then persist. The fruit is cleared inside the
|
|
402
|
+
// persist step, so a failing embedding leaves it harvested for a retry
|
|
403
|
+
// rather than losing it to a provider outage -- Python's ordering.
|
|
404
|
+
const plan = planHarvestSave(db, resolved, (conversationId) =>
|
|
405
|
+
deps.readMessages(db, conversationId),
|
|
406
|
+
);
|
|
407
|
+
const embedding = await deps.embed(db, plan.embedText);
|
|
408
|
+
const result = persistHarvestSave(
|
|
409
|
+
db,
|
|
410
|
+
plan,
|
|
411
|
+
{ sessionId: resolved, journey: option("--journey") },
|
|
412
|
+
{ newId: deps.newId, nowIso: deps.nowIso(), embedding },
|
|
413
|
+
);
|
|
414
|
+
process.stdout.write(`Harvest saved to journal. ${result.memoryId}\n`);
|
|
415
|
+
return 0;
|
|
416
|
+
}
|
|
417
|
+
if (action === "decline") {
|
|
418
|
+
clearHarvestedFruit(db, resolved, deps.nowIso());
|
|
419
|
+
process.stdout.write("Harvest discarded without journal save.\n");
|
|
420
|
+
return 0;
|
|
421
|
+
}
|
|
422
|
+
return usageError(`soul harvest: unknown action ${action ?? ""}`);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
function runPrompt(db: Database, voice: string | undefined): number {
|
|
426
|
+
// `print(prompt)` appends a newline to a template that already ends with one,
|
|
427
|
+
// so the shipped output ends with two. Stripping either is a divergence.
|
|
428
|
+
if (voice === "self") return write(composeSoulSelfVoicePrompt(db));
|
|
429
|
+
if (voice === "wisdom") return write(composeSoulWisdomVoicePrompt());
|
|
430
|
+
if (voice === "beauty") return write(composeSoulBeautyVoicePrompt());
|
|
431
|
+
process.stderr.write(`Error: unsupported Soul Mode prompt voice: ${voice ?? ""}\n`);
|
|
432
|
+
return 1;
|
|
433
|
+
}
|