mirror-mind 0.0.1 → 0.32.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/hooks/log-session-end.sh +85 -0
- package/.claude/hooks/log-user-prompt.sh +85 -0
- package/.claude/hooks/mirror-inject.sh +85 -0
- package/.claude/hooks/session-start.sh +85 -0
- package/.gemini/hooks/log-assistant.sh +85 -0
- package/.gemini/hooks/log-user.sh +85 -0
- package/.gemini/hooks/session-end.sh +85 -0
- package/.gemini/hooks/session-start.sh +85 -0
- package/.pi/extensions/mirror-logger.ts +561 -0
- package/.pi/skills/mm-backup/SKILL.md +21 -0
- package/.pi/skills/mm-build/SKILL.md +1206 -0
- package/.pi/skills/mm-consolidate/SKILL.md +112 -0
- package/.pi/skills/mm-consult/SKILL.md +70 -0
- package/.pi/skills/mm-conversations/SKILL.md +16 -0
- package/.pi/skills/mm-discard/SKILL.md +39 -0
- package/.pi/skills/mm-explore/SKILL.md +255 -0
- package/.pi/skills/mm-help/SKILL.md +74 -0
- package/.pi/skills/mm-identity/SKILL.md +96 -0
- package/.pi/skills/mm-journal/SKILL.md +16 -0
- package/.pi/skills/mm-journey/SKILL.md +23 -0
- package/.pi/skills/mm-journeys/SKILL.md +15 -0
- package/.pi/skills/mm-memories/SKILL.md +17 -0
- package/.pi/skills/mm-mirror/SKILL.md +98 -0
- package/.pi/skills/mm-mute/SKILL.md +18 -0
- package/.pi/skills/mm-new/SKILL.md +18 -0
- package/.pi/skills/mm-recall/SKILL.md +17 -0
- package/.pi/skills/mm-release-notes/SKILL.md +52 -0
- package/.pi/skills/mm-seed/SKILL.md +19 -0
- package/.pi/skills/mm-shadow/SKILL.md +117 -0
- package/.pi/skills/mm-soul/SKILL.md +502 -0
- package/.pi/skills/mm-tasks/SKILL.md +24 -0
- package/.pi/skills/mm-update/SKILL.md +32 -0
- package/.pi/skills/mm-week/SKILL.md +28 -0
- package/.pi/skills/mm-welcome/SKILL.md +24 -0
- package/AGENTS.md +188 -0
- package/README.md +126 -9
- package/bin/loader.js +67 -0
- package/bin/mirror-hook.js +7 -0
- package/bin/mirror.js +7 -0
- package/docs/releases/index.md +99 -0
- package/docs/releases/pending-cutoffs.md +510 -0
- package/docs/releases/v0.10.0.md +123 -0
- package/docs/releases/v0.10.1.md +66 -0
- package/docs/releases/v0.10.10.md +51 -0
- package/docs/releases/v0.10.11.md +39 -0
- package/docs/releases/v0.10.12.md +57 -0
- package/docs/releases/v0.10.13.md +35 -0
- package/docs/releases/v0.10.14.md +42 -0
- package/docs/releases/v0.10.15.md +53 -0
- package/docs/releases/v0.10.16.md +35 -0
- package/docs/releases/v0.10.17.md +43 -0
- package/docs/releases/v0.10.2.md +64 -0
- package/docs/releases/v0.10.3.md +78 -0
- package/docs/releases/v0.10.4.md +42 -0
- package/docs/releases/v0.10.5.md +57 -0
- package/docs/releases/v0.10.6.md +47 -0
- package/docs/releases/v0.10.7.md +53 -0
- package/docs/releases/v0.10.8.md +49 -0
- package/docs/releases/v0.10.9.md +37 -0
- package/docs/releases/v0.11.0.md +86 -0
- package/docs/releases/v0.12.0.md +67 -0
- package/docs/releases/v0.13.0.md +64 -0
- package/docs/releases/v0.14.0.md +68 -0
- package/docs/releases/v0.15.0.md +48 -0
- package/docs/releases/v0.16.0.md +50 -0
- package/docs/releases/v0.16.1.md +55 -0
- package/docs/releases/v0.16.2.md +36 -0
- package/docs/releases/v0.17.0.md +50 -0
- package/docs/releases/v0.17.1.md +58 -0
- package/docs/releases/v0.17.2.md +42 -0
- package/docs/releases/v0.17.3.md +58 -0
- package/docs/releases/v0.18.0.md +84 -0
- package/docs/releases/v0.18.1.md +51 -0
- package/docs/releases/v0.18.2.md +69 -0
- package/docs/releases/v0.19.0.md +85 -0
- package/docs/releases/v0.19.1.md +46 -0
- package/docs/releases/v0.20.0.md +85 -0
- package/docs/releases/v0.21.0.md +61 -0
- package/docs/releases/v0.21.1.md +47 -0
- package/docs/releases/v0.22.0.md +77 -0
- package/docs/releases/v0.22.1.md +42 -0
- package/docs/releases/v0.23.0.md +105 -0
- package/docs/releases/v0.24.0.md +125 -0
- package/docs/releases/v0.25.0.md +107 -0
- package/docs/releases/v0.26.0.md +139 -0
- package/docs/releases/v0.26.1.md +65 -0
- package/docs/releases/v0.26.2.md +43 -0
- package/docs/releases/v0.27.0.md +63 -0
- package/docs/releases/v0.28.0.md +59 -0
- package/docs/releases/v0.29.0.md +61 -0
- package/docs/releases/v0.29.1.md +59 -0
- package/docs/releases/v0.29.2.md +59 -0
- package/docs/releases/v0.29.3.md +93 -0
- package/docs/releases/v0.30.0.md +75 -0
- package/docs/releases/v0.30.1.md +93 -0
- package/docs/releases/v0.30.2.md +66 -0
- package/docs/releases/v0.31.0.md +122 -0
- package/docs/releases/v0.31.1.md +142 -0
- package/docs/releases/v0.31.10.md +118 -0
- package/docs/releases/v0.31.11.md +88 -0
- package/docs/releases/v0.31.12.md +105 -0
- package/docs/releases/v0.31.13.md +99 -0
- package/docs/releases/v0.31.14.md +97 -0
- package/docs/releases/v0.31.2.md +115 -0
- package/docs/releases/v0.31.3.md +142 -0
- package/docs/releases/v0.31.4.md +117 -0
- package/docs/releases/v0.31.5.md +155 -0
- package/docs/releases/v0.31.6.md +132 -0
- package/docs/releases/v0.31.7.md +132 -0
- package/docs/releases/v0.31.8.md +120 -0
- package/docs/releases/v0.31.9.md +120 -0
- package/docs/releases/v0.32.0.md +261 -0
- package/docs/releases/v0.32.1.md +59 -0
- package/docs/releases/v0.8.0.md +118 -0
- package/docs/releases/v0.9.0.md +98 -0
- package/docs/releases/v0.9.1.md +83 -0
- package/package.json +102 -6
- package/plugins/mirror-mind/.claude-plugin/plugin.json +13 -0
- package/plugins/mirror-mind/hooks/hooks.json +50 -0
- package/plugins/mirror-mind/hooks/log-session-end.sh +98 -0
- package/plugins/mirror-mind/hooks/log-user-prompt.sh +98 -0
- package/plugins/mirror-mind/hooks/mirror-inject.sh +98 -0
- package/plugins/mirror-mind/hooks/session-start.sh +98 -0
- package/plugins/mirror-mind/mcp/launch.sh +52 -0
- package/plugins/mirror-mind/skills/mm-backup/SKILL.md +27 -0
- package/plugins/mirror-mind/skills/mm-build/SKILL.md +1206 -0
- package/plugins/mirror-mind/skills/mm-consolidate/SKILL.md +112 -0
- package/plugins/mirror-mind/skills/mm-consult/SKILL.md +83 -0
- package/plugins/mirror-mind/skills/mm-conversations/SKILL.md +17 -0
- package/plugins/mirror-mind/skills/mm-discard/SKILL.md +40 -0
- package/plugins/mirror-mind/skills/mm-explore/SKILL.md +241 -0
- package/plugins/mirror-mind/skills/mm-help/SKILL.md +104 -0
- package/plugins/mirror-mind/skills/mm-identity/SKILL.md +96 -0
- package/plugins/mirror-mind/skills/mm-journal/SKILL.md +21 -0
- package/plugins/mirror-mind/skills/mm-journey/SKILL.md +37 -0
- package/plugins/mirror-mind/skills/mm-journeys/SKILL.md +15 -0
- package/plugins/mirror-mind/skills/mm-memories/SKILL.md +20 -0
- package/plugins/mirror-mind/skills/mm-mirror/SKILL.md +65 -0
- package/plugins/mirror-mind/skills/mm-mute/SKILL.md +18 -0
- package/plugins/mirror-mind/skills/mm-new/SKILL.md +18 -0
- package/plugins/mirror-mind/skills/mm-recall/SKILL.md +17 -0
- package/plugins/mirror-mind/skills/mm-release-notes/SKILL.md +31 -0
- package/plugins/mirror-mind/skills/mm-seed/SKILL.md +15 -0
- package/plugins/mirror-mind/skills/mm-shadow/SKILL.md +117 -0
- package/plugins/mirror-mind/skills/mm-soul/SKILL.md +488 -0
- package/plugins/mirror-mind/skills/mm-tasks/SKILL.md +36 -0
- package/plugins/mirror-mind/skills/mm-update/SKILL.md +30 -0
- package/plugins/mirror-mind/skills/mm-week/SKILL.md +46 -0
- package/plugins/mirror-mind/skills/mm-welcome/SKILL.md +22 -0
- package/scripts/codex-hooks/session-end.sh +85 -0
- package/scripts/codex-hooks/session-start.sh +85 -0
- package/scripts/codex-mirror.sh +48 -0
- package/templates/identity/README.md +119 -0
- package/templates/identity/ego/behavior.yaml +52 -0
- package/templates/identity/ego/constraints.yaml +15 -0
- package/templates/identity/ego/identity.yaml +43 -0
- package/templates/identity/journeys/personal-growth.yaml +39 -0
- package/templates/identity/organization/identity.yaml +24 -0
- package/templates/identity/organization/principles.yaml +17 -0
- package/templates/identity/personas/coach.yaml +94 -0
- package/templates/identity/personas/designer.yaml +93 -0
- package/templates/identity/personas/doctor.yaml +96 -0
- package/templates/identity/personas/engineer.yaml +77 -0
- package/templates/identity/personas/financial.yaml +97 -0
- package/templates/identity/personas/prompt-engineer.yaml +122 -0
- package/templates/identity/personas/researcher.yaml +88 -0
- package/templates/identity/personas/strategist.yaml +91 -0
- package/templates/identity/personas/teacher.yaml +95 -0
- package/templates/identity/personas/therapist.yaml +91 -0
- package/templates/identity/personas/thinker.yaml +74 -0
- package/templates/identity/personas/writer.yaml +76 -0
- package/templates/identity/self/config.yaml +16 -0
- package/templates/identity/self/soul.yaml +105 -0
- package/templates/identity/user/identity.yaml +30 -0
- package/ts/src/backup/staging.ts +62 -0
- package/ts/src/backup/zipBackup.ts +267 -0
- package/ts/src/backup/zipReader.ts +109 -0
- package/ts/src/backup/zipWriter.ts +148 -0
- package/ts/src/builder/activeCheckpoint.ts +134 -0
- package/ts/src/builder/aggregateStatus.ts +34 -0
- package/ts/src/builder/approve.ts +149 -0
- package/ts/src/builder/argv.ts +292 -0
- package/ts/src/builder/ariadMethod.ts +536 -0
- package/ts/src/builder/artifacts/artifactSurfaces.ts +106 -0
- package/ts/src/builder/artifacts/artifactWriter.ts +113 -0
- package/ts/src/builder/artifacts/closureArtifacts.ts +199 -0
- package/ts/src/builder/artifacts/deliveryStoryArtifacts.ts +316 -0
- package/ts/src/builder/artifacts/planArtifacts.ts +242 -0
- package/ts/src/builder/artifacts/scaffoldSections.ts +273 -0
- package/ts/src/builder/artifacts/scaffoldState.ts +227 -0
- package/ts/src/builder/artifacts/storyFiles.ts +61 -0
- package/ts/src/builder/artifacts/storyIndex.ts +166 -0
- package/ts/src/builder/cadence.ts +59 -0
- package/ts/src/builder/card.ts +192 -0
- package/ts/src/builder/checkpointRefused.ts +66 -0
- package/ts/src/builder/closure.ts +964 -0
- package/ts/src/builder/commands.ts +2053 -0
- package/ts/src/builder/cursorPosition.ts +176 -0
- package/ts/src/builder/cursorTransitions.ts +77 -0
- package/ts/src/builder/deliveryCursor.ts +577 -0
- package/ts/src/builder/deliveryStoryClosure.ts +521 -0
- package/ts/src/builder/deliveryStoryPlan.ts +536 -0
- package/ts/src/builder/deliveryStoryReady.ts +160 -0
- package/ts/src/builder/deliveryStoryRoadmapClosure.ts +151 -0
- package/ts/src/builder/expand.ts +336 -0
- package/ts/src/builder/flowUnit.ts +339 -0
- package/ts/src/builder/homeSurface.ts +122 -0
- package/ts/src/builder/implementationGuard.ts +125 -0
- package/ts/src/builder/index.ts +11 -0
- package/ts/src/builder/lifecycleRefusal.ts +92 -0
- package/ts/src/builder/lifecycleRibbon.ts +159 -0
- package/ts/src/builder/load.ts +313 -0
- package/ts/src/builder/methodAdoption.ts +113 -0
- package/ts/src/builder/methodDefinition.ts +429 -0
- package/ts/src/builder/methodInspection.ts +371 -0
- package/ts/src/builder/plan.ts +408 -0
- package/ts/src/builder/planPreauthorization.ts +265 -0
- package/ts/src/builder/prepare.ts +225 -0
- package/ts/src/builder/projectPaths.ts +64 -0
- package/ts/src/builder/pull.ts +356 -0
- package/ts/src/builder/pullCandidates.ts +463 -0
- package/ts/src/builder/pullCandidatesRender.ts +238 -0
- package/ts/src/builder/refinementField.ts +93 -0
- package/ts/src/builder/releaseIntent.ts +173 -0
- package/ts/src/builder/resumeState.ts +84 -0
- package/ts/src/builder/resumeSurface.ts +149 -0
- package/ts/src/builder/roadmapGrammar.ts +300 -0
- package/ts/src/builder/roadmapScan.ts +97 -0
- package/ts/src/builder/roadmapScope.ts +210 -0
- package/ts/src/builder/scopePhrases.ts +146 -0
- package/ts/src/builder/storyPaths.ts +196 -0
- package/ts/src/builder/storyPlanPreauthorization.ts +287 -0
- package/ts/src/builder/surfaceProtocol.ts +42 -0
- package/ts/src/builder/templateGeneration.ts +148 -0
- package/ts/src/builder/transition.ts +178 -0
- package/ts/src/consult/args.ts +99 -0
- package/ts/src/consult/core.ts +122 -0
- package/ts/src/consult/modelCatalog.ts +55 -0
- package/ts/src/consult/render.ts +47 -0
- package/ts/src/conversation/append.ts +414 -0
- package/ts/src/conversation/backfill.ts +387 -0
- package/ts/src/conversation/closeTail.ts +467 -0
- package/ts/src/conversation/extraction.ts +333 -0
- package/ts/src/conversation/extractionDriver.ts +130 -0
- package/ts/src/conversation/extractionRun.ts +70 -0
- package/ts/src/conversation/journeyInference.ts +165 -0
- package/ts/src/conversation/journeyRepair.ts +114 -0
- package/ts/src/conversation/lifecycleFaces.ts +197 -0
- package/ts/src/conversation/lifecycleWrites.ts +313 -0
- package/ts/src/conversation/listing.ts +77 -0
- package/ts/src/conversation/logger.ts +576 -0
- package/ts/src/conversation/loggerCli.ts +282 -0
- package/ts/src/conversation/loggerRuntime.ts +259 -0
- package/ts/src/conversation/metadataLifecycle.ts +443 -0
- package/ts/src/conversation/recall.ts +66 -0
- package/ts/src/conversation/sessionComposites.ts +258 -0
- package/ts/src/conversation/sessionImport.ts +90 -0
- package/ts/src/conversation/transcriptBackfill.ts +141 -0
- package/ts/src/cultivation/applyActions.ts +241 -0
- package/ts/src/cultivation/cluster.ts +86 -0
- package/ts/src/cultivation/consolidationStore.ts +281 -0
- package/ts/src/cultivation/promptContext.ts +62 -0
- package/ts/src/cultivation/propose.ts +305 -0
- package/ts/src/cultivation/scan.ts +155 -0
- package/ts/src/db/backupGate.ts +47 -0
- package/ts/src/db/bootstrap.ts +108 -0
- package/ts/src/db/bootstrapLock.ts +223 -0
- package/ts/src/db/copyGuard.ts +60 -0
- package/ts/src/db/database.ts +397 -0
- package/ts/src/db/decode.ts +66 -0
- package/ts/src/db/ftsIntegrity.ts +41 -0
- package/ts/src/db/migrateOnOpen.ts +214 -0
- package/ts/src/db/migrations.ts +576 -0
- package/ts/src/db/readyOnOpen.ts +35 -0
- package/ts/src/db/rowDecode.ts +42 -0
- package/ts/src/db/schema.ts +432 -0
- package/ts/src/db/schemaInventory.ts +225 -0
- package/ts/src/db/schemaInventorySnapshot.ts +2107 -0
- package/ts/src/db/schemaState.ts +91 -0
- package/ts/src/db/schemaTsDivergence.ts +114 -0
- package/ts/src/descriptor/descriptorRead.ts +49 -0
- package/ts/src/docs/docsLint.ts +265 -0
- package/ts/src/explorer/handoff.ts +522 -0
- package/ts/src/explorer/render.ts +335 -0
- package/ts/src/explorer/story.ts +769 -0
- package/ts/src/explorer/transition.ts +72 -0
- package/ts/src/extensions/bindings.ts +170 -0
- package/ts/src/extensions/catalog.ts +249 -0
- package/ts/src/extensions/catalogCommands.ts +513 -0
- package/ts/src/extensions/catalogWrites.ts +652 -0
- package/ts/src/extensions/contextRuntime.ts +325 -0
- package/ts/src/extensions/dispatch.ts +374 -0
- package/ts/src/extensions/errors.ts +24 -0
- package/ts/src/extensions/manifest.ts +356 -0
- package/ts/src/extensions/migrations.ts +350 -0
- package/ts/src/extraction/conversation.ts +304 -0
- package/ts/src/extraction/conversationMetadata.ts +206 -0
- package/ts/src/extraction/fencing.ts +106 -0
- package/ts/src/extraction/json.ts +17 -0
- package/ts/src/extraction/prompts.ts +470 -0
- package/ts/src/frontDoor/args.ts +30 -0
- package/ts/src/frontDoor/argvShape.ts +106 -0
- package/ts/src/frontDoor/buildLoadRuntime.ts +139 -0
- package/ts/src/frontDoor/buildRoute.ts +398 -0
- package/ts/src/frontDoor/cli.ts +1865 -0
- package/ts/src/frontDoor/cloneRoleGuard.ts +98 -0
- package/ts/src/frontDoor/consultRoute.ts +50 -0
- package/ts/src/frontDoor/contentTailRoute.ts +239 -0
- package/ts/src/frontDoor/conversationLoggerRoute.ts +73 -0
- package/ts/src/frontDoor/cultivationRoute.ts +210 -0
- package/ts/src/frontDoor/dbPath.ts +131 -0
- package/ts/src/frontDoor/dbSafetyToolsRoute.ts +240 -0
- package/ts/src/frontDoor/exploreRoute.ts +572 -0
- package/ts/src/frontDoor/extensionCatalogRoute.ts +252 -0
- package/ts/src/frontDoor/frontDoorLog.ts +57 -0
- package/ts/src/frontDoor/identityWrite.ts +36 -0
- package/ts/src/frontDoor/journeyWriteRoute.ts +26 -0
- package/ts/src/frontDoor/lifecycleWriteRoute.ts +110 -0
- package/ts/src/frontDoor/liveBackup.ts +100 -0
- package/ts/src/frontDoor/mirrorModeRoute.ts +163 -0
- package/ts/src/frontDoor/namedRefusal.ts +35 -0
- package/ts/src/frontDoor/nodeSupport.ts +24 -0
- package/ts/src/frontDoor/render/consolidate.ts +199 -0
- package/ts/src/frontDoor/render/conversations.ts +33 -0
- package/ts/src/frontDoor/render/cultivationShared.ts +21 -0
- package/ts/src/frontDoor/render/descriptor.ts +20 -0
- package/ts/src/frontDoor/render/detectPersona.ts +50 -0
- package/ts/src/frontDoor/render/icons.ts +13 -0
- package/ts/src/frontDoor/render/identity.ts +59 -0
- package/ts/src/frontDoor/render/identityRows.ts +17 -0
- package/ts/src/frontDoor/render/inspectPersona.ts +53 -0
- package/ts/src/frontDoor/render/journeyStatus.ts +78 -0
- package/ts/src/frontDoor/render/journeys.ts +79 -0
- package/ts/src/frontDoor/render/list.ts +41 -0
- package/ts/src/frontDoor/render/memories.ts +67 -0
- package/ts/src/frontDoor/render/recall.ts +49 -0
- package/ts/src/frontDoor/render/shadow.ts +155 -0
- package/ts/src/frontDoor/render/tasks.ts +99 -0
- package/ts/src/frontDoor/render/tasksImportSync.ts +63 -0
- package/ts/src/frontDoor/render/week.ts +113 -0
- package/ts/src/frontDoor/routing.ts +999 -0
- package/ts/src/frontDoor/runtimeRoute.ts +623 -0
- package/ts/src/frontDoor/searchRoute.ts +166 -0
- package/ts/src/frontDoor/seedPaths.ts +98 -0
- package/ts/src/frontDoor/soulRoute.ts +433 -0
- package/ts/src/frontDoor/tasksImportSyncRoute.ts +110 -0
- package/ts/src/frontDoor/tasksWriteRoute.ts +101 -0
- package/ts/src/frontDoor/usage.ts +176 -0
- package/ts/src/guards/claudePlugin.ts +252 -0
- package/ts/src/guards/packContents.ts +306 -0
- package/ts/src/guards/piSourcedSkills.ts +70 -0
- package/ts/src/guards/retiredRouteShadows.ts +57 -0
- package/ts/src/guards/retiredSurfaces.ts +564 -0
- package/ts/src/hooks/claude.ts +176 -0
- package/ts/src/hooks/codex.ts +45 -0
- package/ts/src/hooks/gemini.ts +126 -0
- package/ts/src/hooks/main.ts +109 -0
- package/ts/src/hooks/mirrorState.ts +71 -0
- package/ts/src/hooks/payload.ts +68 -0
- package/ts/src/hooks/runtime.ts +159 -0
- package/ts/src/identity/applyConsolidationIdentityUpdate.ts +67 -0
- package/ts/src/identity/descriptorGenerate.ts +162 -0
- package/ts/src/identity/identityEdit.ts +143 -0
- package/ts/src/identity/identityKey.ts +77 -0
- package/ts/src/identity/identityRead.ts +69 -0
- package/ts/src/identity/identityStore.ts +80 -0
- package/ts/src/identity/journeyListing.ts +53 -0
- package/ts/src/identity/personaInspect.ts +44 -0
- package/ts/src/identity/personaListing.ts +37 -0
- package/ts/src/identity/setIdentity.ts +64 -0
- package/ts/src/index.ts +231 -0
- package/ts/src/init/init.ts +111 -0
- package/ts/src/journey/journeyOptions.ts +131 -0
- package/ts/src/journey/journeyRemoval.ts +158 -0
- package/ts/src/journey/journeyStatus.ts +149 -0
- package/ts/src/journey/journeySyncFile.ts +87 -0
- package/ts/src/journey/journeyWrite.ts +203 -0
- package/ts/src/journey/parentJourney.ts +41 -0
- package/ts/src/journey/validateParentJourney.ts +55 -0
- package/ts/src/mcp/boundary.ts +151 -0
- package/ts/src/mcp/guards.ts +244 -0
- package/ts/src/mcp/main.ts +121 -0
- package/ts/src/mcp/payload.ts +141 -0
- package/ts/src/mcp/protocol.ts +141 -0
- package/ts/src/mcp/readModels.ts +241 -0
- package/ts/src/mcp/registry.ts +201 -0
- package/ts/src/mcp/serve.ts +120 -0
- package/ts/src/mcp/tools/deterministic.ts +137 -0
- package/ts/src/mcp/tools/providerCrossing.ts +187 -0
- package/ts/src/mcp/wire.ts +8 -0
- package/ts/src/memory/journal.ts +180 -0
- package/ts/src/memory/listing.ts +107 -0
- package/ts/src/memory/memoryWrite.ts +88 -0
- package/ts/src/memory/reinforcement.ts +35 -0
- package/ts/src/mirror/context.ts +161 -0
- package/ts/src/mirror/defaultResolution.ts +199 -0
- package/ts/src/mirror/orchestration.ts +364 -0
- package/ts/src/mirror/reception.ts +194 -0
- package/ts/src/mirror/render.ts +75 -0
- package/ts/src/mirror/runtimeSession.ts +205 -0
- package/ts/src/mode/operatingMode.ts +148 -0
- package/ts/src/observability/callOutcome.ts +115 -0
- package/ts/src/observability/inspectLedger.ts +329 -0
- package/ts/src/observability/ledgerHooks.ts +126 -0
- package/ts/src/observability/llmCalls.ts +154 -0
- package/ts/src/persona/detectPersona.ts +81 -0
- package/ts/src/planning/promptAssembly.ts +111 -0
- package/ts/src/planning/weekPending.ts +81 -0
- package/ts/src/planning/weekPlan.ts +183 -0
- package/ts/src/planning/weekSave.ts +117 -0
- package/ts/src/providers/config.ts +189 -0
- package/ts/src/providers/cost.ts +64 -0
- package/ts/src/providers/credits.ts +204 -0
- package/ts/src/providers/embedding.ts +321 -0
- package/ts/src/providers/familyProviders.ts +147 -0
- package/ts/src/providers/llm.ts +334 -0
- package/ts/src/providers/openrouter.ts +244 -0
- package/ts/src/providers/redaction.ts +110 -0
- package/ts/src/providers/replay.ts +15 -0
- package/ts/src/providers/transport.ts +410 -0
- package/ts/src/repair/encodingRepair.ts +253 -0
- package/ts/src/runtime/backup.ts +151 -0
- package/ts/src/runtime/channel.ts +146 -0
- package/ts/src/runtime/config.ts +168 -0
- package/ts/src/runtime/diagnose.ts +686 -0
- package/ts/src/runtime/git.ts +510 -0
- package/ts/src/runtime/installKind.ts +220 -0
- package/ts/src/runtime/migrate.ts +154 -0
- package/ts/src/runtime/packageIdentity.ts +102 -0
- package/ts/src/runtime/releaseNotes.ts +244 -0
- package/ts/src/runtime/status.ts +557 -0
- package/ts/src/runtime/strategies/clone.ts +200 -0
- package/ts/src/runtime/strategies/package.ts +328 -0
- package/ts/src/runtime/treeRoot.ts +30 -0
- package/ts/src/runtime/update.ts +445 -0
- package/ts/src/runtime/updateGate.ts +40 -0
- package/ts/src/runtime/updatePipeline.ts +159 -0
- package/ts/src/runtime/version.ts +36 -0
- package/ts/src/runtime/wiring.ts +130 -0
- package/ts/src/search/memorySearch.ts +338 -0
- package/ts/src/search/ranker.ts +154 -0
- package/ts/src/seed/seed.ts +256 -0
- package/ts/src/seed/seedYaml.ts +133 -0
- package/ts/src/soul/apply.ts +218 -0
- package/ts/src/soul/harvest.ts +267 -0
- package/ts/src/soul/prompts/soul_beauty_voice.md +103 -0
- package/ts/src/soul/prompts/soul_self_voice.md +219 -0
- package/ts/src/soul/prompts/soul_wisdom_voice.md +121 -0
- package/ts/src/soul/prompts.ts +85 -0
- package/ts/src/soul/render.ts +358 -0
- package/ts/src/soul/state.ts +212 -0
- package/ts/src/soul/transition.ts +37 -0
- package/ts/src/tasks/journeyPathParse.ts +113 -0
- package/ts/src/tasks/taskImportSync.ts +142 -0
- package/ts/src/tasks/taskStore.ts +312 -0
- package/ts/src/tasks/weekView.ts +120 -0
- package/ts/src/util/clipCodePoints.ts +26 -0
- package/ts/src/util/paths.ts +63 -0
- package/ts/src/util/program.ts +9 -0
- package/ts/src/util/pyGenerators.ts +144 -0
- package/ts/src/util/pythonText.ts +317 -0
- package/ts/src/util/semver.ts +45 -0
- package/ts/src/util/shellWord.ts +25 -0
- package/ts/src/util/slug.ts +64 -0
- package/ts/src/welcome/card.ts +339 -0
- package/ts/src/welcome/statusLine.ts +101 -0
- package/ts/src/welcome/updateCache.ts +129 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// CV22.DS7.US8 plateau 7 — the production-clone guard `build load` runs first.
|
|
2
|
+
//
|
|
3
|
+
// Port of `_is_mirror_mind_checkout` and `_check_clone_role_guard` in
|
|
4
|
+
// `src/memory/cli/build.py`. It lives in the route layer, not in `builder/`,
|
|
5
|
+
// because its inputs are the MACHINE's — a git root, a marker file, a
|
|
6
|
+
// `pyproject.toml` — and `builder/` is graded as a pure port over a database.
|
|
7
|
+
// `runBuildLoad` therefore takes the outcome as an injected seam, which is also
|
|
8
|
+
// what lets the corpus grade the ordering (a refusal prints no banner) without
|
|
9
|
+
// depending on the developer's own checkout.
|
|
10
|
+
//
|
|
11
|
+
// Two behaviors, and the second is easy to miss: `--ignore-production-role` does
|
|
12
|
+
// not skip the guard, it DOWNGRADES it — an orange warning on stderr, and the
|
|
13
|
+
// session start continues. A port that treated the flag as "do not look" would
|
|
14
|
+
// lose the warning, which is the only trace that a Navigator worked in a
|
|
15
|
+
// production clone on purpose.
|
|
16
|
+
|
|
17
|
+
import { inspectCloneRole } from "#runtime/git.ts";
|
|
18
|
+
import { findPackageIdentity } from "#runtime/packageIdentity.ts";
|
|
19
|
+
|
|
20
|
+
/** What the guard decided, or null when it has nothing to say. */
|
|
21
|
+
export interface CloneRoleGuardOutcome {
|
|
22
|
+
/** Bytes for stderr, `print`'s trailing newline included. */
|
|
23
|
+
readonly stderr: string;
|
|
24
|
+
/**
|
|
25
|
+
* The exit code, or null to CONTINUE after printing.
|
|
26
|
+
*
|
|
27
|
+
* Python exits **2** here, not 1: the guard is a usage-level refusal, in the
|
|
28
|
+
* same class as argparse's. Recorded as a value rather than assumed by the
|
|
29
|
+
* caller, because `runBuildLoad`'s other refusal (an unknown journey) exits 1
|
|
30
|
+
* and the two are one `if` apart.
|
|
31
|
+
*/
|
|
32
|
+
readonly exitCode: number | null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface CloneRoleGuardOptions {
|
|
36
|
+
readonly ignoreProductionRole?: boolean;
|
|
37
|
+
/** Where the walk starts when the journey has no project path: Python uses `cwd`. */
|
|
38
|
+
readonly currentDirectory?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Was `_is_mirror_mind_checkout`: walk upward to the first directory holding
|
|
43
|
+
* both `pyproject.toml` and `src/memory/`, and ask whether that pyproject
|
|
44
|
+
* declares Mirror Mind.
|
|
45
|
+
*
|
|
46
|
+
* **CV22.DS10.TS5 re-points the markers** (decision D1) to the TypeScript
|
|
47
|
+
* package, because `src/memory/` is deleted at plateau 3 — and a guard keyed
|
|
48
|
+
* to a deleted directory does not fail, it silently answers "not a checkout"
|
|
49
|
+
* and stops guarding. The production clone-role refusal would have
|
|
50
|
+
* disappeared with no test going red. See `#runtime/packageIdentity.ts`.
|
|
51
|
+
*
|
|
52
|
+
* The two properties Python had are preserved exactly, and are the reason this
|
|
53
|
+
* is a walk rather than a lookup:
|
|
54
|
+
*
|
|
55
|
+
* * the walk STOPS at the first directory carrying the structural markers,
|
|
56
|
+
* whatever it then answers — a nested project decides for itself instead
|
|
57
|
+
* of inheriting its parent's identity;
|
|
58
|
+
* * an unreadable or malformed manifest answers `false` rather than
|
|
59
|
+
* continuing upward — Python's `return False` inside the `except`.
|
|
60
|
+
*/
|
|
61
|
+
export function isMirrorMindCheckout(start: string): boolean {
|
|
62
|
+
return findPackageIdentity(start)?.isMirrorMind === true;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Python `_check_clone_role_guard`, as a value.
|
|
67
|
+
*
|
|
68
|
+
* `null` means the guard has nothing to say: the path is not a Mirror Mind
|
|
69
|
+
* checkout, or its clone is not marked `production`.
|
|
70
|
+
*/
|
|
71
|
+
export function inspectBuilderCloneRole(
|
|
72
|
+
projectPath: string | null,
|
|
73
|
+
options: CloneRoleGuardOptions = {},
|
|
74
|
+
): CloneRoleGuardOutcome | null {
|
|
75
|
+
const start = projectPath ?? options.currentDirectory ?? process.cwd();
|
|
76
|
+
if (!isMirrorMindCheckout(start)) return null;
|
|
77
|
+
const role = inspectCloneRole(start);
|
|
78
|
+
if (role.value !== "production") return null;
|
|
79
|
+
if (options.ignoreProductionRole) {
|
|
80
|
+
return {
|
|
81
|
+
stderr:
|
|
82
|
+
"\u001b[38;5;208m⚠️ Production clone override: --ignore-production-role passed.\u001b[0m\n",
|
|
83
|
+
exitCode: null,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
// Python renders the absent marker as this literal, so the message names the
|
|
87
|
+
// reason the default applied instead of printing `None`.
|
|
88
|
+
const source = role.source ?? "<default: missing marker>";
|
|
89
|
+
return {
|
|
90
|
+
stderr:
|
|
91
|
+
"Builder Mode refused: the journey project clone is marked 'production'.\n" +
|
|
92
|
+
` Project path: ${projectPath || "<current directory>"}\n` +
|
|
93
|
+
` Clone role source: ${source}\n` +
|
|
94
|
+
" Development should happen in a clone marked 'dev'.\n" +
|
|
95
|
+
" To proceed here anyway, pass --ignore-production-role.\n",
|
|
96
|
+
exitCode: 2,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { parseConsultArgs } from "#consult/args.ts";
|
|
2
|
+
import { runConsult } from "#consult/core.ts";
|
|
3
|
+
import type { WritableDatabase } from "#db/database.ts";
|
|
4
|
+
import { resolveFamilyProviders } from "#providers/familyProviders.ts";
|
|
5
|
+
import { CONSULT_ASK_TRANSPORT, CONSULT_CREDITS_TRANSPORT } from "#providers/transport.ts";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* `db` is optional (AI-09): when provided, `consult ask` logs to the
|
|
9
|
+
* llm_calls ledger with its real fetched cost. `consult credits` never logs
|
|
10
|
+
* (Python doesn't either), so `db` is passed through but unused on that path.
|
|
11
|
+
*/
|
|
12
|
+
export async function runConsultRoute(
|
|
13
|
+
db: WritableDatabase | null,
|
|
14
|
+
args: readonly string[],
|
|
15
|
+
): Promise<string> {
|
|
16
|
+
const command = parseConsultArgs(args);
|
|
17
|
+
// Per leaf, from the same specs `routing.ts` decided with: `credits` needs
|
|
18
|
+
// only a credit provider, `ask` needs a chat provider too (CV22.DS8.US3).
|
|
19
|
+
const spec = command.command === "credits" ? CONSULT_CREDITS_TRANSPORT : CONSULT_ASK_TRANSPORT;
|
|
20
|
+
const family = await resolveFamilyProviders(process.env, spec);
|
|
21
|
+
if (!family?.credits) {
|
|
22
|
+
// `routing.ts` sends a reverted or half-configured family to Python before
|
|
23
|
+
// the route is reached, so this is defense in depth.
|
|
24
|
+
throw new Error("consult requires a credit provider");
|
|
25
|
+
}
|
|
26
|
+
if (command.command === "credits") {
|
|
27
|
+
return `${await runConsult(command, {
|
|
28
|
+
credits: family.credits,
|
|
29
|
+
llm: missingLlm(),
|
|
30
|
+
loadContext: () => "",
|
|
31
|
+
db: db ?? undefined,
|
|
32
|
+
})}\n`;
|
|
33
|
+
}
|
|
34
|
+
if (!family.llm) throw new Error("consult ask requires an LLM provider");
|
|
35
|
+
const context = process.env.MIRROR_TS_CONSULT_CONTEXT ?? "";
|
|
36
|
+
return `${await runConsult(command, {
|
|
37
|
+
credits: family.credits,
|
|
38
|
+
llm: family.llm,
|
|
39
|
+
loadContext: () => context,
|
|
40
|
+
db: db ?? undefined,
|
|
41
|
+
})}\n`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function missingLlm() {
|
|
45
|
+
return {
|
|
46
|
+
async complete(): Promise<never> {
|
|
47
|
+
throw new Error("LLM provider is not available for consult credits");
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Front-door serving paths for the CV22.DS7.US11 content & planning tail.
|
|
3
|
+
*
|
|
4
|
+
* `routing.ts` decides; these functions answer. Each mirrors the shape the
|
|
5
|
+
* cultivation and soul routes established: the replay provider is loaded from
|
|
6
|
+
* the environment variable `routing.ts` already gated on, so reaching one of
|
|
7
|
+
* these without it is direct misuse rather than a user-visible path.
|
|
8
|
+
*
|
|
9
|
+
* The ported logic lives in `#memory/journal.ts`, `#planning/weekPlan.ts`, and
|
|
10
|
+
* `#identity/descriptorGenerate.ts` and is graded there against the Python
|
|
11
|
+
* goldens. This module only assembles prompts, awaits providers, and hands the
|
|
12
|
+
* results to that synchronous logic — deliberately, so the graded units stay
|
|
13
|
+
* pure and the async seam stays thin.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import {
|
|
17
|
+
LifecycleFaceError,
|
|
18
|
+
runLifecycleDryRun,
|
|
19
|
+
runLifecyclePreviewAtMessage,
|
|
20
|
+
} from "#conversation/lifecycleFaces.ts";
|
|
21
|
+
import type { Database, WritableDatabase } from "#db/database.ts";
|
|
22
|
+
import { parseJsonResponse } from "#extraction/json.ts";
|
|
23
|
+
import { resolveDescriptorTargets, runDescriptorGenerate } from "#identity/descriptorGenerate.ts";
|
|
24
|
+
import {
|
|
25
|
+
addJournal,
|
|
26
|
+
interpretJournalClassification,
|
|
27
|
+
renderJournalReceipt,
|
|
28
|
+
} from "#memory/journal.ts";
|
|
29
|
+
import { classifyProviderError, type OnProviderCallOutcome } from "#observability/callOutcome.ts";
|
|
30
|
+
import { chatLedgerHook, embeddingLedgerHook } from "#observability/ledgerHooks.ts";
|
|
31
|
+
import {
|
|
32
|
+
buildDescriptorPrompt,
|
|
33
|
+
buildJournalClassificationPrompt,
|
|
34
|
+
buildWeekPlanPrompt,
|
|
35
|
+
DESCRIPTOR_TEMPERATURE,
|
|
36
|
+
JOURNAL_TEMPERATURE,
|
|
37
|
+
WEEK_PLAN_TEMPERATURE,
|
|
38
|
+
weekPlanClock,
|
|
39
|
+
} from "#planning/promptAssembly.ts";
|
|
40
|
+
import { defaultPendingPath } from "#planning/weekPending.ts";
|
|
41
|
+
import { buildJourneyContext, runWeekPlan } from "#planning/weekPlan.ts";
|
|
42
|
+
import { type EmbeddingProvider, generateEmbeddingSafely } from "#providers/embedding.ts";
|
|
43
|
+
import type { LlmProvider } from "#providers/llm.ts";
|
|
44
|
+
import { memoryEmbedText } from "#soul/harvest.ts";
|
|
45
|
+
import { newId, nowIso } from "#util/pyGenerators.ts";
|
|
46
|
+
import { pyStrip } from "#util/pythonText.ts";
|
|
47
|
+
|
|
48
|
+
/** Python `journal`: positional words joined by a single space, plus `--journey`. */
|
|
49
|
+
export function parseJournalArgs(args: readonly string[]): {
|
|
50
|
+
content: string;
|
|
51
|
+
journey: string | null;
|
|
52
|
+
} {
|
|
53
|
+
const words: string[] = [];
|
|
54
|
+
let journey: string | null = null;
|
|
55
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
56
|
+
const token = args[index];
|
|
57
|
+
if (token === "--journey") {
|
|
58
|
+
journey = args[index + 1] ?? null;
|
|
59
|
+
index += 1;
|
|
60
|
+
} else if (token === "--mirror-home" || token === "--db-path") {
|
|
61
|
+
index += 1;
|
|
62
|
+
} else {
|
|
63
|
+
words.push(token);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return { content: words.join(" "), journey };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export async function runJournalRoute(
|
|
70
|
+
db: WritableDatabase,
|
|
71
|
+
args: readonly string[],
|
|
72
|
+
providers: { llm: LlmProvider; embedding: EmbeddingProvider },
|
|
73
|
+
): Promise<number> {
|
|
74
|
+
const { content, journey } = parseJournalArgs(args);
|
|
75
|
+
if (!content.trim()) {
|
|
76
|
+
console.error("Error: journal entry text cannot be empty");
|
|
77
|
+
return 1;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const prompt = buildJournalClassificationPrompt(content);
|
|
81
|
+
const response = await providers.llm.complete({
|
|
82
|
+
role: "journal_classification",
|
|
83
|
+
prompt,
|
|
84
|
+
temperature: JOURNAL_TEMPERATURE,
|
|
85
|
+
});
|
|
86
|
+
// Python's `add_journal` passes build_llm_logger(role="journal_classification").
|
|
87
|
+
chatLedgerHook(db, "journal_classification")(response, prompt);
|
|
88
|
+
const classification = interpretJournalClassification(
|
|
89
|
+
response.content,
|
|
90
|
+
content,
|
|
91
|
+
parseJsonResponse,
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
// Embed the SAME text `addJournal` would compute, so the vector and the
|
|
95
|
+
// graded write path cannot drift apart.
|
|
96
|
+
//
|
|
97
|
+
// Through `generateEmbeddingSafely`, not `provider.embed` directly (CR075).
|
|
98
|
+
// The wrapper owns three behaviors Python's `generate_embedding` has and a
|
|
99
|
+
// bare call does not: bounded retry of a transient empty payload, the AI-07
|
|
100
|
+
// permanent dimension-mismatch guard, and the ledger hook. All three were
|
|
101
|
+
// harmless while a fixture always answered with a well-formed vector for
|
|
102
|
+
// free, and all three become reachable the moment this is billable.
|
|
103
|
+
const vector = await generateEmbeddingSafely(
|
|
104
|
+
providers.embedding,
|
|
105
|
+
memoryEmbedText(classification.title, content, null),
|
|
106
|
+
{ onAttempt: embeddingLedgerHook(db) },
|
|
107
|
+
);
|
|
108
|
+
const embedding = new Uint8Array(new Float32Array(vector).buffer);
|
|
109
|
+
|
|
110
|
+
const result = addJournal(
|
|
111
|
+
db,
|
|
112
|
+
{ content, journey, classification },
|
|
113
|
+
{ newId, nowIso: nowIso(), embed: () => embedding },
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
const stored = db
|
|
117
|
+
.prepare("SELECT tags FROM memories WHERE id = ?")
|
|
118
|
+
.get(result.memoryId) as unknown as { tags: string | null } | undefined;
|
|
119
|
+
process.stdout.write(renderJournalReceipt(result, journey, stored?.tags ?? null));
|
|
120
|
+
return 0;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export async function runWeekPlanRoute(
|
|
124
|
+
db: WritableDatabase,
|
|
125
|
+
args: readonly string[],
|
|
126
|
+
llm: LlmProvider,
|
|
127
|
+
// Test seam only: the pending file lives at one well-known path, shared with
|
|
128
|
+
// Python, and a test must not write to the operator's real one. The default
|
|
129
|
+
// is unchanged -- hardening the file itself is CR074, which has to move both
|
|
130
|
+
// engines together.
|
|
131
|
+
options: { pendingPath?: string } = {},
|
|
132
|
+
): Promise<number> {
|
|
133
|
+
const text = args.find((token) => !token.startsWith("--")) ?? "";
|
|
134
|
+
const clock = weekPlanClock(new Date());
|
|
135
|
+
|
|
136
|
+
// Assembled here so the bytes sent are the bytes the digest pins, then the
|
|
137
|
+
// response is handed to the synchronous, golden-graded walk.
|
|
138
|
+
const prompt = buildWeekPlanPrompt(text, buildJourneyContext(db), clock);
|
|
139
|
+
const response = await llm.complete({
|
|
140
|
+
role: "week_plan",
|
|
141
|
+
prompt,
|
|
142
|
+
temperature: WEEK_PLAN_TEMPERATURE,
|
|
143
|
+
});
|
|
144
|
+
// Python's `services/tasks.py` passes build_llm_logger(role="week_plan").
|
|
145
|
+
chatLedgerHook(db, "week_plan")(response, prompt);
|
|
146
|
+
|
|
147
|
+
runWeekPlan(db, text, {
|
|
148
|
+
complete: () => response.content,
|
|
149
|
+
parseJson: parseJsonResponse,
|
|
150
|
+
clock,
|
|
151
|
+
pendingPath: options.pendingPath ?? defaultPendingPath(),
|
|
152
|
+
print: (value) => process.stdout.write(value),
|
|
153
|
+
});
|
|
154
|
+
return 0;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export async function runDescriptorGenerateRoute(
|
|
158
|
+
db: WritableDatabase,
|
|
159
|
+
args: readonly string[],
|
|
160
|
+
llm: LlmProvider,
|
|
161
|
+
// Without `--layer/--key` this makes ONE CALL PER persona AND per journey.
|
|
162
|
+
// Nothing bounds it but the argument, so the fan-out is reported rather than
|
|
163
|
+
// inferred from the ledger afterwards (CV22.DS8.US3 plan review, security).
|
|
164
|
+
onOutcome?: OnProviderCallOutcome,
|
|
165
|
+
): Promise<number> {
|
|
166
|
+
const flag = (name: string): string | null => {
|
|
167
|
+
const index = args.indexOf(name);
|
|
168
|
+
return index === -1 ? null : (args[index + 1] ?? null);
|
|
169
|
+
};
|
|
170
|
+
const selection = { layer: flag("--layer"), key: flag("--key") };
|
|
171
|
+
|
|
172
|
+
const resolved = resolveDescriptorTargets(db, selection);
|
|
173
|
+
const descriptors = new Map<string, string>();
|
|
174
|
+
if (resolved.kind === "targets") {
|
|
175
|
+
for (const target of resolved.targets) {
|
|
176
|
+
// `generate_descriptor` returns "" for empty content BEFORE the model
|
|
177
|
+
// call, and "" on any provider exception. Both reach the same
|
|
178
|
+
// "skipped (empty response)" line.
|
|
179
|
+
if (!pyStrip(target.content)) {
|
|
180
|
+
// Python returns "" BEFORE the model call, so this entity costs
|
|
181
|
+
// nothing and is not a call at all.
|
|
182
|
+
descriptors.set(`${target.layer}/${target.key}`, "");
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
try {
|
|
186
|
+
const prompt = buildDescriptorPrompt(target.content, target.layer, target.key);
|
|
187
|
+
const response = await llm.complete({
|
|
188
|
+
role: "descriptor",
|
|
189
|
+
prompt,
|
|
190
|
+
temperature: DESCRIPTOR_TEMPERATURE,
|
|
191
|
+
});
|
|
192
|
+
// A DELIBERATE DIVERGENCE, decided at plan time (CV22.DS8.US3).
|
|
193
|
+
// Python's `generate_descriptor` is the one LLM caller that takes no
|
|
194
|
+
// `on_llm_call`, so neither engine recorded this spend. US11 preserved
|
|
195
|
+
// the gap as parity and flagged it as a DS8 input; DS8 closes it in
|
|
196
|
+
// TypeScript, which is the product authority for a ported command
|
|
197
|
+
// (2026-08-13). `descriptor generate` fans out one call per persona
|
|
198
|
+
// and per journey, so a ledger blind to it is blind to the largest
|
|
199
|
+
// single-command spend in this story. Recorded in decisions.md; the
|
|
200
|
+
// descriptor golden now reads "Python zero rows, TS one per entity".
|
|
201
|
+
chatLedgerHook(db, "descriptor")(response, prompt);
|
|
202
|
+
const descriptor = pyStrip(response.content);
|
|
203
|
+
onOutcome?.({ outcome: descriptor ? "answered" : "empty" });
|
|
204
|
+
descriptors.set(`${target.layer}/${target.key}`, descriptor);
|
|
205
|
+
} catch (error) {
|
|
206
|
+
onOutcome?.({ outcome: "transport_failed", kind: classifyProviderError(error) });
|
|
207
|
+
descriptors.set(`${target.layer}/${target.key}`, "");
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return runDescriptorGenerate(db, selection, {
|
|
213
|
+
generate: (target) => descriptors.get(`${target.layer}/${target.key}`) ?? "",
|
|
214
|
+
nowIso: nowIso(),
|
|
215
|
+
}).exitCode;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/** The two ES-001 read faces; `routing.ts` has already refused the write faces. */
|
|
219
|
+
export function runLifecycleFaceRead(db: Database, args: readonly string[]): number {
|
|
220
|
+
const value = (name: string): string | null => {
|
|
221
|
+
const index = args.indexOf(name);
|
|
222
|
+
return index === -1 ? null : (args[index + 1] ?? null);
|
|
223
|
+
};
|
|
224
|
+
try {
|
|
225
|
+
const dryRunId = value("--metadata-lifecycle-dry-run");
|
|
226
|
+
const report =
|
|
227
|
+
dryRunId !== null
|
|
228
|
+
? runLifecycleDryRun(db, dryRunId)
|
|
229
|
+
: runLifecyclePreviewAtMessage(db, value("--metadata-lifecycle-preview-at-message") ?? "");
|
|
230
|
+
process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
|
|
231
|
+
return 0;
|
|
232
|
+
} catch (error) {
|
|
233
|
+
if (error instanceof LifecycleFaceError) {
|
|
234
|
+
console.error(`Error: ${error.message}`);
|
|
235
|
+
return 1;
|
|
236
|
+
}
|
|
237
|
+
throw error;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// Front-door route for `conversation-logger` (CV22.DS7.US5 slice A; completed
|
|
2
|
+
// in CV22.DS7.US10 slice F).
|
|
3
|
+
//
|
|
4
|
+
// Routing (`routing.ts`) decides the route; this module builds the logger
|
|
5
|
+
// runtime from the process environment, performs the work, and writes the CLI
|
|
6
|
+
// contract. (Until CV22.DS10.TS5 it could also return `null` to hand an LLM-tail
|
|
7
|
+
// subcommand back to Python; that path left with the fallback.)
|
|
8
|
+
|
|
9
|
+
import { readFileSync } from "node:fs";
|
|
10
|
+
import { homedir } from "node:os";
|
|
11
|
+
import { dirname } from "node:path";
|
|
12
|
+
import { createZipBackup } from "#backup/zipBackup.ts";
|
|
13
|
+
import { runConversationLoggerCommand, STDIN_SUBCOMMANDS } from "#conversation/loggerCli.ts";
|
|
14
|
+
import { createLoggerRuntime } from "#conversation/loggerRuntime.ts";
|
|
15
|
+
import type { WritableDatabase } from "#db/database.ts";
|
|
16
|
+
import { newId, nowIso } from "#util/pyGenerators.ts";
|
|
17
|
+
|
|
18
|
+
export function isConversationLoggerCommand(argv: readonly string[]): boolean {
|
|
19
|
+
return argv[0] === "conversation-logger";
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Python's `main()`: strip `--mirror-home X` and `--session-id X`, then `args[0]`. */
|
|
23
|
+
function subcommandOf(args: readonly string[]): string {
|
|
24
|
+
const remaining = [...args];
|
|
25
|
+
for (const option of ["--mirror-home", "--session-id"]) {
|
|
26
|
+
const index = remaining.indexOf(option);
|
|
27
|
+
if (index !== -1) remaining.splice(index, 2);
|
|
28
|
+
}
|
|
29
|
+
return remaining[0] ?? "";
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** The hook subcommands read their JSON payload from stdin. */
|
|
33
|
+
function readStdin(): string {
|
|
34
|
+
try {
|
|
35
|
+
return readFileSync(0, "utf-8");
|
|
36
|
+
} catch {
|
|
37
|
+
return "";
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export async function runConversationLoggerRoute(
|
|
42
|
+
db: WritableDatabase,
|
|
43
|
+
dbPath: string,
|
|
44
|
+
argv: readonly string[],
|
|
45
|
+
): Promise<number> {
|
|
46
|
+
const args = argv.slice(1);
|
|
47
|
+
const subcommand = subcommandOf(args);
|
|
48
|
+
const runtime = createLoggerRuntime({
|
|
49
|
+
db,
|
|
50
|
+
// The mute flag and the backups live beside the database in the resolved
|
|
51
|
+
// mirror home.
|
|
52
|
+
mirrorHome: dirname(dbPath),
|
|
53
|
+
homeDir: homedir(),
|
|
54
|
+
env: process.env,
|
|
55
|
+
deps: { newId, nowIso },
|
|
56
|
+
// The dated zip Python's `repair-journeys --apply` gates on (DS7.TS1):
|
|
57
|
+
// non-silent, beside the database, `Mirror home:` line included.
|
|
58
|
+
backup: (stdout) =>
|
|
59
|
+
createZipBackup({
|
|
60
|
+
dbPath,
|
|
61
|
+
mirrorHome: dirname(dbPath),
|
|
62
|
+
backupDir: null,
|
|
63
|
+
silent: false,
|
|
64
|
+
stdout,
|
|
65
|
+
}),
|
|
66
|
+
});
|
|
67
|
+
const result = await runConversationLoggerCommand(db, args, runtime, {
|
|
68
|
+
stdin: STDIN_SUBCOMMANDS.has(subcommand) ? readStdin() : undefined,
|
|
69
|
+
});
|
|
70
|
+
for (const line of result.stdout) process.stdout.write(`${line}\n`);
|
|
71
|
+
for (const line of result.stderr) process.stderr.write(`${line}\n`);
|
|
72
|
+
return result.exitCode;
|
|
73
|
+
}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
// `consolidate`/`shadow` front-door route (CV22.DS7.US3): the testable core
|
|
2
|
+
// deciding WHAT happened for `list`/`reject`/`apply`/`scan` across both
|
|
3
|
+
// command families. Rendering (HOW it is printed) lives in
|
|
4
|
+
// `render/consolidate.ts` / `render/shadow.ts`.
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
applyIdentityUpdate,
|
|
8
|
+
applyMerge,
|
|
9
|
+
applyShadowApply,
|
|
10
|
+
applyShadowCandidate,
|
|
11
|
+
rejectConsolidation,
|
|
12
|
+
} from "#cultivation/applyActions.ts";
|
|
13
|
+
import {
|
|
14
|
+
type ConsolidationRow,
|
|
15
|
+
resolveProposalByIdOrPrefix,
|
|
16
|
+
updateConsolidationStatus,
|
|
17
|
+
} from "#cultivation/consolidationStore.ts";
|
|
18
|
+
import type { WritableDatabase } from "#db/database.ts";
|
|
19
|
+
import { embeddingLedgerHook } from "#observability/ledgerHooks.ts";
|
|
20
|
+
import type { EmbeddingProvider } from "#providers/embedding.ts";
|
|
21
|
+
|
|
22
|
+
// --- Shared resolution (both families) ---------------------------------------
|
|
23
|
+
|
|
24
|
+
export type ResolveProposalOutcome =
|
|
25
|
+
| { kind: "not_found"; proposalId: string }
|
|
26
|
+
| { kind: "already_reviewed"; consolidation: ConsolidationRow }
|
|
27
|
+
| { kind: "pending"; consolidation: ConsolidationRow };
|
|
28
|
+
|
|
29
|
+
/** Port of the `_resolve_proposal` + "already reviewed" check shared by every
|
|
30
|
+
* `reject`/`apply` command in both `consolidate_cmd.py` and `shadow_cmd.py`. */
|
|
31
|
+
export function resolveProposalForReview(
|
|
32
|
+
db: WritableDatabase,
|
|
33
|
+
proposalId: string,
|
|
34
|
+
): ResolveProposalOutcome {
|
|
35
|
+
const consolidation = resolveProposalByIdOrPrefix(db, proposalId);
|
|
36
|
+
if (consolidation === null) return { kind: "not_found", proposalId };
|
|
37
|
+
if (consolidation.status !== "pending") return { kind: "already_reviewed", consolidation };
|
|
38
|
+
return { kind: "pending", consolidation };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// --- consolidate reject / shadow reject ---------------------------------------
|
|
42
|
+
|
|
43
|
+
/** `ResolveProposalOutcome` minus its (internal-only) `"pending"` member --
|
|
44
|
+
* no route ever RETURNS `"pending"` to a caller; it is always converted into
|
|
45
|
+
* a more specific outcome first. Narrowing to this excludes `"pending"` from
|
|
46
|
+
* every exposed union below so a renderer's `if (outcome.kind === "applied")`
|
|
47
|
+
* (or equivalent) narrows completely, with no unreachable-but-typed member. */
|
|
48
|
+
export type UnresolvedOutcome = Exclude<ResolveProposalOutcome, { kind: "pending" }>;
|
|
49
|
+
|
|
50
|
+
export type RejectOutcome =
|
|
51
|
+
| UnresolvedOutcome
|
|
52
|
+
| { kind: "rejected"; consolidation: ConsolidationRow };
|
|
53
|
+
|
|
54
|
+
/** Shared by `consolidate reject` and `shadow reject` -- identical Python logic in both CLIs. */
|
|
55
|
+
export function runReject(db: WritableDatabase, proposalId: string, nowIso: string): RejectOutcome {
|
|
56
|
+
const resolved = resolveProposalForReview(db, proposalId);
|
|
57
|
+
if (resolved.kind !== "pending") return resolved;
|
|
58
|
+
rejectConsolidation(db, resolved.consolidation.id, nowIso);
|
|
59
|
+
return { kind: "rejected", consolidation: resolved.consolidation };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// --- consolidate apply ---------------------------------------------------------
|
|
63
|
+
|
|
64
|
+
export type ConsolidateApplyOutcome =
|
|
65
|
+
| UnresolvedOutcome
|
|
66
|
+
| { kind: "identity_missing_target" }
|
|
67
|
+
| { kind: "identity_refused"; message: string }
|
|
68
|
+
| { kind: "merge_source_not_found" }
|
|
69
|
+
| {
|
|
70
|
+
kind: "applied";
|
|
71
|
+
action: string;
|
|
72
|
+
consolidation: ConsolidationRow;
|
|
73
|
+
resultContent: string;
|
|
74
|
+
identityUpdate?: { targetLayer: string; targetKey: string; sourceMemoryIds: string[] };
|
|
75
|
+
merge?: { mergedMemoryId: string; mergedTitle: string; sourceMemoryIds: string[] };
|
|
76
|
+
shadowCandidate?: { sourceMemoryIds: string[] };
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export interface ConsolidateApplyIds {
|
|
80
|
+
/** Injected id for a new identity row (only consumed on INSERT; ignored on UPDATE). */
|
|
81
|
+
identityId: string;
|
|
82
|
+
/** Injected id for a freshly merged memory (only consumed by the `merge` action). */
|
|
83
|
+
mergeMemoryId: string;
|
|
84
|
+
nowIso: string;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Port of `consolidate_cmd.cmd_apply`'s full dispatch, INCLUDING Python's
|
|
89
|
+
* silent fallthrough: an action outside `{identity_update, merge,
|
|
90
|
+
* shadow_candidate}` (never produced by `proposeConsolidation`'s allowlist,
|
|
91
|
+
* but reachable via a hand-crafted/legacy row) still reaches the SHARED
|
|
92
|
+
* "mark accepted" statement Python runs unconditionally after the
|
|
93
|
+
* if/elif chain -- no actual effect, but the consolidation is marked
|
|
94
|
+
* `accepted` regardless. `embeddingProvider` is required only by the `merge`
|
|
95
|
+
* branch; the caller (front door) only reaches this function when the
|
|
96
|
+
* DS7.US3 replay/live embedding gate is already satisfied.
|
|
97
|
+
*/
|
|
98
|
+
export async function runConsolidateApply(
|
|
99
|
+
db: WritableDatabase,
|
|
100
|
+
proposalId: string,
|
|
101
|
+
overrideContent: string | null,
|
|
102
|
+
ids: ConsolidateApplyIds,
|
|
103
|
+
embeddingProvider: EmbeddingProvider,
|
|
104
|
+
): Promise<ConsolidateApplyOutcome> {
|
|
105
|
+
const resolved = resolveProposalForReview(db, proposalId);
|
|
106
|
+
if (resolved.kind !== "pending") return resolved;
|
|
107
|
+
const consolidation = resolved.consolidation;
|
|
108
|
+
const resultContent = overrideContent ?? consolidation.proposal;
|
|
109
|
+
|
|
110
|
+
if (consolidation.action === "identity_update") {
|
|
111
|
+
const outcome = applyIdentityUpdate(db, consolidation, resultContent, {
|
|
112
|
+
id: ids.identityId,
|
|
113
|
+
nowIso: ids.nowIso,
|
|
114
|
+
});
|
|
115
|
+
if (outcome.kind === "missing_target") return { kind: "identity_missing_target" };
|
|
116
|
+
if (outcome.kind === "refused") return { kind: "identity_refused", message: outcome.message };
|
|
117
|
+
return {
|
|
118
|
+
kind: "applied",
|
|
119
|
+
action: "identity_update",
|
|
120
|
+
consolidation,
|
|
121
|
+
resultContent,
|
|
122
|
+
identityUpdate: {
|
|
123
|
+
targetLayer: outcome.targetLayer,
|
|
124
|
+
targetKey: outcome.targetKey,
|
|
125
|
+
sourceMemoryIds: outcome.sourceMemoryIds,
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (consolidation.action === "merge") {
|
|
131
|
+
const outcome = await applyMerge(db, consolidation, resultContent, {
|
|
132
|
+
embeddingProvider,
|
|
133
|
+
id: ids.mergeMemoryId,
|
|
134
|
+
nowIso: ids.nowIso,
|
|
135
|
+
// Python's `consolidate_cmd` passes build_llm_logger(role="embedding")
|
|
136
|
+
// into `generate_embedding` here. The wrapper's hook already existed on
|
|
137
|
+
// this path; nobody had ever passed it one (CV22.DS8.US3, CR075's
|
|
138
|
+
// adjacent case).
|
|
139
|
+
onEmbeddingAttempt: embeddingLedgerHook(db, { now: () => ids.nowIso }),
|
|
140
|
+
});
|
|
141
|
+
if (outcome.kind === "source_not_found") return { kind: "merge_source_not_found" };
|
|
142
|
+
return {
|
|
143
|
+
kind: "applied",
|
|
144
|
+
action: "merge",
|
|
145
|
+
consolidation,
|
|
146
|
+
resultContent,
|
|
147
|
+
merge: {
|
|
148
|
+
mergedMemoryId: outcome.mergedMemoryId,
|
|
149
|
+
mergedTitle: outcome.mergedTitle,
|
|
150
|
+
sourceMemoryIds: outcome.sourceMemoryIds,
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (consolidation.action === "shadow_candidate") {
|
|
156
|
+
const outcome = applyShadowCandidate(db, consolidation, resultContent, ids.nowIso);
|
|
157
|
+
return {
|
|
158
|
+
kind: "applied",
|
|
159
|
+
action: "shadow_candidate",
|
|
160
|
+
consolidation,
|
|
161
|
+
resultContent,
|
|
162
|
+
shadowCandidate: outcome,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Unknown action: Python's fallthrough -- the if/elif chain has no `else`,
|
|
167
|
+
// so an unrecognized action performs NO actual write, yet the shared
|
|
168
|
+
// post-chain statement still runs and marks the proposal accepted.
|
|
169
|
+
updateConsolidationStatus(db, consolidation.id, "accepted", resultContent, ids.nowIso);
|
|
170
|
+
return { kind: "applied", action: consolidation.action, consolidation, resultContent };
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// --- shadow apply ---------------------------------------------------------------
|
|
174
|
+
|
|
175
|
+
export type ShadowApplyRouteOutcome =
|
|
176
|
+
| UnresolvedOutcome
|
|
177
|
+
| { kind: "wrong_action"; consolidation: ConsolidationRow }
|
|
178
|
+
| {
|
|
179
|
+
kind: "applied";
|
|
180
|
+
consolidation: ConsolidationRow;
|
|
181
|
+
targetKey: string;
|
|
182
|
+
sourceMemoryIds: string[];
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Port of `shadow_cmd.cmd_apply`'s full dispatch: resolve, then refuse any
|
|
187
|
+
* `action !== 'shadow_observation'` loudly (Python's own guard, separate from
|
|
188
|
+
* -- and checked BEFORE -- the allowlist gate), then apply.
|
|
189
|
+
*/
|
|
190
|
+
export function runShadowApply(
|
|
191
|
+
db: WritableDatabase,
|
|
192
|
+
proposalId: string,
|
|
193
|
+
overrideContent: string | null,
|
|
194
|
+
ids: { id: string; nowIso: string },
|
|
195
|
+
): ShadowApplyRouteOutcome {
|
|
196
|
+
const resolved = resolveProposalForReview(db, proposalId);
|
|
197
|
+
if (resolved.kind !== "pending") return resolved;
|
|
198
|
+
const consolidation = resolved.consolidation;
|
|
199
|
+
if (consolidation.action !== "shadow_observation") {
|
|
200
|
+
return { kind: "wrong_action", consolidation };
|
|
201
|
+
}
|
|
202
|
+
const resultContent = overrideContent ?? consolidation.proposal;
|
|
203
|
+
const outcome = applyShadowApply(db, consolidation, resultContent, ids);
|
|
204
|
+
return {
|
|
205
|
+
kind: "applied",
|
|
206
|
+
consolidation,
|
|
207
|
+
targetKey: outcome.targetKey,
|
|
208
|
+
sourceMemoryIds: outcome.sourceMemoryIds,
|
|
209
|
+
};
|
|
210
|
+
}
|