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,252 @@
|
|
|
1
|
+
// Front-door route for the extension catalog family (CV22.DS7.TS4 plateau 7):
|
|
2
|
+
// `extensions`, `ext`, `list extensions|all`, and `inspect`'s four TS4 targets.
|
|
3
|
+
//
|
|
4
|
+
// The route is thin on purpose. Every byte it prints was decided by
|
|
5
|
+
// `catalogCommands.ts` or `inspectLedger.ts`, both pure and graded by their own
|
|
6
|
+
// corpora. What happens here is the part a corpus cannot cover: choosing a read
|
|
7
|
+
// or a write handle, composing `list all` from three ported renderers,
|
|
8
|
+
// executing a dispatch decision in a real process, and reporting a LEAF NAME to
|
|
9
|
+
// the front-door log.
|
|
10
|
+
//
|
|
11
|
+
// Two rules shape it:
|
|
12
|
+
//
|
|
13
|
+
// * **Redaction.** `detail` carries the leaf and nothing else. An extension
|
|
14
|
+
// subcommand's argv routinely holds account ids, campaign names, and folder
|
|
15
|
+
// paths — the security-engineer's plan-stage requirement — so a dispatch
|
|
16
|
+
// logs `leaf=<id>` and never what followed it.
|
|
17
|
+
// * **No handle while the host runs.** `ext <id> <subcommand>` reaches the
|
|
18
|
+
// compat host, which opens the database itself, exactly as Python's
|
|
19
|
+
// dispatcher does. So the dispatch decision is taken WITHOUT a handle and
|
|
20
|
+
// the host is spawned with none open: two writers on one file is a
|
|
21
|
+
// SQLITE_BUSY this route would be creating for itself.
|
|
22
|
+
|
|
23
|
+
import type { Database, WritableDatabase } from "#db/database.ts";
|
|
24
|
+
import type { BindingDeps } from "#extensions/bindings.ts";
|
|
25
|
+
import type { RenderedCommand } from "#extensions/catalog.ts";
|
|
26
|
+
import {
|
|
27
|
+
type CatalogContext,
|
|
28
|
+
type ExtWriteContext,
|
|
29
|
+
runExtCommand,
|
|
30
|
+
runExtensionsCommand,
|
|
31
|
+
runInspectCommand,
|
|
32
|
+
runListCommand,
|
|
33
|
+
UnsupportedCatalogCommandError,
|
|
34
|
+
} from "#extensions/catalogCommands.ts";
|
|
35
|
+
import {
|
|
36
|
+
type ExtensionDispatch,
|
|
37
|
+
isExtensionDispatch,
|
|
38
|
+
runExtensionSubcommand,
|
|
39
|
+
} from "#extensions/dispatch.ts";
|
|
40
|
+
import { listJourneysForListCommand } from "#identity/journeyListing.ts";
|
|
41
|
+
import { listPersonas } from "#identity/personaListing.ts";
|
|
42
|
+
import { runInspectEmbeddingProvenance, runInspectLlmCalls } from "#observability/inspectLedger.ts";
|
|
43
|
+
import { renderListJourneys, renderListPersonas } from "./render/list.ts";
|
|
44
|
+
|
|
45
|
+
export interface ExtensionRouteOutcome {
|
|
46
|
+
exitCode: number;
|
|
47
|
+
/** Leaf names only. Never an argument value. */
|
|
48
|
+
detail: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface ExtensionRouteDeps {
|
|
52
|
+
mirrorHome: string;
|
|
53
|
+
databasePath: string;
|
|
54
|
+
deps: BindingDeps;
|
|
55
|
+
/**
|
|
56
|
+
* The database seams, narrowed to an EXIT CODE on purpose: every caller here
|
|
57
|
+
* answers a command, and a generic `<T>` would let a future branch smuggle a
|
|
58
|
+
* value out of a handle that is already closed by the time it is read.
|
|
59
|
+
*/
|
|
60
|
+
withReadOnlyDatabase: (run: (db: Database) => number) => number;
|
|
61
|
+
withWritableDatabase: (
|
|
62
|
+
run: (db: WritableDatabase) => Promise<number> | number,
|
|
63
|
+
) => Promise<number>;
|
|
64
|
+
environment?: NodeJS.ProcessEnv;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** The `extensions` verbs that need a writable handle; the rest read. */
|
|
68
|
+
const EXTENSIONS_WRITE_VERBS = new Set([
|
|
69
|
+
"sync",
|
|
70
|
+
"install",
|
|
71
|
+
"uninstall",
|
|
72
|
+
"expose-claude",
|
|
73
|
+
"clean-claude",
|
|
74
|
+
]);
|
|
75
|
+
/** The `ext` built-in verbs that write. `bindings` reads; the rest dispatch. */
|
|
76
|
+
const EXT_WRITE_VERBS = new Set(["bind", "unbind", "migrate"]);
|
|
77
|
+
const EXT_READ_VERBS = new Set(["bindings"]);
|
|
78
|
+
const EXT_HELP_HEADS = new Set(["list", "--help", "-h", "help"]);
|
|
79
|
+
|
|
80
|
+
/** A failed `register(api)` during install: Python's traceback, one TS line. */
|
|
81
|
+
|
|
82
|
+
function emit(rendered: RenderedCommand): number {
|
|
83
|
+
if (rendered.stdout) process.stdout.write(rendered.stdout);
|
|
84
|
+
if (rendered.stderr) process.stderr.write(rendered.stderr);
|
|
85
|
+
return rendered.exitCode;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** The dispatcher's own `--mirror-home` consumption, so the verb is the verb. */
|
|
89
|
+
function extArgs(argv: readonly string[]): string[] {
|
|
90
|
+
const args: string[] = [];
|
|
91
|
+
for (let index = 1; index < argv.length; index += 1) {
|
|
92
|
+
const token = argv[index] as string;
|
|
93
|
+
if (token === "--mirror-home" && argv[index + 1] !== undefined) {
|
|
94
|
+
index += 1;
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
args.push(token);
|
|
98
|
+
}
|
|
99
|
+
return args;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* The leaf name for the front-door log.
|
|
104
|
+
*
|
|
105
|
+
* `extensions install ext-google-ads` logs `leaf=install`: the verb is the
|
|
106
|
+
* leaf and the id is an argument. `ext google-ads campaigns --account 1234`
|
|
107
|
+
* logs `leaf=google-ads` — the installed extension that answered — because the
|
|
108
|
+
* subcommand's arguments are where account ids and campaign names live.
|
|
109
|
+
*/
|
|
110
|
+
export function leafFor(argv: readonly string[]): string {
|
|
111
|
+
const command = argv[0] ?? "";
|
|
112
|
+
if (command === "extensions") return firstPositional(argv.slice(1)) ?? "list";
|
|
113
|
+
if (command === "inspect") return firstPositional(argv.slice(1)) ?? "(none)";
|
|
114
|
+
if (command === "list") return firstPositional(argv.slice(1)) ?? "all";
|
|
115
|
+
if (command === "ext") {
|
|
116
|
+
const args = extArgs(argv);
|
|
117
|
+
const head = args[0];
|
|
118
|
+
if (head === undefined) return "(none)";
|
|
119
|
+
if (EXT_HELP_HEADS.has(head)) return head;
|
|
120
|
+
const verb = args[1];
|
|
121
|
+
if (verb !== undefined && (EXT_WRITE_VERBS.has(verb) || EXT_READ_VERBS.has(verb))) {
|
|
122
|
+
return `${head}/${verb}`;
|
|
123
|
+
}
|
|
124
|
+
return head;
|
|
125
|
+
}
|
|
126
|
+
return command;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function firstPositional(args: readonly string[]): string | null {
|
|
130
|
+
const valued = new Set(["--mirror-home", "--extensions-root", "--runtime", "--target-root"]);
|
|
131
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
132
|
+
const token = args[index] as string;
|
|
133
|
+
if (valued.has(token)) {
|
|
134
|
+
index += 1;
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
if (!token.startsWith("--")) return token;
|
|
138
|
+
}
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function readContextFor(route: ExtensionRouteDeps): CatalogContext {
|
|
143
|
+
return { mirrorHome: route.mirrorHome };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function dispatchOptions(route: ExtensionRouteDeps) {
|
|
147
|
+
return {
|
|
148
|
+
databasePath: route.databasePath,
|
|
149
|
+
...(route.environment ? { environment: route.environment } : {}),
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function writeContextFor(route: ExtensionRouteDeps, db: WritableDatabase): ExtWriteContext {
|
|
154
|
+
return { mirrorHome: route.mirrorHome, db, deps: route.deps };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export async function runExtensionCatalogRoute(
|
|
158
|
+
argv: readonly string[],
|
|
159
|
+
route: ExtensionRouteDeps,
|
|
160
|
+
): Promise<ExtensionRouteOutcome> {
|
|
161
|
+
const command = argv[0] ?? "";
|
|
162
|
+
const args = argv.slice(1);
|
|
163
|
+
const detail = `leaf=${leafFor(argv)}`;
|
|
164
|
+
|
|
165
|
+
// No translation layer here any more: `install` no longer imports extension
|
|
166
|
+
// code, so the one failure class this route used to catch -- a `register(api)`
|
|
167
|
+
// raising inside the Python host -- cannot occur. Manifest and migration
|
|
168
|
+
// failures keep the paths they already had.
|
|
169
|
+
if (command === "extensions") {
|
|
170
|
+
const verb = firstPositional(args) ?? "list";
|
|
171
|
+
if (!EXTENSIONS_WRITE_VERBS.has(verb)) {
|
|
172
|
+
return { exitCode: emit(runExtensionsCommand(readContextFor(route), args)), detail };
|
|
173
|
+
}
|
|
174
|
+
const exitCode = await route.withWritableDatabase((db) =>
|
|
175
|
+
emit(runExtensionsCommand(writeContextFor(route, db), args)),
|
|
176
|
+
);
|
|
177
|
+
return { exitCode, detail };
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (command === "inspect") return { exitCode: runInspect(args, route), detail };
|
|
181
|
+
if (command === "list") return { exitCode: runList(args, route), detail };
|
|
182
|
+
if (command === "ext") return { exitCode: await runExt(argv, route), detail };
|
|
183
|
+
|
|
184
|
+
throw new UnsupportedCatalogCommandError(`extension catalog route: ${command}`);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* `inspect`'s four TS4 targets, dispatched by target because they do not share
|
|
189
|
+
* a refusal class: the catalog pair prints usage on stdout at exit 1, and the
|
|
190
|
+
* ledger pair is argparse and exits 2 on stderr. One command, two classes.
|
|
191
|
+
*/
|
|
192
|
+
function runInspect(args: readonly string[], route: ExtensionRouteDeps): number {
|
|
193
|
+
const target = firstPositional(args);
|
|
194
|
+
if (target === "llm-calls" || target === "embedding-provenance") {
|
|
195
|
+
return route.withReadOnlyDatabase((db) => {
|
|
196
|
+
const result =
|
|
197
|
+
target === "llm-calls"
|
|
198
|
+
? runInspectLlmCalls(db, args.slice(1))
|
|
199
|
+
: runInspectEmbeddingProvenance(db, args.slice(1));
|
|
200
|
+
return emit(result);
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
return emit(runInspectCommand(readContextFor(route), args));
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* `list`, composed.
|
|
208
|
+
*
|
|
209
|
+
* `all` prints personas, journeys, and extensions in that order with a blank
|
|
210
|
+
* line between blocks — three renderers from two stories, which is why this
|
|
211
|
+
* composition lives at the front door rather than inside either of them.
|
|
212
|
+
*/
|
|
213
|
+
function runList(args: readonly string[], route: ExtensionRouteDeps): number {
|
|
214
|
+
const target = firstPositional(args) ?? "all";
|
|
215
|
+
const verbose = args.includes("--verbose");
|
|
216
|
+
if (target === "extensions") return emit(runListCommand(readContextFor(route), args));
|
|
217
|
+
|
|
218
|
+
return route.withReadOnlyDatabase((db) => {
|
|
219
|
+
process.stdout.write(renderListPersonas(listPersonas(db), verbose));
|
|
220
|
+
process.stdout.write("\n");
|
|
221
|
+
process.stdout.write(renderListJourneys(listJourneysForListCommand(db)));
|
|
222
|
+
process.stdout.write("\n");
|
|
223
|
+
// The extension block of `all` is `list extensions` with the same options.
|
|
224
|
+
return emit(runListCommand(readContextFor(route), ["extensions", ...args.slice(1)]));
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* `ext`, in three shapes.
|
|
230
|
+
*
|
|
231
|
+
* The built-in verbs take a handle (write or read). The dispatch takes NONE:
|
|
232
|
+
* the decision is pure, and the host that executes it opens the database
|
|
233
|
+
* itself, exactly as Python's dispatcher does.
|
|
234
|
+
*/
|
|
235
|
+
async function runExt(argv: readonly string[], route: ExtensionRouteDeps): Promise<number> {
|
|
236
|
+
const args = extArgs(argv);
|
|
237
|
+
const verb = args[1];
|
|
238
|
+
if (verb !== undefined && EXT_WRITE_VERBS.has(verb)) {
|
|
239
|
+
return route.withWritableDatabase((db) =>
|
|
240
|
+
emit(runExtCommand(writeContextFor(route, db), argv.slice(1)) as RenderedCommand),
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
if (verb !== undefined && EXT_READ_VERBS.has(verb)) {
|
|
244
|
+
return route.withWritableDatabase((db) =>
|
|
245
|
+
emit(runExtCommand(writeContextFor(route, db), argv.slice(1)) as RenderedCommand),
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const answer = runExtCommand(readContextFor(route), argv.slice(1));
|
|
250
|
+
if (!isExtensionDispatch(answer)) return emit(answer);
|
|
251
|
+
return runExtensionSubcommand(answer as ExtensionDispatch, dispatchOptions(route));
|
|
252
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// Durable, redacted observability for the front door (CR026).
|
|
2
|
+
//
|
|
3
|
+
// The front door is a production write path that previously left no trace of
|
|
4
|
+
// backup failures, guard refusals, SQLITE_BUSY, or resolution problems. This
|
|
5
|
+
// appends a metadata-only line per invocation to `<mirror home>/front-door.log`
|
|
6
|
+
// (beside `mirror-logger.log`), under a fail-quietly contract: logging never
|
|
7
|
+
// throws and never blocks the user command.
|
|
8
|
+
//
|
|
9
|
+
// Redaction is structural (RS005/CR033): a `FrontDoorLogEntry` carries only the
|
|
10
|
+
// command name, the route, the exit code, and a short error CATEGORY — never
|
|
11
|
+
// argv payloads (`--content`) or stdin. Callers must not put argument values in
|
|
12
|
+
// `detail`.
|
|
13
|
+
|
|
14
|
+
import { appendFileSync } from "node:fs";
|
|
15
|
+
import { dirname, join } from "node:path";
|
|
16
|
+
|
|
17
|
+
export interface FrontDoorLogEntry {
|
|
18
|
+
command: string | null;
|
|
19
|
+
/**
|
|
20
|
+
* `retired` (CV22.DS10.TS4) is a route like the other two: the front door
|
|
21
|
+
* answered, and the answer was that the surface no longer exists. It is
|
|
22
|
+
* logged so "my command disappeared" stays answerable from the log after the
|
|
23
|
+
* story that removed it is history.
|
|
24
|
+
*
|
|
25
|
+
* `usage` (CV22.DS10.TS5) is the front door's answer for a name it does not
|
|
26
|
+
* know -- the answer Python owned until TS5 deleted it.
|
|
27
|
+
*/
|
|
28
|
+
route: "ts" | "retired" | "usage";
|
|
29
|
+
exitCode: number;
|
|
30
|
+
/** A short, content-free note or error category. Never argument values. */
|
|
31
|
+
detail?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** The log path for a resolved database path (sibling of the DB). */
|
|
35
|
+
export function frontDoorLogPath(dbPath: string): string {
|
|
36
|
+
return join(dirname(dbPath), "front-door.log");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Append one redacted line. Fail-quietly: any error is swallowed. */
|
|
40
|
+
export function logFrontDoor(logPath: string | null, entry: FrontDoorLogEntry): void {
|
|
41
|
+
if (!logPath) return;
|
|
42
|
+
try {
|
|
43
|
+
const level = entry.exitCode === 0 ? "INFO" : "ERROR";
|
|
44
|
+
const detail = entry.detail ? entry.detail.replace(/[\r\n\t]+/g, " ") : "";
|
|
45
|
+
const line = [
|
|
46
|
+
new Date().toISOString(),
|
|
47
|
+
level,
|
|
48
|
+
entry.command ?? "(none)",
|
|
49
|
+
entry.route,
|
|
50
|
+
`exit=${entry.exitCode}`,
|
|
51
|
+
detail,
|
|
52
|
+
].join("\t");
|
|
53
|
+
appendFileSync(logPath, `${line}\n`);
|
|
54
|
+
} catch {
|
|
55
|
+
// Logging must never break the user command (fail-quietly contract).
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// Front-door `identity set` write handler (US4).
|
|
2
|
+
//
|
|
3
|
+
// `applyIdentitySet` is the testable core of routing `identity set` to the TS
|
|
4
|
+
// core: it decides the created/updated verb from the pre-write state and applies
|
|
5
|
+
// the ported `setIdentity` (US3) with a generated id/now. The pre-write backup
|
|
6
|
+
// that `openDatabaseForWrite` requires lives in `liveBackup.ts`. Only
|
|
7
|
+
// `identity set` routes here directly. Since CV22.DS7.TS4 plateau 6,
|
|
8
|
+
// `identity edit` shares this write: it drives $EDITOR, then applies the buffer
|
|
9
|
+
// through `applyIdentitySet` so both commands report the same created/updated
|
|
10
|
+
// verb from the same pre-write check.
|
|
11
|
+
|
|
12
|
+
import type { WritableDatabase } from "#db/database.ts";
|
|
13
|
+
import { setIdentity } from "#identity/setIdentity.ts";
|
|
14
|
+
|
|
15
|
+
export interface IdentitySetOutcome {
|
|
16
|
+
action: "created" | "updated";
|
|
17
|
+
layer: string;
|
|
18
|
+
key: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Apply `identity set`: upsert content (metadata inherited), reporting the verb. */
|
|
22
|
+
export function applyIdentitySet(
|
|
23
|
+
db: WritableDatabase,
|
|
24
|
+
params: { layer: string; key: string; content: string; id: string; nowIso: string },
|
|
25
|
+
): IdentitySetOutcome {
|
|
26
|
+
const existing =
|
|
27
|
+
db
|
|
28
|
+
.prepare("SELECT id FROM identity WHERE layer = ? AND key = ?")
|
|
29
|
+
.get(params.layer, params.key) !== undefined;
|
|
30
|
+
setIdentity(
|
|
31
|
+
db,
|
|
32
|
+
{ id: params.id, layer: params.layer, key: params.key, content: params.content },
|
|
33
|
+
params.nowIso,
|
|
34
|
+
);
|
|
35
|
+
return { action: existing ? "updated" : "created", layer: params.layer, key: params.key };
|
|
36
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Front-door `journey set-path` write handler (US5).
|
|
2
|
+
//
|
|
3
|
+
// applyJourneySetPath is the testable core of routing `journey set-path` to the TS
|
|
4
|
+
// core: it normalizes the path the way Python does, then delegates to the ported
|
|
5
|
+
// US2 setProjectPath over the US4 live-write seam. setProjectPath throws when the
|
|
6
|
+
// journey does not exist; the cli wrapper maps that to Python's not-found output.
|
|
7
|
+
|
|
8
|
+
import type { WritableDatabase } from "#db/database.ts";
|
|
9
|
+
import { setProjectPath } from "#journey/journeyWrite.ts";
|
|
10
|
+
import { normalizeProjectPath } from "#util/paths.ts";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Apply `journey set-path`: normalize `rawPath` (expanduser + resolve, matching
|
|
14
|
+
* Python), write it into the journey's `project_path` metadata via setProjectPath,
|
|
15
|
+
* and return the resolved path. Throws `journey not found: <slug>` when absent.
|
|
16
|
+
*/
|
|
17
|
+
export function applyJourneySetPath(
|
|
18
|
+
db: WritableDatabase,
|
|
19
|
+
slug: string,
|
|
20
|
+
rawPath: string,
|
|
21
|
+
nowIso: string,
|
|
22
|
+
): string {
|
|
23
|
+
const resolved = normalizeProjectPath(rawPath);
|
|
24
|
+
setProjectPath(db, slug, resolved, nowIso);
|
|
25
|
+
return resolved;
|
|
26
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
// The ES-001 metadata-lifecycle WRITE faces at the front door
|
|
2
|
+
// (CV22.DS7.TS4 plateau 7).
|
|
3
|
+
//
|
|
4
|
+
// `conversations --metadata-lifecycle-apply <id> [--title/--summary/--tag]` and
|
|
5
|
+
// `--metadata-lifecycle-demo`. Both print `json.dumps(report, ensure_ascii=
|
|
6
|
+
// False, indent=2)`, so the rendering is `JSON.stringify(report, null, 2)` plus
|
|
7
|
+
// the newline `print` adds, and key ORDER is part of those bytes.
|
|
8
|
+
//
|
|
9
|
+
// The demo builds its own throwaway world. Python's is `sqlite3.connect(
|
|
10
|
+
// ":memory:")` with the schema applied; here it is a bootstrapped temp file
|
|
11
|
+
// removed on the way out, because `node:sqlite`'s in-memory handle cannot be
|
|
12
|
+
// given the migration ledger the bootstrap writes. Either way the claim it
|
|
13
|
+
// prints — `uses_production_data: false` — stays literally true: the caller's
|
|
14
|
+
// database is opened, never written, and never read by the demo.
|
|
15
|
+
|
|
16
|
+
import { mkdtempSync, rmSync } from "node:fs";
|
|
17
|
+
import { tmpdir } from "node:os";
|
|
18
|
+
import { join } from "node:path";
|
|
19
|
+
import { LifecycleFaceError } from "#conversation/lifecycleFaces.ts";
|
|
20
|
+
import {
|
|
21
|
+
applyMetadataLifecycle,
|
|
22
|
+
type DemoSeed,
|
|
23
|
+
metadataLifecycleDemoReport,
|
|
24
|
+
writeTitle,
|
|
25
|
+
} from "#conversation/lifecycleWrites.ts";
|
|
26
|
+
import { bootstrapDatabase } from "#db/bootstrap.ts";
|
|
27
|
+
import type { WritableDatabase } from "#db/database.ts";
|
|
28
|
+
import { newId, nowIso } from "#util/pyGenerators.ts";
|
|
29
|
+
|
|
30
|
+
/** Python's `print(json.dumps(report, ensure_ascii=False, indent=2))`. */
|
|
31
|
+
function printReport(report: unknown): number {
|
|
32
|
+
process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
|
|
33
|
+
return 0;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function optionValue(argv: readonly string[], flag: string): string | undefined {
|
|
37
|
+
const index = argv.indexOf(flag);
|
|
38
|
+
return index >= 0 ? argv[index + 1] : undefined;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** `--tag` is singular and REPEATABLE (`dest="tags"`, `action="append"`). */
|
|
42
|
+
function tagValues(argv: readonly string[]): string[] | undefined {
|
|
43
|
+
const tags: string[] = [];
|
|
44
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
45
|
+
if (argv[index] !== "--tag") continue;
|
|
46
|
+
const value = argv[index + 1];
|
|
47
|
+
if (value !== undefined) tags.push(value);
|
|
48
|
+
}
|
|
49
|
+
return tags.length > 0 ? tags : undefined;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function runLifecycleWriteRoute(db: WritableDatabase, args: readonly string[]): number {
|
|
53
|
+
if (args.includes("--metadata-lifecycle-demo")) return runDemo();
|
|
54
|
+
|
|
55
|
+
const conversationId = optionValue(args, "--metadata-lifecycle-apply");
|
|
56
|
+
if (conversationId === undefined) {
|
|
57
|
+
console.error("Mirror TS front door: --metadata-lifecycle-apply requires a conversation id");
|
|
58
|
+
return 2;
|
|
59
|
+
}
|
|
60
|
+
const title = optionValue(args, "--title");
|
|
61
|
+
const summary = optionValue(args, "--summary");
|
|
62
|
+
const tags = tagValues(args);
|
|
63
|
+
try {
|
|
64
|
+
return printReport(
|
|
65
|
+
applyMetadataLifecycle(db, conversationId, {
|
|
66
|
+
...(title !== undefined ? { title } : {}),
|
|
67
|
+
...(summary !== undefined ? { summary } : {}),
|
|
68
|
+
...(tags !== undefined ? { tags } : {}),
|
|
69
|
+
}),
|
|
70
|
+
);
|
|
71
|
+
} catch (error) {
|
|
72
|
+
if (!(error instanceof LifecycleFaceError)) throw error;
|
|
73
|
+
// Python raises ValueError and the traceback escapes: same exit code, same
|
|
74
|
+
// stream, one line.
|
|
75
|
+
console.error(error.message);
|
|
76
|
+
return 1;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function runDemo(): number {
|
|
81
|
+
const root = mkdtempSync(join(tmpdir(), "mirror-lifecycle-demo-"));
|
|
82
|
+
try {
|
|
83
|
+
const db = bootstrapDatabase(join(root, "memory_test.db"));
|
|
84
|
+
try {
|
|
85
|
+
const seed: DemoSeed = {
|
|
86
|
+
startConversation: (interfaceName, title) => {
|
|
87
|
+
const id = newId();
|
|
88
|
+
db.prepare(
|
|
89
|
+
"INSERT INTO conversations (id, interface, title, started_at) VALUES (?, ?, ?, ?)",
|
|
90
|
+
).run(id, interfaceName, title ?? null, nowIso());
|
|
91
|
+
return id;
|
|
92
|
+
},
|
|
93
|
+
addMessage: (conversationId, role, content) => {
|
|
94
|
+
db.prepare(
|
|
95
|
+
"INSERT INTO messages (id, conversation_id, role, content, created_at) " +
|
|
96
|
+
"VALUES (?, ?, ?, ?, ?)",
|
|
97
|
+
).run(newId(), conversationId, role, content, nowIso());
|
|
98
|
+
},
|
|
99
|
+
setProvisionalTitle: (conversationId, title) =>
|
|
100
|
+
writeTitle(db, conversationId, title, "provisional"),
|
|
101
|
+
updateTitle: (conversationId, title) => writeTitle(db, conversationId, title, "manual"),
|
|
102
|
+
};
|
|
103
|
+
return printReport(metadataLifecycleDemoReport(db, seed));
|
|
104
|
+
} finally {
|
|
105
|
+
db.close();
|
|
106
|
+
}
|
|
107
|
+
} finally {
|
|
108
|
+
rmSync(root, { recursive: true, force: true });
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// Pre-write backup and restore for the front door's sanctioned live writes.
|
|
2
|
+
//
|
|
3
|
+
// Every routed live write takes a fresh snapshot first; `openDatabaseForWrite`
|
|
4
|
+
// refuses to open without a hash-verified record of it. The snapshot is taken
|
|
5
|
+
// with `VACUUM INTO` (WAL-correct — see `snapshotDatabaseTo`), stored under the
|
|
6
|
+
// mirror home's `backups/` directory (the same convention the Python backup
|
|
7
|
+
// tooling uses), and permission-tightened to owner-only.
|
|
8
|
+
//
|
|
9
|
+
// Overwrite-window semantics, stated plainly: the backup file has a FIXED name
|
|
10
|
+
// and is replaced before EVERY write, so it always holds the state immediately
|
|
11
|
+
// before the most recent routed write — and nothing older. If a write corrupts
|
|
12
|
+
// data and another routed write runs before anyone notices, the corrupted
|
|
13
|
+
// state becomes the backed-up state. The backup is a last-write undo, not an
|
|
14
|
+
// archive; scheduled archives remain `mm-backup`'s job.
|
|
15
|
+
//
|
|
16
|
+
// Concurrent writers (CV22.DS10.TS5, finding F21). Until F21 every writer
|
|
17
|
+
// removed the fixed file and recreated it in place with `VACUUM INTO`, with no
|
|
18
|
+
// coordination. Two writers at the same instant broke each other: the second
|
|
19
|
+
// `VACUUM INTO` found the tables the first was still creating ("table
|
|
20
|
+
// conversations already exists"), or one removed the file the other was still
|
|
21
|
+
// writing ("disk I/O error"), and the losing write was aborted. Claude Code
|
|
22
|
+
// runs its two prompt hooks at once and both write, so the Navigator walk lost
|
|
23
|
+
// two injections in three prompts; Pi had been losing turns in daily use.
|
|
24
|
+
//
|
|
25
|
+
// So each writer now snapshots into a staging file of its own, the gate
|
|
26
|
+
// verifies THAT file -- which no other writer can touch -- and only then is it
|
|
27
|
+
// promoted to the fixed name by an atomic rename. The fixed name keeps its
|
|
28
|
+
// meaning (the state before the most recently promoted write) and can no
|
|
29
|
+
// longer be observed half-written. Under true concurrency "the most recent
|
|
30
|
+
// write" is whichever promoted last; nothing stronger was ever true.
|
|
31
|
+
//
|
|
32
|
+
// One residue remains possible: a writer KILLED between its snapshot and its
|
|
33
|
+
// rename leaves its staging file, the size of the database. Each write sweeps
|
|
34
|
+
// those before taking its own -- a staging file whose process no longer runs
|
|
35
|
+
// and which is older than any snapshot takes (TS5 handoff review, finding N4).
|
|
36
|
+
|
|
37
|
+
import { chmodSync, copyFileSync, mkdirSync, renameSync, rmSync } from "node:fs";
|
|
38
|
+
import { dirname, join } from "node:path";
|
|
39
|
+
import { stagingToken, sweepAbandoned } from "#backup/staging.ts";
|
|
40
|
+
import { type BackupRecord, requireBackup, sha256File } from "#db/backupGate.ts";
|
|
41
|
+
import { openDatabaseForWrite, snapshotDatabaseTo, type WritableDatabase } from "#db/database.ts";
|
|
42
|
+
|
|
43
|
+
const BACKUP_DIR_NAME = "backups";
|
|
44
|
+
const BACKUP_FILE_NAME = "frontdoor-pre-write-backup.db";
|
|
45
|
+
|
|
46
|
+
/** Where the pre-write backup for a given live database lives. */
|
|
47
|
+
export function backupPathFor(dbPath: string): string {
|
|
48
|
+
return join(dirname(dbPath), BACKUP_DIR_NAME, BACKUP_FILE_NAME);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** The name a writer gives its staging file: `<fixed name>.<pid>-<12 hex>.staging`. */
|
|
52
|
+
const STAGING_FILE = /^frontdoor-pre-write-backup\.db\.(\d+)-[0-9a-f]{12}\.staging$/;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Open the live database for ONE routed write, behind a verified pre-write
|
|
56
|
+
* snapshot. The only sanctioned way to open a live `memory.db` for writing.
|
|
57
|
+
*
|
|
58
|
+
* 1. Snapshot into a staging file unique to this process and call
|
|
59
|
+
* (WAL-correct via `VACUUM INTO`; owner-only: 0700 dir, 0600 file).
|
|
60
|
+
* 2. The backup gate verifies the staging file's hash, and the database opens.
|
|
61
|
+
* 3. The staging file is promoted to the fixed name by an atomic rename.
|
|
62
|
+
*
|
|
63
|
+
* A failure at any step aborts the write and removes the staging file, so a
|
|
64
|
+
* failed snapshot never leaves a half-written file under the fixed name. A
|
|
65
|
+
* staging file a KILLED writer left behind is swept first (finding N4).
|
|
66
|
+
*/
|
|
67
|
+
export function openLiveWriteDatabase(dbPath: string): WritableDatabase {
|
|
68
|
+
const backupPath = backupPathFor(dbPath);
|
|
69
|
+
mkdirSync(dirname(backupPath), { recursive: true, mode: 0o700 });
|
|
70
|
+
sweepAbandoned(dirname(backupPath), STAGING_FILE);
|
|
71
|
+
const staging = `${backupPath}.${stagingToken()}.staging`;
|
|
72
|
+
let db: WritableDatabase | null = null;
|
|
73
|
+
try {
|
|
74
|
+
snapshotDatabaseTo(dbPath, staging);
|
|
75
|
+
chmodSync(staging, 0o600);
|
|
76
|
+
db = openDatabaseForWrite(dbPath, { path: staging, sha256: sha256File(staging) });
|
|
77
|
+
renameSync(staging, backupPath);
|
|
78
|
+
return db;
|
|
79
|
+
} catch (error) {
|
|
80
|
+
db?.close();
|
|
81
|
+
throw error;
|
|
82
|
+
} finally {
|
|
83
|
+
// A no-op after the rename; the cleanup when anything before it failed.
|
|
84
|
+
rmSync(staging, { force: true });
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Restore the live database from a verified pre-write backup: re-verify the
|
|
90
|
+
* record's hash, copy the snapshot over the live file, and remove stale
|
|
91
|
+
* `-wal`/`-shm` sidecars so SQLite cannot replay post-backup pages over the
|
|
92
|
+
* restored state. Callers must ensure nothing holds the database open.
|
|
93
|
+
*/
|
|
94
|
+
export function restoreFromBackup(backup: BackupRecord, dbPath: string): void {
|
|
95
|
+
requireBackup(backup);
|
|
96
|
+
copyFileSync(backup.path, dbPath);
|
|
97
|
+
rmSync(`${dbPath}-wal`, { force: true });
|
|
98
|
+
rmSync(`${dbPath}-shm`, { force: true });
|
|
99
|
+
chmodSync(dbPath, 0o600);
|
|
100
|
+
}
|