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