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,999 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BUILD_LOAD_COMPOSITION,
|
|
3
|
+
CONSULT_ASK_TRANSPORT,
|
|
4
|
+
CONSULT_CREDITS_TRANSPORT,
|
|
5
|
+
CONVERSATION_TAIL_TRANSPORT,
|
|
6
|
+
type ComposedProviderTransportSpec,
|
|
7
|
+
CULTIVATION_APPLY_TRANSPORT,
|
|
8
|
+
CULTIVATION_SCAN_TRANSPORT,
|
|
9
|
+
DESCRIPTOR_TRANSPORT,
|
|
10
|
+
JOURNAL_TRANSPORT,
|
|
11
|
+
MIRROR_QUERY_TRANSPORT,
|
|
12
|
+
type ProviderTransportSpec,
|
|
13
|
+
resolveComposedProviderTransport,
|
|
14
|
+
resolveProviderTransport,
|
|
15
|
+
SEARCH_TRANSPORT,
|
|
16
|
+
SOUL_HARVEST_TRANSPORT,
|
|
17
|
+
WEEK_PLAN_TRANSPORT,
|
|
18
|
+
} from "#providers/transport.ts";
|
|
19
|
+
|
|
20
|
+
import {
|
|
21
|
+
RUNTIME_SUBCOMMANDS,
|
|
22
|
+
TS_RUNTIME_READ_SUBCOMMANDS,
|
|
23
|
+
TS_RUNTIME_UPDATE_SUBCOMMANDS,
|
|
24
|
+
} from "./runtimeRoute.ts";
|
|
25
|
+
import type { UsageRequest } from "./usage.ts";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The engine a routed command answers from. One member since CV22.DS10.TS5:
|
|
29
|
+
* `"python"` left with the fallback it named, and every site that still
|
|
30
|
+
* reached for it stopped compiling -- which is how the last of them were found.
|
|
31
|
+
*/
|
|
32
|
+
export type FrontDoorEngine = "ts";
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* A surface CV22.DS10 removed rather than ported.
|
|
36
|
+
*
|
|
37
|
+
* Until TS4 a retired name was simply unclaimed: it fell through to Python,
|
|
38
|
+
* which answered `Unknown command: <name>` with its usage block. That answer
|
|
39
|
+
* belongs to the engine TS5 deletes, and it names commands DS10 has already
|
|
40
|
+
* removed. These entries let the front door say *removed* itself, in one line,
|
|
41
|
+
* pointing at the cutoff that explains what to do instead.
|
|
42
|
+
*
|
|
43
|
+
* `matches` reads argv by NAME. Nothing here is matched by inheritance: a verb
|
|
44
|
+
* a retired family never had keeps its family's existing answer (the CR055
|
|
45
|
+
* lesson `conversations append` paid for).
|
|
46
|
+
*/
|
|
47
|
+
export interface RetiredSurface {
|
|
48
|
+
/** The shape as a user types it, e.g. `journey export-registry`. */
|
|
49
|
+
readonly surface: string;
|
|
50
|
+
/** Heading anchor in `docs/releases/pending-cutoffs.md`. A CONSTANT. */
|
|
51
|
+
readonly anchor: string;
|
|
52
|
+
readonly matches: (argv: readonly string[]) => boolean;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export type RouteDecision =
|
|
56
|
+
| {
|
|
57
|
+
command: string | null;
|
|
58
|
+
engine: FrontDoorEngine;
|
|
59
|
+
reason: string;
|
|
60
|
+
}
|
|
61
|
+
| {
|
|
62
|
+
command: string | null;
|
|
63
|
+
engine: "retired";
|
|
64
|
+
reason: string;
|
|
65
|
+
/** The retired shape, from the matched entry -- never from argv. */
|
|
66
|
+
surface: string;
|
|
67
|
+
/** The cutoff anchor, from the matched entry -- never from argv. */
|
|
68
|
+
anchor: string;
|
|
69
|
+
}
|
|
70
|
+
| {
|
|
71
|
+
command: string | null;
|
|
72
|
+
/**
|
|
73
|
+
* CV22.DS10.TS5 (D2): a name the front door does not answer. Rendered by
|
|
74
|
+
* `usage.ts` before dispatch, like `retired` -- nothing opened, nothing
|
|
75
|
+
* read from stdin, nothing spawned.
|
|
76
|
+
*/
|
|
77
|
+
engine: "usage";
|
|
78
|
+
reason: string;
|
|
79
|
+
request: UsageRequest;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* A family was named and its subcommand is not one it answers (D2).
|
|
84
|
+
*
|
|
85
|
+
* `choices` is the allowlist the family ALREADY routes by, so the usage line a
|
|
86
|
+
* user sees cannot drift from what the build answers.
|
|
87
|
+
*/
|
|
88
|
+
function unknownSubcommand(
|
|
89
|
+
command: string,
|
|
90
|
+
choices: Iterable<string>,
|
|
91
|
+
given: string | undefined,
|
|
92
|
+
program: string = command,
|
|
93
|
+
): RouteDecision {
|
|
94
|
+
const name = given ?? "";
|
|
95
|
+
return {
|
|
96
|
+
command,
|
|
97
|
+
engine: "usage",
|
|
98
|
+
reason:
|
|
99
|
+
name === "" ? `${program} needs a subcommand` : `unknown ${program} subcommand: ${name}`,
|
|
100
|
+
request: { scope: "family", program, choices: [...choices], given: name },
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** A top-level name nothing answers, or no command at all (D2). */
|
|
105
|
+
function unknownCommand(given: string | null): RouteDecision {
|
|
106
|
+
return {
|
|
107
|
+
// Whatever the caller typed is not a command name, so the log records none.
|
|
108
|
+
command: null,
|
|
109
|
+
engine: "usage",
|
|
110
|
+
reason: given === null ? "no command" : `unknown command: ${given}`,
|
|
111
|
+
request: { scope: "top-level", given },
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const TS_READ_COMMANDS = new Set(["detect-persona", "journeys"]);
|
|
116
|
+
|
|
117
|
+
// The Conversation Metadata Lifecycle (ES-001) flags on `conversations`.
|
|
118
|
+
// CR068 found the whole family unowned: DS7.US1 called it "own slice" and no
|
|
119
|
+
// slice claimed it. DS7.US11 splits it three ways by what each flag actually
|
|
120
|
+
// needs, rather than treating them as one block.
|
|
121
|
+
//
|
|
122
|
+
// READS -- pure, over the engine DS7.US10 already ported.
|
|
123
|
+
const TS_LIFECYCLE_READ_FLAGS = [
|
|
124
|
+
"--metadata-lifecycle-dry-run",
|
|
125
|
+
"--metadata-lifecycle-preview-at-message",
|
|
126
|
+
];
|
|
127
|
+
|
|
128
|
+
// WRITES -- both need `apply_metadata_lifecycle`, ~80 lines of unported
|
|
129
|
+
// decision logic (`closeTail.ts` carries the same refusal), and `demo` calls
|
|
130
|
+
// `apply`. Refused BY NAME and assigned to DS7.TS4 by Navigator decision
|
|
131
|
+
// 2026-09-09, rather than silently inheriting a route or being reported as
|
|
132
|
+
// ported.
|
|
133
|
+
const TS4_LIFECYCLE_WRITE_FLAGS = ["--metadata-lifecycle-apply", "--metadata-lifecycle-demo"];
|
|
134
|
+
|
|
135
|
+
// One-shot backfill of pre-ES-001 rows, retired by CV22.DS10.TS4 with a
|
|
136
|
+
// documented cutoff. Named here ONLY for the retired entries below: those match
|
|
137
|
+
// before any family, so a family branch naming these flags would be dead code
|
|
138
|
+
// (TS5 deleted the one that was -- `retiredRouteShadows.ts` keeps it deleted).
|
|
139
|
+
const DS10_BACKFILL_FLAGS = ["--metadata-backfill-preview", "--metadata-backfill-apply"];
|
|
140
|
+
|
|
141
|
+
// CV22.DS7.TS4: the extension catalog family, allowlisted by NAME.
|
|
142
|
+
//
|
|
143
|
+
// `extensions` and `inspect` are claimed commands with subcommands Python may
|
|
144
|
+
// grow, and this family has already paid for inheritance once: `conversations`
|
|
145
|
+
// grew `append` after DS7.US1 claimed the command, and the new subcommand
|
|
146
|
+
// silently rendered a listing and discarded the caller's messages. So every
|
|
147
|
+
// verb TS answers is listed here, and anything else stays Python's.
|
|
148
|
+
export const TS4_EXTENSIONS_VERBS = new Set([
|
|
149
|
+
"list",
|
|
150
|
+
"validate",
|
|
151
|
+
"sync",
|
|
152
|
+
"install",
|
|
153
|
+
"uninstall",
|
|
154
|
+
"expose-claude",
|
|
155
|
+
"clean-claude",
|
|
156
|
+
]);
|
|
157
|
+
|
|
158
|
+
const TS4_INSPECT_TARGETS = new Set([
|
|
159
|
+
"extension",
|
|
160
|
+
"runtime-catalog",
|
|
161
|
+
"llm-calls",
|
|
162
|
+
"embedding-provenance",
|
|
163
|
+
]);
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* `ext`'s two levels, exported as the AUDITED denominator rather than as a
|
|
167
|
+
* filter.
|
|
168
|
+
*
|
|
169
|
+
* Level one is every head `cmd_ext` recognises as a verb; level two is the four
|
|
170
|
+
* built-in verbs that follow an id. Everything else — `<id>`, `<id> <builtin>`,
|
|
171
|
+
* `<id> <subcommand>` — is the dynamic leaf, whose subcommand belongs to an
|
|
172
|
+
* extension Mirror does not own and therefore cannot be enumerated.
|
|
173
|
+
*
|
|
174
|
+
* Because the route cannot refuse an unknown head (Python reads it as an id),
|
|
175
|
+
* these sets are checked against `cli/ext.py` itself by a route test. That is
|
|
176
|
+
* what keeps the inheritance honest: the day Python grows a verb beside
|
|
177
|
+
* `list`, the test fails instead of the user silently getting `extension not
|
|
178
|
+
* installed: .../doctor`.
|
|
179
|
+
*/
|
|
180
|
+
export const TS4_EXT_TOP_LEVEL_VERBS = new Set(["list", "--help", "-h", "help"]);
|
|
181
|
+
export const TS4_EXT_BUILTIN_VERBS = new Set(["bind", "unbind", "bindings", "migrate"]);
|
|
182
|
+
|
|
183
|
+
// CV22.DS7.US8 plateau 8: the 27 Builder leaves ported in plateaus 1–7, plus `show`
|
|
184
|
+
// (CR067 with CR020), the one leaf written new in TypeScript rather than ported.
|
|
185
|
+
// Exported so the route tests can prove that the allowlist and its audited
|
|
186
|
+
// denominator stay the same. The two legacy Workbench groups are deliberately
|
|
187
|
+
// absent: their twenty leaves retire unported in DS10.
|
|
188
|
+
export const TS_BUILD_SUBCOMMANDS = new Set([
|
|
189
|
+
"load",
|
|
190
|
+
"inspect-method",
|
|
191
|
+
"adopt",
|
|
192
|
+
"prepare-templates",
|
|
193
|
+
"sync-cursor",
|
|
194
|
+
"pull-candidates",
|
|
195
|
+
"pull-item",
|
|
196
|
+
"prepare-item",
|
|
197
|
+
"plan-item",
|
|
198
|
+
"approve-plan",
|
|
199
|
+
"cancel-plan-preauthorization",
|
|
200
|
+
"check-implementation",
|
|
201
|
+
"show",
|
|
202
|
+
"validate-item",
|
|
203
|
+
"review-item",
|
|
204
|
+
"coherence-item",
|
|
205
|
+
"done-item",
|
|
206
|
+
"set-flow-unit",
|
|
207
|
+
"plan-delivery-story",
|
|
208
|
+
"approve-delivery-story-plan",
|
|
209
|
+
"cancel-delivery-story-plan-preauthorization",
|
|
210
|
+
"validate-delivery-story",
|
|
211
|
+
"review-delivery-story",
|
|
212
|
+
"coherence-delivery-story",
|
|
213
|
+
"done-delivery-story",
|
|
214
|
+
"set-cadence",
|
|
215
|
+
"release-intent",
|
|
216
|
+
"continue-lifecycle",
|
|
217
|
+
]);
|
|
218
|
+
|
|
219
|
+
export const TS_BUILD_WORKBENCH_ACTIONS = {
|
|
220
|
+
"refinement-story": new Set([
|
|
221
|
+
"create",
|
|
222
|
+
"overview",
|
|
223
|
+
"pull",
|
|
224
|
+
"review",
|
|
225
|
+
"coherence",
|
|
226
|
+
"close",
|
|
227
|
+
"park",
|
|
228
|
+
]),
|
|
229
|
+
"change-request": new Set([
|
|
230
|
+
"capture",
|
|
231
|
+
"attach",
|
|
232
|
+
"discard",
|
|
233
|
+
"select",
|
|
234
|
+
"confirm",
|
|
235
|
+
"resume",
|
|
236
|
+
"plan",
|
|
237
|
+
"mark-implemented",
|
|
238
|
+
"validate",
|
|
239
|
+
"done",
|
|
240
|
+
"park",
|
|
241
|
+
"reject",
|
|
242
|
+
"promote",
|
|
243
|
+
]),
|
|
244
|
+
} as const;
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* The surfaces CV22.DS10.TS4 removed, each with the cutoff that explains it.
|
|
248
|
+
*
|
|
249
|
+
* A LIST OF PREDICATES, not a map keyed by command: the six shapes are four
|
|
250
|
+
* different matchers (whole command; command + verb; command + flag; command +
|
|
251
|
+
* subcommand + verb), and a map would push those differences into ad-hoc string
|
|
252
|
+
* checks inside `routeMemoryCommand`.
|
|
253
|
+
*
|
|
254
|
+
* The surfaces TS1, US1, and TS3 retired are deliberately NOT here. Their
|
|
255
|
+
* stories are closed, and the post-Python answer for every unclaimed name is
|
|
256
|
+
* TS5's to shape at once -- adding them now would reopen three done packages
|
|
257
|
+
* for a cosmetic change. (Naming them here would also trip their own
|
|
258
|
+
* retired-surface rows, which is the guard working.)
|
|
259
|
+
*/
|
|
260
|
+
export const RETIRED_SURFACES: readonly RetiredSurface[] = [
|
|
261
|
+
{
|
|
262
|
+
surface: "migrate-legacy",
|
|
263
|
+
anchor: "legacy-migration",
|
|
264
|
+
matches: (argv) => argv[0] === "migrate-legacy",
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
surface: "journey export-registry",
|
|
268
|
+
anchor: "journey-admin-verbs",
|
|
269
|
+
matches: (argv) => argv[0] === "journey" && argv[1] === "export-registry",
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
surface: "journey mutate",
|
|
273
|
+
anchor: "journey-admin-verbs",
|
|
274
|
+
matches: (argv) => argv[0] === "journey" && argv[1] === "mutate",
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
surface: "conversations --metadata-backfill-preview",
|
|
278
|
+
anchor: "conversation-metadata-backfill",
|
|
279
|
+
matches: (argv) => argv[0] === "conversations" && argv.includes(DS10_BACKFILL_FLAGS[0]),
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
surface: "conversations --metadata-backfill-apply",
|
|
283
|
+
anchor: "conversation-metadata-backfill",
|
|
284
|
+
matches: (argv) => argv[0] === "conversations" && argv.includes(DS10_BACKFILL_FLAGS[1]),
|
|
285
|
+
},
|
|
286
|
+
// CV22.DS10.US2 (D1): the release chain leaves the PRODUCT surface. These
|
|
287
|
+
// two were offered to every installed user and need a git checkout, a clean
|
|
288
|
+
// tree, tags, and push rights -- none of which an installed user has. They
|
|
289
|
+
// are maintainer tooling now, reached through `npm run release:*`, so the
|
|
290
|
+
// old names answer with their cutoff rather than being ported.
|
|
291
|
+
{
|
|
292
|
+
surface: "runtime release-doctor",
|
|
293
|
+
anchor: "release-tooling-leaves-the-product-command-surface",
|
|
294
|
+
matches: (argv) => argv[0] === "runtime" && argv[1] === "release-doctor",
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
surface: "runtime release-promote",
|
|
298
|
+
anchor: "release-tooling-leaves-the-product-command-surface",
|
|
299
|
+
matches: (argv) => argv[0] === "runtime" && argv[1] === "release-promote",
|
|
300
|
+
},
|
|
301
|
+
...(
|
|
302
|
+
[
|
|
303
|
+
"refinement-story",
|
|
304
|
+
"change-request",
|
|
305
|
+
] as const satisfies readonly (keyof typeof TS_BUILD_WORKBENCH_ACTIONS)[]
|
|
306
|
+
).flatMap((family) =>
|
|
307
|
+
[...TS_BUILD_WORKBENCH_ACTIONS[family]].map((verb) => ({
|
|
308
|
+
surface: `build ${family} ${verb}`,
|
|
309
|
+
anchor: "the-sqlite-refinement-workbench",
|
|
310
|
+
matches: (argv: readonly string[]) =>
|
|
311
|
+
argv[0] === "build" && argv[1] === family && argv[2] === verb,
|
|
312
|
+
})),
|
|
313
|
+
),
|
|
314
|
+
];
|
|
315
|
+
|
|
316
|
+
/** The matched entry, or `null` when nothing this story retired was named. */
|
|
317
|
+
function retiredSurfaceFor(argv: readonly string[]): RetiredSurface | null {
|
|
318
|
+
return RETIRED_SURFACES.find((entry) => entry.matches(argv)) ?? null;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* The one line a caller sees. No traceback, no usage block, no partial output.
|
|
323
|
+
*
|
|
324
|
+
* Every substitution comes from the matched entry, never from argv: a caller
|
|
325
|
+
* cannot push text into this message, and a path passed to a retired command
|
|
326
|
+
* cannot reach a terminal, a log, or a screen recording through it.
|
|
327
|
+
*/
|
|
328
|
+
export function retiredRefusal(decision: RouteDecision & { engine: "retired" }): string {
|
|
329
|
+
return (
|
|
330
|
+
`Mirror: '${decision.surface}' was removed in the CV22 migration. ` +
|
|
331
|
+
`See docs/releases/pending-cutoffs.md#${decision.anchor}.\n`
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// A type alias rather than an interface: aliases get an implicit index
|
|
336
|
+
// signature, which is what lets the named variables below still be passed to
|
|
337
|
+
// `resolveProviderTransport`, whose families name their variables as data.
|
|
338
|
+
//
|
|
339
|
+
// Only what still CHOOSES something is here: the replay fixtures, which choose
|
|
340
|
+
// a test transport, and `MEMORY_RECEPTION`. The `MIRROR_TS_<FAMILY>` revert
|
|
341
|
+
// gates chose an engine, and left with it (CV22.DS10.TS5, D3). A value left in
|
|
342
|
+
// someone's `.env` changes no route; `runtime diagnose` names it inert.
|
|
343
|
+
export type RouteEnvironment = {
|
|
344
|
+
MIRROR_TS_SEARCH_EMBEDDING_REPLAY?: string;
|
|
345
|
+
MIRROR_TS_CONSULT_LLM_REPLAY?: string;
|
|
346
|
+
MIRROR_TS_CREDITS_REPLAY?: string;
|
|
347
|
+
MIRROR_TS_CULTIVATION_LLM_REPLAY?: string;
|
|
348
|
+
MIRROR_TS_CULTIVATION_EMBEDDING_REPLAY?: string;
|
|
349
|
+
MIRROR_TS_MIRROR_LLM_REPLAY?: string;
|
|
350
|
+
MIRROR_TS_MIRROR_EMBEDDING_REPLAY?: string;
|
|
351
|
+
MIRROR_TS_CONVERSATION_LLM_REPLAY?: string;
|
|
352
|
+
MIRROR_TS_CONVERSATION_EMBEDDING_REPLAY?: string;
|
|
353
|
+
MIRROR_TS_SOUL_EMBEDDING_REPLAY?: string;
|
|
354
|
+
MIRROR_TS_BUILD_LLM_REPLAY?: string;
|
|
355
|
+
MIRROR_TS_BUILD_EMBEDDING_REPLAY?: string;
|
|
356
|
+
MIRROR_TS_JOURNAL_LLM_REPLAY?: string;
|
|
357
|
+
MIRROR_TS_JOURNAL_EMBEDDING_REPLAY?: string;
|
|
358
|
+
MIRROR_TS_WEEK_LLM_REPLAY?: string;
|
|
359
|
+
MIRROR_TS_DESCRIPTOR_LLM_REPLAY?: string;
|
|
360
|
+
MEMORY_RECEPTION?: string;
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
// CV22.DS7.US5 slice A, extended by CV22.DS7.US10 slice F. These
|
|
364
|
+
// `conversation-logger` subcommands are deterministic end to end and route to
|
|
365
|
+
// TS under the family switch alone.
|
|
366
|
+
const TS_CONVERSATION_LOGGER_SUBCOMMANDS = new Set([
|
|
367
|
+
"mute",
|
|
368
|
+
"unmute",
|
|
369
|
+
"status",
|
|
370
|
+
"log-user",
|
|
371
|
+
"log-assistant",
|
|
372
|
+
"user-prompt",
|
|
373
|
+
"discard-current",
|
|
374
|
+
// US10 slice F, group 2 (need slice E): diagnose, dry-run repair, Codex import.
|
|
375
|
+
"diagnose-journeys",
|
|
376
|
+
"repair-journeys",
|
|
377
|
+
"backfill-codex-session",
|
|
378
|
+
]);
|
|
379
|
+
|
|
380
|
+
// CV22.DS7.US10 slice F. These reach Python's `end_conversation`, which runs
|
|
381
|
+
// extraction and close-time metadata finalization through the LLM. They route
|
|
382
|
+
// to TS only under the replay transport; an unconfigured install keeps the
|
|
383
|
+
// Python fallback, which is the live-cutover boundary DS8 owns. Flipped in the
|
|
384
|
+
// plan's dependency order: `switch`, `session-end-pi`, `session-end` first
|
|
385
|
+
// (they need slice C' only), then `session-start` and `session-maintenance`
|
|
386
|
+
// (they compose the close tail with slices D and E).
|
|
387
|
+
const TS_CONVERSATION_LOGGER_LLM_SUBCOMMANDS = new Set([
|
|
388
|
+
"switch",
|
|
389
|
+
"session-end-pi",
|
|
390
|
+
"session-end",
|
|
391
|
+
"session-start",
|
|
392
|
+
"session-maintenance",
|
|
393
|
+
]);
|
|
394
|
+
|
|
395
|
+
// CV22.DS8.US2 staged the live cutover in the order US10 proved and flipped
|
|
396
|
+
// these in: group 1 (`switch`, `session-end-pi`, `session-end`) first, because
|
|
397
|
+
// `session-end` fires unattended from the Pi hook; then group 2
|
|
398
|
+
// (`session-start` full, `session-maintenance`), which composes the close tail
|
|
399
|
+
// WITH backfill and orphan handling and can close several conversations in one
|
|
400
|
+
// run, multiplying any defect.
|
|
401
|
+
//
|
|
402
|
+
// Group 1 was observed live on the real home on 2026-09-11 -- a full close
|
|
403
|
+
// tail with extraction_status=ok, six memories at 1536 dims, and every ledger
|
|
404
|
+
// row priced with bodies withheld -- so the staging has served its purpose and
|
|
405
|
+
// all five subcommands now share one live decision.
|
|
406
|
+
|
|
407
|
+
/** Python's `main()`: strip `--mirror-home X` and `--session-id X`, then `args[0]`. */
|
|
408
|
+
function conversationLoggerSubcommand(argv: readonly string[]): string | undefined {
|
|
409
|
+
const args = [...argv.slice(1)];
|
|
410
|
+
for (const option of ["--mirror-home", "--session-id"]) {
|
|
411
|
+
const index = args.indexOf(option);
|
|
412
|
+
if (index !== -1) args.splice(index, 2);
|
|
413
|
+
}
|
|
414
|
+
return args[0];
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* One decision for every provider-backed leaf (CV22.DS8.US3).
|
|
419
|
+
*
|
|
420
|
+
* `MIRROR_TS_EXTERNAL_ROUTES` is GONE, not merely unused. It was DS5's safety
|
|
421
|
+
* catch while replay was the PRODUCTION route for these leaves: requiring an
|
|
422
|
+
* extra opt-in made sense when "routed to TS" meant "answered from a fixture".
|
|
423
|
+
* After the live cutover replay is a test transport, so the gate would only
|
|
424
|
+
* add a second thing to set in CI and a stale value to trip over in a shell.
|
|
425
|
+
* A leftover `=1` or `=0` is inert, and a test pins that.
|
|
426
|
+
*/
|
|
427
|
+
function providerRoute(
|
|
428
|
+
command: string | null,
|
|
429
|
+
env: RouteEnvironment,
|
|
430
|
+
spec: ProviderTransportSpec | ComposedProviderTransportSpec,
|
|
431
|
+
leaf?: string,
|
|
432
|
+
): RouteDecision {
|
|
433
|
+
// A composition resolves three specs at once (`build load` and the one leaf
|
|
434
|
+
// whose tail it is, `explore story promote`); a plain spec resolves one. Both
|
|
435
|
+
// yield the same decision shape, so the reason below is written once.
|
|
436
|
+
const transport =
|
|
437
|
+
"owner" in spec
|
|
438
|
+
? resolveComposedProviderTransport(env, spec)
|
|
439
|
+
: resolveProviderTransport(env, spec);
|
|
440
|
+
const reason = leaf ? `${transport.reason} (${leaf})` : transport.reason;
|
|
441
|
+
// Every transport answers from TypeScript, `incomplete_replay` included: the
|
|
442
|
+
// route refuses it by name before any provider is built. Until CV22.DS10.TS5
|
|
443
|
+
// a PLAIN family sent it to Python instead -- which has no replay transport
|
|
444
|
+
// and would have called the live provider, the silent spend CR077 made
|
|
445
|
+
// compositions refuse.
|
|
446
|
+
return { command, engine: "ts", reason };
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
export function routeMemoryCommand(
|
|
450
|
+
argv: readonly string[],
|
|
451
|
+
env: RouteEnvironment = process.env,
|
|
452
|
+
): RouteDecision {
|
|
453
|
+
// Retired surfaces are matched FIRST, before any family claims the command.
|
|
454
|
+
// This is what closes CR089: `journey export-registry` and `journey mutate`
|
|
455
|
+
// can no longer fall into the `journey` status read and be treated as slugs
|
|
456
|
+
// -- a wrong answer with exit 0 on a read, and a silent no-op on a write.
|
|
457
|
+
const retired = retiredSurfaceFor(argv);
|
|
458
|
+
if (retired) {
|
|
459
|
+
return {
|
|
460
|
+
command: argv[0] ?? null,
|
|
461
|
+
engine: "retired",
|
|
462
|
+
reason: `${retired.surface} retired in CV22.DS10.TS4`,
|
|
463
|
+
surface: retired.surface,
|
|
464
|
+
anchor: retired.anchor,
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
return routeByFamily(argv, env);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* The family routes, WITHOUT the retired-surface match in front of them.
|
|
472
|
+
*
|
|
473
|
+
* Exported for one reader: the guard that proves no family branch still names
|
|
474
|
+
* a shape a retired entry already answers (CV22.DS10.TS5, inventory F2). A
|
|
475
|
+
* branch like that is dead code nothing can observe -- the retired match wins
|
|
476
|
+
* before it is reached -- so every front-door test passes over it, which is
|
|
477
|
+
* exactly how TS4 left two of them behind. The front door itself always calls
|
|
478
|
+
* `routeMemoryCommand`.
|
|
479
|
+
*/
|
|
480
|
+
export function routeByFamily(
|
|
481
|
+
argv: readonly string[],
|
|
482
|
+
env: RouteEnvironment = process.env,
|
|
483
|
+
): RouteDecision {
|
|
484
|
+
const command = argv[0] ?? null;
|
|
485
|
+
if (!command) return unknownCommand(null);
|
|
486
|
+
|
|
487
|
+
if (TS_READ_COMMANDS.has(command)) {
|
|
488
|
+
return { command, engine: "ts", reason: "DS2 read command ported to TS" };
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
if (command === "memories") {
|
|
492
|
+
if (argv.includes("--search")) {
|
|
493
|
+
// CV22.DS8.US1: fresh semantic search reaches the live provider from TS
|
|
494
|
+
// by default; a replay fixture wins for CI and the smokes. One
|
|
495
|
+
// precedence, shared with every provider-backed family.
|
|
496
|
+
return providerRoute(command, env, SEARCH_TRANSPORT);
|
|
497
|
+
}
|
|
498
|
+
return { command, engine: "ts", reason: "DS2 memory listing read ported to TS" };
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
if (command === "consult") {
|
|
502
|
+
// Per leaf, because the fixtures are: `credits` needs the credits fixture
|
|
503
|
+
// alone, `ask` needs the chat fixture too. With only the credits one set,
|
|
504
|
+
// `credits` replays and `ask` refuses rather than going half-live.
|
|
505
|
+
return argv[1] === "credits"
|
|
506
|
+
? providerRoute(command, env, CONSULT_CREDITS_TRANSPORT, "credits")
|
|
507
|
+
: providerRoute(command, env, CONSULT_ASK_TRANSPORT, "ask");
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
if (command === "identity") {
|
|
511
|
+
// `set` (DS4), `list`/`get` (DS7.US1), and `edit` (DS7.TS4's editor seam).
|
|
512
|
+
if (argv[1] === "set") {
|
|
513
|
+
return { command, engine: "ts", reason: "DS4 identity set write ported to TS" };
|
|
514
|
+
}
|
|
515
|
+
if (argv[1] === "list" || argv[1] === "get") {
|
|
516
|
+
return { command, engine: "ts", reason: "DS7.US1 identity list/get read ported to TS" };
|
|
517
|
+
}
|
|
518
|
+
if (argv[1] === "edit") {
|
|
519
|
+
return { command, engine: "ts", reason: "DS7.TS4 identity edit ported to TS" };
|
|
520
|
+
}
|
|
521
|
+
// Allowlisted by name rather than inherited: `identity` grew `edit` after
|
|
522
|
+
// `set` claimed the command.
|
|
523
|
+
return unknownSubcommand(command, IDENTITY_SUBCOMMANDS, argv[1]);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
if (command === "extensions") {
|
|
527
|
+
const verb = argv[1] ?? "list";
|
|
528
|
+
if (!TS4_EXTENSIONS_VERBS.has(verb)) {
|
|
529
|
+
return unknownSubcommand(command, TS4_EXTENSIONS_VERBS, verb);
|
|
530
|
+
}
|
|
531
|
+
return { command, engine: "ts", reason: `DS7.TS4 extensions ${verb} ported to TS` };
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
if (command === "ext") {
|
|
535
|
+
// No verb allowlist is possible here, and saying so is better than writing
|
|
536
|
+
// one that accepts everything: `cmd_ext` treats EVERY head that is not
|
|
537
|
+
// `list` or a help flag as an extension id, so TS must too. The exposure
|
|
538
|
+
// that creates is real and named — a new top-level verb beside `list`
|
|
539
|
+
// would be read as an id and answered `extension not installed` — and it
|
|
540
|
+
// is guarded by a test that reads `cli/ext.py` and fails when that set of
|
|
541
|
+
// literals changes, rather than by a set duplicated here that nothing
|
|
542
|
+
// checks.
|
|
543
|
+
return { command, engine: "ts", reason: "DS7.TS4 ext dispatcher ported to TS" };
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
if (command === "init") {
|
|
547
|
+
return {
|
|
548
|
+
command,
|
|
549
|
+
engine: "ts",
|
|
550
|
+
reason: "DS7.US1 Slice B init (filesystem bootstrap) ported to TS",
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
if (command === "seed") {
|
|
555
|
+
return { command, engine: "ts", reason: "DS7.US1 Slice B seed write ported to TS" };
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
if (command === "recall") {
|
|
559
|
+
return { command, engine: "ts", reason: "DS7.US1 recall read ported to TS" };
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
if (command === "conversations") {
|
|
563
|
+
// The plain listing (DS7.US1) and `append` (DS7.US10) are ported; the
|
|
564
|
+
// metadata-lifecycle/backfill writes are not.
|
|
565
|
+
//
|
|
566
|
+
// `append` (v0.31.13) arrived on main after DS7.US1 claimed this command,
|
|
567
|
+
// and the entry matched every argv shape: the append request routed to TS,
|
|
568
|
+
// rendered a listing, exited 0, and silently discarded the caller's
|
|
569
|
+
// messages. Subcommands of a claimed family must be allowlisted, never
|
|
570
|
+
// inherited -- which is why `append` gets its own explicit entry below
|
|
571
|
+
// even now that it points at TS.
|
|
572
|
+
if (argv[1] === "append") {
|
|
573
|
+
return {
|
|
574
|
+
command,
|
|
575
|
+
engine: "ts",
|
|
576
|
+
reason: "DS7.US10 conversations append boundary ported to TS",
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
const writeFlag = TS4_LIFECYCLE_WRITE_FLAGS.find((flag) => argv.includes(flag));
|
|
580
|
+
if (writeFlag) {
|
|
581
|
+
return { command, engine: "ts", reason: `DS7.TS4 ${writeFlag} ported to TS` };
|
|
582
|
+
}
|
|
583
|
+
const readFlag = TS_LIFECYCLE_READ_FLAGS.find((flag) => argv.includes(flag));
|
|
584
|
+
if (readFlag) {
|
|
585
|
+
return {
|
|
586
|
+
command,
|
|
587
|
+
engine: "ts",
|
|
588
|
+
reason: `DS7.US11 ${readFlag} ported to TS`,
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
return { command, engine: "ts", reason: "DS7.US1 conversations listing read ported to TS" };
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
if (command === "inspect") {
|
|
595
|
+
// `persona` (DS7.US1) is a deterministic identity read. The other four
|
|
596
|
+
// targets are DS7.TS4's: `extension`/`runtime-catalog` share the catalog
|
|
597
|
+
// machinery, `llm-calls`/`embedding-provenance` read the ledger.
|
|
598
|
+
if (argv[1] === "persona") {
|
|
599
|
+
return { command, engine: "ts", reason: "DS7.US1 inspect persona read ported to TS" };
|
|
600
|
+
}
|
|
601
|
+
if (TS4_INSPECT_TARGETS.has(argv[1] ?? "")) {
|
|
602
|
+
return { command, engine: "ts", reason: `DS7.TS4 inspect ${argv[1]} ported to TS` };
|
|
603
|
+
}
|
|
604
|
+
return unknownSubcommand(command, ["persona", ...TS4_INSPECT_TARGETS], argv[1]);
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
if (command === "list") {
|
|
608
|
+
// `personas`/`journeys` (DS7.US1) are deterministic identity reads;
|
|
609
|
+
// `extensions` and `all` (which is also the no-target default) touch the
|
|
610
|
+
// extension catalog and belong to DS7.TS4.
|
|
611
|
+
if (argv[1] === "personas") {
|
|
612
|
+
return { command, engine: "ts", reason: "DS7.US1 list personas read ported to TS" };
|
|
613
|
+
}
|
|
614
|
+
if (argv[1] === "journeys") {
|
|
615
|
+
return { command, engine: "ts", reason: "DS7.US1 list journeys read ported to TS" };
|
|
616
|
+
}
|
|
617
|
+
const listTarget = argv[1] ?? "all";
|
|
618
|
+
if (listTarget === "extensions" || listTarget === "all") {
|
|
619
|
+
return { command, engine: "ts", reason: `DS7.TS4 list ${listTarget} ported to TS` };
|
|
620
|
+
}
|
|
621
|
+
return unknownSubcommand(command, LIST_TARGETS, listTarget);
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
if (command === "descriptor") {
|
|
625
|
+
// `list` (DS7.US1) is a deterministic read; `generate` calls the LLM
|
|
626
|
+
// (generate_descriptor) and stays on Python as the DS7↔DS8 live seam.
|
|
627
|
+
if (argv[1] === "list") {
|
|
628
|
+
return { command, engine: "ts", reason: "DS7.US1 descriptor list read ported to TS" };
|
|
629
|
+
}
|
|
630
|
+
if (argv[1] === "generate") {
|
|
631
|
+
return providerRoute(command, env, DESCRIPTOR_TRANSPORT, "generate");
|
|
632
|
+
}
|
|
633
|
+
return unknownSubcommand(command, DESCRIPTOR_SUBCOMMANDS, argv[1]);
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
if (command === "tasks") {
|
|
637
|
+
// `list` (and the bare `tasks` default) is a read; `add/done/doing/block/
|
|
638
|
+
// delete` are the deterministic writes ported in DS7.US2 slice 3a;
|
|
639
|
+
// `import/sync/sync-config` (which also touch the journey sync-file/
|
|
640
|
+
// project-path metadata subsystem) are ported in slice 3c.
|
|
641
|
+
//
|
|
642
|
+
// A LEADING OPTION IS NOT A LIST ANY MORE (CV22.DS10.TS5, F5). The rule
|
|
643
|
+
// here used to read `tasks --anything ...` as `list`, which is how
|
|
644
|
+
// `tasks --mirror-home H add "x"` printed the list and wrote nothing. The
|
|
645
|
+
// options the oracle accepts before the subcommand are now moved after it
|
|
646
|
+
// by `argvShape.ts` before routing runs, so one that is still here is one
|
|
647
|
+
// the oracle's parser rejected too -- or `-h`.
|
|
648
|
+
const sub = argv[1];
|
|
649
|
+
if (sub === undefined || sub === "list") {
|
|
650
|
+
return { command, engine: "ts", reason: "DS7.US2 tasks list read ported to TS" };
|
|
651
|
+
}
|
|
652
|
+
if (TASKS_SUBCOMMANDS.includes(sub)) {
|
|
653
|
+
return { command, engine: "ts", reason: "DS7.US2 tasks write ported to TS" };
|
|
654
|
+
}
|
|
655
|
+
return unknownSubcommand(command, TASKS_SUBCOMMANDS, sub);
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
if (command === "week") {
|
|
659
|
+
// `view` (and the bare `week` default) is a deterministic read ported in
|
|
660
|
+
// DS7.US2. CR068 corrected this family's accounting: US2 sent `plan` and
|
|
661
|
+
// `save` to US5, US5 was re-scoped without them, and nothing inherited the
|
|
662
|
+
// work -- and the reason recorded here claimed BOTH were "LLM-gated",
|
|
663
|
+
// which is false for `save`. `save_week_items` reads the pending file and
|
|
664
|
+
// calls `add_task`; it crosses no provider seam. Only `plan` calls a model.
|
|
665
|
+
const sub = argv[1];
|
|
666
|
+
if (sub === undefined || sub === "view") {
|
|
667
|
+
return { command, engine: "ts", reason: "DS7.US2 week view read ported to TS" };
|
|
668
|
+
}
|
|
669
|
+
if (sub === "save") {
|
|
670
|
+
return { command, engine: "ts", reason: "DS7.US11 week save (deterministic) ported to TS" };
|
|
671
|
+
}
|
|
672
|
+
if (sub === "plan") {
|
|
673
|
+
// One model call, no embedding -- so it needs the LLM replay fixture
|
|
674
|
+
// only, unlike `journal` which crosses the seam twice.
|
|
675
|
+
return providerRoute(command, env, WEEK_PLAN_TRANSPORT, "plan");
|
|
676
|
+
}
|
|
677
|
+
return unknownSubcommand(command, WEEK_SUBCOMMANDS, sub);
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
if (command === "consolidate") {
|
|
681
|
+
// `list`/`reject` are deterministic reads/writes -- always TS (DS7.US3).
|
|
682
|
+
// `apply` is gated as a WHOLE on the embedding replay config: its action
|
|
683
|
+
// is read from the DB, not argv, so the routing decision (made before any
|
|
684
|
+
// DB is opened) cannot special-case identity_update/shadow_candidate --
|
|
685
|
+
// the entire command routes together, same principle as `scan`.
|
|
686
|
+
const sub = argv[1];
|
|
687
|
+
if (sub === "list" || sub === "reject") {
|
|
688
|
+
return { command, engine: "ts", reason: "DS7.US3 consolidate list/reject ported to TS" };
|
|
689
|
+
}
|
|
690
|
+
// `apply` sends NO prompt: a merge embeds the merged content and an
|
|
691
|
+
// identity_update makes no provider call at all. So it is not blocked by
|
|
692
|
+
// TS2, unlike `scan`. Gated as a whole because its action is read from the
|
|
693
|
+
// DB, not argv, and the routing decision precedes any DB open.
|
|
694
|
+
if (sub === "apply") {
|
|
695
|
+
return providerRoute(command, env, CULTIVATION_APPLY_TRANSPORT, "apply");
|
|
696
|
+
}
|
|
697
|
+
if (sub === "scan") {
|
|
698
|
+
return providerRoute(command, env, CULTIVATION_SCAN_TRANSPORT, "scan");
|
|
699
|
+
}
|
|
700
|
+
return unknownSubcommand(command, CONSOLIDATE_SUBCOMMANDS, sub);
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
if (command === "shadow") {
|
|
704
|
+
// `list`/`show`/`reject`/`apply` are all deterministic -- `apply`'s write
|
|
705
|
+
// is a hardcoded-layer identity append, no LLM/embedding call, unlike
|
|
706
|
+
// consolidate's `apply` (which may need to embed a merge).
|
|
707
|
+
const sub = argv[1];
|
|
708
|
+
if (sub === "list" || sub === "show" || sub === "reject" || sub === "apply") {
|
|
709
|
+
return {
|
|
710
|
+
command,
|
|
711
|
+
engine: "ts",
|
|
712
|
+
reason: "DS7.US3 shadow list/show/reject/apply ported to TS",
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
if (sub === "scan") {
|
|
716
|
+
return providerRoute(command, env, CULTIVATION_SCAN_TRANSPORT, "shadow scan");
|
|
717
|
+
}
|
|
718
|
+
return unknownSubcommand(command, SHADOW_SUBCOMMANDS, sub);
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
if (command === "mirror") {
|
|
722
|
+
const sub = argv[1];
|
|
723
|
+
if (sub === "load") {
|
|
724
|
+
const hasQuery = argv.includes("--query");
|
|
725
|
+
if (!hasQuery) {
|
|
726
|
+
return { command, engine: "ts", reason: "DS7.US4 deterministic mirror load ported to TS" };
|
|
727
|
+
}
|
|
728
|
+
// `MEMORY_RECEPTION=0` skips the classifier on both engines, so the
|
|
729
|
+
// chat fixture stops being required for a replay run; the embedding half
|
|
730
|
+
// still is, because the query still searches attachments and journeys.
|
|
731
|
+
const spec =
|
|
732
|
+
env.MEMORY_RECEPTION === "0"
|
|
733
|
+
? {
|
|
734
|
+
...MIRROR_QUERY_TRANSPORT,
|
|
735
|
+
replay: { embedding: MIRROR_QUERY_TRANSPORT.replay?.embedding },
|
|
736
|
+
}
|
|
737
|
+
: MIRROR_QUERY_TRANSPORT;
|
|
738
|
+
return providerRoute(command, env, spec, "load --query");
|
|
739
|
+
}
|
|
740
|
+
if (sub === "deactivate" || sub === "log" || sub === "journeys") {
|
|
741
|
+
return { command, engine: "ts", reason: `DS7.US4 mirror ${sub} ported to TS` };
|
|
742
|
+
}
|
|
743
|
+
return unknownSubcommand(command, MIRROR_SUBCOMMANDS, sub);
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
if (command === "conversation-logger") {
|
|
747
|
+
// CV22.DS7.US5 slice A: flipped 2026-09-02 after the seven-point checklist
|
|
748
|
+
// went green (goldens, real-DB-copy write parity, hook-inclusive E2E,
|
|
749
|
+
// regression pass, redaction, revertibility, ledger).
|
|
750
|
+
const sub = conversationLoggerSubcommand(argv);
|
|
751
|
+
if (sub === "repair-journeys" && argv.includes("--apply")) {
|
|
752
|
+
// The mutating repair takes the dated zip backup (DS7.TS1) before it
|
|
753
|
+
// writes -- the route itself refuses without it.
|
|
754
|
+
return {
|
|
755
|
+
command,
|
|
756
|
+
engine: "ts",
|
|
757
|
+
reason: "DS7.TS1 conversation-logger repair-journeys --apply ported to TS",
|
|
758
|
+
};
|
|
759
|
+
}
|
|
760
|
+
if (sub && TS_CONVERSATION_LOGGER_SUBCOMMANDS.has(sub)) {
|
|
761
|
+
return {
|
|
762
|
+
command,
|
|
763
|
+
engine: "ts",
|
|
764
|
+
reason: `DS7.US5 conversation-logger ${sub} ported to TS`,
|
|
765
|
+
};
|
|
766
|
+
}
|
|
767
|
+
if (sub === "session-start" && argv.includes("--fast")) {
|
|
768
|
+
// Unmute and reorient only: no close tail, so no gate.
|
|
769
|
+
return {
|
|
770
|
+
command,
|
|
771
|
+
engine: "ts",
|
|
772
|
+
reason: "DS7.US10 conversation-logger session-start --fast ported to TS",
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
if (sub && TS_CONVERSATION_LOGGER_LLM_SUBCOMMANDS.has(sub)) {
|
|
776
|
+
return providerRoute(command, env, CONVERSATION_TAIL_TRANSPORT, sub);
|
|
777
|
+
}
|
|
778
|
+
// The oracle answered an unknown logger subcommand with NOTHING, exit 0 --
|
|
779
|
+
// a silent success for a hook that named a subcommand that does not exist.
|
|
780
|
+
// It now gets the family's answer like every other family (D2, F6).
|
|
781
|
+
return unknownSubcommand(
|
|
782
|
+
command,
|
|
783
|
+
[...TS_CONVERSATION_LOGGER_SUBCOMMANDS, ...TS_CONVERSATION_LOGGER_LLM_SUBCOMMANDS],
|
|
784
|
+
sub,
|
|
785
|
+
);
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
if (command === "mode") {
|
|
789
|
+
const sub = argv.find((value, index) => index > 0 && MODE_SUBCOMMANDS.includes(value));
|
|
790
|
+
return sub
|
|
791
|
+
? { command, engine: "ts", reason: "DS7.US4 operating mode lifecycle ported to TS" }
|
|
792
|
+
: unknownSubcommand(command, MODE_SUBCOMMANDS, modeGivenSubcommand(argv));
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
if (command === "journey") {
|
|
796
|
+
// `set-path` (DS4), `update` (DS7.US1 Slice B), and the status read
|
|
797
|
+
// (DS7.US1 Slice A) are all ported. Everything besides `set-path`/
|
|
798
|
+
// `update` -- `status [slug]`, a bare slug, or no argument at all -- is a
|
|
799
|
+
// status read in the real Python dispatch (see render/journeyStatus.ts's
|
|
800
|
+
// slug-resolution quirk), so it all routes to the same TS status handler.
|
|
801
|
+
if (argv[1] === "set-path") {
|
|
802
|
+
return { command, engine: "ts", reason: "DS4 journey set-path write ported to TS" };
|
|
803
|
+
}
|
|
804
|
+
if (argv[1] === "update") {
|
|
805
|
+
return { command, engine: "ts", reason: "DS7.US1 Slice B journey update write ported to TS" };
|
|
806
|
+
}
|
|
807
|
+
return { command, engine: "ts", reason: "DS7.US1 journey status read ported to TS" };
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
if (command === "backup") {
|
|
811
|
+
return { command, engine: "ts", reason: "DS7.TS1 backup ported to TS" };
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
if (command === "repair-encoding") {
|
|
815
|
+
return { command, engine: "ts", reason: "DS7.TS1 repair-encoding ported to TS" };
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
if (command === "welcome") {
|
|
819
|
+
return { command, engine: "ts", reason: "DS7.TS3 welcome ported to TS" };
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
if (command === "runtime") {
|
|
823
|
+
const subcommand = argv[1] ?? "";
|
|
824
|
+
// Allowlist, not blocklist: a subcommand this build has never heard of
|
|
825
|
+
// must not acquire a TS route because `runtime` already has one.
|
|
826
|
+
if (!TS_RUNTIME_READ_SUBCOMMANDS.has(subcommand)) {
|
|
827
|
+
// CV22.DS10.US2: the updater family.
|
|
828
|
+
const reason = TS_RUNTIME_UPDATE_SUBCOMMANDS.get(subcommand);
|
|
829
|
+
if (reason !== undefined) return { command, engine: "ts", reason };
|
|
830
|
+
// CV22.DS10.US2 made the unknown answer TypeScript's own, rendered by
|
|
831
|
+
// the runtime route; CV22.DS10.TS5 moved it to the shared usage answer
|
|
832
|
+
// (D2), which renders the same bytes for every family.
|
|
833
|
+
return unknownSubcommand(command, RUNTIME_SUBCOMMANDS, subcommand);
|
|
834
|
+
}
|
|
835
|
+
return { command, engine: "ts", reason: `DS7.TS3 runtime ${subcommand} ported to TS` };
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
if (command === "soul") {
|
|
839
|
+
const subcommand = argv[1] ?? "";
|
|
840
|
+
// Allowlist by NAME, like `runtime`, and for the same reason: a subcommand
|
|
841
|
+
// the route never implemented must not inherit it because the family is
|
|
842
|
+
// claimed. That is the `conversations append` defect (RS009/CR055), which
|
|
843
|
+
// exited 0 and discarded the caller's payload.
|
|
844
|
+
if (!TS_SOUL_SUBCOMMANDS.has(subcommand)) {
|
|
845
|
+
return unknownSubcommand(command, TS_SOUL_SUBCOMMANDS, subcommand);
|
|
846
|
+
}
|
|
847
|
+
// `harvest save` is the one leaf that crosses the provider seam, through
|
|
848
|
+
// the embedding alone.
|
|
849
|
+
if (subcommand === "harvest" && soulHarvestAction(argv) === "save") {
|
|
850
|
+
return providerRoute(command, env, SOUL_HARVEST_TRANSPORT, "harvest save");
|
|
851
|
+
}
|
|
852
|
+
return { command, engine: "ts", reason: `DS7.US6 soul ${subcommand} ported to TS` };
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
if (command === "explore") {
|
|
856
|
+
const subcommand = argv[1] ?? "";
|
|
857
|
+
// TWO levels, because `explore` is the first family with a nested
|
|
858
|
+
// subparser. A single-level allowlist would claim `explore story
|
|
859
|
+
// <anything>` and answer an argv shape this route has never implemented.
|
|
860
|
+
if (!TS_EXPLORE_SUBCOMMANDS.has(subcommand)) {
|
|
861
|
+
return unknownSubcommand(command, TS_EXPLORE_SUBCOMMANDS, subcommand);
|
|
862
|
+
}
|
|
863
|
+
if (subcommand === "story") {
|
|
864
|
+
const action = exploreStoryAction(argv);
|
|
865
|
+
if (!TS_EXPLORE_STORY_ACTIONS.has(action ?? "")) {
|
|
866
|
+
return unknownSubcommand(command, TS_EXPLORE_STORY_ACTIONS, action, "explore story");
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
// `story promote` ends in a Builder session start (`cmd_story_promote`
|
|
870
|
+
// calls `cmd_load`), so it answers to the SAME composed transport decision
|
|
871
|
+
// `build load` does -- resolved here rather than inside the route because
|
|
872
|
+
// promote's writes are not idempotent, so a half-configured replay harness
|
|
873
|
+
// must be refused before the first mutation, not after.
|
|
874
|
+
if (subcommand === "story" && exploreStoryAction(argv) === "promote") {
|
|
875
|
+
return providerRoute(command, env, BUILD_LOAD_COMPOSITION, "story promote");
|
|
876
|
+
}
|
|
877
|
+
return { command, engine: "ts", reason: `DS7.US7 explore ${subcommand} ported to TS` };
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
if (command === "build") {
|
|
881
|
+
const subcommand = argv[1] ?? "";
|
|
882
|
+
// `refinement-story` and `change-request` are not build subcommands: the
|
|
883
|
+
// twenty verbs they had are answered by the retired table before this
|
|
884
|
+
// line, and anything else under them is an unknown build subcommand --
|
|
885
|
+
// what the oracle itself has answered since TS4 deleted the groups.
|
|
886
|
+
if (!TS_BUILD_SUBCOMMANDS.has(subcommand)) {
|
|
887
|
+
return unknownSubcommand(command, TS_BUILD_SUBCOMMANDS, subcommand);
|
|
888
|
+
}
|
|
889
|
+
// `load` composes the search and conversation-tail provider families. Its
|
|
890
|
+
// transport must be decided before the banner or any surface is printed.
|
|
891
|
+
if (subcommand === "load") {
|
|
892
|
+
return providerRoute(command, env, BUILD_LOAD_COMPOSITION, "load");
|
|
893
|
+
}
|
|
894
|
+
return { command, engine: "ts", reason: `DS7.US8 build ${subcommand} ported to TS` };
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
if (command === "journal") {
|
|
898
|
+
return providerRoute(command, env, JOURNAL_TRANSPORT);
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
if (command === "mcp") {
|
|
902
|
+
// CV22.DS10.TS5 (F9): the server DS9 ported, reached through the front
|
|
903
|
+
// door as the oracle's `mcp` was. The plugin launches it directly; this is
|
|
904
|
+
// the same server, not a second one.
|
|
905
|
+
return { command, engine: "ts", reason: "DS9 MCP server, launched through the front door" };
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
return unknownCommand(command);
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
// The subcommands of families whose routes above test them one by one. Named
|
|
912
|
+
// here so the usage answer lists exactly what the routes answer.
|
|
913
|
+
const IDENTITY_SUBCOMMANDS = ["list", "get", "set", "edit"];
|
|
914
|
+
const LIST_TARGETS = ["personas", "journeys", "extensions", "all"];
|
|
915
|
+
const DESCRIPTOR_SUBCOMMANDS = ["generate", "list"];
|
|
916
|
+
const TASKS_SUBCOMMANDS = [
|
|
917
|
+
"list",
|
|
918
|
+
"add",
|
|
919
|
+
"done",
|
|
920
|
+
"doing",
|
|
921
|
+
"block",
|
|
922
|
+
"import",
|
|
923
|
+
"delete",
|
|
924
|
+
"sync",
|
|
925
|
+
"sync-config",
|
|
926
|
+
];
|
|
927
|
+
const WEEK_SUBCOMMANDS = ["view", "plan", "save"];
|
|
928
|
+
const CONSOLIDATE_SUBCOMMANDS = ["scan", "apply", "reject", "list"];
|
|
929
|
+
const SHADOW_SUBCOMMANDS = ["scan", "apply", "reject", "list", "show"];
|
|
930
|
+
const MIRROR_SUBCOMMANDS = ["load", "deactivate", "log", "journeys"];
|
|
931
|
+
const MODE_SUBCOMMANDS = ["activate", "deactivate", "status"];
|
|
932
|
+
|
|
933
|
+
/** `mode`'s first positional, past the two options its parser declares; "" when none. */
|
|
934
|
+
function modeGivenSubcommand(argv: readonly string[]): string {
|
|
935
|
+
const args = [...argv.slice(1)];
|
|
936
|
+
for (const option of ["--mirror-home", "--session-id"]) {
|
|
937
|
+
const index = args.indexOf(option);
|
|
938
|
+
if (index !== -1) args.splice(index, 2);
|
|
939
|
+
}
|
|
940
|
+
return args[0] ?? "";
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
// Python's argparse subcommands for `soul`, by name.
|
|
944
|
+
const TS_SOUL_SUBCOMMANDS = new Set([
|
|
945
|
+
"load",
|
|
946
|
+
"listen",
|
|
947
|
+
"rite",
|
|
948
|
+
"close",
|
|
949
|
+
"review",
|
|
950
|
+
"propose",
|
|
951
|
+
"apply",
|
|
952
|
+
"fruit",
|
|
953
|
+
"harvest",
|
|
954
|
+
"prompt",
|
|
955
|
+
]);
|
|
956
|
+
|
|
957
|
+
// Python's argparse subcommands for `explore`, by name.
|
|
958
|
+
const TS_EXPLORE_SUBCOMMANDS = new Set(["load", "deactivate", "story"]);
|
|
959
|
+
|
|
960
|
+
// Python's `explore story` actions, by name.
|
|
961
|
+
//
|
|
962
|
+
// `promote` joined at CV22.DS7.US8 plateau 7, having waited on Python by name
|
|
963
|
+
// since US7 because `cmd_story_promote` ends by calling Builder `load`. It is
|
|
964
|
+
// the one action here whose engine also depends on the provider composition;
|
|
965
|
+
// see the decision above.
|
|
966
|
+
const TS_EXPLORE_STORY_ACTIONS = new Set([
|
|
967
|
+
"promote",
|
|
968
|
+
"show",
|
|
969
|
+
"list",
|
|
970
|
+
"archive",
|
|
971
|
+
"update",
|
|
972
|
+
"clear",
|
|
973
|
+
"open",
|
|
974
|
+
"thicken",
|
|
975
|
+
"snapshot",
|
|
976
|
+
"attractors",
|
|
977
|
+
"experiment",
|
|
978
|
+
"handoff",
|
|
979
|
+
]);
|
|
980
|
+
|
|
981
|
+
/** `explore story <action>`, skipping the options argparse strips first. */
|
|
982
|
+
function exploreStoryAction(argv: readonly string[]): string | undefined {
|
|
983
|
+
const args = [...argv.slice(2)];
|
|
984
|
+
for (const option of ["--mirror-home", "--db-path", "--session-id"]) {
|
|
985
|
+
const index = args.indexOf(option);
|
|
986
|
+
if (index !== -1) args.splice(index, 2);
|
|
987
|
+
}
|
|
988
|
+
return args[0];
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
/** `soul harvest <action>`, skipping the options argparse strips first. */
|
|
992
|
+
function soulHarvestAction(argv: readonly string[]): string | undefined {
|
|
993
|
+
const args = [...argv.slice(2)];
|
|
994
|
+
for (const option of ["--mirror-home", "--db-path", "--session-id", "--journey"]) {
|
|
995
|
+
const index = args.indexOf(option);
|
|
996
|
+
if (index !== -1) args.splice(index, 2);
|
|
997
|
+
}
|
|
998
|
+
return args[0];
|
|
999
|
+
}
|