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,387 @@
1
+ // Session backfills from runtime-written JSONL (CV22.DS7.US10 slice E).
2
+ //
3
+ // Ports `backfill_pi_sessions` and `backfill_codex_session` from
4
+ // `src/memory/cli/conversation_logger.py`. Both import a closed session the
5
+ // live hooks never saw, through the atomic seam in `sessionImport.ts`, and
6
+ // both are graded against `ts/test/goldens/backfill.golden.json`.
7
+ //
8
+ // The two are deliberately NOT unified beyond that seam, because Python's
9
+ // asymmetries are the contract:
10
+ // * Pi titles through `set_provisional_title` (whitespace collapse plus
11
+ // `title_source`/`title_status` provenance in metadata); Codex titles
12
+ // through `_generate_title` and a bare column write with no provenance.
13
+ // * Pi tolerates a corrupt or structurally invalid file by skipping THAT
14
+ // file and continuing the walk; Codex gives up on its single file.
15
+ // * Pi parses every timestamp form (millisecond epoch, string, absent);
16
+ // Codex stores the raw `timestamp` value and lets the model reject a
17
+ // missing one.
18
+ //
19
+ // Nothing here touches a provider or the clock directly: `now` is injected
20
+ // through `LoggerDeps`, which is what makes "no timestamp -> now" pinnable.
21
+ //
22
+ // CR121 (2026-10-11): a home imports only the Pi sessions newer than itself.
23
+ // Python imported every untracked session on the machine, which for a new
24
+ // home meant the person's whole Pi history, copied and spent on before the
25
+ // first prompt. The floor is explicit (`notBefore`), never defaulted, and
26
+ // the rule fails closed: there is no value that means "everything" except
27
+ // the epoch, which says so. The Codex importer is untouched: it imports the
28
+ // one session the wrapper names, not a history.
29
+
30
+ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
31
+ import { join, relative, sep } from "node:path";
32
+ import { generateTitle, type LoggerDeps, setProvisionalTitle } from "#conversation/logger.ts";
33
+ import { type ImportedMessage, importClosedConversation } from "#conversation/sessionImport.ts";
34
+ import type { WritableDatabase } from "#db/database.ts";
35
+ import { expandHome } from "#util/paths.ts";
36
+ import { comparePathComponents, sliceCodePoints } from "#util/pythonText.ts";
37
+
38
+ /** Python `_DEFAULT_PI_SESSIONS_DIR`: `~/.pi/agent/sessions`. */
39
+ export function defaultPiSessionsDir(homeDir: string): string {
40
+ return join(homeDir, ".pi", "agent", "sessions");
41
+ }
42
+
43
+ /**
44
+ * Python's source-directory resolution for the Pi backfill, in order:
45
+ * explicit argument -> `PI_SESSIONS_DIR` -> `~/.pi/agent/sessions`.
46
+ * (Python's module-level `_PI_SESSIONS_DIR` override exists only for its own
47
+ * test monkey-patching and has no runtime input; it is not a contract.)
48
+ */
49
+ export function resolvePiSessionsDir(
50
+ explicit: string | null | undefined,
51
+ env: { PI_SESSIONS_DIR?: string },
52
+ homeDir: string,
53
+ ): string {
54
+ if (explicit) return expandHome(explicit);
55
+ if (env.PI_SESSIONS_DIR) return expandHome(env.PI_SESSIONS_DIR);
56
+ return defaultPiSessionsDir(homeDir);
57
+ }
58
+
59
+ function isRecord(value: unknown): value is Record<string, unknown> {
60
+ return typeof value === "object" && value !== null && !Array.isArray(value);
61
+ }
62
+
63
+ /**
64
+ * Python `dict.get(key, default)` on a value that MUST be a dict: anything
65
+ * else raises AttributeError, which each parser turns into its own failure
66
+ * mode. The default applies only when the key is ABSENT -- a present `null`
67
+ * is returned as null, exactly as Python returns None, so a later `.get` on
68
+ * it raises rather than silently reading from the default.
69
+ */
70
+ function getOr(container: unknown, key: string, fallback: unknown): unknown {
71
+ if (!isRecord(container)) throw new TypeError(`expected an object with '${key}'`);
72
+ return key in container ? container[key] : fallback;
73
+ }
74
+
75
+ function getFrom(container: unknown, key: string): unknown {
76
+ return getOr(container, key, undefined);
77
+ }
78
+
79
+ /** Python truthiness for JSON values: empty containers are falsy too. */
80
+ function pythonTruthy(value: unknown): boolean {
81
+ if (Array.isArray(value)) return value.length > 0;
82
+ if (isRecord(value)) return Object.keys(value).length > 0;
83
+ return Boolean(value);
84
+ }
85
+
86
+ /** Python `_parse_pi_content`: a string as-is, text blocks joined by `\n`, else `""`. */
87
+ export function parsePiContent(content: unknown): string {
88
+ if (typeof content === "string") return content;
89
+ if (Array.isArray(content)) {
90
+ const parts: string[] = [];
91
+ for (const block of content) {
92
+ if (!isRecord(block) || block.type !== "text") continue;
93
+ const text = block.text ?? "";
94
+ // `"\n".join(...)` raises on a non-str item; the file is skipped.
95
+ if (typeof text !== "string") throw new TypeError("text block is not a string");
96
+ parts.push(text);
97
+ }
98
+ return parts.join("\n");
99
+ }
100
+ return "";
101
+ }
102
+
103
+ /**
104
+ * Python `datetime.fromtimestamp(ms / 1000, tz=utc).isoformat()`:
105
+ * `+00:00` suffix (not `Z`), microseconds only when non-zero, rounded from
106
+ * the fractional second the way CPython does (`round(frac * 1e6)`).
107
+ */
108
+ function pythonUtcIsoFromEpochMs(epochMs: number): string {
109
+ const seconds = epochMs / 1000;
110
+ let whole = Math.floor(seconds);
111
+ let micros = Math.round((seconds - whole) * 1e6);
112
+ if (micros >= 1_000_000) {
113
+ whole += 1;
114
+ micros -= 1_000_000;
115
+ }
116
+ const base = new Date(whole * 1000).toISOString().slice(0, 19);
117
+ const fraction = micros === 0 ? "" : `.${String(micros).padStart(6, "0")}`;
118
+ return `${base}${fraction}+00:00`;
119
+ }
120
+
121
+ /**
122
+ * Python `_parse_pi_timestamp`: a number above 1e10 is a millisecond epoch;
123
+ * a string is stored verbatim; anything else (absent, null, a seconds epoch,
124
+ * a boolean -- which is an int in Python but never exceeds 1e10) is `now`.
125
+ */
126
+ export function parsePiTimestamp(value: unknown, now: () => string): string {
127
+ if (typeof value === "number" && value > 1e10) return pythonUtcIsoFromEpochMs(value);
128
+ if (typeof value === "string") return value;
129
+ return now();
130
+ }
131
+
132
+ /**
133
+ * Parse one Pi session file into importable messages. Throws on any line
134
+ * that is not valid JSON or not the expected shape -- Python wraps the whole
135
+ * file loop in one `try` and skips the file on the first failure, even after
136
+ * earlier lines parsed cleanly.
137
+ */
138
+ function parsePiSessionFile(path: string, now: () => string): ImportedMessage[] {
139
+ const messages: ImportedMessage[] = [];
140
+ for (const line of readFileSync(path, "utf8").split(/\r?\n|\r/)) {
141
+ if (!line.trim()) continue;
142
+ const entry: unknown = JSON.parse(line);
143
+ if (getFrom(entry, "type") !== "message") continue;
144
+ const message = getOr(entry, "message", {});
145
+ const role = getOr(message, "role", "");
146
+ if (role !== "user" && role !== "assistant") continue;
147
+ const content = parsePiContent(getOr(message, "content", ""));
148
+ if (!content.trim()) continue;
149
+ const createdAt = parsePiTimestamp(getFrom(message, "timestamp"), now);
150
+ messages.push({ role, content, createdAt });
151
+ }
152
+ return messages;
153
+ }
154
+
155
+ /**
156
+ * Python `sorted(pi_sessions_dir.rglob("*.jsonl"))`: every `.jsonl` file
157
+ * under the directory, ordered by path components (see
158
+ * `comparePathComponents` for why that is not a string sort).
159
+ */
160
+ function listPiSessionFiles(sessionsDir: string): string[] {
161
+ const entries = readdirSync(sessionsDir, { recursive: true, withFileTypes: true });
162
+ const found: string[][] = [];
163
+ for (const entry of entries) {
164
+ if (!entry.name.endsWith(".jsonl")) continue;
165
+ const parents = relative(sessionsDir, entry.parentPath).split(sep).filter(Boolean);
166
+ found.push([...parents, entry.name]);
167
+ }
168
+ found.sort(comparePathComponents);
169
+ return found.map((components) => join(sessionsDir, ...components));
170
+ }
171
+
172
+ export interface PiBackfillOptions {
173
+ /** Already resolved through `resolvePiSessionsDir`. */
174
+ sessionsDir: string;
175
+ /**
176
+ * An ISO instant: a session that ended at or before it is not imported
177
+ * (CR121). Production passes the home's birth (`homeBornAt`); a caller
178
+ * that wants no floor passes the epoch and says so.
179
+ */
180
+ notBefore: string;
181
+ }
182
+
183
+ /**
184
+ * When this home was born: the first `applied_at` in the `_migrations`
185
+ * ledger. A fresh database stamps its whole ledger at its first open, and a
186
+ * database from any earlier era carries the ledger from its own first open,
187
+ * so the minimum is the birth of every home there is -- with no new table,
188
+ * column, or marker. Read once per maintenance run.
189
+ *
190
+ * The ledger means "which migrations ran, when"; reading its minimum as a
191
+ * birth is an inference, accepted because every plausible edit to the ledger
192
+ * moves the minimum forward and imports less, never more. `migrations.ts`
193
+ * names this consumer at the ledger's definition. A second consumer of
194
+ * "birth" is the trigger to model it as a row of its own.
195
+ *
196
+ * Fails closed: an empty ledger is an error, not a home born at the epoch.
197
+ */
198
+ export function homeBornAt(db: WritableDatabase): string {
199
+ const row = db.prepare("SELECT MIN(applied_at) AS born FROM _migrations").get() as
200
+ | { born: string | null }
201
+ | undefined;
202
+ if (typeof row?.born !== "string" || !row.born) {
203
+ throw new Error("the _migrations ledger is empty: this home's birth is unknown");
204
+ }
205
+ return row.born;
206
+ }
207
+
208
+ /** The instant of an ISO timestamp, or NaN for anything `Date.parse` rejects. */
209
+ function instant(iso: string): number {
210
+ return Date.parse(iso);
211
+ }
212
+
213
+ /**
214
+ * Python `backfill_pi_sessions`: import every untracked Pi session with at
215
+ * least two messages as a closed `pi` conversation, titled provisionally from
216
+ * the first user line. Returns the number imported.
217
+ *
218
+ * The session id is the session file's path, exactly as Python's
219
+ * `str(session_file)` -- which is also what the live Pi extension binds, so a
220
+ * session the hooks already track is skipped before its file is even read.
221
+ *
222
+ * CR121: a session is Mirror's only if it ended after `notBefore`. Two
223
+ * checks, in cost order. A file whose mtime is at or before the floor is
224
+ * skipped unread: its last message was written at or before its last write,
225
+ * so it cannot be newer. (This is what keeps a skipped session, which is
226
+ * never tracked, from being parsed again at every start for the rest of the
227
+ * home's life.) A file written after the floor is parsed and judged by its
228
+ * last message, the same instant it stores as `endedAt` -- a copy or a sync
229
+ * can touch a file long after its conversation ended. A session whose
230
+ * messages carry no parseable instant dates to `now` under the existing rule
231
+ * and is imported: undated is newer, not older.
232
+ */
233
+ export function backfillPiSessions(
234
+ db: WritableDatabase,
235
+ options: PiBackfillOptions,
236
+ deps: LoggerDeps,
237
+ ): number {
238
+ const floor = instant(options.notBefore);
239
+ if (Number.isNaN(floor)) {
240
+ throw new Error(`notBefore is not an instant: ${JSON.stringify(options.notBefore)}`);
241
+ }
242
+ if (!existsSync(options.sessionsDir)) return 0;
243
+
244
+ let count = 0;
245
+ for (const sessionFile of listPiSessionFiles(options.sessionsDir)) {
246
+ const sessionId = sessionFile;
247
+ if (db.prepare("SELECT 1 FROM runtime_sessions WHERE session_id = ?").get(sessionId)) {
248
+ continue;
249
+ }
250
+ if (statSync(sessionFile).mtimeMs <= floor) continue;
251
+ let messages: ImportedMessage[];
252
+ try {
253
+ messages = parsePiSessionFile(sessionFile, deps.nowIso);
254
+ } catch {
255
+ continue;
256
+ }
257
+ if (messages.length < 2) continue;
258
+ const endedAt = instant((messages.at(-1) as ImportedMessage).createdAt);
259
+ if (endedAt <= floor) continue;
260
+
261
+ const conversationId = importClosedConversation(
262
+ db,
263
+ sessionId,
264
+ { interface: "pi", messages, endedAt: (messages.at(-1) as ImportedMessage).createdAt },
265
+ deps,
266
+ );
267
+ if (conversationId === null) {
268
+ // A concurrent writer (a live hook) bound this session while the import
269
+ // waited for the write lock -- their binding wins.
270
+ continue;
271
+ }
272
+ const firstUser = messages.find((message) => message.role === "user");
273
+ if (firstUser) {
274
+ // Python: `content.strip().split("\n")[0][:60]` -- cut BEFORE the
275
+ // provisional-title whitespace collapse, by code point.
276
+ const title = sliceCodePoints(firstUser.content.trim().split("\n")[0] ?? "", 60);
277
+ setProvisionalTitle(db, conversationId, title);
278
+ }
279
+ count += 1;
280
+ }
281
+ return count;
282
+ }
283
+
284
+ interface CodexParse {
285
+ sessionId: string | null;
286
+ messages: { role: string; content: unknown; createdAt: unknown }[];
287
+ }
288
+
289
+ /**
290
+ * Parse a Codex session file. Throws on the first line that is not valid
291
+ * JSON or not the expected shape; Python's single `try` turns that into
292
+ * "return 0". Values are collected raw: Python validates them only when the
293
+ * import constructs its models.
294
+ */
295
+ function parseCodexSessionFile(path: string): CodexParse {
296
+ const parsed: CodexParse = { sessionId: null, messages: [] };
297
+ for (const line of readFileSync(path, "utf8").split(/\r?\n|\r/)) {
298
+ if (!line.trim()) continue;
299
+ const entry: unknown = JSON.parse(line);
300
+ const entryType = getFrom(entry, "type");
301
+ const payload = getOr(entry, "payload", {});
302
+ if (entryType === "session_meta") {
303
+ // Codex writes a UUID string; a non-string id is treated as absent
304
+ // (the same simplification the hook payloads make).
305
+ const id = getFrom(payload, "id");
306
+ parsed.sessionId = typeof id === "string" ? id : null;
307
+ } else if (entryType === "event_msg") {
308
+ const payloadType = getFrom(payload, "type");
309
+ if (payloadType === "user_message" || payloadType === "agent_message") {
310
+ const content = getOr(payload, "message", "");
311
+ // Python: `if content:` -- any truthy value is kept and validated later.
312
+ if (pythonTruthy(content)) {
313
+ parsed.messages.push({
314
+ role: payloadType === "user_message" ? "user" : "assistant",
315
+ content,
316
+ createdAt: getFrom(entry, "timestamp"),
317
+ });
318
+ }
319
+ }
320
+ }
321
+ }
322
+ return parsed;
323
+ }
324
+
325
+ export interface CodexBackfillOptions {
326
+ jsonlPath: string;
327
+ interface?: string;
328
+ }
329
+
330
+ /**
331
+ * Python `backfill_codex_session`: import one Codex session file as a closed
332
+ * conversation titled through `_generate_title`. Returns 1 on import, else 0.
333
+ *
334
+ * Throws when a kept event has a non-string `message` or `timestamp`: Python's
335
+ * `Message(...)` model rejects those inside the import transaction, so the
336
+ * database is left untouched and the caller sees the failure.
337
+ */
338
+ export function backfillCodexSession(
339
+ db: WritableDatabase,
340
+ options: CodexBackfillOptions,
341
+ deps: LoggerDeps,
342
+ ): number {
343
+ const jsonlPath = expandHome(options.jsonlPath);
344
+ if (!existsSync(jsonlPath) || !statSync(jsonlPath).isFile()) return 0;
345
+
346
+ let parsed: CodexParse;
347
+ try {
348
+ parsed = parseCodexSessionFile(jsonlPath);
349
+ } catch {
350
+ return 0;
351
+ }
352
+ if (!parsed.sessionId || parsed.messages.length === 0) return 0;
353
+ if (db.prepare("SELECT 1 FROM runtime_sessions WHERE session_id = ?").get(parsed.sessionId)) {
354
+ return 0;
355
+ }
356
+
357
+ const messages: ImportedMessage[] = parsed.messages.map((message) => {
358
+ if (typeof message.content !== "string") {
359
+ throw new TypeError("Codex event message must be a string");
360
+ }
361
+ if (typeof message.createdAt !== "string") {
362
+ throw new TypeError("Codex event timestamp must be a string");
363
+ }
364
+ return { role: message.role, content: message.content, createdAt: message.createdAt };
365
+ });
366
+
367
+ const conversationId = importClosedConversation(
368
+ db,
369
+ parsed.sessionId,
370
+ {
371
+ interface: options.interface ?? "codex",
372
+ messages,
373
+ endedAt: (messages.at(-1) as ImportedMessage).createdAt,
374
+ },
375
+ deps,
376
+ );
377
+ if (conversationId === null) return 0;
378
+
379
+ const firstUser = messages.find((message) => message.role === "user");
380
+ if (firstUser) {
381
+ db.prepare("UPDATE conversations SET title = ? WHERE id = ?").run(
382
+ generateTitle(firstUser.content),
383
+ conversationId,
384
+ );
385
+ }
386
+ return 1;
387
+ }