mirror-mind 0.0.1 → 0.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/hooks/log-assistant-reply.sh +85 -0
- package/.claude/hooks/log-session-end.sh +85 -0
- package/.claude/hooks/log-user-prompt.sh +85 -0
- package/.claude/hooks/mirror-inject.sh +85 -0
- package/.claude/hooks/session-start.sh +85 -0
- package/.gemini/hooks/log-assistant.sh +85 -0
- package/.gemini/hooks/log-user.sh +85 -0
- package/.gemini/hooks/session-end.sh +85 -0
- package/.gemini/hooks/session-start.sh +85 -0
- package/.pi/extensions/mirror-logger.ts +646 -0
- package/.pi/skills/mm-backup/SKILL.md +21 -0
- package/.pi/skills/mm-build/SKILL.md +1206 -0
- package/.pi/skills/mm-consolidate/SKILL.md +112 -0
- package/.pi/skills/mm-consult/SKILL.md +70 -0
- package/.pi/skills/mm-conversations/SKILL.md +16 -0
- package/.pi/skills/mm-discard/SKILL.md +39 -0
- package/.pi/skills/mm-explore/SKILL.md +255 -0
- package/.pi/skills/mm-help/SKILL.md +74 -0
- package/.pi/skills/mm-identity/SKILL.md +96 -0
- package/.pi/skills/mm-journal/SKILL.md +16 -0
- package/.pi/skills/mm-journey/SKILL.md +23 -0
- package/.pi/skills/mm-journeys/SKILL.md +15 -0
- package/.pi/skills/mm-memories/SKILL.md +17 -0
- package/.pi/skills/mm-mirror/SKILL.md +98 -0
- package/.pi/skills/mm-mute/SKILL.md +18 -0
- package/.pi/skills/mm-new/SKILL.md +18 -0
- package/.pi/skills/mm-recall/SKILL.md +17 -0
- package/.pi/skills/mm-release-notes/SKILL.md +52 -0
- package/.pi/skills/mm-seed/SKILL.md +19 -0
- package/.pi/skills/mm-shadow/SKILL.md +117 -0
- package/.pi/skills/mm-soul/SKILL.md +502 -0
- package/.pi/skills/mm-tasks/SKILL.md +24 -0
- package/.pi/skills/mm-update/SKILL.md +32 -0
- package/.pi/skills/mm-week/SKILL.md +28 -0
- package/.pi/skills/mm-welcome/SKILL.md +24 -0
- package/AGENTS.md +188 -0
- package/README.md +126 -9
- package/bin/loader.js +67 -0
- package/bin/mirror-hook.js +7 -0
- package/bin/mirror.js +7 -0
- package/docs/releases/index.md +100 -0
- package/docs/releases/pending-cutoffs.md +510 -0
- package/docs/releases/v0.10.0.md +123 -0
- package/docs/releases/v0.10.1.md +66 -0
- package/docs/releases/v0.10.10.md +51 -0
- package/docs/releases/v0.10.11.md +39 -0
- package/docs/releases/v0.10.12.md +57 -0
- package/docs/releases/v0.10.13.md +35 -0
- package/docs/releases/v0.10.14.md +42 -0
- package/docs/releases/v0.10.15.md +53 -0
- package/docs/releases/v0.10.16.md +35 -0
- package/docs/releases/v0.10.17.md +43 -0
- package/docs/releases/v0.10.2.md +64 -0
- package/docs/releases/v0.10.3.md +78 -0
- package/docs/releases/v0.10.4.md +42 -0
- package/docs/releases/v0.10.5.md +57 -0
- package/docs/releases/v0.10.6.md +47 -0
- package/docs/releases/v0.10.7.md +53 -0
- package/docs/releases/v0.10.8.md +49 -0
- package/docs/releases/v0.10.9.md +37 -0
- package/docs/releases/v0.11.0.md +86 -0
- package/docs/releases/v0.12.0.md +67 -0
- package/docs/releases/v0.13.0.md +64 -0
- package/docs/releases/v0.14.0.md +68 -0
- package/docs/releases/v0.15.0.md +48 -0
- package/docs/releases/v0.16.0.md +50 -0
- package/docs/releases/v0.16.1.md +55 -0
- package/docs/releases/v0.16.2.md +36 -0
- package/docs/releases/v0.17.0.md +50 -0
- package/docs/releases/v0.17.1.md +58 -0
- package/docs/releases/v0.17.2.md +42 -0
- package/docs/releases/v0.17.3.md +58 -0
- package/docs/releases/v0.18.0.md +84 -0
- package/docs/releases/v0.18.1.md +51 -0
- package/docs/releases/v0.18.2.md +69 -0
- package/docs/releases/v0.19.0.md +85 -0
- package/docs/releases/v0.19.1.md +46 -0
- package/docs/releases/v0.20.0.md +85 -0
- package/docs/releases/v0.21.0.md +61 -0
- package/docs/releases/v0.21.1.md +47 -0
- package/docs/releases/v0.22.0.md +77 -0
- package/docs/releases/v0.22.1.md +42 -0
- package/docs/releases/v0.23.0.md +105 -0
- package/docs/releases/v0.24.0.md +125 -0
- package/docs/releases/v0.25.0.md +107 -0
- package/docs/releases/v0.26.0.md +139 -0
- package/docs/releases/v0.26.1.md +65 -0
- package/docs/releases/v0.26.2.md +43 -0
- package/docs/releases/v0.27.0.md +63 -0
- package/docs/releases/v0.28.0.md +59 -0
- package/docs/releases/v0.29.0.md +61 -0
- package/docs/releases/v0.29.1.md +59 -0
- package/docs/releases/v0.29.2.md +59 -0
- package/docs/releases/v0.29.3.md +93 -0
- package/docs/releases/v0.30.0.md +75 -0
- package/docs/releases/v0.30.1.md +93 -0
- package/docs/releases/v0.30.2.md +66 -0
- package/docs/releases/v0.31.0.md +122 -0
- package/docs/releases/v0.31.1.md +142 -0
- package/docs/releases/v0.31.10.md +118 -0
- package/docs/releases/v0.31.11.md +88 -0
- package/docs/releases/v0.31.12.md +105 -0
- package/docs/releases/v0.31.13.md +99 -0
- package/docs/releases/v0.31.14.md +97 -0
- package/docs/releases/v0.31.2.md +115 -0
- package/docs/releases/v0.31.3.md +142 -0
- package/docs/releases/v0.31.4.md +117 -0
- package/docs/releases/v0.31.5.md +155 -0
- package/docs/releases/v0.31.6.md +132 -0
- package/docs/releases/v0.31.7.md +132 -0
- package/docs/releases/v0.31.8.md +120 -0
- package/docs/releases/v0.31.9.md +120 -0
- package/docs/releases/v0.32.0.md +261 -0
- package/docs/releases/v0.32.1.md +59 -0
- package/docs/releases/v0.33.0.md +164 -0
- package/docs/releases/v0.8.0.md +118 -0
- package/docs/releases/v0.9.0.md +98 -0
- package/docs/releases/v0.9.1.md +83 -0
- package/package.json +102 -6
- package/plugins/mirror-mind/.claude-plugin/plugin.json +17 -0
- package/plugins/mirror-mind/hooks/hooks.json +62 -0
- package/plugins/mirror-mind/hooks/log-assistant-reply.sh +98 -0
- package/plugins/mirror-mind/hooks/log-session-end.sh +98 -0
- package/plugins/mirror-mind/hooks/log-user-prompt.sh +98 -0
- package/plugins/mirror-mind/hooks/mirror-inject.sh +98 -0
- package/plugins/mirror-mind/hooks/session-start.sh +98 -0
- package/plugins/mirror-mind/skills/mm-backup/SKILL.md +27 -0
- package/plugins/mirror-mind/skills/mm-build/SKILL.md +1206 -0
- package/plugins/mirror-mind/skills/mm-consolidate/SKILL.md +112 -0
- package/plugins/mirror-mind/skills/mm-consult/SKILL.md +83 -0
- package/plugins/mirror-mind/skills/mm-conversations/SKILL.md +17 -0
- package/plugins/mirror-mind/skills/mm-discard/SKILL.md +40 -0
- package/plugins/mirror-mind/skills/mm-explore/SKILL.md +241 -0
- package/plugins/mirror-mind/skills/mm-help/SKILL.md +104 -0
- package/plugins/mirror-mind/skills/mm-identity/SKILL.md +96 -0
- package/plugins/mirror-mind/skills/mm-journal/SKILL.md +21 -0
- package/plugins/mirror-mind/skills/mm-journey/SKILL.md +37 -0
- package/plugins/mirror-mind/skills/mm-journeys/SKILL.md +15 -0
- package/plugins/mirror-mind/skills/mm-memories/SKILL.md +20 -0
- package/plugins/mirror-mind/skills/mm-mirror/SKILL.md +67 -0
- package/plugins/mirror-mind/skills/mm-mute/SKILL.md +18 -0
- package/plugins/mirror-mind/skills/mm-new/SKILL.md +18 -0
- package/plugins/mirror-mind/skills/mm-recall/SKILL.md +17 -0
- package/plugins/mirror-mind/skills/mm-release-notes/SKILL.md +31 -0
- package/plugins/mirror-mind/skills/mm-seed/SKILL.md +15 -0
- package/plugins/mirror-mind/skills/mm-shadow/SKILL.md +117 -0
- package/plugins/mirror-mind/skills/mm-soul/SKILL.md +488 -0
- package/plugins/mirror-mind/skills/mm-tasks/SKILL.md +36 -0
- package/plugins/mirror-mind/skills/mm-update/SKILL.md +30 -0
- package/plugins/mirror-mind/skills/mm-week/SKILL.md +46 -0
- package/plugins/mirror-mind/skills/mm-welcome/SKILL.md +22 -0
- package/scripts/codex-hooks/session-end.sh +85 -0
- package/scripts/codex-hooks/session-start.sh +85 -0
- package/scripts/codex-mirror.sh +48 -0
- package/templates/identity/README.md +119 -0
- package/templates/identity/ego/behavior.yaml +52 -0
- package/templates/identity/ego/constraints.yaml +15 -0
- package/templates/identity/ego/identity.yaml +43 -0
- package/templates/identity/journeys/personal-growth.yaml +39 -0
- package/templates/identity/organization/identity.yaml +24 -0
- package/templates/identity/organization/principles.yaml +17 -0
- package/templates/identity/personas/coach.yaml +94 -0
- package/templates/identity/personas/designer.yaml +93 -0
- package/templates/identity/personas/doctor.yaml +96 -0
- package/templates/identity/personas/engineer.yaml +77 -0
- package/templates/identity/personas/financial.yaml +97 -0
- package/templates/identity/personas/prompt-engineer.yaml +122 -0
- package/templates/identity/personas/researcher.yaml +88 -0
- package/templates/identity/personas/strategist.yaml +91 -0
- package/templates/identity/personas/teacher.yaml +95 -0
- package/templates/identity/personas/therapist.yaml +91 -0
- package/templates/identity/personas/thinker.yaml +74 -0
- package/templates/identity/personas/writer.yaml +76 -0
- package/templates/identity/self/config.yaml +16 -0
- package/templates/identity/self/soul.yaml +105 -0
- package/templates/identity/user/identity.yaml +30 -0
- package/ts/src/backup/staging.ts +62 -0
- package/ts/src/backup/zipBackup.ts +267 -0
- package/ts/src/backup/zipReader.ts +109 -0
- package/ts/src/backup/zipWriter.ts +148 -0
- package/ts/src/builder/activeCheckpoint.ts +134 -0
- package/ts/src/builder/aggregateStatus.ts +34 -0
- package/ts/src/builder/approve.ts +149 -0
- package/ts/src/builder/argv.ts +292 -0
- package/ts/src/builder/ariadMethod.ts +536 -0
- package/ts/src/builder/artifacts/artifactSurfaces.ts +106 -0
- package/ts/src/builder/artifacts/artifactWriter.ts +113 -0
- package/ts/src/builder/artifacts/closureArtifacts.ts +199 -0
- package/ts/src/builder/artifacts/deliveryStoryArtifacts.ts +316 -0
- package/ts/src/builder/artifacts/planArtifacts.ts +242 -0
- package/ts/src/builder/artifacts/scaffoldSections.ts +273 -0
- package/ts/src/builder/artifacts/scaffoldState.ts +227 -0
- package/ts/src/builder/artifacts/storyFiles.ts +61 -0
- package/ts/src/builder/artifacts/storyIndex.ts +166 -0
- package/ts/src/builder/cadence.ts +59 -0
- package/ts/src/builder/card.ts +192 -0
- package/ts/src/builder/checkpointRefused.ts +66 -0
- package/ts/src/builder/closure.ts +964 -0
- package/ts/src/builder/commands.ts +2053 -0
- package/ts/src/builder/cursorPosition.ts +176 -0
- package/ts/src/builder/cursorTransitions.ts +77 -0
- package/ts/src/builder/deliveryCursor.ts +577 -0
- package/ts/src/builder/deliveryStoryClosure.ts +521 -0
- package/ts/src/builder/deliveryStoryPlan.ts +536 -0
- package/ts/src/builder/deliveryStoryReady.ts +160 -0
- package/ts/src/builder/deliveryStoryRoadmapClosure.ts +151 -0
- package/ts/src/builder/expand.ts +336 -0
- package/ts/src/builder/flowUnit.ts +339 -0
- package/ts/src/builder/homeSurface.ts +122 -0
- package/ts/src/builder/implementationGuard.ts +125 -0
- package/ts/src/builder/index.ts +11 -0
- package/ts/src/builder/lifecycleRefusal.ts +92 -0
- package/ts/src/builder/lifecycleRibbon.ts +159 -0
- package/ts/src/builder/load.ts +313 -0
- package/ts/src/builder/methodAdoption.ts +113 -0
- package/ts/src/builder/methodDefinition.ts +429 -0
- package/ts/src/builder/methodInspection.ts +371 -0
- package/ts/src/builder/plan.ts +408 -0
- package/ts/src/builder/planPreauthorization.ts +265 -0
- package/ts/src/builder/prepare.ts +225 -0
- package/ts/src/builder/projectPaths.ts +64 -0
- package/ts/src/builder/pull.ts +356 -0
- package/ts/src/builder/pullCandidates.ts +463 -0
- package/ts/src/builder/pullCandidatesRender.ts +238 -0
- package/ts/src/builder/refinementField.ts +93 -0
- package/ts/src/builder/releaseIntent.ts +173 -0
- package/ts/src/builder/resumeState.ts +84 -0
- package/ts/src/builder/resumeSurface.ts +149 -0
- package/ts/src/builder/roadmapGrammar.ts +300 -0
- package/ts/src/builder/roadmapScan.ts +97 -0
- package/ts/src/builder/roadmapScope.ts +210 -0
- package/ts/src/builder/scopePhrases.ts +146 -0
- package/ts/src/builder/storyPaths.ts +196 -0
- package/ts/src/builder/storyPlanPreauthorization.ts +287 -0
- package/ts/src/builder/surfaceProtocol.ts +42 -0
- package/ts/src/builder/templateGeneration.ts +148 -0
- package/ts/src/builder/transition.ts +178 -0
- package/ts/src/consult/args.ts +99 -0
- package/ts/src/consult/core.ts +122 -0
- package/ts/src/consult/modelCatalog.ts +55 -0
- package/ts/src/consult/render.ts +47 -0
- package/ts/src/conversation/append.ts +414 -0
- package/ts/src/conversation/backfill.ts +387 -0
- package/ts/src/conversation/closeTail.ts +467 -0
- package/ts/src/conversation/extraction.ts +333 -0
- package/ts/src/conversation/extractionDriver.ts +130 -0
- package/ts/src/conversation/extractionRun.ts +70 -0
- package/ts/src/conversation/hookPayload.ts +51 -0
- package/ts/src/conversation/journeyInference.ts +165 -0
- package/ts/src/conversation/journeyRepair.ts +114 -0
- package/ts/src/conversation/lifecycleFaces.ts +197 -0
- package/ts/src/conversation/lifecycleWrites.ts +313 -0
- package/ts/src/conversation/listing.ts +77 -0
- package/ts/src/conversation/logger.ts +851 -0
- package/ts/src/conversation/loggerCli.ts +321 -0
- package/ts/src/conversation/loggerRuntime.ts +259 -0
- package/ts/src/conversation/metadataLifecycle.ts +443 -0
- package/ts/src/conversation/recall.ts +66 -0
- package/ts/src/conversation/sessionComposites.ts +258 -0
- package/ts/src/conversation/sessionImport.ts +90 -0
- package/ts/src/conversation/transcriptBackfill.ts +141 -0
- package/ts/src/cultivation/applyActions.ts +241 -0
- package/ts/src/cultivation/cluster.ts +86 -0
- package/ts/src/cultivation/consolidationStore.ts +281 -0
- package/ts/src/cultivation/promptContext.ts +62 -0
- package/ts/src/cultivation/propose.ts +305 -0
- package/ts/src/cultivation/scan.ts +155 -0
- package/ts/src/db/backupGate.ts +47 -0
- package/ts/src/db/bootstrap.ts +108 -0
- package/ts/src/db/bootstrapLock.ts +223 -0
- package/ts/src/db/copyGuard.ts +60 -0
- package/ts/src/db/database.ts +397 -0
- package/ts/src/db/decode.ts +66 -0
- package/ts/src/db/ftsIntegrity.ts +41 -0
- package/ts/src/db/migrateOnOpen.ts +214 -0
- package/ts/src/db/migrations.ts +576 -0
- package/ts/src/db/readyOnOpen.ts +35 -0
- package/ts/src/db/rowDecode.ts +42 -0
- package/ts/src/db/schema.ts +432 -0
- package/ts/src/db/schemaInventory.ts +225 -0
- package/ts/src/db/schemaInventorySnapshot.ts +2107 -0
- package/ts/src/db/schemaState.ts +91 -0
- package/ts/src/db/schemaTsDivergence.ts +114 -0
- package/ts/src/descriptor/descriptorRead.ts +49 -0
- package/ts/src/docs/docsLint.ts +265 -0
- package/ts/src/explorer/handoff.ts +522 -0
- package/ts/src/explorer/render.ts +335 -0
- package/ts/src/explorer/story.ts +769 -0
- package/ts/src/explorer/transition.ts +72 -0
- package/ts/src/extensions/bindings.ts +170 -0
- package/ts/src/extensions/catalog.ts +249 -0
- package/ts/src/extensions/catalogCommands.ts +513 -0
- package/ts/src/extensions/catalogWrites.ts +652 -0
- package/ts/src/extensions/contextRuntime.ts +325 -0
- package/ts/src/extensions/dispatch.ts +374 -0
- package/ts/src/extensions/errors.ts +24 -0
- package/ts/src/extensions/manifest.ts +356 -0
- package/ts/src/extensions/migrations.ts +350 -0
- package/ts/src/extraction/conversation.ts +304 -0
- package/ts/src/extraction/conversationMetadata.ts +206 -0
- package/ts/src/extraction/fencing.ts +106 -0
- package/ts/src/extraction/json.ts +17 -0
- package/ts/src/extraction/prompts.ts +470 -0
- package/ts/src/frontDoor/args.ts +30 -0
- package/ts/src/frontDoor/argvShape.ts +106 -0
- package/ts/src/frontDoor/buildLoadRuntime.ts +139 -0
- package/ts/src/frontDoor/buildRoute.ts +398 -0
- package/ts/src/frontDoor/cli.ts +1872 -0
- package/ts/src/frontDoor/cloneRoleGuard.ts +98 -0
- package/ts/src/frontDoor/consultRoute.ts +50 -0
- package/ts/src/frontDoor/contentTailRoute.ts +239 -0
- package/ts/src/frontDoor/conversationLoggerRoute.ts +83 -0
- package/ts/src/frontDoor/cultivationRoute.ts +210 -0
- package/ts/src/frontDoor/dbPath.ts +131 -0
- package/ts/src/frontDoor/dbSafetyToolsRoute.ts +240 -0
- package/ts/src/frontDoor/exploreRoute.ts +572 -0
- package/ts/src/frontDoor/extensionCatalogRoute.ts +252 -0
- package/ts/src/frontDoor/frontDoorLog.ts +57 -0
- package/ts/src/frontDoor/identityWrite.ts +36 -0
- package/ts/src/frontDoor/journeyWriteRoute.ts +26 -0
- package/ts/src/frontDoor/lifecycleWriteRoute.ts +110 -0
- package/ts/src/frontDoor/liveBackup.ts +100 -0
- package/ts/src/frontDoor/mirrorModeRoute.ts +163 -0
- package/ts/src/frontDoor/namedRefusal.ts +35 -0
- package/ts/src/frontDoor/nodeSupport.ts +24 -0
- package/ts/src/frontDoor/render/consolidate.ts +199 -0
- package/ts/src/frontDoor/render/conversations.ts +33 -0
- package/ts/src/frontDoor/render/cultivationShared.ts +21 -0
- package/ts/src/frontDoor/render/descriptor.ts +20 -0
- package/ts/src/frontDoor/render/detectPersona.ts +50 -0
- package/ts/src/frontDoor/render/icons.ts +13 -0
- package/ts/src/frontDoor/render/identity.ts +59 -0
- package/ts/src/frontDoor/render/identityRows.ts +17 -0
- package/ts/src/frontDoor/render/inspectPersona.ts +53 -0
- package/ts/src/frontDoor/render/journeyStatus.ts +78 -0
- package/ts/src/frontDoor/render/journeys.ts +79 -0
- package/ts/src/frontDoor/render/list.ts +41 -0
- package/ts/src/frontDoor/render/memories.ts +67 -0
- package/ts/src/frontDoor/render/recall.ts +49 -0
- package/ts/src/frontDoor/render/shadow.ts +155 -0
- package/ts/src/frontDoor/render/tasks.ts +99 -0
- package/ts/src/frontDoor/render/tasksImportSync.ts +63 -0
- package/ts/src/frontDoor/render/week.ts +113 -0
- package/ts/src/frontDoor/routing.ts +1001 -0
- package/ts/src/frontDoor/runtimeRoute.ts +623 -0
- package/ts/src/frontDoor/searchRoute.ts +166 -0
- package/ts/src/frontDoor/seedPaths.ts +98 -0
- package/ts/src/frontDoor/sessionStartWelcomeRoute.ts +166 -0
- package/ts/src/frontDoor/soulRoute.ts +433 -0
- package/ts/src/frontDoor/stdinSource.ts +37 -0
- package/ts/src/frontDoor/tasksImportSyncRoute.ts +110 -0
- package/ts/src/frontDoor/tasksWriteRoute.ts +101 -0
- package/ts/src/frontDoor/usage.ts +176 -0
- package/ts/src/guards/claudePlugin.ts +298 -0
- package/ts/src/guards/packContents.ts +306 -0
- package/ts/src/guards/piSourcedSkills.ts +70 -0
- package/ts/src/guards/retiredRouteShadows.ts +57 -0
- package/ts/src/guards/retiredSurfaces.ts +564 -0
- package/ts/src/hooks/claude.ts +214 -0
- package/ts/src/hooks/codex.ts +45 -0
- package/ts/src/hooks/gemini.ts +126 -0
- package/ts/src/hooks/main.ts +109 -0
- package/ts/src/hooks/mirrorState.ts +71 -0
- package/ts/src/hooks/payload.ts +73 -0
- package/ts/src/hooks/runtime.ts +179 -0
- package/ts/src/hooks/runtimeDetection.ts +34 -0
- package/ts/src/identity/applyConsolidationIdentityUpdate.ts +67 -0
- package/ts/src/identity/descriptorGenerate.ts +162 -0
- package/ts/src/identity/identityEdit.ts +143 -0
- package/ts/src/identity/identityKey.ts +77 -0
- package/ts/src/identity/identityRead.ts +69 -0
- package/ts/src/identity/identityStore.ts +80 -0
- package/ts/src/identity/journeyListing.ts +53 -0
- package/ts/src/identity/personaInspect.ts +44 -0
- package/ts/src/identity/personaListing.ts +37 -0
- package/ts/src/identity/setIdentity.ts +64 -0
- package/ts/src/index.ts +231 -0
- package/ts/src/init/init.ts +111 -0
- package/ts/src/journey/journeyOptions.ts +131 -0
- package/ts/src/journey/journeyRemoval.ts +158 -0
- package/ts/src/journey/journeyStatus.ts +149 -0
- package/ts/src/journey/journeySyncFile.ts +87 -0
- package/ts/src/journey/journeyWrite.ts +203 -0
- package/ts/src/journey/parentJourney.ts +41 -0
- package/ts/src/journey/validateParentJourney.ts +55 -0
- package/ts/src/mcp/boundary.ts +151 -0
- package/ts/src/mcp/guards.ts +244 -0
- package/ts/src/mcp/main.ts +121 -0
- package/ts/src/mcp/payload.ts +141 -0
- package/ts/src/mcp/protocol.ts +141 -0
- package/ts/src/mcp/readModels.ts +241 -0
- package/ts/src/mcp/registry.ts +201 -0
- package/ts/src/mcp/serve.ts +120 -0
- package/ts/src/mcp/tools/deterministic.ts +137 -0
- package/ts/src/mcp/tools/providerCrossing.ts +187 -0
- package/ts/src/mcp/wire.ts +8 -0
- package/ts/src/memory/journal.ts +180 -0
- package/ts/src/memory/listing.ts +107 -0
- package/ts/src/memory/memoryWrite.ts +88 -0
- package/ts/src/memory/reinforcement.ts +35 -0
- package/ts/src/mirror/context.ts +161 -0
- package/ts/src/mirror/defaultResolution.ts +199 -0
- package/ts/src/mirror/mirrorForm.ts +43 -0
- package/ts/src/mirror/orchestration.ts +364 -0
- package/ts/src/mirror/reception.ts +194 -0
- package/ts/src/mirror/render.ts +75 -0
- package/ts/src/mirror/runtimeSession.ts +205 -0
- package/ts/src/mode/operatingMode.ts +148 -0
- package/ts/src/observability/callOutcome.ts +115 -0
- package/ts/src/observability/inspectLedger.ts +329 -0
- package/ts/src/observability/ledgerHooks.ts +126 -0
- package/ts/src/observability/llmCalls.ts +154 -0
- package/ts/src/persona/detectPersona.ts +81 -0
- package/ts/src/planning/promptAssembly.ts +111 -0
- package/ts/src/planning/weekPending.ts +81 -0
- package/ts/src/planning/weekPlan.ts +183 -0
- package/ts/src/planning/weekSave.ts +117 -0
- package/ts/src/providers/config.ts +189 -0
- package/ts/src/providers/cost.ts +64 -0
- package/ts/src/providers/credits.ts +204 -0
- package/ts/src/providers/embedding.ts +321 -0
- package/ts/src/providers/familyProviders.ts +147 -0
- package/ts/src/providers/llm.ts +334 -0
- package/ts/src/providers/openrouter.ts +244 -0
- package/ts/src/providers/redaction.ts +110 -0
- package/ts/src/providers/replay.ts +15 -0
- package/ts/src/providers/transport.ts +410 -0
- package/ts/src/repair/encodingRepair.ts +253 -0
- package/ts/src/runtime/backup.ts +151 -0
- package/ts/src/runtime/channel.ts +146 -0
- package/ts/src/runtime/config.ts +168 -0
- package/ts/src/runtime/diagnose.ts +686 -0
- package/ts/src/runtime/git.ts +510 -0
- package/ts/src/runtime/installKind.ts +220 -0
- package/ts/src/runtime/migrate.ts +154 -0
- package/ts/src/runtime/packageIdentity.ts +102 -0
- package/ts/src/runtime/releaseNotes.ts +244 -0
- package/ts/src/runtime/status.ts +557 -0
- package/ts/src/runtime/strategies/clone.ts +200 -0
- package/ts/src/runtime/strategies/package.ts +328 -0
- package/ts/src/runtime/treeRoot.ts +30 -0
- package/ts/src/runtime/update.ts +445 -0
- package/ts/src/runtime/updateGate.ts +40 -0
- package/ts/src/runtime/updatePipeline.ts +159 -0
- package/ts/src/runtime/version.ts +36 -0
- package/ts/src/runtime/wiring.ts +130 -0
- package/ts/src/search/memorySearch.ts +338 -0
- package/ts/src/search/ranker.ts +154 -0
- package/ts/src/seed/seed.ts +256 -0
- package/ts/src/seed/seedYaml.ts +133 -0
- package/ts/src/soul/apply.ts +218 -0
- package/ts/src/soul/harvest.ts +267 -0
- package/ts/src/soul/prompts/soul_beauty_voice.md +103 -0
- package/ts/src/soul/prompts/soul_self_voice.md +219 -0
- package/ts/src/soul/prompts/soul_wisdom_voice.md +121 -0
- package/ts/src/soul/prompts.ts +85 -0
- package/ts/src/soul/render.ts +358 -0
- package/ts/src/soul/state.ts +212 -0
- package/ts/src/soul/transition.ts +37 -0
- package/ts/src/tasks/journeyPathParse.ts +113 -0
- package/ts/src/tasks/taskImportSync.ts +142 -0
- package/ts/src/tasks/taskStore.ts +312 -0
- package/ts/src/tasks/weekView.ts +120 -0
- package/ts/src/util/clipCodePoints.ts +26 -0
- package/ts/src/util/paths.ts +63 -0
- package/ts/src/util/program.ts +9 -0
- package/ts/src/util/pyGenerators.ts +144 -0
- package/ts/src/util/pythonText.ts +317 -0
- package/ts/src/util/semver.ts +45 -0
- package/ts/src/util/shellWord.ts +25 -0
- package/ts/src/util/slug.ts +64 -0
- package/ts/src/welcome/card.ts +339 -0
- package/ts/src/welcome/statusLine.ts +101 -0
- package/ts/src/welcome/updateCache.ts +129 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
// Read models the MCP tools need and the TS core did not yet have (CV22.DS9.US2).
|
|
2
|
+
//
|
|
3
|
+
// Two of the seven tools read shapes no ported command produced. `list_journeys`
|
|
4
|
+
// wants `JourneyService.list_journeys()`' dicts -- TS had only
|
|
5
|
+
// `listActiveMirrorJourneys`, which renders a string for Mirror Mode -- and
|
|
6
|
+
// `journey_status` wants `get_journey_status()`' dict, where TS had only the
|
|
7
|
+
// status renderer's projection of `created_at, title`.
|
|
8
|
+
//
|
|
9
|
+
// Both are ported from the Python services, not reconstructed from renderers.
|
|
10
|
+
// What already existed is reused rather than rewritten: `extractStatus` from
|
|
11
|
+
// `identity/journeyListing.ts` (so the `**Status:**` regex does not get a third
|
|
12
|
+
// copy), and `allJourneyKeys` plus `getJourneyPathContent` from
|
|
13
|
+
// `journey/journeyStatus.ts` -- the latter carries the sync-file fallback that
|
|
14
|
+
// `get_journey_path` performs before reading the database, which a fresh
|
|
15
|
+
// implementation here would have silently dropped.
|
|
16
|
+
|
|
17
|
+
import type { Database, Row } from "#db/database.ts";
|
|
18
|
+
import { optionalString, requireString } from "#db/rowDecode.ts";
|
|
19
|
+
import { extractStatus } from "#identity/journeyListing.ts";
|
|
20
|
+
import { allJourneyKeys, getJourneyPathContent } from "#journey/journeyStatus.ts";
|
|
21
|
+
|
|
22
|
+
/** One row of `JourneyService.list_journeys()`. */
|
|
23
|
+
export interface JourneyDto {
|
|
24
|
+
id: string;
|
|
25
|
+
name: string;
|
|
26
|
+
description: string;
|
|
27
|
+
status: string;
|
|
28
|
+
metadata: Record<string, unknown>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** The `_memory_to_dict` shape the tools serialize. */
|
|
32
|
+
export interface MemoryDto {
|
|
33
|
+
id: string | null;
|
|
34
|
+
title: string | null;
|
|
35
|
+
memory_type: string | null;
|
|
36
|
+
layer: string | null;
|
|
37
|
+
journey: string | null;
|
|
38
|
+
tags: string | null;
|
|
39
|
+
content: string | null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** The `_conversation_to_dict` shape (CV22.DS9.US2 D1). */
|
|
43
|
+
export interface ConversationDto {
|
|
44
|
+
id: string | null;
|
|
45
|
+
title: string | null;
|
|
46
|
+
started_at: string | null;
|
|
47
|
+
ended_at: string | null;
|
|
48
|
+
interface: string | null;
|
|
49
|
+
persona: string | null;
|
|
50
|
+
journey: string | null;
|
|
51
|
+
summary: string | null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** One journey's entry in `get_journey_status()`. */
|
|
55
|
+
export interface JourneyStatusEntry {
|
|
56
|
+
identity: string | null;
|
|
57
|
+
journey_path: string | null;
|
|
58
|
+
recent_memories: MemoryDto[];
|
|
59
|
+
recent_conversations: ConversationDto[];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Python's `_metadata_dict`: `{}` for absent, unparseable, or non-object JSON. */
|
|
63
|
+
function metadataObject(raw: unknown): Record<string, unknown> {
|
|
64
|
+
if (typeof raw !== "string" || raw === "") return {};
|
|
65
|
+
try {
|
|
66
|
+
const parsed: unknown = JSON.parse(raw);
|
|
67
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return {};
|
|
68
|
+
return parsed as Record<string, unknown>;
|
|
69
|
+
} catch {
|
|
70
|
+
return {};
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Port of `list_journeys`' name/description derivation.
|
|
76
|
+
*
|
|
77
|
+
* `name` is the first line with leading `#` and spaces stripped; `description`
|
|
78
|
+
* is the first paragraph under `## Description` (or `## Descrição`), truncated
|
|
79
|
+
* to 150 characters. The description regex needs a blank line or a following
|
|
80
|
+
* heading to terminate on -- a journey whose description runs to end-of-content
|
|
81
|
+
* yields `""`, which is the oracle's behavior and not a bug to fix here.
|
|
82
|
+
*/
|
|
83
|
+
function journeyName(content: string): string {
|
|
84
|
+
return (content.split("\n")[0] ?? "")
|
|
85
|
+
.trim()
|
|
86
|
+
.replace(/^[# ]+/, "")
|
|
87
|
+
.trim();
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function journeyDescription(content: string): string {
|
|
91
|
+
const match = content.match(/## (?:Description|Descrição)\s*\n+([\s\S]+?)(?:\n\n|\n##)/);
|
|
92
|
+
return match ? match[1].trim().slice(0, 150) : "";
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Port of `JourneyService.list_journeys()`. */
|
|
96
|
+
export function listJourneyDtos(db: Database): JourneyDto[] {
|
|
97
|
+
return db
|
|
98
|
+
.prepare("SELECT key, content, metadata FROM identity WHERE layer = 'journey' ORDER BY key")
|
|
99
|
+
.all()
|
|
100
|
+
.map((row: Row) => {
|
|
101
|
+
const content = optionalString(row, "content") ?? "";
|
|
102
|
+
return {
|
|
103
|
+
id: requireString(row, "key"),
|
|
104
|
+
name: journeyName(content),
|
|
105
|
+
description: journeyDescription(content),
|
|
106
|
+
// `list_journeys` uses `\w+` here while `list_journey_options` uses
|
|
107
|
+
// `[^\n]+`: the same service reads the same field two ways, so
|
|
108
|
+
// `**Status:** in progress` is "in" in a card and "in progress" in a
|
|
109
|
+
// selector. Ported as-is; converging them is a product decision.
|
|
110
|
+
status: extractStatus(content),
|
|
111
|
+
metadata: metadataObject(optionalString(row, "metadata")),
|
|
112
|
+
};
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Port of `JourneyService.list_active_journeys()`. */
|
|
117
|
+
export function listActiveJourneyDtos(db: Database): JourneyDto[] {
|
|
118
|
+
return listJourneyDtos(db).filter((journey) => journey.status === "active");
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function memoryDto(row: Row): MemoryDto {
|
|
122
|
+
return {
|
|
123
|
+
id: optionalString(row, "id"),
|
|
124
|
+
title: optionalString(row, "title"),
|
|
125
|
+
memory_type: optionalString(row, "memory_type"),
|
|
126
|
+
layer: optionalString(row, "layer"),
|
|
127
|
+
journey: optionalString(row, "journey"),
|
|
128
|
+
tags: optionalString(row, "tags"),
|
|
129
|
+
content: optionalString(row, "content"),
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function conversationDto(row: Row): ConversationDto {
|
|
134
|
+
return {
|
|
135
|
+
id: optionalString(row, "id"),
|
|
136
|
+
title: optionalString(row, "title"),
|
|
137
|
+
started_at: optionalString(row, "started_at"),
|
|
138
|
+
ended_at: optionalString(row, "ended_at"),
|
|
139
|
+
interface: optionalString(row, "interface"),
|
|
140
|
+
persona: optionalString(row, "persona"),
|
|
141
|
+
journey: optionalString(row, "journey"),
|
|
142
|
+
summary: optionalString(row, "summary"),
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Port of `get_journey_status()` for one journey or all of them.
|
|
148
|
+
*
|
|
149
|
+
* Python reads `SELECT *` with no LIMIT and slices `[:10]` in memory, loading
|
|
150
|
+
* every memory of the journey -- embedding blobs included -- to render seven
|
|
151
|
+
* fields of ten rows. This reproduces the RESULT with a projection and a LIMIT;
|
|
152
|
+
* the bytes are identical and the read is not. Neither query carries a tie-break
|
|
153
|
+
* beyond its timestamp, so equal timestamps resolve by rowid on both sides.
|
|
154
|
+
*/
|
|
155
|
+
export function journeyStatus(
|
|
156
|
+
db: Database,
|
|
157
|
+
slug: string | null,
|
|
158
|
+
): Record<string, JourneyStatusEntry> {
|
|
159
|
+
const journeyIds = slug ? [slug] : allJourneyKeys(db);
|
|
160
|
+
|
|
161
|
+
const result: Record<string, JourneyStatusEntry> = {};
|
|
162
|
+
for (const journeyId of journeyIds) {
|
|
163
|
+
const identityRow = db
|
|
164
|
+
.prepare("SELECT content FROM identity WHERE layer = 'journey' AND key = ?")
|
|
165
|
+
.get(journeyId);
|
|
166
|
+
// Not a plain identity read: `get_journey_path` prefers a configured
|
|
167
|
+
// sync_file on disk and falls back to the database.
|
|
168
|
+
const journeyPath = getJourneyPathContent(db, journeyId);
|
|
169
|
+
const memories = db
|
|
170
|
+
.prepare(
|
|
171
|
+
"SELECT id, title, memory_type, layer, journey, tags, content FROM memories " +
|
|
172
|
+
"WHERE journey = ? ORDER BY created_at DESC LIMIT 10",
|
|
173
|
+
)
|
|
174
|
+
.all(journeyId);
|
|
175
|
+
const conversations = db
|
|
176
|
+
.prepare(
|
|
177
|
+
"SELECT id, title, started_at, ended_at, interface, persona, journey, summary " +
|
|
178
|
+
"FROM conversations WHERE journey = ? ORDER BY started_at DESC LIMIT 5",
|
|
179
|
+
)
|
|
180
|
+
.all(journeyId);
|
|
181
|
+
|
|
182
|
+
result[journeyId] = {
|
|
183
|
+
identity: identityRow === undefined ? null : optionalString(identityRow, "content"),
|
|
184
|
+
journey_path: journeyPath,
|
|
185
|
+
recent_memories: memories.map((row) => memoryDto(row as Record<string, unknown>)),
|
|
186
|
+
recent_conversations: conversations.map((row) =>
|
|
187
|
+
conversationDto(row as Record<string, unknown>),
|
|
188
|
+
),
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
return result;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/** Memories by journey / layer / type, as the filter paths of `search_memories` read them. */
|
|
195
|
+
export function memoriesByFilter(
|
|
196
|
+
db: Database,
|
|
197
|
+
column: "journey" | "layer" | "memory_type",
|
|
198
|
+
value: string,
|
|
199
|
+
): MemoryDto[] {
|
|
200
|
+
return db
|
|
201
|
+
.prepare(
|
|
202
|
+
`SELECT id, title, memory_type, layer, journey, tags, content FROM memories ` +
|
|
203
|
+
`WHERE ${column} = ? ORDER BY created_at DESC`,
|
|
204
|
+
)
|
|
205
|
+
.all(value)
|
|
206
|
+
.map((row: Row) => memoryDto(row));
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/** Messages for `recall_conversation`, which needs `created_at` the recall renderer does not. */
|
|
210
|
+
export function recallMessages(
|
|
211
|
+
db: Database,
|
|
212
|
+
conversationId: string,
|
|
213
|
+
): { role: string; content: string; created_at: string | null }[] {
|
|
214
|
+
return db
|
|
215
|
+
.prepare(
|
|
216
|
+
"SELECT role, content, created_at FROM messages WHERE conversation_id = ? ORDER BY created_at",
|
|
217
|
+
)
|
|
218
|
+
.all(conversationId)
|
|
219
|
+
.map((row: Row) => ({
|
|
220
|
+
role: requireString(row, "role"),
|
|
221
|
+
content: requireString(row, "content"),
|
|
222
|
+
created_at: optionalString(row, "created_at"),
|
|
223
|
+
}));
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Full rows for an explicit id list, as `search_memories`' query path needs
|
|
228
|
+
* after ranking returns ids and scores. Order is the caller's, not the
|
|
229
|
+
* database's: the ranking decided it.
|
|
230
|
+
*/
|
|
231
|
+
export function memoriesByIds(db: Database, ids: readonly string[]): MemoryDto[] {
|
|
232
|
+
if (ids.length === 0) return [];
|
|
233
|
+
const placeholders = ids.map(() => "?").join(", ");
|
|
234
|
+
return db
|
|
235
|
+
.prepare(
|
|
236
|
+
`SELECT id, title, memory_type, layer, journey, tags, content FROM memories ` +
|
|
237
|
+
`WHERE id IN (${placeholders})`,
|
|
238
|
+
)
|
|
239
|
+
.all(...ids)
|
|
240
|
+
.map((row: Row) => memoryDto(row));
|
|
241
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
// MCP tool registry (CV22.DS9.US1).
|
|
2
|
+
//
|
|
3
|
+
// A faithful port of `src/memory/mcp/tools.py`'s declarations: the seven tool
|
|
4
|
+
// names, descriptions, and JSON Schemas that `tools/list` serializes. These
|
|
5
|
+
// bytes are the client contract -- some clients cache them across sessions, and
|
|
6
|
+
// the descriptions are prompt text the model reads every time it decides
|
|
7
|
+
// whether to call a tool -- so US2 may fill in behavior but must not disturb
|
|
8
|
+
// the declarations.
|
|
9
|
+
//
|
|
10
|
+
// The absence of `annotations` is deliberate, not an omission. The 2025-06-18
|
|
11
|
+
// protocol lets a server declare `readOnlyHint`, and clients use it to skip the
|
|
12
|
+
// per-call permission prompt. Every tool here IS read-only, so the hint would be
|
|
13
|
+
// truthful -- and it would remove the only human-in-the-loop gate on what the
|
|
14
|
+
// DS9 threat model calls a read oracle over private memory: the caller is the
|
|
15
|
+
// model, and the model is driven by anything in its context. Parity and security
|
|
16
|
+
// agree here; the golden's byte-identical `tools/list` enforces it.
|
|
17
|
+
//
|
|
18
|
+
// US1 shipped declarations over stub handlers so the protocol could be graded
|
|
19
|
+
// without a database; US2 wires them to the TS capabilities that already exist
|
|
20
|
+
// (`loadMirrorContext`, journey listing/status, `searchMemories`, conversation
|
|
21
|
+
// listing/recall, `detectPersona`).
|
|
22
|
+
|
|
23
|
+
import type { Database } from "#db/database.ts";
|
|
24
|
+
import {
|
|
25
|
+
detectPersonaTool,
|
|
26
|
+
journeyStatusTool,
|
|
27
|
+
listConversationsTool,
|
|
28
|
+
listJourneysTool,
|
|
29
|
+
recallConversationTool,
|
|
30
|
+
} from "./tools/deterministic.ts";
|
|
31
|
+
import {
|
|
32
|
+
mirrorContextTool,
|
|
33
|
+
searchMemoriesTool,
|
|
34
|
+
type ToolRuntime,
|
|
35
|
+
} from "./tools/providerCrossing.ts";
|
|
36
|
+
|
|
37
|
+
/** JSON Schema as the tool declares it; shape is opaque to the protocol layer. */
|
|
38
|
+
export type JsonSchema = Record<string, unknown>;
|
|
39
|
+
|
|
40
|
+
/** A tool handler: receives the raw `arguments` value and returns text content. */
|
|
41
|
+
export type ToolHandler = (args: unknown) => string | Promise<string>;
|
|
42
|
+
|
|
43
|
+
/** An MCP tool: name, description, schema, handler -- mirroring Python's `Tool`. */
|
|
44
|
+
export interface Tool {
|
|
45
|
+
name: string;
|
|
46
|
+
description: string;
|
|
47
|
+
inputSchema: JsonSchema;
|
|
48
|
+
handler: ToolHandler;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** The registry the protocol layer dispatches against. */
|
|
52
|
+
export interface ToolRegistry {
|
|
53
|
+
/** Declaration order, as `tools/list` serializes it. */
|
|
54
|
+
list: readonly Tool[];
|
|
55
|
+
/** Dispatch map for `tools/call`. May hold entries absent from `list`. */
|
|
56
|
+
byName: ReadonlyMap<string, Tool>;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function notImplemented(name: string): ToolHandler {
|
|
60
|
+
return () => {
|
|
61
|
+
throw new Error(`Tool '${name}' is not wired yet (CV22.DS9.US2 owns tool behavior)`);
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** What the wired handlers need from the process. */
|
|
66
|
+
export interface ToolContext {
|
|
67
|
+
db: Database;
|
|
68
|
+
runtime: ToolRuntime;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* The registry with real handlers, in Python's declaration order.
|
|
73
|
+
*
|
|
74
|
+
* Declarations stay exactly as `TOOL_DECLARATIONS` has them -- `tools/list` is
|
|
75
|
+
* the client contract and must not shift when behavior arrives.
|
|
76
|
+
*/
|
|
77
|
+
export function wiredRegistry(context: ToolContext): ToolRegistry {
|
|
78
|
+
const handlers: Record<string, ToolHandler> = {
|
|
79
|
+
mirror_context: (args) => mirrorContextTool(context.db, args, context.runtime),
|
|
80
|
+
list_journeys: (args) => listJourneysTool(context.db, args),
|
|
81
|
+
journey_status: (args) => journeyStatusTool(context.db, args),
|
|
82
|
+
search_memories: (args) => searchMemoriesTool(context.db, args, context.runtime),
|
|
83
|
+
list_conversations: (args) => listConversationsTool(context.db, args),
|
|
84
|
+
recall_conversation: (args) => recallConversationTool(context.db, args),
|
|
85
|
+
detect_persona: (args) => detectPersonaTool(context.db, args),
|
|
86
|
+
};
|
|
87
|
+
return buildRegistry(
|
|
88
|
+
TOOL_DECLARATIONS.map((declaration) => {
|
|
89
|
+
const handler = handlers[declaration.name];
|
|
90
|
+
if (!handler) throw new Error(`no handler wired for tool '${declaration.name}'`);
|
|
91
|
+
return { ...declaration, handler };
|
|
92
|
+
}),
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* The seven tools, in Python's declaration order. Descriptions and schemas are
|
|
98
|
+
* byte-for-byte the strings in `tools.py`; changing one is a client-visible
|
|
99
|
+
* contract change, and the parity golden fails on it.
|
|
100
|
+
*/
|
|
101
|
+
export const TOOL_DECLARATIONS: readonly Omit<Tool, "handler">[] = [
|
|
102
|
+
{
|
|
103
|
+
name: "mirror_context",
|
|
104
|
+
description:
|
|
105
|
+
"Load Mirror identity context on demand (side-effect-free). Optionally " +
|
|
106
|
+
"scope by persona and journey; pass the user query for attachment search.",
|
|
107
|
+
inputSchema: {
|
|
108
|
+
type: "object",
|
|
109
|
+
properties: {
|
|
110
|
+
query: { type: "string", description: "User query for attachment search." },
|
|
111
|
+
persona: { type: "string", description: "Persona id to scope context." },
|
|
112
|
+
journey: { type: "string", description: "Journey slug to scope context." },
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
name: "list_journeys",
|
|
118
|
+
description: "List active journeys with status, stage, and description.",
|
|
119
|
+
inputSchema: { type: "object", properties: {} },
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
name: "journey_status",
|
|
123
|
+
description: "Get status for one journey, or overall status when no slug is given.",
|
|
124
|
+
inputSchema: {
|
|
125
|
+
type: "object",
|
|
126
|
+
properties: { slug: { type: "string", description: "Journey slug." } },
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
name: "search_memories",
|
|
131
|
+
description: "Search memories by query, or list by type/layer/journey filter.",
|
|
132
|
+
inputSchema: {
|
|
133
|
+
type: "object",
|
|
134
|
+
properties: {
|
|
135
|
+
query: { type: "string" },
|
|
136
|
+
type: { type: "string", description: "Memory type filter." },
|
|
137
|
+
layer: { type: "string", description: "Jungian layer filter." },
|
|
138
|
+
journey: { type: "string", description: "Journey slug filter." },
|
|
139
|
+
limit: { type: "integer", default: 5 },
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
name: "list_conversations",
|
|
145
|
+
description: "List recent conversations with optional journey/persona filters.",
|
|
146
|
+
inputSchema: {
|
|
147
|
+
type: "object",
|
|
148
|
+
properties: {
|
|
149
|
+
limit: { type: "integer", default: 20 },
|
|
150
|
+
journey: { type: "string" },
|
|
151
|
+
persona: { type: "string" },
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
name: "recall_conversation",
|
|
157
|
+
description: "Load messages from a previous conversation by id (prefix accepted).",
|
|
158
|
+
inputSchema: {
|
|
159
|
+
type: "object",
|
|
160
|
+
properties: {
|
|
161
|
+
conversation_id: { type: "string" },
|
|
162
|
+
limit: { type: "integer", default: 50 },
|
|
163
|
+
},
|
|
164
|
+
required: ["conversation_id"],
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
name: "detect_persona",
|
|
169
|
+
description: "Show persona routing matches for a query.",
|
|
170
|
+
inputSchema: {
|
|
171
|
+
type: "object",
|
|
172
|
+
properties: { query: { type: "string" } },
|
|
173
|
+
required: ["query"],
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
];
|
|
177
|
+
|
|
178
|
+
/** Build a registry from tools, optionally adding dispatch-only entries. */
|
|
179
|
+
export function buildRegistry(
|
|
180
|
+
list: readonly Tool[],
|
|
181
|
+
dispatchOnly: readonly Tool[] = [],
|
|
182
|
+
): ToolRegistry {
|
|
183
|
+
const byName = new Map<string, Tool>();
|
|
184
|
+
for (const tool of [...list, ...dispatchOnly]) byName.set(tool.name, tool);
|
|
185
|
+
return { list, byName };
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* The seven declarations over handlers that refuse.
|
|
190
|
+
*
|
|
191
|
+
* Kept for the protocol tests, which grade dispatch without a database. The
|
|
192
|
+
* server uses `wiredRegistry`.
|
|
193
|
+
*/
|
|
194
|
+
export function defaultRegistry(): ToolRegistry {
|
|
195
|
+
return buildRegistry(
|
|
196
|
+
TOOL_DECLARATIONS.map((declaration) => ({
|
|
197
|
+
...declaration,
|
|
198
|
+
handler: notImplemented(declaration.name),
|
|
199
|
+
})),
|
|
200
|
+
);
|
|
201
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// MCP stdio transport loop (CV22.DS9.US1).
|
|
2
|
+
//
|
|
3
|
+
// The port of `serve()` in `src/memory/mcp/server.py`: newline-delimited
|
|
4
|
+
// JSON-RPC on stdin/stdout, diagnostics on stderr, one JSON object per line.
|
|
5
|
+
// Python guarantees these rules by reading -- it has no test of its own for
|
|
6
|
+
// `serve()` -- so the parity harness that grades this loop is the first
|
|
7
|
+
// executable statement of them on either side.
|
|
8
|
+
//
|
|
9
|
+
// Two places where a faithful-looking port is wrong:
|
|
10
|
+
//
|
|
11
|
+
// 1. **Draining.** Python's `sys.stdout.write` + `flush()` is synchronous. Node's
|
|
12
|
+
// `process.stdout` is synchronous to a TTY and to a file, but ASYNCHRONOUS to
|
|
13
|
+
// a pipe on macOS -- and a pipe is exactly what an MCP client gives us. A
|
|
14
|
+
// `process.exit()` on end-of-input therefore truncates whatever is still
|
|
15
|
+
// queued, and a half-written JSON line reaches the model as a malformed tool
|
|
16
|
+
// result from a server that then vanishes. `recall_conversation` returns whole
|
|
17
|
+
// transcripts, so the queue is not hypothetical. This loop never exits with a
|
|
18
|
+
// write pending.
|
|
19
|
+
//
|
|
20
|
+
// 2. **Staying alive.** A throw anywhere in the loop kills the process, and the
|
|
21
|
+
// client does not restart a server mid-session: every tool disappears at once.
|
|
22
|
+
// Each message is handled inside its own try, and failures become JSON-RPC
|
|
23
|
+
// responses rather than exits.
|
|
24
|
+
|
|
25
|
+
import { dirname } from "node:path";
|
|
26
|
+
import { createInterface } from "node:readline";
|
|
27
|
+
import { fileURLToPath } from "node:url";
|
|
28
|
+
import { packageVersion } from "#runtime/version.ts";
|
|
29
|
+
import { handleMessage, type JsonRpcResponse } from "./protocol.ts";
|
|
30
|
+
import { defaultRegistry, type ToolRegistry } from "./registry.ts";
|
|
31
|
+
import { encodeJsonLine } from "./wire.ts";
|
|
32
|
+
|
|
33
|
+
// Resolved from this file, not the cwd: an MCP client spawns the server from
|
|
34
|
+
// whatever directory the session happens to be in.
|
|
35
|
+
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
36
|
+
|
|
37
|
+
export interface ServeOptions {
|
|
38
|
+
registry?: ToolRegistry;
|
|
39
|
+
serverVersion?: string;
|
|
40
|
+
input?: NodeJS.ReadableStream;
|
|
41
|
+
output?: NodeJS.WritableStream;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Resolve the server version the way `initialize` reports it. */
|
|
45
|
+
export function resolveServerVersion(): string {
|
|
46
|
+
// The Python server reported `importlib.metadata.version("mirror")` --
|
|
47
|
+
// 0.31.x. This reports THE product version, `packageVersion`, which reads
|
|
48
|
+
// `package.json` since CV22.DS10.TS5 (decision D1; at the root since US3 D2): the same source the
|
|
49
|
+
// plugin manifest is generated from and the same one `runtime version`
|
|
50
|
+
// reads. An explicit MIRROR_MCP_VERSION pin still wins; the DS9 parity
|
|
51
|
+
// harness that set it is gone, and the guard probe unsets it. "0.0.0" is
|
|
52
|
+
// only what a tree that is not Mirror Mind reports.
|
|
53
|
+
return process.env.MIRROR_MCP_VERSION ?? packageVersion(HERE) ?? "0.0.0";
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Write one line and resolve once it has actually left the process. */
|
|
57
|
+
function writeLine(output: NodeJS.WritableStream, line: string): Promise<void> {
|
|
58
|
+
return new Promise((resolve, reject) => {
|
|
59
|
+
output.write(`${line}\n`, (err) => (err ? reject(err) : resolve()));
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Read newline-delimited JSON-RPC from `input` and answer on `output`.
|
|
65
|
+
*
|
|
66
|
+
* Resolves when input ends and every response has drained. The caller exits;
|
|
67
|
+
* this function never calls `process.exit` itself, so it stays testable.
|
|
68
|
+
*/
|
|
69
|
+
export async function serve(options: ServeOptions = {}): Promise<void> {
|
|
70
|
+
const registry = options.registry ?? defaultRegistry();
|
|
71
|
+
const serverVersion = options.serverVersion ?? resolveServerVersion();
|
|
72
|
+
const input = options.input ?? process.stdin;
|
|
73
|
+
const output = options.output ?? process.stdout;
|
|
74
|
+
|
|
75
|
+
const reader = createInterface({ input, crlfDelay: Number.POSITIVE_INFINITY });
|
|
76
|
+
|
|
77
|
+
for await (const rawLine of reader) {
|
|
78
|
+
// Python: `line = line.strip()`, then skip when empty -- so a
|
|
79
|
+
// whitespace-only line is skipped too, not answered with a parse error.
|
|
80
|
+
const line = rawLine.trim();
|
|
81
|
+
if (!line) continue;
|
|
82
|
+
|
|
83
|
+
let response: JsonRpcResponse | null;
|
|
84
|
+
try {
|
|
85
|
+
let message: unknown;
|
|
86
|
+
try {
|
|
87
|
+
message = JSON.parse(line);
|
|
88
|
+
} catch {
|
|
89
|
+
await writeLine(output, encodeJsonLine(error(null, -32700, "Parse error")));
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (message === null || typeof message !== "object" || Array.isArray(message)) {
|
|
93
|
+
await writeLine(output, encodeJsonLine(error(null, -32600, "Invalid Request")));
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
response = await handleMessage(message as Record<string, unknown>, registry, {
|
|
97
|
+
serverVersion,
|
|
98
|
+
});
|
|
99
|
+
} catch (thrown) {
|
|
100
|
+
// Reaching here means the dispatcher itself failed, which the oracle has
|
|
101
|
+
// no branch for. Answer rather than die: the alternative is a dead server.
|
|
102
|
+
process.stderr.write(`mcp: internal dispatch failure: ${describe(thrown)}\n`);
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (response !== null) await writeLine(output, encodeJsonLine(response));
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function error(id: unknown, code: number, message: string): JsonRpcResponse {
|
|
111
|
+
return { jsonrpc: "2.0", id, error: { code, message } };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Describe a thrown value without leaking arguments or results into stderr. */
|
|
115
|
+
function describe(thrown: unknown): string {
|
|
116
|
+
// RS005: stderr is the MCP client's log for this server. Tool arguments are
|
|
117
|
+
// agent-authored text and tool results are identity and transcript content;
|
|
118
|
+
// neither may appear here. Only the error type crosses.
|
|
119
|
+
return thrown instanceof Error ? thrown.name : typeof thrown;
|
|
120
|
+
}
|