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,364 @@
|
|
|
1
|
+
import type { Database, WritableDatabase } from "#db/database.ts";
|
|
2
|
+
import {
|
|
3
|
+
type CollectExtensionContextOptions,
|
|
4
|
+
collectExtensionContext,
|
|
5
|
+
type ExtensionContextDiagnostic,
|
|
6
|
+
} from "#extensions/contextRuntime.ts";
|
|
7
|
+
import { activateOperatingMode } from "#mode/operatingMode.ts";
|
|
8
|
+
import type { OnProviderCallOutcome } from "#observability/callOutcome.ts";
|
|
9
|
+
import { logLlmCall } from "#observability/llmCalls.ts";
|
|
10
|
+
import { computeCost } from "#providers/cost.ts";
|
|
11
|
+
import type { EmbeddingProvider } from "#providers/embedding.ts";
|
|
12
|
+
import type { LlmProvider } from "#providers/llm.ts";
|
|
13
|
+
import { loadMirrorContext } from "./context.ts";
|
|
14
|
+
import { type ResolvedMirrorDefaults, resolveMirrorDefaults } from "./defaultResolution.ts";
|
|
15
|
+
import { pythonTruthy, sliceCodePoints } from "./reception.ts";
|
|
16
|
+
import {
|
|
17
|
+
renderJourneyDetected,
|
|
18
|
+
renderMirrorBanner,
|
|
19
|
+
renderMirrorJourneys,
|
|
20
|
+
renderMirrorModeTransition,
|
|
21
|
+
} from "./render.ts";
|
|
22
|
+
import {
|
|
23
|
+
decodeMetadata,
|
|
24
|
+
GLOBAL_STICKY_DEFAULTS_SESSION_ID,
|
|
25
|
+
getRuntimeSession,
|
|
26
|
+
resolveRuntimeSessionId,
|
|
27
|
+
upsertRuntimeSession,
|
|
28
|
+
} from "./runtimeSession.ts";
|
|
29
|
+
|
|
30
|
+
export interface MirrorLoadInput {
|
|
31
|
+
identity: string;
|
|
32
|
+
persona?: string | null;
|
|
33
|
+
journey?: string | null;
|
|
34
|
+
query?: string | null;
|
|
35
|
+
org?: boolean;
|
|
36
|
+
contextOnly?: boolean;
|
|
37
|
+
sessionId?: string | null;
|
|
38
|
+
environmentSessionId?: string | null;
|
|
39
|
+
receptionEnabled: boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Reception's outcome, for the front-door log. `mirror load --query` is the
|
|
42
|
+
* per-activation surface: it swallows every failure into an empty context,
|
|
43
|
+
* so without this a provider outage, a prompt-layer parse failure, and a
|
|
44
|
+
* genuinely unclassifiable message all look like "no persona routed".
|
|
45
|
+
*/
|
|
46
|
+
onReceptionOutcome?: OnProviderCallOutcome;
|
|
47
|
+
llmProvider?: LlmProvider;
|
|
48
|
+
embeddingProvider?: EmbeddingProvider;
|
|
49
|
+
databasePath?: string;
|
|
50
|
+
mirrorHome?: string;
|
|
51
|
+
user?: string;
|
|
52
|
+
extensionRuntime?: Pick<
|
|
53
|
+
CollectExtensionContextOptions,
|
|
54
|
+
"timeoutMs" | "maxOutputBytes" | "environment"
|
|
55
|
+
>;
|
|
56
|
+
newId: () => string;
|
|
57
|
+
nowIso: () => string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface RenderedMirrorLoad {
|
|
61
|
+
stdout: string;
|
|
62
|
+
stderr: string;
|
|
63
|
+
resolved: ResolvedMirrorDefaults;
|
|
64
|
+
extensionDiagnostics: ExtensionContextDiagnostic[];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export async function runMirrorLoad(
|
|
68
|
+
db: WritableDatabase,
|
|
69
|
+
input: MirrorLoadInput,
|
|
70
|
+
): Promise<RenderedMirrorLoad> {
|
|
71
|
+
const resolved = await resolveMirrorDefaults(db, {
|
|
72
|
+
...input,
|
|
73
|
+
onReceptionOutcome: input.onReceptionOutcome,
|
|
74
|
+
// Priced, as Python's `build_llm_logger(role="reception")` prices it
|
|
75
|
+
// (CV22.DS8.US3). The row existed before but carried a null cost, so
|
|
76
|
+
// every Mirror Mode activation with a query would have logged live spend
|
|
77
|
+
// as free.
|
|
78
|
+
onReceptionLlmCall: (response, prompt) =>
|
|
79
|
+
logLlmCall(
|
|
80
|
+
db,
|
|
81
|
+
{
|
|
82
|
+
role: "reception",
|
|
83
|
+
model: response.model ?? "unknown",
|
|
84
|
+
prompt,
|
|
85
|
+
response: response.content,
|
|
86
|
+
promptTokens: response.promptTokens,
|
|
87
|
+
completionTokens: response.completionTokens,
|
|
88
|
+
latencyMs: response.latencyMs,
|
|
89
|
+
costUsd: computeCost(
|
|
90
|
+
response.model ?? "unknown",
|
|
91
|
+
response.promptTokens ?? null,
|
|
92
|
+
response.completionTokens ?? null,
|
|
93
|
+
),
|
|
94
|
+
sessionId: input.sessionId ?? null,
|
|
95
|
+
},
|
|
96
|
+
{ id: input.newId, now: input.nowIso },
|
|
97
|
+
),
|
|
98
|
+
});
|
|
99
|
+
const extensionContext =
|
|
100
|
+
input.databasePath && input.mirrorHome
|
|
101
|
+
? collectExtensionContext(db, {
|
|
102
|
+
mirrorHome: input.mirrorHome,
|
|
103
|
+
databasePath: input.databasePath,
|
|
104
|
+
personaId: resolved.persona,
|
|
105
|
+
journeyId: resolved.journey,
|
|
106
|
+
user: input.user ?? "",
|
|
107
|
+
query: input.query,
|
|
108
|
+
...input.extensionRuntime,
|
|
109
|
+
})
|
|
110
|
+
: { rendered: "", diagnostics: [] };
|
|
111
|
+
const context = await loadMirrorContext(db, {
|
|
112
|
+
persona: resolved.persona,
|
|
113
|
+
journey: resolved.journey,
|
|
114
|
+
org: input.org,
|
|
115
|
+
query: input.query,
|
|
116
|
+
touchesIdentity: resolved.touchesIdentity,
|
|
117
|
+
touchesShadow: resolved.touchesShadow,
|
|
118
|
+
embeddingProvider: input.embeddingProvider,
|
|
119
|
+
extensionContext: extensionContext.rendered,
|
|
120
|
+
});
|
|
121
|
+
persistStickyDefaults(db, resolved.persona, resolved.journey, input.nowIso());
|
|
122
|
+
activateOperatingMode(db, { mode: "Mirror Mode", journey: resolved.journey }, input.nowIso());
|
|
123
|
+
writeMirrorState(
|
|
124
|
+
db,
|
|
125
|
+
{
|
|
126
|
+
active: true,
|
|
127
|
+
persona: resolved.persona,
|
|
128
|
+
journey: resolved.journey,
|
|
129
|
+
sessionId: input.sessionId ?? null,
|
|
130
|
+
},
|
|
131
|
+
input.nowIso(),
|
|
132
|
+
);
|
|
133
|
+
if (!input.contextOnly) {
|
|
134
|
+
bindConversationContext(
|
|
135
|
+
db,
|
|
136
|
+
input.sessionId ?? null,
|
|
137
|
+
input.environmentSessionId ?? null,
|
|
138
|
+
resolved.persona,
|
|
139
|
+
resolved.journey,
|
|
140
|
+
input.newId,
|
|
141
|
+
input.nowIso,
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
const personas = db
|
|
145
|
+
.prepare("SELECT key FROM identity WHERE layer = 'persona' ORDER BY key")
|
|
146
|
+
.all()
|
|
147
|
+
.map((row) => String(row.key));
|
|
148
|
+
const transition = renderMirrorModeTransition({
|
|
149
|
+
identity: input.identity,
|
|
150
|
+
journey: resolved.journey,
|
|
151
|
+
personas,
|
|
152
|
+
});
|
|
153
|
+
const detected = resolved.detectedJourney?.[0];
|
|
154
|
+
return {
|
|
155
|
+
stdout: `${transition}\n${context}\n`,
|
|
156
|
+
stderr: `${detected ? renderJourneyDetected(detected) : ""}${renderMirrorBanner(resolved.persona)}`,
|
|
157
|
+
resolved,
|
|
158
|
+
extensionDiagnostics: extensionContext.diagnostics,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export function deactivateMirrorState(
|
|
163
|
+
db: WritableDatabase,
|
|
164
|
+
sessionId: string,
|
|
165
|
+
nowIso: string,
|
|
166
|
+
): void {
|
|
167
|
+
writeMirrorState(db, { active: false, sessionId }, nowIso);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function logMirrorResponse(
|
|
171
|
+
db: WritableDatabase,
|
|
172
|
+
input: {
|
|
173
|
+
summary: string;
|
|
174
|
+
sessionId?: string | null;
|
|
175
|
+
environmentSessionId?: string | null;
|
|
176
|
+
muted: boolean;
|
|
177
|
+
newId: () => string;
|
|
178
|
+
nowIso: () => string;
|
|
179
|
+
},
|
|
180
|
+
): void {
|
|
181
|
+
if (input.muted) return;
|
|
182
|
+
const sessionId = resolveRuntimeSessionId(
|
|
183
|
+
db,
|
|
184
|
+
input.sessionId ?? null,
|
|
185
|
+
input.environmentSessionId ?? null,
|
|
186
|
+
);
|
|
187
|
+
if (!sessionId) return;
|
|
188
|
+
const session = getRuntimeSession(db, sessionId);
|
|
189
|
+
const discarded = pythonTruthy(
|
|
190
|
+
decodeMetadata(session?.metadata ?? null).discard_current_conversation,
|
|
191
|
+
);
|
|
192
|
+
const conversationId = discarded
|
|
193
|
+
? session?.conversationId &&
|
|
194
|
+
db.prepare("SELECT 1 FROM conversations WHERE id = ?").get(session.conversationId)
|
|
195
|
+
? session.conversationId
|
|
196
|
+
: null
|
|
197
|
+
: getOrCreateConversation(db, sessionId, input.newId, input.nowIso);
|
|
198
|
+
if (!conversationId) return;
|
|
199
|
+
if (!discarded) {
|
|
200
|
+
db.prepare(
|
|
201
|
+
`INSERT INTO messages (id, conversation_id, role, content, created_at, token_count, metadata)
|
|
202
|
+
VALUES (?, ?, 'assistant', ?, ?, NULL, NULL)`,
|
|
203
|
+
).run(input.newId(), conversationId, input.summary, input.nowIso());
|
|
204
|
+
}
|
|
205
|
+
db.prepare("UPDATE conversations SET title = ? WHERE id = ?").run(
|
|
206
|
+
titleFromSummary(input.summary),
|
|
207
|
+
conversationId,
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export function listActiveMirrorJourneys(db: Database): string {
|
|
212
|
+
const rows = db
|
|
213
|
+
.prepare("SELECT key, content FROM identity WHERE layer = 'journey' ORDER BY key")
|
|
214
|
+
.all()
|
|
215
|
+
.map((row) => journeyDto(String(row.key), typeof row.content === "string" ? row.content : ""))
|
|
216
|
+
.filter((row) => row.status === "active");
|
|
217
|
+
return renderMirrorJourneys(rows);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export function titleFromSummary(summary: string): string {
|
|
221
|
+
let title = summary.split(/[.!?]/, 1)[0]?.trim() ?? "";
|
|
222
|
+
if ([...title].length > 60) {
|
|
223
|
+
const prefix = [...title].slice(0, 60).join("");
|
|
224
|
+
const space = prefix.lastIndexOf(" ");
|
|
225
|
+
title = `${space >= 0 ? prefix.slice(0, space) : prefix}...`;
|
|
226
|
+
}
|
|
227
|
+
return title;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Port of `_persist_global_sticky_defaults`. Exported for `soul load`, which
|
|
232
|
+
* calls the same writer with `persona=None` (CV22.DS7.US6).
|
|
233
|
+
*/
|
|
234
|
+
export function persistStickyDefaults(
|
|
235
|
+
db: WritableDatabase,
|
|
236
|
+
persona: string | null,
|
|
237
|
+
journey: string | null,
|
|
238
|
+
nowIso: string,
|
|
239
|
+
): void {
|
|
240
|
+
if (persona === null && journey === null) return;
|
|
241
|
+
upsertRuntimeSession(
|
|
242
|
+
db,
|
|
243
|
+
GLOBAL_STICKY_DEFAULTS_SESSION_ID,
|
|
244
|
+
{
|
|
245
|
+
interface: "global_defaults",
|
|
246
|
+
mirrorActive: false,
|
|
247
|
+
...(persona !== null ? { persona } : {}),
|
|
248
|
+
...(journey !== null ? { journey } : {}),
|
|
249
|
+
hookInjected: true,
|
|
250
|
+
active: false,
|
|
251
|
+
},
|
|
252
|
+
nowIso,
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function writeMirrorState(
|
|
257
|
+
db: WritableDatabase,
|
|
258
|
+
input: {
|
|
259
|
+
active: boolean;
|
|
260
|
+
persona?: string | null;
|
|
261
|
+
journey?: string | null;
|
|
262
|
+
sessionId: string | null;
|
|
263
|
+
},
|
|
264
|
+
nowIso: string,
|
|
265
|
+
): void {
|
|
266
|
+
if (!input.sessionId) return;
|
|
267
|
+
upsertRuntimeSession(
|
|
268
|
+
db,
|
|
269
|
+
input.sessionId,
|
|
270
|
+
{
|
|
271
|
+
mirrorActive: input.active,
|
|
272
|
+
...(input.persona !== undefined && input.persona !== null ? { persona: input.persona } : {}),
|
|
273
|
+
...(input.journey !== undefined && input.journey !== null ? { journey: input.journey } : {}),
|
|
274
|
+
hookInjected: !input.active,
|
|
275
|
+
},
|
|
276
|
+
nowIso,
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function bindConversationContext(
|
|
281
|
+
db: WritableDatabase,
|
|
282
|
+
explicitSessionId: string | null,
|
|
283
|
+
environmentSessionId: string | null,
|
|
284
|
+
persona: string | null,
|
|
285
|
+
journey: string | null,
|
|
286
|
+
newId: () => string,
|
|
287
|
+
nowIso: () => string,
|
|
288
|
+
): string | null {
|
|
289
|
+
const sessionId = resolveRuntimeSessionId(db, explicitSessionId, environmentSessionId);
|
|
290
|
+
if (!sessionId) return null;
|
|
291
|
+
const session = getRuntimeSession(db, sessionId);
|
|
292
|
+
const existing = session?.conversationId
|
|
293
|
+
? db.prepare("SELECT id, ended_at FROM conversations WHERE id = ?").get(session.conversationId)
|
|
294
|
+
: undefined;
|
|
295
|
+
let conversationId: string;
|
|
296
|
+
if (existing && existing.ended_at === null) {
|
|
297
|
+
conversationId = String(existing.id);
|
|
298
|
+
db.prepare("UPDATE conversations SET persona = ?, journey = ? WHERE id = ?").run(
|
|
299
|
+
persona,
|
|
300
|
+
journey,
|
|
301
|
+
conversationId,
|
|
302
|
+
);
|
|
303
|
+
} else {
|
|
304
|
+
conversationId = newId();
|
|
305
|
+
db.prepare(
|
|
306
|
+
`INSERT INTO conversations
|
|
307
|
+
(id, title, started_at, ended_at, interface, persona, journey, summary, tags, metadata)
|
|
308
|
+
VALUES (?, NULL, ?, NULL, ?, ?, ?, NULL, NULL, NULL)`,
|
|
309
|
+
).run(conversationId, nowIso(), session?.interface ?? "claude_code", persona, journey);
|
|
310
|
+
}
|
|
311
|
+
upsertRuntimeSession(
|
|
312
|
+
db,
|
|
313
|
+
sessionId,
|
|
314
|
+
{
|
|
315
|
+
conversationId,
|
|
316
|
+
interface: session?.interface ?? "claude_code",
|
|
317
|
+
...(persona !== null ? { persona } : {}),
|
|
318
|
+
...(journey !== null ? { journey } : {}),
|
|
319
|
+
active: true,
|
|
320
|
+
closedAt: null,
|
|
321
|
+
},
|
|
322
|
+
nowIso(),
|
|
323
|
+
);
|
|
324
|
+
return conversationId;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function getOrCreateConversation(
|
|
328
|
+
db: WritableDatabase,
|
|
329
|
+
sessionId: string,
|
|
330
|
+
newId: () => string,
|
|
331
|
+
nowIso: () => string,
|
|
332
|
+
): string {
|
|
333
|
+
const session = getRuntimeSession(db, sessionId);
|
|
334
|
+
if (session?.conversationId) {
|
|
335
|
+
const existing = db
|
|
336
|
+
.prepare("SELECT id FROM conversations WHERE id = ?")
|
|
337
|
+
.get(session.conversationId);
|
|
338
|
+
if (existing) return String(existing.id);
|
|
339
|
+
}
|
|
340
|
+
const id = newId();
|
|
341
|
+
const now = nowIso();
|
|
342
|
+
const interfaceName = session?.interface ?? "claude_code";
|
|
343
|
+
db.prepare(
|
|
344
|
+
`INSERT INTO conversations
|
|
345
|
+
(id, title, started_at, ended_at, interface, persona, journey, summary, tags, metadata)
|
|
346
|
+
VALUES (?, NULL, ?, NULL, ?, ?, ?, NULL, NULL, NULL)`,
|
|
347
|
+
).run(id, now, interfaceName, session?.persona ?? null, session?.journey ?? null);
|
|
348
|
+
upsertRuntimeSession(
|
|
349
|
+
db,
|
|
350
|
+
sessionId,
|
|
351
|
+
{ conversationId: id, interface: interfaceName, active: true, closedAt: null },
|
|
352
|
+
now,
|
|
353
|
+
);
|
|
354
|
+
return id;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function journeyDto(id: string, content: string) {
|
|
358
|
+
const name = (content.split("\n")[0] ?? "").trim().replace(/^[# ]+/, "");
|
|
359
|
+
const status = content.match(/\*\*Status:\*\*\s*([\p{L}\p{N}_]+)/u)?.[1] ?? "unknown";
|
|
360
|
+
const description =
|
|
361
|
+
content.match(/## (?:Description|Descrição)\s*\n+(.+?)(?:\n\n|\n##)/s)?.[1]?.trim() ?? "";
|
|
362
|
+
const truncatedDescription = sliceCodePoints(description, 150);
|
|
363
|
+
return { id, name, description: truncatedDescription, status };
|
|
364
|
+
}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { parseJsonResponse } from "#extraction/json.ts";
|
|
2
|
+
import { classifyProviderError, type OnProviderCallOutcome } from "#observability/callOutcome.ts";
|
|
3
|
+
import type { LlmProvider, LlmResponse } from "#providers/llm.ts";
|
|
4
|
+
import { pyFormat } from "#util/pythonText.ts";
|
|
5
|
+
|
|
6
|
+
export interface ReceptionPersona {
|
|
7
|
+
slug: string;
|
|
8
|
+
description: string;
|
|
9
|
+
routingKeywords: readonly string[];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface ReceptionJourney {
|
|
13
|
+
slug: string;
|
|
14
|
+
description: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface ReceptionResult {
|
|
18
|
+
personas: string[];
|
|
19
|
+
journey: string | null;
|
|
20
|
+
touchesIdentity: boolean;
|
|
21
|
+
touchesShadow: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const EMPTY_RECEPTION_RESULT: ReceptionResult = Object.freeze({
|
|
25
|
+
personas: [],
|
|
26
|
+
journey: null,
|
|
27
|
+
touchesIdentity: false,
|
|
28
|
+
touchesShadow: false,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Byte-identical to Python's `RECEPTION_PROMPT`, doubled braces included.
|
|
33
|
+
*
|
|
34
|
+
* The braces are not cosmetic. Python substitutes through `str.format`, which
|
|
35
|
+
* requires `{{` to emit a literal `{`, so the JSON example in the response
|
|
36
|
+
* format section is doubled at the source. Holding the same bytes here means
|
|
37
|
+
* the template can be compared to the oracle directly, and it forces assembly
|
|
38
|
+
* through `pyFormat` -- which is the point: `String.replace()` with a string
|
|
39
|
+
* pattern treats `$&`, `` $` ``, `$'`, and `$1` in the REPLACEMENT as
|
|
40
|
+
* substitution directives, so a persona described as "Cost: $& per hour"
|
|
41
|
+
* assembled as "Cost: {personas} per hour" (CV22.DS8.US3). Python's `format`
|
|
42
|
+
* has no such behavior, no fixture ever carried a `$` pattern, and replay
|
|
43
|
+
* never reads a prompt -- so only a live call would have shown it.
|
|
44
|
+
*/
|
|
45
|
+
export const RECEPTION_PROMPT = `You are the reception classifier for Mirror Mind, a Jungian mirror AI.
|
|
46
|
+
|
|
47
|
+
Your job is to classify a single user message on four axes so the mirror can
|
|
48
|
+
compose the right context for its response. Read the message carefully, then
|
|
49
|
+
return a JSON object — nothing else.
|
|
50
|
+
|
|
51
|
+
## Available personas
|
|
52
|
+
{personas}
|
|
53
|
+
|
|
54
|
+
## Active journeys
|
|
55
|
+
{journeys}
|
|
56
|
+
|
|
57
|
+
## Classification rules
|
|
58
|
+
|
|
59
|
+
**personas** (array of slugs, ordered most-to-least relevant, or [] if none apply)
|
|
60
|
+
- Return the personas whose domain clearly covers this message.
|
|
61
|
+
- Action verbs dominate topic: "write a post about X" → writer, not the X-domain persona.
|
|
62
|
+
- When a single persona's domain covers the message, return only that one.
|
|
63
|
+
- When genuinely ambiguous, return the most relevant one only.
|
|
64
|
+
- Return [] when the ego should answer alone (open questions, general curiosity,
|
|
65
|
+
meta questions about the mirror itself).
|
|
66
|
+
|
|
67
|
+
**journey** (slug string or null)
|
|
68
|
+
- Return the slug whose description best matches the context of this message.
|
|
69
|
+
- Conservative: prefer null over a speculative match.
|
|
70
|
+
- Return null if no journey is clearly relevant.
|
|
71
|
+
|
|
72
|
+
**touches_identity** (boolean)
|
|
73
|
+
- true ONLY when the message explicitly invites reflection on personal values,
|
|
74
|
+
life purpose, meaning, or deep self-examination.
|
|
75
|
+
- Operational and technical questions are false even if they involve important decisions.
|
|
76
|
+
- Default false. The cost of missing a touch is a lighter context load;
|
|
77
|
+
the cost of over-triggering is token waste on every routine turn.
|
|
78
|
+
|
|
79
|
+
**touches_shadow** (boolean)
|
|
80
|
+
- true ONLY when there is explicit evidence of avoidance, internal contradiction,
|
|
81
|
+
or a recurring pattern the user is naming or circling around.
|
|
82
|
+
- Requires positive signal. Vague discomfort or uncertainty alone is false.
|
|
83
|
+
- Default false. Conservative by design.
|
|
84
|
+
|
|
85
|
+
## Response format
|
|
86
|
+
Return ONLY a JSON object, no markdown:
|
|
87
|
+
{{
|
|
88
|
+
"personas": ["slug", ...],
|
|
89
|
+
"journey": "slug" or null,
|
|
90
|
+
"touches_identity": true or false,
|
|
91
|
+
"touches_shadow": true or false
|
|
92
|
+
}}
|
|
93
|
+
|
|
94
|
+
## User message
|
|
95
|
+
`;
|
|
96
|
+
|
|
97
|
+
export async function runReception(
|
|
98
|
+
query: string,
|
|
99
|
+
personas: readonly ReceptionPersona[],
|
|
100
|
+
journeys: readonly ReceptionJourney[],
|
|
101
|
+
provider: LlmProvider,
|
|
102
|
+
onLlmCall?: (response: LlmResponse, prompt: string) => void,
|
|
103
|
+
onOutcome?: OnProviderCallOutcome,
|
|
104
|
+
): Promise<ReceptionResult> {
|
|
105
|
+
if (!query.trim()) return { ...EMPTY_RECEPTION_RESULT, personas: [] };
|
|
106
|
+
const prompt = buildReceptionPrompt(query, personas, journeys);
|
|
107
|
+
try {
|
|
108
|
+
const response = await provider.complete({ role: "reception", prompt, temperature: 0.1 });
|
|
109
|
+
onLlmCall?.(response, prompt);
|
|
110
|
+
const parsed = parseJsonResponse(response.content);
|
|
111
|
+
if (!isRecord(parsed)) {
|
|
112
|
+
// The classifier answered with something the parser could not use. The
|
|
113
|
+
// result is the same empty context as a provider outage, and the cause
|
|
114
|
+
// is not: this one is prompt-layer, and it was paid for.
|
|
115
|
+
onOutcome?.({ outcome: "parse_failed" });
|
|
116
|
+
return { ...EMPTY_RECEPTION_RESULT, personas: [] };
|
|
117
|
+
}
|
|
118
|
+
const personasOut = Array.isArray(parsed.personas)
|
|
119
|
+
? parsed.personas.filter((item): item is string => typeof item === "string")
|
|
120
|
+
: [];
|
|
121
|
+
const result = {
|
|
122
|
+
personas: personasOut,
|
|
123
|
+
journey: typeof parsed.journey === "string" ? parsed.journey : null,
|
|
124
|
+
touchesIdentity: pythonTruthy(parsed.touches_identity),
|
|
125
|
+
touchesShadow: pythonTruthy(parsed.touches_shadow),
|
|
126
|
+
};
|
|
127
|
+
const carriedSignal =
|
|
128
|
+
result.personas.length > 0 ||
|
|
129
|
+
result.journey !== null ||
|
|
130
|
+
result.touchesIdentity ||
|
|
131
|
+
result.touchesShadow;
|
|
132
|
+
onOutcome?.({ outcome: carriedSignal ? "answered" : "empty" });
|
|
133
|
+
return result;
|
|
134
|
+
} catch (error) {
|
|
135
|
+
onOutcome?.({ outcome: "transport_failed", kind: classifyProviderError(error) });
|
|
136
|
+
return { ...EMPTY_RECEPTION_RESULT, personas: [] };
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Python's assembled reception prompt: the formatted template, then the raw
|
|
142
|
+
* query appended after the `## User message` heading.
|
|
143
|
+
*
|
|
144
|
+
* Pure and exported so the assembled BYTES can be graded against the oracle
|
|
145
|
+
* corpus without a provider (CV22.DS8.US3). Replay resolves a fixture by role
|
|
146
|
+
* alone and never reads the prompt, so a drift here would have surfaced first
|
|
147
|
+
* against a real model.
|
|
148
|
+
*/
|
|
149
|
+
export function buildReceptionPrompt(
|
|
150
|
+
query: string,
|
|
151
|
+
personas: readonly ReceptionPersona[],
|
|
152
|
+
journeys: readonly ReceptionJourney[],
|
|
153
|
+
): string {
|
|
154
|
+
return (
|
|
155
|
+
pyFormat(RECEPTION_PROMPT, {
|
|
156
|
+
personas: formatPersonas(personas),
|
|
157
|
+
journeys: formatJourneys(journeys),
|
|
158
|
+
}) + query
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export function formatPersonas(personas: readonly ReceptionPersona[]): string {
|
|
163
|
+
if (personas.length === 0) return "(none available)";
|
|
164
|
+
return personas
|
|
165
|
+
.map((persona) => {
|
|
166
|
+
const keywords = persona.routingKeywords.slice(0, 6);
|
|
167
|
+
const suffix = keywords.length > 0 ? ` [keywords: ${keywords.join(", ")}]` : "";
|
|
168
|
+
return `- ${persona.slug}: ${sliceCodePoints(persona.description, 120)}${suffix}`;
|
|
169
|
+
})
|
|
170
|
+
.join("\n");
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export function formatJourneys(journeys: readonly ReceptionJourney[]): string {
|
|
174
|
+
if (journeys.length === 0) return "(none available)";
|
|
175
|
+
return journeys
|
|
176
|
+
.map((journey) => `- ${journey.slug}: ${sliceCodePoints(journey.description, 120)}`)
|
|
177
|
+
.join("\n");
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function sliceCodePoints(value: string, limit: number): string {
|
|
181
|
+
return [...value].slice(0, limit).join("");
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export function pythonTruthy(value: unknown): boolean {
|
|
185
|
+
if (value === null || value === undefined || value === false) return false;
|
|
186
|
+
if (typeof value === "number") return value !== 0;
|
|
187
|
+
if (typeof value === "string" || Array.isArray(value)) return value.length > 0;
|
|
188
|
+
if (typeof value === "object") return Object.keys(value).length > 0;
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
193
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
194
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
const WIDTH = 56;
|
|
2
|
+
|
|
3
|
+
export function renderMirrorModeTransition(input: {
|
|
4
|
+
identity: string;
|
|
5
|
+
journey?: string | null;
|
|
6
|
+
personas?: readonly string[];
|
|
7
|
+
}): string {
|
|
8
|
+
const rows: [string, string][] = [["identity", input.identity]];
|
|
9
|
+
if (input.journey) rows.push(["active journey", input.journey]);
|
|
10
|
+
rows.push([
|
|
11
|
+
"what this mode is",
|
|
12
|
+
"Identity lens. Mirror reflects from memory, values, journeys, tensions, and personas.",
|
|
13
|
+
]);
|
|
14
|
+
rows.push(["persona routing", personaRoutingLine([...(input.personas ?? [])].sort())]);
|
|
15
|
+
rows.push(["available lenses", "◌ Mirror Mode · ■ Builder Mode · △ Explorer Mode · ☾ Soul Mode"]);
|
|
16
|
+
return box("◌ MIRROR MODE ACTIVE", rows);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function renderMirrorBanner(persona: string | null): string {
|
|
20
|
+
const first = "\u001b[38;5;183m⏺ Mirror Mode active\u001b[0m\n";
|
|
21
|
+
return persona
|
|
22
|
+
? `${first}\u001b[38;5;183m ✦ Persona: ${persona}\u001b[0m\n`
|
|
23
|
+
: `${first}\u001b[38;5;183m Ego responding without persona\u001b[0m\n`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function renderJourneyDetected(match: [string, number, string]): string {
|
|
27
|
+
return `\u001b[38;5;183m 🧭 Journey detected: ${match[0]} (${match[2]}, score: ${match[1].toFixed(2)})\u001b[0m\n`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function renderMirrorJourneys(
|
|
31
|
+
journeys: readonly { id: string; name: string; description: string }[],
|
|
32
|
+
): string {
|
|
33
|
+
return journeys
|
|
34
|
+
.map((journey) => `- **${journey.id}** — ${journey.name}: ${journey.description}\n`)
|
|
35
|
+
.join("");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function personaRoutingLine(personas: string[]): string {
|
|
39
|
+
if (personas.length === 0) return "when the topic asks: personas route naturally";
|
|
40
|
+
const shown = personas.slice(0, 3);
|
|
41
|
+
const remaining = personas.length - shown.length;
|
|
42
|
+
return `when the topic asks: ${shown.join(", ")}${remaining > 0 ? ` and ${remaining} more available` : ""}`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function box(title: string, rows: readonly [string, string][]): string {
|
|
46
|
+
const lines = ["Mirror", `╭${"─".repeat(WIDTH)}╮`, line(` ${title}`)];
|
|
47
|
+
for (const [label, value] of rows) {
|
|
48
|
+
lines.push(line(""));
|
|
49
|
+
lines.push(line(` ${label}`));
|
|
50
|
+
for (const wrapped of wrap(value)) lines.push(line(` ${wrapped}`));
|
|
51
|
+
}
|
|
52
|
+
lines.push(`╰${"─".repeat(WIDTH)}╯`);
|
|
53
|
+
return lines.join("\n");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function line(text: string): string {
|
|
57
|
+
const content = [...text].slice(0, WIDTH).join("");
|
|
58
|
+
return `│${content}${" ".repeat(Math.max(0, WIDTH - [...content].length))}│`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function wrap(text: string): string[] {
|
|
62
|
+
const words = text.split(/\s+/).filter(Boolean);
|
|
63
|
+
if (words.length === 0) return [""];
|
|
64
|
+
const lines: string[] = [];
|
|
65
|
+
let current = words[0] ?? "";
|
|
66
|
+
for (const word of words.slice(1)) {
|
|
67
|
+
if ([...current].length + [...word].length + 1 <= WIDTH - 2) current += ` ${word}`;
|
|
68
|
+
else {
|
|
69
|
+
lines.push(current);
|
|
70
|
+
current = word;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
lines.push(current);
|
|
74
|
+
return lines;
|
|
75
|
+
}
|