mirror-mind 0.0.1 → 0.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/hooks/log-assistant-reply.sh +85 -0
- package/.claude/hooks/log-session-end.sh +85 -0
- package/.claude/hooks/log-user-prompt.sh +85 -0
- package/.claude/hooks/mirror-inject.sh +85 -0
- package/.claude/hooks/session-start.sh +85 -0
- package/.gemini/hooks/log-assistant.sh +85 -0
- package/.gemini/hooks/log-user.sh +85 -0
- package/.gemini/hooks/session-end.sh +85 -0
- package/.gemini/hooks/session-start.sh +85 -0
- package/.pi/extensions/mirror-logger.ts +646 -0
- package/.pi/skills/mm-backup/SKILL.md +21 -0
- package/.pi/skills/mm-build/SKILL.md +1206 -0
- package/.pi/skills/mm-consolidate/SKILL.md +112 -0
- package/.pi/skills/mm-consult/SKILL.md +70 -0
- package/.pi/skills/mm-conversations/SKILL.md +16 -0
- package/.pi/skills/mm-discard/SKILL.md +39 -0
- package/.pi/skills/mm-explore/SKILL.md +255 -0
- package/.pi/skills/mm-help/SKILL.md +74 -0
- package/.pi/skills/mm-identity/SKILL.md +96 -0
- package/.pi/skills/mm-journal/SKILL.md +16 -0
- package/.pi/skills/mm-journey/SKILL.md +23 -0
- package/.pi/skills/mm-journeys/SKILL.md +15 -0
- package/.pi/skills/mm-memories/SKILL.md +17 -0
- package/.pi/skills/mm-mirror/SKILL.md +98 -0
- package/.pi/skills/mm-mute/SKILL.md +18 -0
- package/.pi/skills/mm-new/SKILL.md +18 -0
- package/.pi/skills/mm-recall/SKILL.md +17 -0
- package/.pi/skills/mm-release-notes/SKILL.md +52 -0
- package/.pi/skills/mm-seed/SKILL.md +19 -0
- package/.pi/skills/mm-shadow/SKILL.md +117 -0
- package/.pi/skills/mm-soul/SKILL.md +502 -0
- package/.pi/skills/mm-tasks/SKILL.md +24 -0
- package/.pi/skills/mm-update/SKILL.md +32 -0
- package/.pi/skills/mm-week/SKILL.md +28 -0
- package/.pi/skills/mm-welcome/SKILL.md +24 -0
- package/AGENTS.md +188 -0
- package/README.md +126 -9
- package/bin/loader.js +67 -0
- package/bin/mirror-hook.js +7 -0
- package/bin/mirror.js +7 -0
- package/docs/releases/index.md +100 -0
- package/docs/releases/pending-cutoffs.md +510 -0
- package/docs/releases/v0.10.0.md +123 -0
- package/docs/releases/v0.10.1.md +66 -0
- package/docs/releases/v0.10.10.md +51 -0
- package/docs/releases/v0.10.11.md +39 -0
- package/docs/releases/v0.10.12.md +57 -0
- package/docs/releases/v0.10.13.md +35 -0
- package/docs/releases/v0.10.14.md +42 -0
- package/docs/releases/v0.10.15.md +53 -0
- package/docs/releases/v0.10.16.md +35 -0
- package/docs/releases/v0.10.17.md +43 -0
- package/docs/releases/v0.10.2.md +64 -0
- package/docs/releases/v0.10.3.md +78 -0
- package/docs/releases/v0.10.4.md +42 -0
- package/docs/releases/v0.10.5.md +57 -0
- package/docs/releases/v0.10.6.md +47 -0
- package/docs/releases/v0.10.7.md +53 -0
- package/docs/releases/v0.10.8.md +49 -0
- package/docs/releases/v0.10.9.md +37 -0
- package/docs/releases/v0.11.0.md +86 -0
- package/docs/releases/v0.12.0.md +67 -0
- package/docs/releases/v0.13.0.md +64 -0
- package/docs/releases/v0.14.0.md +68 -0
- package/docs/releases/v0.15.0.md +48 -0
- package/docs/releases/v0.16.0.md +50 -0
- package/docs/releases/v0.16.1.md +55 -0
- package/docs/releases/v0.16.2.md +36 -0
- package/docs/releases/v0.17.0.md +50 -0
- package/docs/releases/v0.17.1.md +58 -0
- package/docs/releases/v0.17.2.md +42 -0
- package/docs/releases/v0.17.3.md +58 -0
- package/docs/releases/v0.18.0.md +84 -0
- package/docs/releases/v0.18.1.md +51 -0
- package/docs/releases/v0.18.2.md +69 -0
- package/docs/releases/v0.19.0.md +85 -0
- package/docs/releases/v0.19.1.md +46 -0
- package/docs/releases/v0.20.0.md +85 -0
- package/docs/releases/v0.21.0.md +61 -0
- package/docs/releases/v0.21.1.md +47 -0
- package/docs/releases/v0.22.0.md +77 -0
- package/docs/releases/v0.22.1.md +42 -0
- package/docs/releases/v0.23.0.md +105 -0
- package/docs/releases/v0.24.0.md +125 -0
- package/docs/releases/v0.25.0.md +107 -0
- package/docs/releases/v0.26.0.md +139 -0
- package/docs/releases/v0.26.1.md +65 -0
- package/docs/releases/v0.26.2.md +43 -0
- package/docs/releases/v0.27.0.md +63 -0
- package/docs/releases/v0.28.0.md +59 -0
- package/docs/releases/v0.29.0.md +61 -0
- package/docs/releases/v0.29.1.md +59 -0
- package/docs/releases/v0.29.2.md +59 -0
- package/docs/releases/v0.29.3.md +93 -0
- package/docs/releases/v0.30.0.md +75 -0
- package/docs/releases/v0.30.1.md +93 -0
- package/docs/releases/v0.30.2.md +66 -0
- package/docs/releases/v0.31.0.md +122 -0
- package/docs/releases/v0.31.1.md +142 -0
- package/docs/releases/v0.31.10.md +118 -0
- package/docs/releases/v0.31.11.md +88 -0
- package/docs/releases/v0.31.12.md +105 -0
- package/docs/releases/v0.31.13.md +99 -0
- package/docs/releases/v0.31.14.md +97 -0
- package/docs/releases/v0.31.2.md +115 -0
- package/docs/releases/v0.31.3.md +142 -0
- package/docs/releases/v0.31.4.md +117 -0
- package/docs/releases/v0.31.5.md +155 -0
- package/docs/releases/v0.31.6.md +132 -0
- package/docs/releases/v0.31.7.md +132 -0
- package/docs/releases/v0.31.8.md +120 -0
- package/docs/releases/v0.31.9.md +120 -0
- package/docs/releases/v0.32.0.md +261 -0
- package/docs/releases/v0.32.1.md +59 -0
- package/docs/releases/v0.33.0.md +164 -0
- package/docs/releases/v0.8.0.md +118 -0
- package/docs/releases/v0.9.0.md +98 -0
- package/docs/releases/v0.9.1.md +83 -0
- package/package.json +102 -6
- package/plugins/mirror-mind/.claude-plugin/plugin.json +17 -0
- package/plugins/mirror-mind/hooks/hooks.json +62 -0
- package/plugins/mirror-mind/hooks/log-assistant-reply.sh +98 -0
- package/plugins/mirror-mind/hooks/log-session-end.sh +98 -0
- package/plugins/mirror-mind/hooks/log-user-prompt.sh +98 -0
- package/plugins/mirror-mind/hooks/mirror-inject.sh +98 -0
- package/plugins/mirror-mind/hooks/session-start.sh +98 -0
- package/plugins/mirror-mind/skills/mm-backup/SKILL.md +27 -0
- package/plugins/mirror-mind/skills/mm-build/SKILL.md +1206 -0
- package/plugins/mirror-mind/skills/mm-consolidate/SKILL.md +112 -0
- package/plugins/mirror-mind/skills/mm-consult/SKILL.md +83 -0
- package/plugins/mirror-mind/skills/mm-conversations/SKILL.md +17 -0
- package/plugins/mirror-mind/skills/mm-discard/SKILL.md +40 -0
- package/plugins/mirror-mind/skills/mm-explore/SKILL.md +241 -0
- package/plugins/mirror-mind/skills/mm-help/SKILL.md +104 -0
- package/plugins/mirror-mind/skills/mm-identity/SKILL.md +96 -0
- package/plugins/mirror-mind/skills/mm-journal/SKILL.md +21 -0
- package/plugins/mirror-mind/skills/mm-journey/SKILL.md +37 -0
- package/plugins/mirror-mind/skills/mm-journeys/SKILL.md +15 -0
- package/plugins/mirror-mind/skills/mm-memories/SKILL.md +20 -0
- package/plugins/mirror-mind/skills/mm-mirror/SKILL.md +67 -0
- package/plugins/mirror-mind/skills/mm-mute/SKILL.md +18 -0
- package/plugins/mirror-mind/skills/mm-new/SKILL.md +18 -0
- package/plugins/mirror-mind/skills/mm-recall/SKILL.md +17 -0
- package/plugins/mirror-mind/skills/mm-release-notes/SKILL.md +31 -0
- package/plugins/mirror-mind/skills/mm-seed/SKILL.md +15 -0
- package/plugins/mirror-mind/skills/mm-shadow/SKILL.md +117 -0
- package/plugins/mirror-mind/skills/mm-soul/SKILL.md +488 -0
- package/plugins/mirror-mind/skills/mm-tasks/SKILL.md +36 -0
- package/plugins/mirror-mind/skills/mm-update/SKILL.md +30 -0
- package/plugins/mirror-mind/skills/mm-week/SKILL.md +46 -0
- package/plugins/mirror-mind/skills/mm-welcome/SKILL.md +22 -0
- package/scripts/codex-hooks/session-end.sh +85 -0
- package/scripts/codex-hooks/session-start.sh +85 -0
- package/scripts/codex-mirror.sh +48 -0
- package/templates/identity/README.md +119 -0
- package/templates/identity/ego/behavior.yaml +52 -0
- package/templates/identity/ego/constraints.yaml +15 -0
- package/templates/identity/ego/identity.yaml +43 -0
- package/templates/identity/journeys/personal-growth.yaml +39 -0
- package/templates/identity/organization/identity.yaml +24 -0
- package/templates/identity/organization/principles.yaml +17 -0
- package/templates/identity/personas/coach.yaml +94 -0
- package/templates/identity/personas/designer.yaml +93 -0
- package/templates/identity/personas/doctor.yaml +96 -0
- package/templates/identity/personas/engineer.yaml +77 -0
- package/templates/identity/personas/financial.yaml +97 -0
- package/templates/identity/personas/prompt-engineer.yaml +122 -0
- package/templates/identity/personas/researcher.yaml +88 -0
- package/templates/identity/personas/strategist.yaml +91 -0
- package/templates/identity/personas/teacher.yaml +95 -0
- package/templates/identity/personas/therapist.yaml +91 -0
- package/templates/identity/personas/thinker.yaml +74 -0
- package/templates/identity/personas/writer.yaml +76 -0
- package/templates/identity/self/config.yaml +16 -0
- package/templates/identity/self/soul.yaml +105 -0
- package/templates/identity/user/identity.yaml +30 -0
- package/ts/src/backup/staging.ts +62 -0
- package/ts/src/backup/zipBackup.ts +267 -0
- package/ts/src/backup/zipReader.ts +109 -0
- package/ts/src/backup/zipWriter.ts +148 -0
- package/ts/src/builder/activeCheckpoint.ts +134 -0
- package/ts/src/builder/aggregateStatus.ts +34 -0
- package/ts/src/builder/approve.ts +149 -0
- package/ts/src/builder/argv.ts +292 -0
- package/ts/src/builder/ariadMethod.ts +536 -0
- package/ts/src/builder/artifacts/artifactSurfaces.ts +106 -0
- package/ts/src/builder/artifacts/artifactWriter.ts +113 -0
- package/ts/src/builder/artifacts/closureArtifacts.ts +199 -0
- package/ts/src/builder/artifacts/deliveryStoryArtifacts.ts +316 -0
- package/ts/src/builder/artifacts/planArtifacts.ts +242 -0
- package/ts/src/builder/artifacts/scaffoldSections.ts +273 -0
- package/ts/src/builder/artifacts/scaffoldState.ts +227 -0
- package/ts/src/builder/artifacts/storyFiles.ts +61 -0
- package/ts/src/builder/artifacts/storyIndex.ts +166 -0
- package/ts/src/builder/cadence.ts +59 -0
- package/ts/src/builder/card.ts +192 -0
- package/ts/src/builder/checkpointRefused.ts +66 -0
- package/ts/src/builder/closure.ts +964 -0
- package/ts/src/builder/commands.ts +2053 -0
- package/ts/src/builder/cursorPosition.ts +176 -0
- package/ts/src/builder/cursorTransitions.ts +77 -0
- package/ts/src/builder/deliveryCursor.ts +577 -0
- package/ts/src/builder/deliveryStoryClosure.ts +521 -0
- package/ts/src/builder/deliveryStoryPlan.ts +536 -0
- package/ts/src/builder/deliveryStoryReady.ts +160 -0
- package/ts/src/builder/deliveryStoryRoadmapClosure.ts +151 -0
- package/ts/src/builder/expand.ts +336 -0
- package/ts/src/builder/flowUnit.ts +339 -0
- package/ts/src/builder/homeSurface.ts +122 -0
- package/ts/src/builder/implementationGuard.ts +125 -0
- package/ts/src/builder/index.ts +11 -0
- package/ts/src/builder/lifecycleRefusal.ts +92 -0
- package/ts/src/builder/lifecycleRibbon.ts +159 -0
- package/ts/src/builder/load.ts +313 -0
- package/ts/src/builder/methodAdoption.ts +113 -0
- package/ts/src/builder/methodDefinition.ts +429 -0
- package/ts/src/builder/methodInspection.ts +371 -0
- package/ts/src/builder/plan.ts +408 -0
- package/ts/src/builder/planPreauthorization.ts +265 -0
- package/ts/src/builder/prepare.ts +225 -0
- package/ts/src/builder/projectPaths.ts +64 -0
- package/ts/src/builder/pull.ts +356 -0
- package/ts/src/builder/pullCandidates.ts +463 -0
- package/ts/src/builder/pullCandidatesRender.ts +238 -0
- package/ts/src/builder/refinementField.ts +93 -0
- package/ts/src/builder/releaseIntent.ts +173 -0
- package/ts/src/builder/resumeState.ts +84 -0
- package/ts/src/builder/resumeSurface.ts +149 -0
- package/ts/src/builder/roadmapGrammar.ts +300 -0
- package/ts/src/builder/roadmapScan.ts +97 -0
- package/ts/src/builder/roadmapScope.ts +210 -0
- package/ts/src/builder/scopePhrases.ts +146 -0
- package/ts/src/builder/storyPaths.ts +196 -0
- package/ts/src/builder/storyPlanPreauthorization.ts +287 -0
- package/ts/src/builder/surfaceProtocol.ts +42 -0
- package/ts/src/builder/templateGeneration.ts +148 -0
- package/ts/src/builder/transition.ts +178 -0
- package/ts/src/consult/args.ts +99 -0
- package/ts/src/consult/core.ts +122 -0
- package/ts/src/consult/modelCatalog.ts +55 -0
- package/ts/src/consult/render.ts +47 -0
- package/ts/src/conversation/append.ts +414 -0
- package/ts/src/conversation/backfill.ts +387 -0
- package/ts/src/conversation/closeTail.ts +467 -0
- package/ts/src/conversation/extraction.ts +333 -0
- package/ts/src/conversation/extractionDriver.ts +130 -0
- package/ts/src/conversation/extractionRun.ts +70 -0
- package/ts/src/conversation/hookPayload.ts +51 -0
- package/ts/src/conversation/journeyInference.ts +165 -0
- package/ts/src/conversation/journeyRepair.ts +114 -0
- package/ts/src/conversation/lifecycleFaces.ts +197 -0
- package/ts/src/conversation/lifecycleWrites.ts +313 -0
- package/ts/src/conversation/listing.ts +77 -0
- package/ts/src/conversation/logger.ts +851 -0
- package/ts/src/conversation/loggerCli.ts +321 -0
- package/ts/src/conversation/loggerRuntime.ts +259 -0
- package/ts/src/conversation/metadataLifecycle.ts +443 -0
- package/ts/src/conversation/recall.ts +66 -0
- package/ts/src/conversation/sessionComposites.ts +258 -0
- package/ts/src/conversation/sessionImport.ts +90 -0
- package/ts/src/conversation/transcriptBackfill.ts +141 -0
- package/ts/src/cultivation/applyActions.ts +241 -0
- package/ts/src/cultivation/cluster.ts +86 -0
- package/ts/src/cultivation/consolidationStore.ts +281 -0
- package/ts/src/cultivation/promptContext.ts +62 -0
- package/ts/src/cultivation/propose.ts +305 -0
- package/ts/src/cultivation/scan.ts +155 -0
- package/ts/src/db/backupGate.ts +47 -0
- package/ts/src/db/bootstrap.ts +108 -0
- package/ts/src/db/bootstrapLock.ts +223 -0
- package/ts/src/db/copyGuard.ts +60 -0
- package/ts/src/db/database.ts +397 -0
- package/ts/src/db/decode.ts +66 -0
- package/ts/src/db/ftsIntegrity.ts +41 -0
- package/ts/src/db/migrateOnOpen.ts +214 -0
- package/ts/src/db/migrations.ts +576 -0
- package/ts/src/db/readyOnOpen.ts +35 -0
- package/ts/src/db/rowDecode.ts +42 -0
- package/ts/src/db/schema.ts +432 -0
- package/ts/src/db/schemaInventory.ts +225 -0
- package/ts/src/db/schemaInventorySnapshot.ts +2107 -0
- package/ts/src/db/schemaState.ts +91 -0
- package/ts/src/db/schemaTsDivergence.ts +114 -0
- package/ts/src/descriptor/descriptorRead.ts +49 -0
- package/ts/src/docs/docsLint.ts +265 -0
- package/ts/src/explorer/handoff.ts +522 -0
- package/ts/src/explorer/render.ts +335 -0
- package/ts/src/explorer/story.ts +769 -0
- package/ts/src/explorer/transition.ts +72 -0
- package/ts/src/extensions/bindings.ts +170 -0
- package/ts/src/extensions/catalog.ts +249 -0
- package/ts/src/extensions/catalogCommands.ts +513 -0
- package/ts/src/extensions/catalogWrites.ts +652 -0
- package/ts/src/extensions/contextRuntime.ts +325 -0
- package/ts/src/extensions/dispatch.ts +374 -0
- package/ts/src/extensions/errors.ts +24 -0
- package/ts/src/extensions/manifest.ts +356 -0
- package/ts/src/extensions/migrations.ts +350 -0
- package/ts/src/extraction/conversation.ts +304 -0
- package/ts/src/extraction/conversationMetadata.ts +206 -0
- package/ts/src/extraction/fencing.ts +106 -0
- package/ts/src/extraction/json.ts +17 -0
- package/ts/src/extraction/prompts.ts +470 -0
- package/ts/src/frontDoor/args.ts +30 -0
- package/ts/src/frontDoor/argvShape.ts +106 -0
- package/ts/src/frontDoor/buildLoadRuntime.ts +139 -0
- package/ts/src/frontDoor/buildRoute.ts +398 -0
- package/ts/src/frontDoor/cli.ts +1872 -0
- package/ts/src/frontDoor/cloneRoleGuard.ts +98 -0
- package/ts/src/frontDoor/consultRoute.ts +50 -0
- package/ts/src/frontDoor/contentTailRoute.ts +239 -0
- package/ts/src/frontDoor/conversationLoggerRoute.ts +83 -0
- package/ts/src/frontDoor/cultivationRoute.ts +210 -0
- package/ts/src/frontDoor/dbPath.ts +131 -0
- package/ts/src/frontDoor/dbSafetyToolsRoute.ts +240 -0
- package/ts/src/frontDoor/exploreRoute.ts +572 -0
- package/ts/src/frontDoor/extensionCatalogRoute.ts +252 -0
- package/ts/src/frontDoor/frontDoorLog.ts +57 -0
- package/ts/src/frontDoor/identityWrite.ts +36 -0
- package/ts/src/frontDoor/journeyWriteRoute.ts +26 -0
- package/ts/src/frontDoor/lifecycleWriteRoute.ts +110 -0
- package/ts/src/frontDoor/liveBackup.ts +100 -0
- package/ts/src/frontDoor/mirrorModeRoute.ts +163 -0
- package/ts/src/frontDoor/namedRefusal.ts +35 -0
- package/ts/src/frontDoor/nodeSupport.ts +24 -0
- package/ts/src/frontDoor/render/consolidate.ts +199 -0
- package/ts/src/frontDoor/render/conversations.ts +33 -0
- package/ts/src/frontDoor/render/cultivationShared.ts +21 -0
- package/ts/src/frontDoor/render/descriptor.ts +20 -0
- package/ts/src/frontDoor/render/detectPersona.ts +50 -0
- package/ts/src/frontDoor/render/icons.ts +13 -0
- package/ts/src/frontDoor/render/identity.ts +59 -0
- package/ts/src/frontDoor/render/identityRows.ts +17 -0
- package/ts/src/frontDoor/render/inspectPersona.ts +53 -0
- package/ts/src/frontDoor/render/journeyStatus.ts +78 -0
- package/ts/src/frontDoor/render/journeys.ts +79 -0
- package/ts/src/frontDoor/render/list.ts +41 -0
- package/ts/src/frontDoor/render/memories.ts +67 -0
- package/ts/src/frontDoor/render/recall.ts +49 -0
- package/ts/src/frontDoor/render/shadow.ts +155 -0
- package/ts/src/frontDoor/render/tasks.ts +99 -0
- package/ts/src/frontDoor/render/tasksImportSync.ts +63 -0
- package/ts/src/frontDoor/render/week.ts +113 -0
- package/ts/src/frontDoor/routing.ts +1001 -0
- package/ts/src/frontDoor/runtimeRoute.ts +623 -0
- package/ts/src/frontDoor/searchRoute.ts +166 -0
- package/ts/src/frontDoor/seedPaths.ts +98 -0
- package/ts/src/frontDoor/sessionStartWelcomeRoute.ts +166 -0
- package/ts/src/frontDoor/soulRoute.ts +433 -0
- package/ts/src/frontDoor/stdinSource.ts +37 -0
- package/ts/src/frontDoor/tasksImportSyncRoute.ts +110 -0
- package/ts/src/frontDoor/tasksWriteRoute.ts +101 -0
- package/ts/src/frontDoor/usage.ts +176 -0
- package/ts/src/guards/claudePlugin.ts +298 -0
- package/ts/src/guards/packContents.ts +306 -0
- package/ts/src/guards/piSourcedSkills.ts +70 -0
- package/ts/src/guards/retiredRouteShadows.ts +57 -0
- package/ts/src/guards/retiredSurfaces.ts +564 -0
- package/ts/src/hooks/claude.ts +214 -0
- package/ts/src/hooks/codex.ts +45 -0
- package/ts/src/hooks/gemini.ts +126 -0
- package/ts/src/hooks/main.ts +109 -0
- package/ts/src/hooks/mirrorState.ts +71 -0
- package/ts/src/hooks/payload.ts +73 -0
- package/ts/src/hooks/runtime.ts +179 -0
- package/ts/src/hooks/runtimeDetection.ts +34 -0
- package/ts/src/identity/applyConsolidationIdentityUpdate.ts +67 -0
- package/ts/src/identity/descriptorGenerate.ts +162 -0
- package/ts/src/identity/identityEdit.ts +143 -0
- package/ts/src/identity/identityKey.ts +77 -0
- package/ts/src/identity/identityRead.ts +69 -0
- package/ts/src/identity/identityStore.ts +80 -0
- package/ts/src/identity/journeyListing.ts +53 -0
- package/ts/src/identity/personaInspect.ts +44 -0
- package/ts/src/identity/personaListing.ts +37 -0
- package/ts/src/identity/setIdentity.ts +64 -0
- package/ts/src/index.ts +231 -0
- package/ts/src/init/init.ts +111 -0
- package/ts/src/journey/journeyOptions.ts +131 -0
- package/ts/src/journey/journeyRemoval.ts +158 -0
- package/ts/src/journey/journeyStatus.ts +149 -0
- package/ts/src/journey/journeySyncFile.ts +87 -0
- package/ts/src/journey/journeyWrite.ts +203 -0
- package/ts/src/journey/parentJourney.ts +41 -0
- package/ts/src/journey/validateParentJourney.ts +55 -0
- package/ts/src/mcp/boundary.ts +151 -0
- package/ts/src/mcp/guards.ts +244 -0
- package/ts/src/mcp/main.ts +121 -0
- package/ts/src/mcp/payload.ts +141 -0
- package/ts/src/mcp/protocol.ts +141 -0
- package/ts/src/mcp/readModels.ts +241 -0
- package/ts/src/mcp/registry.ts +201 -0
- package/ts/src/mcp/serve.ts +120 -0
- package/ts/src/mcp/tools/deterministic.ts +137 -0
- package/ts/src/mcp/tools/providerCrossing.ts +187 -0
- package/ts/src/mcp/wire.ts +8 -0
- package/ts/src/memory/journal.ts +180 -0
- package/ts/src/memory/listing.ts +107 -0
- package/ts/src/memory/memoryWrite.ts +88 -0
- package/ts/src/memory/reinforcement.ts +35 -0
- package/ts/src/mirror/context.ts +161 -0
- package/ts/src/mirror/defaultResolution.ts +199 -0
- package/ts/src/mirror/mirrorForm.ts +43 -0
- package/ts/src/mirror/orchestration.ts +364 -0
- package/ts/src/mirror/reception.ts +194 -0
- package/ts/src/mirror/render.ts +75 -0
- package/ts/src/mirror/runtimeSession.ts +205 -0
- package/ts/src/mode/operatingMode.ts +148 -0
- package/ts/src/observability/callOutcome.ts +115 -0
- package/ts/src/observability/inspectLedger.ts +329 -0
- package/ts/src/observability/ledgerHooks.ts +126 -0
- package/ts/src/observability/llmCalls.ts +154 -0
- package/ts/src/persona/detectPersona.ts +81 -0
- package/ts/src/planning/promptAssembly.ts +111 -0
- package/ts/src/planning/weekPending.ts +81 -0
- package/ts/src/planning/weekPlan.ts +183 -0
- package/ts/src/planning/weekSave.ts +117 -0
- package/ts/src/providers/config.ts +189 -0
- package/ts/src/providers/cost.ts +64 -0
- package/ts/src/providers/credits.ts +204 -0
- package/ts/src/providers/embedding.ts +321 -0
- package/ts/src/providers/familyProviders.ts +147 -0
- package/ts/src/providers/llm.ts +334 -0
- package/ts/src/providers/openrouter.ts +244 -0
- package/ts/src/providers/redaction.ts +110 -0
- package/ts/src/providers/replay.ts +15 -0
- package/ts/src/providers/transport.ts +410 -0
- package/ts/src/repair/encodingRepair.ts +253 -0
- package/ts/src/runtime/backup.ts +151 -0
- package/ts/src/runtime/channel.ts +146 -0
- package/ts/src/runtime/config.ts +168 -0
- package/ts/src/runtime/diagnose.ts +686 -0
- package/ts/src/runtime/git.ts +510 -0
- package/ts/src/runtime/installKind.ts +220 -0
- package/ts/src/runtime/migrate.ts +154 -0
- package/ts/src/runtime/packageIdentity.ts +102 -0
- package/ts/src/runtime/releaseNotes.ts +244 -0
- package/ts/src/runtime/status.ts +557 -0
- package/ts/src/runtime/strategies/clone.ts +200 -0
- package/ts/src/runtime/strategies/package.ts +328 -0
- package/ts/src/runtime/treeRoot.ts +30 -0
- package/ts/src/runtime/update.ts +445 -0
- package/ts/src/runtime/updateGate.ts +40 -0
- package/ts/src/runtime/updatePipeline.ts +159 -0
- package/ts/src/runtime/version.ts +36 -0
- package/ts/src/runtime/wiring.ts +130 -0
- package/ts/src/search/memorySearch.ts +338 -0
- package/ts/src/search/ranker.ts +154 -0
- package/ts/src/seed/seed.ts +256 -0
- package/ts/src/seed/seedYaml.ts +133 -0
- package/ts/src/soul/apply.ts +218 -0
- package/ts/src/soul/harvest.ts +267 -0
- package/ts/src/soul/prompts/soul_beauty_voice.md +103 -0
- package/ts/src/soul/prompts/soul_self_voice.md +219 -0
- package/ts/src/soul/prompts/soul_wisdom_voice.md +121 -0
- package/ts/src/soul/prompts.ts +85 -0
- package/ts/src/soul/render.ts +358 -0
- package/ts/src/soul/state.ts +212 -0
- package/ts/src/soul/transition.ts +37 -0
- package/ts/src/tasks/journeyPathParse.ts +113 -0
- package/ts/src/tasks/taskImportSync.ts +142 -0
- package/ts/src/tasks/taskStore.ts +312 -0
- package/ts/src/tasks/weekView.ts +120 -0
- package/ts/src/util/clipCodePoints.ts +26 -0
- package/ts/src/util/paths.ts +63 -0
- package/ts/src/util/program.ts +9 -0
- package/ts/src/util/pyGenerators.ts +144 -0
- package/ts/src/util/pythonText.ts +317 -0
- package/ts/src/util/semver.ts +45 -0
- package/ts/src/util/shellWord.ts +25 -0
- package/ts/src/util/slug.ts +64 -0
- package/ts/src/welcome/card.ts +339 -0
- package/ts/src/welcome/statusLine.ts +101 -0
- package/ts/src/welcome/updateCache.ts +129 -0
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
// Conversation metadata lifecycle decision policy (CV22.DS7.US10 slice C′).
|
|
2
|
+
//
|
|
3
|
+
// A faithful TypeScript port of `src/memory/services/metadata_lifecycle.py`,
|
|
4
|
+
// plus the `title_needs_improvement` predicate the engine receives as an
|
|
5
|
+
// injected callable (`ConversationService.title_needs_improvement`).
|
|
6
|
+
//
|
|
7
|
+
// This engine is what makes the close tail expensive: `end_conversation`
|
|
8
|
+
// finalizes non-manual metadata through it, and its per-field decisions —
|
|
9
|
+
// combined with the execution profile — determine whether a title, summary, or
|
|
10
|
+
// tag set is generated through the LLM at all. Getting a decision wrong here
|
|
11
|
+
// does not just diverge a report; it changes how many model calls the close
|
|
12
|
+
// tail makes.
|
|
13
|
+
//
|
|
14
|
+
// Like Python's module, this is pure: it reads no storage, no clock, and no
|
|
15
|
+
// provider. Callers supply the conversation, messages, and parsed metadata.
|
|
16
|
+
//
|
|
17
|
+
// Two regex fidelity notes, because Python and JavaScript disagree by default:
|
|
18
|
+
// * Python's `\w` is Unicode-aware in `str` patterns; JavaScript's is ASCII.
|
|
19
|
+
// Every ported pattern therefore spells the class out with `\p{L}\p{N}_`
|
|
20
|
+
// under the `u` flag, so accented content (routine in this corpus) is
|
|
21
|
+
// classified identically.
|
|
22
|
+
// * Python's `\b` is likewise Unicode-aware. The transcript probe replaces it
|
|
23
|
+
// with an explicit `(?<![\p{L}\p{N}_])` lookbehind rather than JS `\b`.
|
|
24
|
+
|
|
25
|
+
import { sortByCodePoint } from "#util/pythonText.ts";
|
|
26
|
+
|
|
27
|
+
/** The conversation fields the lifecycle policy reads. */
|
|
28
|
+
export interface ConversationLike {
|
|
29
|
+
id: string;
|
|
30
|
+
title?: string | null;
|
|
31
|
+
summary?: string | null;
|
|
32
|
+
tags?: string | null;
|
|
33
|
+
metadata?: string | null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** The message fields the lifecycle policy reads. */
|
|
37
|
+
export interface MessageLike {
|
|
38
|
+
role: string;
|
|
39
|
+
content: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Execution posture for applying metadata lifecycle decisions. */
|
|
43
|
+
export interface MetadataExecutionProfile {
|
|
44
|
+
name: string;
|
|
45
|
+
titleApplyDecisions: ReadonlySet<string>;
|
|
46
|
+
summaryApplyDecisions: ReadonlySet<string>;
|
|
47
|
+
tagsApplyDecisions: ReadonlySet<string>;
|
|
48
|
+
forceRegenerate: boolean;
|
|
49
|
+
preserveManual: boolean;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** One field's lifecycle decision report. Shape mirrors Python's dicts. */
|
|
53
|
+
export type FieldReport = Record<string, unknown>;
|
|
54
|
+
|
|
55
|
+
export interface MetadataLifecycleReport {
|
|
56
|
+
conversation_id: string;
|
|
57
|
+
mode: string;
|
|
58
|
+
mutated: boolean;
|
|
59
|
+
metadata_lifecycle_version: number;
|
|
60
|
+
fields: { title: FieldReport; summary: FieldReport; tags: FieldReport };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface LifecycleDeps {
|
|
64
|
+
titleNeedsImprovement: (conversation: ConversationLike) => boolean;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function profile(
|
|
68
|
+
name: string,
|
|
69
|
+
titleApply: string[],
|
|
70
|
+
summaryApply: string[],
|
|
71
|
+
tagsApply: string[],
|
|
72
|
+
forceRegenerate = false,
|
|
73
|
+
preserveManual = true,
|
|
74
|
+
): MetadataExecutionProfile {
|
|
75
|
+
return {
|
|
76
|
+
name,
|
|
77
|
+
titleApplyDecisions: new Set(titleApply),
|
|
78
|
+
summaryApplyDecisions: new Set(summaryApply),
|
|
79
|
+
tagsApplyDecisions: new Set(tagsApply),
|
|
80
|
+
forceRegenerate,
|
|
81
|
+
preserveManual,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export const METADATA_EXECUTION_PROFILES: Readonly<Record<string, MetadataExecutionProfile>> = {
|
|
86
|
+
manual_safe: profile("manual_safe", ["create", "repair"], ["create"], ["create"]),
|
|
87
|
+
close_time: profile(
|
|
88
|
+
"close_time",
|
|
89
|
+
["create", "repair", "keep", "refine_candidate"],
|
|
90
|
+
["create", "keep", "refine_candidate"],
|
|
91
|
+
["create", "keep"],
|
|
92
|
+
true,
|
|
93
|
+
),
|
|
94
|
+
active_runtime: profile("active_runtime", ["create"], [], []),
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
/** Return a named metadata execution profile. Mirrors Python's ValueError. */
|
|
98
|
+
export function metadataExecutionProfile(name: string): MetadataExecutionProfile {
|
|
99
|
+
const found = METADATA_EXECUTION_PROFILES[name];
|
|
100
|
+
if (!found) throw new Error(`Unknown metadata execution profile: ${name}`);
|
|
101
|
+
return found;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Return the profile action for one field report. */
|
|
105
|
+
export function metadataProfileAction(
|
|
106
|
+
executionProfile: MetadataExecutionProfile,
|
|
107
|
+
field: string,
|
|
108
|
+
report: FieldReport,
|
|
109
|
+
): string {
|
|
110
|
+
const decision = report.decision;
|
|
111
|
+
if (
|
|
112
|
+
field === "title" &&
|
|
113
|
+
report.lock_state === "manual_locked" &&
|
|
114
|
+
executionProfile.preserveManual
|
|
115
|
+
) {
|
|
116
|
+
return "preserve_manual";
|
|
117
|
+
}
|
|
118
|
+
if (decision === "defer") return "defer";
|
|
119
|
+
if (decision === "preserve") return "preserve_manual";
|
|
120
|
+
|
|
121
|
+
const applyDecisions =
|
|
122
|
+
field === "title"
|
|
123
|
+
? executionProfile.titleApplyDecisions
|
|
124
|
+
: field === "summary"
|
|
125
|
+
? executionProfile.summaryApplyDecisions
|
|
126
|
+
: field === "tags"
|
|
127
|
+
? executionProfile.tagsApplyDecisions
|
|
128
|
+
: null;
|
|
129
|
+
if (applyDecisions && typeof decision === "string" && applyDecisions.has(decision)) {
|
|
130
|
+
return executionProfile.forceRegenerate ? "regenerate" : "apply";
|
|
131
|
+
}
|
|
132
|
+
if (decision === "refine_candidate") return "review";
|
|
133
|
+
return "skip";
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** Return metadata lifecycle decisions without mutating conversation state. */
|
|
137
|
+
export function dryRunMetadataLifecycle(
|
|
138
|
+
conversation: ConversationLike,
|
|
139
|
+
messages: MessageLike[],
|
|
140
|
+
metadata: Record<string, unknown>,
|
|
141
|
+
deps: LifecycleDeps,
|
|
142
|
+
): MetadataLifecycleReport {
|
|
143
|
+
const titleReport = dryRunTitleLifecycle(conversation, messages, metadata, deps);
|
|
144
|
+
const summaryReport = dryRunSummaryLifecycle(conversation, messages);
|
|
145
|
+
const tagsReport = dryRunTagsLifecycle(conversation, messages);
|
|
146
|
+
return {
|
|
147
|
+
conversation_id: conversation.id,
|
|
148
|
+
mode: "dry_run",
|
|
149
|
+
mutated: false,
|
|
150
|
+
metadata_lifecycle_version: 1,
|
|
151
|
+
fields: { title: titleReport, summary: summaryReport, tags: tagsReport },
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** Return title lifecycle decision for a conversation. */
|
|
156
|
+
export function dryRunTitleLifecycle(
|
|
157
|
+
conversation: ConversationLike,
|
|
158
|
+
messages: MessageLike[],
|
|
159
|
+
metadata: Record<string, unknown>,
|
|
160
|
+
deps: LifecycleDeps,
|
|
161
|
+
): FieldReport {
|
|
162
|
+
const title = (conversation.title ?? "").trim();
|
|
163
|
+
const lockState = titleIsManual(metadata) ? "manual_locked" : "unlocked";
|
|
164
|
+
|
|
165
|
+
if (lockState === "manual_locked") {
|
|
166
|
+
return {
|
|
167
|
+
decision: "preserve",
|
|
168
|
+
reason: "manual title lock is preserved",
|
|
169
|
+
current_value: title || null,
|
|
170
|
+
readiness: "locked",
|
|
171
|
+
provenance: metadata.title_source ?? "manual",
|
|
172
|
+
lock_state: lockState,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
if (!messagesAreTitleable(messages)) {
|
|
176
|
+
return {
|
|
177
|
+
decision: "defer",
|
|
178
|
+
reason: "conversation needs at least one user and one assistant message",
|
|
179
|
+
current_value: title || null,
|
|
180
|
+
readiness: "not_ready",
|
|
181
|
+
provenance: metadata.title_source ?? null,
|
|
182
|
+
lock_state: lockState,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
let confidence: string | null = null;
|
|
187
|
+
let decision: string;
|
|
188
|
+
let reason: string;
|
|
189
|
+
|
|
190
|
+
if (!title) {
|
|
191
|
+
decision = "create";
|
|
192
|
+
reason = "conversation has no title";
|
|
193
|
+
} else if (deps.titleNeedsImprovement(conversation)) {
|
|
194
|
+
decision = "repair";
|
|
195
|
+
reason = "current title is provisional or weak";
|
|
196
|
+
} else {
|
|
197
|
+
const refinementEvidence = titleRefinementEvidence(conversation);
|
|
198
|
+
if (refinementEvidence) {
|
|
199
|
+
return {
|
|
200
|
+
decision: "refine_candidate",
|
|
201
|
+
reason: "later evidence is more specific than the current unlocked title",
|
|
202
|
+
current_value: title || null,
|
|
203
|
+
readiness: "ready",
|
|
204
|
+
provenance: metadata.title_source ?? null,
|
|
205
|
+
lock_state: lockState,
|
|
206
|
+
confidence: refinementEvidence.confidence,
|
|
207
|
+
evidence: refinementEvidence,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
if (titleMayNeedCoherenceRefinement(messages, metadata)) {
|
|
211
|
+
decision = "refine_candidate";
|
|
212
|
+
reason = "conversation has enough later context for coherence refinement";
|
|
213
|
+
confidence = "low";
|
|
214
|
+
} else {
|
|
215
|
+
decision = "keep";
|
|
216
|
+
reason = "current title appears usable";
|
|
217
|
+
confidence = null;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const report: FieldReport = {
|
|
222
|
+
decision,
|
|
223
|
+
reason,
|
|
224
|
+
current_value: title || null,
|
|
225
|
+
readiness: "ready",
|
|
226
|
+
provenance: metadata.title_source ?? null,
|
|
227
|
+
lock_state: lockState,
|
|
228
|
+
};
|
|
229
|
+
if (confidence) report.confidence = confidence;
|
|
230
|
+
return report;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/** Return summary lifecycle decision for a conversation. */
|
|
234
|
+
export function dryRunSummaryLifecycle(
|
|
235
|
+
conversation: ConversationLike,
|
|
236
|
+
messages: MessageLike[],
|
|
237
|
+
): FieldReport {
|
|
238
|
+
const summary = (conversation.summary ?? "").trim();
|
|
239
|
+
if (summary) {
|
|
240
|
+
const qualityIssues = summaryQualityIssues(summary);
|
|
241
|
+
if (qualityIssues.length > 0) {
|
|
242
|
+
return {
|
|
243
|
+
decision: "refine_candidate",
|
|
244
|
+
reason: "stored summary needs editorial refinement",
|
|
245
|
+
// Python reports the raw stored value here, not the stripped one.
|
|
246
|
+
current_value: conversation.summary ?? null,
|
|
247
|
+
readiness: "ready",
|
|
248
|
+
provenance: "stored",
|
|
249
|
+
evidence: { quality_issues: qualityIssues },
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
return {
|
|
253
|
+
decision: "keep",
|
|
254
|
+
reason: "summary already exists",
|
|
255
|
+
current_value: conversation.summary ?? null,
|
|
256
|
+
readiness: "ready",
|
|
257
|
+
provenance: "stored",
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
if (substantiveMessages(messages).length >= 4) {
|
|
261
|
+
return {
|
|
262
|
+
decision: "create",
|
|
263
|
+
reason: "conversation has enough substance for a summary",
|
|
264
|
+
current_value: null,
|
|
265
|
+
readiness: "ready",
|
|
266
|
+
provenance: null,
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
return {
|
|
270
|
+
decision: "defer",
|
|
271
|
+
reason: "summary needs more conversation substance",
|
|
272
|
+
current_value: null,
|
|
273
|
+
readiness: "not_ready",
|
|
274
|
+
provenance: null,
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/** Return user-facing quality issues for stored conversation summaries. */
|
|
279
|
+
export function summaryQualityIssues(summary: string): string[] {
|
|
280
|
+
const issues: string[] = [];
|
|
281
|
+
if (summary.length > 900) issues.push("too_long");
|
|
282
|
+
if (/(^|\n)\s*(?:[-*]|\d+[.)])\s+/u.test(summary)) issues.push("contains_bullets");
|
|
283
|
+
if (/\*\*|__|`/u.test(summary)) issues.push("contains_markdown");
|
|
284
|
+
if (/(?:\/Users\/|~\/|[A-Za-z]:\\|\/[\p{L}\p{N}_ .~:-]+\/[\p{L}\p{N}_ .~:-]+)/u.test(summary)) {
|
|
285
|
+
issues.push("contains_paths");
|
|
286
|
+
}
|
|
287
|
+
if (/(?<![\p{L}\p{N}_])(user|assistant|mirror|você|eu):/iu.test(summary)) {
|
|
288
|
+
issues.push("looks_like_transcript");
|
|
289
|
+
}
|
|
290
|
+
return issues;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/** Return tags lifecycle decision for a conversation. */
|
|
294
|
+
export function dryRunTagsLifecycle(
|
|
295
|
+
conversation: ConversationLike,
|
|
296
|
+
messages: MessageLike[],
|
|
297
|
+
): FieldReport {
|
|
298
|
+
const currentTags = conversation.tags;
|
|
299
|
+
if (currentTags && !["[]", "null"].includes(currentTags.trim())) {
|
|
300
|
+
return {
|
|
301
|
+
decision: "keep",
|
|
302
|
+
reason: "tags already exist",
|
|
303
|
+
current_value: currentTags,
|
|
304
|
+
readiness: "ready",
|
|
305
|
+
provenance: "stored",
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
if (substantiveMessages(messages).length >= 4 || (conversation.summary ?? "").trim()) {
|
|
309
|
+
return {
|
|
310
|
+
decision: "create",
|
|
311
|
+
reason: "conversation has enough substance for tags",
|
|
312
|
+
current_value: null,
|
|
313
|
+
readiness: "ready",
|
|
314
|
+
provenance: null,
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
return {
|
|
318
|
+
decision: "defer",
|
|
319
|
+
reason: "tags need more conversation substance",
|
|
320
|
+
current_value: null,
|
|
321
|
+
readiness: "not_ready",
|
|
322
|
+
provenance: null,
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function substantiveMessages(messages: MessageLike[]): MessageLike[] {
|
|
327
|
+
return messages.filter(
|
|
328
|
+
(msg) => (msg.role === "user" || msg.role === "assistant") && msg.content.trim(),
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/** Return true when messages contain enough exchange context for title work. */
|
|
333
|
+
export function messagesAreTitleable(messages: MessageLike[]): boolean {
|
|
334
|
+
const hasUser = messages.some((msg) => msg.role === "user" && msg.content.trim());
|
|
335
|
+
const hasAssistant = messages.some((msg) => msg.role === "assistant" && msg.content.trim());
|
|
336
|
+
return hasUser && hasAssistant;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/** Return true when title metadata records a manual lock/source. */
|
|
340
|
+
export function titleIsManual(metadata: Record<string, unknown>): boolean {
|
|
341
|
+
return metadata.title_status === "manual" || metadata.title_source === "manual";
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/** Return true for generated titles with enough later context to revisit. */
|
|
345
|
+
export function titleMayNeedCoherenceRefinement(
|
|
346
|
+
messages: MessageLike[],
|
|
347
|
+
metadata: Record<string, unknown>,
|
|
348
|
+
): boolean {
|
|
349
|
+
if (metadata.title_status !== "generated") return false;
|
|
350
|
+
return messages.filter((msg) => msg.role === "user" || msg.role === "assistant").length >= 6;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
export interface TitleRefinementEvidence {
|
|
354
|
+
confidence: string;
|
|
355
|
+
title_terms: string[];
|
|
356
|
+
summary_specific_terms: string[];
|
|
357
|
+
overlap_terms: string[];
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/** Return evidence when the summary carries substantially more specificity. */
|
|
361
|
+
export function titleRefinementEvidence(
|
|
362
|
+
conversation: ConversationLike,
|
|
363
|
+
): TitleRefinementEvidence | null {
|
|
364
|
+
const titleTerms = meaningfulTerms(conversation.title ?? "");
|
|
365
|
+
const summaryTerms = meaningfulTerms(conversation.summary ?? "");
|
|
366
|
+
if (titleTerms.size < 2 || summaryTerms.size < 8) return null;
|
|
367
|
+
|
|
368
|
+
const additionalTerms = sortByCodePoint([...summaryTerms].filter((t) => !titleTerms.has(t)));
|
|
369
|
+
if (additionalTerms.length < 6) return null;
|
|
370
|
+
|
|
371
|
+
const overlap = sortByCodePoint([...titleTerms].filter((t) => summaryTerms.has(t)));
|
|
372
|
+
return {
|
|
373
|
+
confidence: additionalTerms.length >= 10 ? "medium" : "low",
|
|
374
|
+
title_terms: sortByCodePoint([...titleTerms]),
|
|
375
|
+
summary_specific_terms: additionalTerms.slice(0, 12),
|
|
376
|
+
overlap_terms: overlap.slice(0, 8),
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Return coarse meaningful terms for structural title-vs-summary comparison.
|
|
382
|
+
*
|
|
383
|
+
* The character class reproduces Python's `[\wÀ-ÿ]` exactly: Unicode word
|
|
384
|
+
* characters plus the literal `À-ÿ` range — which, as in Python, also admits
|
|
385
|
+
* `×` (U+00D7) and `÷` (U+00F7).
|
|
386
|
+
*/
|
|
387
|
+
export function meaningfulTerms(text: string): Set<string> {
|
|
388
|
+
const stopWords = new Set([
|
|
389
|
+
"about",
|
|
390
|
+
"after",
|
|
391
|
+
"also",
|
|
392
|
+
"antes",
|
|
393
|
+
"com",
|
|
394
|
+
"como",
|
|
395
|
+
"das",
|
|
396
|
+
"dos",
|
|
397
|
+
"for",
|
|
398
|
+
"from",
|
|
399
|
+
"into",
|
|
400
|
+
"mais",
|
|
401
|
+
"não",
|
|
402
|
+
"para",
|
|
403
|
+
"pela",
|
|
404
|
+
"pelo",
|
|
405
|
+
"por",
|
|
406
|
+
"que",
|
|
407
|
+
"the",
|
|
408
|
+
"uma",
|
|
409
|
+
"vamos",
|
|
410
|
+
"with",
|
|
411
|
+
"work",
|
|
412
|
+
"working",
|
|
413
|
+
]);
|
|
414
|
+
const matches = text.match(/[\p{L}\p{N}_\u00C0-\u00FF]{4,}/gu) ?? [];
|
|
415
|
+
const terms = new Set(matches.map((token) => token.toLowerCase()));
|
|
416
|
+
return new Set([...terms].filter((term) => !stopWords.has(term)));
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Mirror of `ConversationService.title_needs_improvement`.
|
|
421
|
+
*
|
|
422
|
+
* The engine receives this as an injected predicate; it lives here so the pure
|
|
423
|
+
* decision core is testable without a Store, exactly as Python's module is.
|
|
424
|
+
*/
|
|
425
|
+
export function titleNeedsImprovement(conversation: ConversationLike): boolean {
|
|
426
|
+
let metadata: Record<string, unknown> = {};
|
|
427
|
+
try {
|
|
428
|
+
const parsed: unknown = JSON.parse(conversation.metadata || "{}");
|
|
429
|
+
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
430
|
+
metadata = parsed as Record<string, unknown>;
|
|
431
|
+
}
|
|
432
|
+
} catch {
|
|
433
|
+
metadata = {};
|
|
434
|
+
}
|
|
435
|
+
if (metadata.title_status === "manual" || metadata.title_source === "manual") return false;
|
|
436
|
+
const title = (conversation.title ?? "").trim();
|
|
437
|
+
if (!title) return true;
|
|
438
|
+
if (metadata.title_status === "provisional") return true;
|
|
439
|
+
if (title.endsWith("...") || title.includes("...")) return true;
|
|
440
|
+
if (title.length >= 55) return true;
|
|
441
|
+
if (title.toLowerCase().startsWith("<skill")) return true;
|
|
442
|
+
return false;
|
|
443
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// `recall` read primitives — the port of ConversationService.find_by_id_prefix
|
|
2
|
+
// and Store.get_messages, plus Python's `messages[-limit:]` tail-slice
|
|
3
|
+
// semantics (memory.cli.recall).
|
|
4
|
+
|
|
5
|
+
import type { Database } from "#db/database.ts";
|
|
6
|
+
import { optionalString, requireString } from "#db/rowDecode.ts";
|
|
7
|
+
|
|
8
|
+
export interface RecallConversationRow {
|
|
9
|
+
id: string;
|
|
10
|
+
title: string | null;
|
|
11
|
+
started_at: string | null;
|
|
12
|
+
persona: string | null;
|
|
13
|
+
journey: string | null;
|
|
14
|
+
summary: string | null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface RecallMessageRow {
|
|
18
|
+
role: string;
|
|
19
|
+
content: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Port of `find_conversation_by_id_prefix`: latest match by started_at DESC, or null. */
|
|
23
|
+
export function findConversationByIdPrefix(
|
|
24
|
+
db: Database,
|
|
25
|
+
prefix: string,
|
|
26
|
+
): RecallConversationRow | null {
|
|
27
|
+
const row = db
|
|
28
|
+
.prepare(
|
|
29
|
+
"SELECT id, title, started_at, persona, journey, summary FROM conversations " +
|
|
30
|
+
"WHERE id LIKE ? ORDER BY started_at DESC LIMIT 1",
|
|
31
|
+
)
|
|
32
|
+
.get(`${prefix}%`);
|
|
33
|
+
if (row === undefined) return null;
|
|
34
|
+
return {
|
|
35
|
+
id: requireString(row, "id"),
|
|
36
|
+
title: optionalString(row, "title"),
|
|
37
|
+
started_at: optionalString(row, "started_at"),
|
|
38
|
+
persona: optionalString(row, "persona"),
|
|
39
|
+
journey: optionalString(row, "journey"),
|
|
40
|
+
summary: optionalString(row, "summary"),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Port of `Store.get_messages`: every message for a conversation, oldest first. */
|
|
45
|
+
export function getMessagesForConversation(
|
|
46
|
+
db: Database,
|
|
47
|
+
conversationId: string,
|
|
48
|
+
): RecallMessageRow[] {
|
|
49
|
+
return db
|
|
50
|
+
.prepare("SELECT role, content FROM messages WHERE conversation_id = ? ORDER BY created_at")
|
|
51
|
+
.all(conversationId)
|
|
52
|
+
.map((row) => ({ role: requireString(row, "role"), content: requireString(row, "content") }));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Port of Python's `arr[-n:]` slice-start semantics for any integer `n`
|
|
57
|
+
* (including zero and negative values), reproduced exactly because a real
|
|
58
|
+
* behavioral quirk depends on it: `n = 0` means `arr[-0:]` — and `-0 == 0` in
|
|
59
|
+
* Python, so it slices from index 0, i.e. the WHOLE array, not an empty one.
|
|
60
|
+
* A negative `n` (e.g. --limit -3) becomes a positive start index and DROPS
|
|
61
|
+
* the first `-n` elements instead of taking a tail.
|
|
62
|
+
*/
|
|
63
|
+
export function pythonTailSliceStart(length: number, n: number): number {
|
|
64
|
+
const idx = n <= 0 ? -n : length - n;
|
|
65
|
+
return Math.max(0, Math.min(idx, length));
|
|
66
|
+
}
|