mirror-mind 0.0.1 → 0.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (474) hide show
  1. package/.claude/hooks/log-assistant-reply.sh +85 -0
  2. package/.claude/hooks/log-session-end.sh +85 -0
  3. package/.claude/hooks/log-user-prompt.sh +85 -0
  4. package/.claude/hooks/mirror-inject.sh +85 -0
  5. package/.claude/hooks/session-start.sh +85 -0
  6. package/.gemini/hooks/log-assistant.sh +85 -0
  7. package/.gemini/hooks/log-user.sh +85 -0
  8. package/.gemini/hooks/session-end.sh +85 -0
  9. package/.gemini/hooks/session-start.sh +85 -0
  10. package/.pi/extensions/mirror-logger.ts +646 -0
  11. package/.pi/skills/mm-backup/SKILL.md +21 -0
  12. package/.pi/skills/mm-build/SKILL.md +1206 -0
  13. package/.pi/skills/mm-consolidate/SKILL.md +112 -0
  14. package/.pi/skills/mm-consult/SKILL.md +70 -0
  15. package/.pi/skills/mm-conversations/SKILL.md +16 -0
  16. package/.pi/skills/mm-discard/SKILL.md +39 -0
  17. package/.pi/skills/mm-explore/SKILL.md +255 -0
  18. package/.pi/skills/mm-help/SKILL.md +74 -0
  19. package/.pi/skills/mm-identity/SKILL.md +96 -0
  20. package/.pi/skills/mm-journal/SKILL.md +16 -0
  21. package/.pi/skills/mm-journey/SKILL.md +23 -0
  22. package/.pi/skills/mm-journeys/SKILL.md +15 -0
  23. package/.pi/skills/mm-memories/SKILL.md +17 -0
  24. package/.pi/skills/mm-mirror/SKILL.md +98 -0
  25. package/.pi/skills/mm-mute/SKILL.md +18 -0
  26. package/.pi/skills/mm-new/SKILL.md +18 -0
  27. package/.pi/skills/mm-recall/SKILL.md +17 -0
  28. package/.pi/skills/mm-release-notes/SKILL.md +52 -0
  29. package/.pi/skills/mm-seed/SKILL.md +19 -0
  30. package/.pi/skills/mm-shadow/SKILL.md +117 -0
  31. package/.pi/skills/mm-soul/SKILL.md +502 -0
  32. package/.pi/skills/mm-tasks/SKILL.md +24 -0
  33. package/.pi/skills/mm-update/SKILL.md +32 -0
  34. package/.pi/skills/mm-week/SKILL.md +28 -0
  35. package/.pi/skills/mm-welcome/SKILL.md +24 -0
  36. package/AGENTS.md +188 -0
  37. package/README.md +126 -9
  38. package/bin/loader.js +67 -0
  39. package/bin/mirror-hook.js +7 -0
  40. package/bin/mirror.js +7 -0
  41. package/docs/releases/index.md +100 -0
  42. package/docs/releases/pending-cutoffs.md +510 -0
  43. package/docs/releases/v0.10.0.md +123 -0
  44. package/docs/releases/v0.10.1.md +66 -0
  45. package/docs/releases/v0.10.10.md +51 -0
  46. package/docs/releases/v0.10.11.md +39 -0
  47. package/docs/releases/v0.10.12.md +57 -0
  48. package/docs/releases/v0.10.13.md +35 -0
  49. package/docs/releases/v0.10.14.md +42 -0
  50. package/docs/releases/v0.10.15.md +53 -0
  51. package/docs/releases/v0.10.16.md +35 -0
  52. package/docs/releases/v0.10.17.md +43 -0
  53. package/docs/releases/v0.10.2.md +64 -0
  54. package/docs/releases/v0.10.3.md +78 -0
  55. package/docs/releases/v0.10.4.md +42 -0
  56. package/docs/releases/v0.10.5.md +57 -0
  57. package/docs/releases/v0.10.6.md +47 -0
  58. package/docs/releases/v0.10.7.md +53 -0
  59. package/docs/releases/v0.10.8.md +49 -0
  60. package/docs/releases/v0.10.9.md +37 -0
  61. package/docs/releases/v0.11.0.md +86 -0
  62. package/docs/releases/v0.12.0.md +67 -0
  63. package/docs/releases/v0.13.0.md +64 -0
  64. package/docs/releases/v0.14.0.md +68 -0
  65. package/docs/releases/v0.15.0.md +48 -0
  66. package/docs/releases/v0.16.0.md +50 -0
  67. package/docs/releases/v0.16.1.md +55 -0
  68. package/docs/releases/v0.16.2.md +36 -0
  69. package/docs/releases/v0.17.0.md +50 -0
  70. package/docs/releases/v0.17.1.md +58 -0
  71. package/docs/releases/v0.17.2.md +42 -0
  72. package/docs/releases/v0.17.3.md +58 -0
  73. package/docs/releases/v0.18.0.md +84 -0
  74. package/docs/releases/v0.18.1.md +51 -0
  75. package/docs/releases/v0.18.2.md +69 -0
  76. package/docs/releases/v0.19.0.md +85 -0
  77. package/docs/releases/v0.19.1.md +46 -0
  78. package/docs/releases/v0.20.0.md +85 -0
  79. package/docs/releases/v0.21.0.md +61 -0
  80. package/docs/releases/v0.21.1.md +47 -0
  81. package/docs/releases/v0.22.0.md +77 -0
  82. package/docs/releases/v0.22.1.md +42 -0
  83. package/docs/releases/v0.23.0.md +105 -0
  84. package/docs/releases/v0.24.0.md +125 -0
  85. package/docs/releases/v0.25.0.md +107 -0
  86. package/docs/releases/v0.26.0.md +139 -0
  87. package/docs/releases/v0.26.1.md +65 -0
  88. package/docs/releases/v0.26.2.md +43 -0
  89. package/docs/releases/v0.27.0.md +63 -0
  90. package/docs/releases/v0.28.0.md +59 -0
  91. package/docs/releases/v0.29.0.md +61 -0
  92. package/docs/releases/v0.29.1.md +59 -0
  93. package/docs/releases/v0.29.2.md +59 -0
  94. package/docs/releases/v0.29.3.md +93 -0
  95. package/docs/releases/v0.30.0.md +75 -0
  96. package/docs/releases/v0.30.1.md +93 -0
  97. package/docs/releases/v0.30.2.md +66 -0
  98. package/docs/releases/v0.31.0.md +122 -0
  99. package/docs/releases/v0.31.1.md +142 -0
  100. package/docs/releases/v0.31.10.md +118 -0
  101. package/docs/releases/v0.31.11.md +88 -0
  102. package/docs/releases/v0.31.12.md +105 -0
  103. package/docs/releases/v0.31.13.md +99 -0
  104. package/docs/releases/v0.31.14.md +97 -0
  105. package/docs/releases/v0.31.2.md +115 -0
  106. package/docs/releases/v0.31.3.md +142 -0
  107. package/docs/releases/v0.31.4.md +117 -0
  108. package/docs/releases/v0.31.5.md +155 -0
  109. package/docs/releases/v0.31.6.md +132 -0
  110. package/docs/releases/v0.31.7.md +132 -0
  111. package/docs/releases/v0.31.8.md +120 -0
  112. package/docs/releases/v0.31.9.md +120 -0
  113. package/docs/releases/v0.32.0.md +261 -0
  114. package/docs/releases/v0.32.1.md +59 -0
  115. package/docs/releases/v0.33.0.md +164 -0
  116. package/docs/releases/v0.8.0.md +118 -0
  117. package/docs/releases/v0.9.0.md +98 -0
  118. package/docs/releases/v0.9.1.md +83 -0
  119. package/package.json +102 -6
  120. package/plugins/mirror-mind/.claude-plugin/plugin.json +17 -0
  121. package/plugins/mirror-mind/hooks/hooks.json +62 -0
  122. package/plugins/mirror-mind/hooks/log-assistant-reply.sh +98 -0
  123. package/plugins/mirror-mind/hooks/log-session-end.sh +98 -0
  124. package/plugins/mirror-mind/hooks/log-user-prompt.sh +98 -0
  125. package/plugins/mirror-mind/hooks/mirror-inject.sh +98 -0
  126. package/plugins/mirror-mind/hooks/session-start.sh +98 -0
  127. package/plugins/mirror-mind/skills/mm-backup/SKILL.md +27 -0
  128. package/plugins/mirror-mind/skills/mm-build/SKILL.md +1206 -0
  129. package/plugins/mirror-mind/skills/mm-consolidate/SKILL.md +112 -0
  130. package/plugins/mirror-mind/skills/mm-consult/SKILL.md +83 -0
  131. package/plugins/mirror-mind/skills/mm-conversations/SKILL.md +17 -0
  132. package/plugins/mirror-mind/skills/mm-discard/SKILL.md +40 -0
  133. package/plugins/mirror-mind/skills/mm-explore/SKILL.md +241 -0
  134. package/plugins/mirror-mind/skills/mm-help/SKILL.md +104 -0
  135. package/plugins/mirror-mind/skills/mm-identity/SKILL.md +96 -0
  136. package/plugins/mirror-mind/skills/mm-journal/SKILL.md +21 -0
  137. package/plugins/mirror-mind/skills/mm-journey/SKILL.md +37 -0
  138. package/plugins/mirror-mind/skills/mm-journeys/SKILL.md +15 -0
  139. package/plugins/mirror-mind/skills/mm-memories/SKILL.md +20 -0
  140. package/plugins/mirror-mind/skills/mm-mirror/SKILL.md +67 -0
  141. package/plugins/mirror-mind/skills/mm-mute/SKILL.md +18 -0
  142. package/plugins/mirror-mind/skills/mm-new/SKILL.md +18 -0
  143. package/plugins/mirror-mind/skills/mm-recall/SKILL.md +17 -0
  144. package/plugins/mirror-mind/skills/mm-release-notes/SKILL.md +31 -0
  145. package/plugins/mirror-mind/skills/mm-seed/SKILL.md +15 -0
  146. package/plugins/mirror-mind/skills/mm-shadow/SKILL.md +117 -0
  147. package/plugins/mirror-mind/skills/mm-soul/SKILL.md +488 -0
  148. package/plugins/mirror-mind/skills/mm-tasks/SKILL.md +36 -0
  149. package/plugins/mirror-mind/skills/mm-update/SKILL.md +30 -0
  150. package/plugins/mirror-mind/skills/mm-week/SKILL.md +46 -0
  151. package/plugins/mirror-mind/skills/mm-welcome/SKILL.md +22 -0
  152. package/scripts/codex-hooks/session-end.sh +85 -0
  153. package/scripts/codex-hooks/session-start.sh +85 -0
  154. package/scripts/codex-mirror.sh +48 -0
  155. package/templates/identity/README.md +119 -0
  156. package/templates/identity/ego/behavior.yaml +52 -0
  157. package/templates/identity/ego/constraints.yaml +15 -0
  158. package/templates/identity/ego/identity.yaml +43 -0
  159. package/templates/identity/journeys/personal-growth.yaml +39 -0
  160. package/templates/identity/organization/identity.yaml +24 -0
  161. package/templates/identity/organization/principles.yaml +17 -0
  162. package/templates/identity/personas/coach.yaml +94 -0
  163. package/templates/identity/personas/designer.yaml +93 -0
  164. package/templates/identity/personas/doctor.yaml +96 -0
  165. package/templates/identity/personas/engineer.yaml +77 -0
  166. package/templates/identity/personas/financial.yaml +97 -0
  167. package/templates/identity/personas/prompt-engineer.yaml +122 -0
  168. package/templates/identity/personas/researcher.yaml +88 -0
  169. package/templates/identity/personas/strategist.yaml +91 -0
  170. package/templates/identity/personas/teacher.yaml +95 -0
  171. package/templates/identity/personas/therapist.yaml +91 -0
  172. package/templates/identity/personas/thinker.yaml +74 -0
  173. package/templates/identity/personas/writer.yaml +76 -0
  174. package/templates/identity/self/config.yaml +16 -0
  175. package/templates/identity/self/soul.yaml +105 -0
  176. package/templates/identity/user/identity.yaml +30 -0
  177. package/ts/src/backup/staging.ts +62 -0
  178. package/ts/src/backup/zipBackup.ts +267 -0
  179. package/ts/src/backup/zipReader.ts +109 -0
  180. package/ts/src/backup/zipWriter.ts +148 -0
  181. package/ts/src/builder/activeCheckpoint.ts +134 -0
  182. package/ts/src/builder/aggregateStatus.ts +34 -0
  183. package/ts/src/builder/approve.ts +149 -0
  184. package/ts/src/builder/argv.ts +292 -0
  185. package/ts/src/builder/ariadMethod.ts +536 -0
  186. package/ts/src/builder/artifacts/artifactSurfaces.ts +106 -0
  187. package/ts/src/builder/artifacts/artifactWriter.ts +113 -0
  188. package/ts/src/builder/artifacts/closureArtifacts.ts +199 -0
  189. package/ts/src/builder/artifacts/deliveryStoryArtifacts.ts +316 -0
  190. package/ts/src/builder/artifacts/planArtifacts.ts +242 -0
  191. package/ts/src/builder/artifacts/scaffoldSections.ts +273 -0
  192. package/ts/src/builder/artifacts/scaffoldState.ts +227 -0
  193. package/ts/src/builder/artifacts/storyFiles.ts +61 -0
  194. package/ts/src/builder/artifacts/storyIndex.ts +166 -0
  195. package/ts/src/builder/cadence.ts +59 -0
  196. package/ts/src/builder/card.ts +192 -0
  197. package/ts/src/builder/checkpointRefused.ts +66 -0
  198. package/ts/src/builder/closure.ts +964 -0
  199. package/ts/src/builder/commands.ts +2053 -0
  200. package/ts/src/builder/cursorPosition.ts +176 -0
  201. package/ts/src/builder/cursorTransitions.ts +77 -0
  202. package/ts/src/builder/deliveryCursor.ts +577 -0
  203. package/ts/src/builder/deliveryStoryClosure.ts +521 -0
  204. package/ts/src/builder/deliveryStoryPlan.ts +536 -0
  205. package/ts/src/builder/deliveryStoryReady.ts +160 -0
  206. package/ts/src/builder/deliveryStoryRoadmapClosure.ts +151 -0
  207. package/ts/src/builder/expand.ts +336 -0
  208. package/ts/src/builder/flowUnit.ts +339 -0
  209. package/ts/src/builder/homeSurface.ts +122 -0
  210. package/ts/src/builder/implementationGuard.ts +125 -0
  211. package/ts/src/builder/index.ts +11 -0
  212. package/ts/src/builder/lifecycleRefusal.ts +92 -0
  213. package/ts/src/builder/lifecycleRibbon.ts +159 -0
  214. package/ts/src/builder/load.ts +313 -0
  215. package/ts/src/builder/methodAdoption.ts +113 -0
  216. package/ts/src/builder/methodDefinition.ts +429 -0
  217. package/ts/src/builder/methodInspection.ts +371 -0
  218. package/ts/src/builder/plan.ts +408 -0
  219. package/ts/src/builder/planPreauthorization.ts +265 -0
  220. package/ts/src/builder/prepare.ts +225 -0
  221. package/ts/src/builder/projectPaths.ts +64 -0
  222. package/ts/src/builder/pull.ts +356 -0
  223. package/ts/src/builder/pullCandidates.ts +463 -0
  224. package/ts/src/builder/pullCandidatesRender.ts +238 -0
  225. package/ts/src/builder/refinementField.ts +93 -0
  226. package/ts/src/builder/releaseIntent.ts +173 -0
  227. package/ts/src/builder/resumeState.ts +84 -0
  228. package/ts/src/builder/resumeSurface.ts +149 -0
  229. package/ts/src/builder/roadmapGrammar.ts +300 -0
  230. package/ts/src/builder/roadmapScan.ts +97 -0
  231. package/ts/src/builder/roadmapScope.ts +210 -0
  232. package/ts/src/builder/scopePhrases.ts +146 -0
  233. package/ts/src/builder/storyPaths.ts +196 -0
  234. package/ts/src/builder/storyPlanPreauthorization.ts +287 -0
  235. package/ts/src/builder/surfaceProtocol.ts +42 -0
  236. package/ts/src/builder/templateGeneration.ts +148 -0
  237. package/ts/src/builder/transition.ts +178 -0
  238. package/ts/src/consult/args.ts +99 -0
  239. package/ts/src/consult/core.ts +122 -0
  240. package/ts/src/consult/modelCatalog.ts +55 -0
  241. package/ts/src/consult/render.ts +47 -0
  242. package/ts/src/conversation/append.ts +414 -0
  243. package/ts/src/conversation/backfill.ts +387 -0
  244. package/ts/src/conversation/closeTail.ts +467 -0
  245. package/ts/src/conversation/extraction.ts +333 -0
  246. package/ts/src/conversation/extractionDriver.ts +130 -0
  247. package/ts/src/conversation/extractionRun.ts +70 -0
  248. package/ts/src/conversation/hookPayload.ts +51 -0
  249. package/ts/src/conversation/journeyInference.ts +165 -0
  250. package/ts/src/conversation/journeyRepair.ts +114 -0
  251. package/ts/src/conversation/lifecycleFaces.ts +197 -0
  252. package/ts/src/conversation/lifecycleWrites.ts +313 -0
  253. package/ts/src/conversation/listing.ts +77 -0
  254. package/ts/src/conversation/logger.ts +851 -0
  255. package/ts/src/conversation/loggerCli.ts +321 -0
  256. package/ts/src/conversation/loggerRuntime.ts +259 -0
  257. package/ts/src/conversation/metadataLifecycle.ts +443 -0
  258. package/ts/src/conversation/recall.ts +66 -0
  259. package/ts/src/conversation/sessionComposites.ts +258 -0
  260. package/ts/src/conversation/sessionImport.ts +90 -0
  261. package/ts/src/conversation/transcriptBackfill.ts +141 -0
  262. package/ts/src/cultivation/applyActions.ts +241 -0
  263. package/ts/src/cultivation/cluster.ts +86 -0
  264. package/ts/src/cultivation/consolidationStore.ts +281 -0
  265. package/ts/src/cultivation/promptContext.ts +62 -0
  266. package/ts/src/cultivation/propose.ts +305 -0
  267. package/ts/src/cultivation/scan.ts +155 -0
  268. package/ts/src/db/backupGate.ts +47 -0
  269. package/ts/src/db/bootstrap.ts +108 -0
  270. package/ts/src/db/bootstrapLock.ts +223 -0
  271. package/ts/src/db/copyGuard.ts +60 -0
  272. package/ts/src/db/database.ts +397 -0
  273. package/ts/src/db/decode.ts +66 -0
  274. package/ts/src/db/ftsIntegrity.ts +41 -0
  275. package/ts/src/db/migrateOnOpen.ts +214 -0
  276. package/ts/src/db/migrations.ts +576 -0
  277. package/ts/src/db/readyOnOpen.ts +35 -0
  278. package/ts/src/db/rowDecode.ts +42 -0
  279. package/ts/src/db/schema.ts +432 -0
  280. package/ts/src/db/schemaInventory.ts +225 -0
  281. package/ts/src/db/schemaInventorySnapshot.ts +2107 -0
  282. package/ts/src/db/schemaState.ts +91 -0
  283. package/ts/src/db/schemaTsDivergence.ts +114 -0
  284. package/ts/src/descriptor/descriptorRead.ts +49 -0
  285. package/ts/src/docs/docsLint.ts +265 -0
  286. package/ts/src/explorer/handoff.ts +522 -0
  287. package/ts/src/explorer/render.ts +335 -0
  288. package/ts/src/explorer/story.ts +769 -0
  289. package/ts/src/explorer/transition.ts +72 -0
  290. package/ts/src/extensions/bindings.ts +170 -0
  291. package/ts/src/extensions/catalog.ts +249 -0
  292. package/ts/src/extensions/catalogCommands.ts +513 -0
  293. package/ts/src/extensions/catalogWrites.ts +652 -0
  294. package/ts/src/extensions/contextRuntime.ts +325 -0
  295. package/ts/src/extensions/dispatch.ts +374 -0
  296. package/ts/src/extensions/errors.ts +24 -0
  297. package/ts/src/extensions/manifest.ts +356 -0
  298. package/ts/src/extensions/migrations.ts +350 -0
  299. package/ts/src/extraction/conversation.ts +304 -0
  300. package/ts/src/extraction/conversationMetadata.ts +206 -0
  301. package/ts/src/extraction/fencing.ts +106 -0
  302. package/ts/src/extraction/json.ts +17 -0
  303. package/ts/src/extraction/prompts.ts +470 -0
  304. package/ts/src/frontDoor/args.ts +30 -0
  305. package/ts/src/frontDoor/argvShape.ts +106 -0
  306. package/ts/src/frontDoor/buildLoadRuntime.ts +139 -0
  307. package/ts/src/frontDoor/buildRoute.ts +398 -0
  308. package/ts/src/frontDoor/cli.ts +1872 -0
  309. package/ts/src/frontDoor/cloneRoleGuard.ts +98 -0
  310. package/ts/src/frontDoor/consultRoute.ts +50 -0
  311. package/ts/src/frontDoor/contentTailRoute.ts +239 -0
  312. package/ts/src/frontDoor/conversationLoggerRoute.ts +83 -0
  313. package/ts/src/frontDoor/cultivationRoute.ts +210 -0
  314. package/ts/src/frontDoor/dbPath.ts +131 -0
  315. package/ts/src/frontDoor/dbSafetyToolsRoute.ts +240 -0
  316. package/ts/src/frontDoor/exploreRoute.ts +572 -0
  317. package/ts/src/frontDoor/extensionCatalogRoute.ts +252 -0
  318. package/ts/src/frontDoor/frontDoorLog.ts +57 -0
  319. package/ts/src/frontDoor/identityWrite.ts +36 -0
  320. package/ts/src/frontDoor/journeyWriteRoute.ts +26 -0
  321. package/ts/src/frontDoor/lifecycleWriteRoute.ts +110 -0
  322. package/ts/src/frontDoor/liveBackup.ts +100 -0
  323. package/ts/src/frontDoor/mirrorModeRoute.ts +163 -0
  324. package/ts/src/frontDoor/namedRefusal.ts +35 -0
  325. package/ts/src/frontDoor/nodeSupport.ts +24 -0
  326. package/ts/src/frontDoor/render/consolidate.ts +199 -0
  327. package/ts/src/frontDoor/render/conversations.ts +33 -0
  328. package/ts/src/frontDoor/render/cultivationShared.ts +21 -0
  329. package/ts/src/frontDoor/render/descriptor.ts +20 -0
  330. package/ts/src/frontDoor/render/detectPersona.ts +50 -0
  331. package/ts/src/frontDoor/render/icons.ts +13 -0
  332. package/ts/src/frontDoor/render/identity.ts +59 -0
  333. package/ts/src/frontDoor/render/identityRows.ts +17 -0
  334. package/ts/src/frontDoor/render/inspectPersona.ts +53 -0
  335. package/ts/src/frontDoor/render/journeyStatus.ts +78 -0
  336. package/ts/src/frontDoor/render/journeys.ts +79 -0
  337. package/ts/src/frontDoor/render/list.ts +41 -0
  338. package/ts/src/frontDoor/render/memories.ts +67 -0
  339. package/ts/src/frontDoor/render/recall.ts +49 -0
  340. package/ts/src/frontDoor/render/shadow.ts +155 -0
  341. package/ts/src/frontDoor/render/tasks.ts +99 -0
  342. package/ts/src/frontDoor/render/tasksImportSync.ts +63 -0
  343. package/ts/src/frontDoor/render/week.ts +113 -0
  344. package/ts/src/frontDoor/routing.ts +1001 -0
  345. package/ts/src/frontDoor/runtimeRoute.ts +623 -0
  346. package/ts/src/frontDoor/searchRoute.ts +166 -0
  347. package/ts/src/frontDoor/seedPaths.ts +98 -0
  348. package/ts/src/frontDoor/sessionStartWelcomeRoute.ts +166 -0
  349. package/ts/src/frontDoor/soulRoute.ts +433 -0
  350. package/ts/src/frontDoor/stdinSource.ts +37 -0
  351. package/ts/src/frontDoor/tasksImportSyncRoute.ts +110 -0
  352. package/ts/src/frontDoor/tasksWriteRoute.ts +101 -0
  353. package/ts/src/frontDoor/usage.ts +176 -0
  354. package/ts/src/guards/claudePlugin.ts +298 -0
  355. package/ts/src/guards/packContents.ts +306 -0
  356. package/ts/src/guards/piSourcedSkills.ts +70 -0
  357. package/ts/src/guards/retiredRouteShadows.ts +57 -0
  358. package/ts/src/guards/retiredSurfaces.ts +564 -0
  359. package/ts/src/hooks/claude.ts +214 -0
  360. package/ts/src/hooks/codex.ts +45 -0
  361. package/ts/src/hooks/gemini.ts +126 -0
  362. package/ts/src/hooks/main.ts +109 -0
  363. package/ts/src/hooks/mirrorState.ts +71 -0
  364. package/ts/src/hooks/payload.ts +73 -0
  365. package/ts/src/hooks/runtime.ts +179 -0
  366. package/ts/src/hooks/runtimeDetection.ts +34 -0
  367. package/ts/src/identity/applyConsolidationIdentityUpdate.ts +67 -0
  368. package/ts/src/identity/descriptorGenerate.ts +162 -0
  369. package/ts/src/identity/identityEdit.ts +143 -0
  370. package/ts/src/identity/identityKey.ts +77 -0
  371. package/ts/src/identity/identityRead.ts +69 -0
  372. package/ts/src/identity/identityStore.ts +80 -0
  373. package/ts/src/identity/journeyListing.ts +53 -0
  374. package/ts/src/identity/personaInspect.ts +44 -0
  375. package/ts/src/identity/personaListing.ts +37 -0
  376. package/ts/src/identity/setIdentity.ts +64 -0
  377. package/ts/src/index.ts +231 -0
  378. package/ts/src/init/init.ts +111 -0
  379. package/ts/src/journey/journeyOptions.ts +131 -0
  380. package/ts/src/journey/journeyRemoval.ts +158 -0
  381. package/ts/src/journey/journeyStatus.ts +149 -0
  382. package/ts/src/journey/journeySyncFile.ts +87 -0
  383. package/ts/src/journey/journeyWrite.ts +203 -0
  384. package/ts/src/journey/parentJourney.ts +41 -0
  385. package/ts/src/journey/validateParentJourney.ts +55 -0
  386. package/ts/src/mcp/boundary.ts +151 -0
  387. package/ts/src/mcp/guards.ts +244 -0
  388. package/ts/src/mcp/main.ts +121 -0
  389. package/ts/src/mcp/payload.ts +141 -0
  390. package/ts/src/mcp/protocol.ts +141 -0
  391. package/ts/src/mcp/readModels.ts +241 -0
  392. package/ts/src/mcp/registry.ts +201 -0
  393. package/ts/src/mcp/serve.ts +120 -0
  394. package/ts/src/mcp/tools/deterministic.ts +137 -0
  395. package/ts/src/mcp/tools/providerCrossing.ts +187 -0
  396. package/ts/src/mcp/wire.ts +8 -0
  397. package/ts/src/memory/journal.ts +180 -0
  398. package/ts/src/memory/listing.ts +107 -0
  399. package/ts/src/memory/memoryWrite.ts +88 -0
  400. package/ts/src/memory/reinforcement.ts +35 -0
  401. package/ts/src/mirror/context.ts +161 -0
  402. package/ts/src/mirror/defaultResolution.ts +199 -0
  403. package/ts/src/mirror/mirrorForm.ts +43 -0
  404. package/ts/src/mirror/orchestration.ts +364 -0
  405. package/ts/src/mirror/reception.ts +194 -0
  406. package/ts/src/mirror/render.ts +75 -0
  407. package/ts/src/mirror/runtimeSession.ts +205 -0
  408. package/ts/src/mode/operatingMode.ts +148 -0
  409. package/ts/src/observability/callOutcome.ts +115 -0
  410. package/ts/src/observability/inspectLedger.ts +329 -0
  411. package/ts/src/observability/ledgerHooks.ts +126 -0
  412. package/ts/src/observability/llmCalls.ts +154 -0
  413. package/ts/src/persona/detectPersona.ts +81 -0
  414. package/ts/src/planning/promptAssembly.ts +111 -0
  415. package/ts/src/planning/weekPending.ts +81 -0
  416. package/ts/src/planning/weekPlan.ts +183 -0
  417. package/ts/src/planning/weekSave.ts +117 -0
  418. package/ts/src/providers/config.ts +189 -0
  419. package/ts/src/providers/cost.ts +64 -0
  420. package/ts/src/providers/credits.ts +204 -0
  421. package/ts/src/providers/embedding.ts +321 -0
  422. package/ts/src/providers/familyProviders.ts +147 -0
  423. package/ts/src/providers/llm.ts +334 -0
  424. package/ts/src/providers/openrouter.ts +244 -0
  425. package/ts/src/providers/redaction.ts +110 -0
  426. package/ts/src/providers/replay.ts +15 -0
  427. package/ts/src/providers/transport.ts +410 -0
  428. package/ts/src/repair/encodingRepair.ts +253 -0
  429. package/ts/src/runtime/backup.ts +151 -0
  430. package/ts/src/runtime/channel.ts +146 -0
  431. package/ts/src/runtime/config.ts +168 -0
  432. package/ts/src/runtime/diagnose.ts +686 -0
  433. package/ts/src/runtime/git.ts +510 -0
  434. package/ts/src/runtime/installKind.ts +220 -0
  435. package/ts/src/runtime/migrate.ts +154 -0
  436. package/ts/src/runtime/packageIdentity.ts +102 -0
  437. package/ts/src/runtime/releaseNotes.ts +244 -0
  438. package/ts/src/runtime/status.ts +557 -0
  439. package/ts/src/runtime/strategies/clone.ts +200 -0
  440. package/ts/src/runtime/strategies/package.ts +328 -0
  441. package/ts/src/runtime/treeRoot.ts +30 -0
  442. package/ts/src/runtime/update.ts +445 -0
  443. package/ts/src/runtime/updateGate.ts +40 -0
  444. package/ts/src/runtime/updatePipeline.ts +159 -0
  445. package/ts/src/runtime/version.ts +36 -0
  446. package/ts/src/runtime/wiring.ts +130 -0
  447. package/ts/src/search/memorySearch.ts +338 -0
  448. package/ts/src/search/ranker.ts +154 -0
  449. package/ts/src/seed/seed.ts +256 -0
  450. package/ts/src/seed/seedYaml.ts +133 -0
  451. package/ts/src/soul/apply.ts +218 -0
  452. package/ts/src/soul/harvest.ts +267 -0
  453. package/ts/src/soul/prompts/soul_beauty_voice.md +103 -0
  454. package/ts/src/soul/prompts/soul_self_voice.md +219 -0
  455. package/ts/src/soul/prompts/soul_wisdom_voice.md +121 -0
  456. package/ts/src/soul/prompts.ts +85 -0
  457. package/ts/src/soul/render.ts +358 -0
  458. package/ts/src/soul/state.ts +212 -0
  459. package/ts/src/soul/transition.ts +37 -0
  460. package/ts/src/tasks/journeyPathParse.ts +113 -0
  461. package/ts/src/tasks/taskImportSync.ts +142 -0
  462. package/ts/src/tasks/taskStore.ts +312 -0
  463. package/ts/src/tasks/weekView.ts +120 -0
  464. package/ts/src/util/clipCodePoints.ts +26 -0
  465. package/ts/src/util/paths.ts +63 -0
  466. package/ts/src/util/program.ts +9 -0
  467. package/ts/src/util/pyGenerators.ts +144 -0
  468. package/ts/src/util/pythonText.ts +317 -0
  469. package/ts/src/util/semver.ts +45 -0
  470. package/ts/src/util/shellWord.ts +25 -0
  471. package/ts/src/util/slug.ts +64 -0
  472. package/ts/src/welcome/card.ts +339 -0
  473. package/ts/src/welcome/statusLine.ts +101 -0
  474. package/ts/src/welcome/updateCache.ts +129 -0
@@ -0,0 +1,467 @@
1
+ // The conversation close tail (CV22.DS7.US10 slice C′).
2
+ //
3
+ // Ports `end_conversation`, `finalize_metadata_on_close`,
4
+ // `apply_generated_metadata_lifecycle`, and
5
+ // `_apply_force_generated_metadata_lifecycle` from
6
+ // `src/memory/services/conversation.py`.
7
+ //
8
+ // This is the path every ended session runs through, and its shape carries two
9
+ // properties that must not be lost:
10
+ //
11
+ // 1. `ended_at` is written BEFORE extraction, and finalization runs in a
12
+ // `finally`. An orphan therefore closes and still gets its non-manual
13
+ // metadata finalized even when extraction raises — the comment in Python
14
+ // calls this out explicitly, so it is behavior, not an accident.
15
+ // 2. The decision engine, not this module, decides whether each field is
16
+ // generated. How many LLM calls the tail makes is therefore a function of
17
+ // the dry-run decisions and the execution profile, which is why the
18
+ // ledger is graded as an ordered call sequence rather than a row count.
19
+
20
+ import {
21
+ type ConversationLike,
22
+ dryRunMetadataLifecycle,
23
+ type FieldReport,
24
+ type MessageLike,
25
+ messagesAreTitleable,
26
+ metadataExecutionProfile,
27
+ metadataProfileAction,
28
+ titleNeedsImprovement,
29
+ } from "#conversation/metadataLifecycle.ts";
30
+ import type { WritableDatabase } from "#db/database.ts";
31
+ import type { ExtractionMessage } from "#extraction/conversation.ts";
32
+ import {
33
+ generateConversationSummary,
34
+ generateConversationTags,
35
+ generateConversationTitle,
36
+ } from "#extraction/conversationMetadata.ts";
37
+ import type { LlmProvider, LlmResponse } from "#providers/llm.ts";
38
+ import { pythonJsonDumps } from "#util/pyGenerators.ts";
39
+
40
+ export interface CloseTailDeps {
41
+ llm: LlmProvider;
42
+ /**
43
+ * Fires per successful LLM call, in call order — the ledger seam. Receives
44
+ * the assembled prompt so a `full`-mode ledger can store it, as Python's
45
+ * `LLMResponse.prompt` does.
46
+ */
47
+ onLlmCall?: (role: string, response: LlmResponse, conversationId: string, prompt: string) => void;
48
+ now?: () => string;
49
+ userName?: string;
50
+ }
51
+
52
+ export interface MetadataLifecycleApplyReport {
53
+ conversation_id: string;
54
+ mode: string;
55
+ mutated: boolean;
56
+ changed: Record<string, unknown>;
57
+ skipped: Record<string, string>;
58
+ dry_run: unknown;
59
+ profile: string;
60
+ actions: Record<string, string>;
61
+ }
62
+
63
+ interface ConversationRow extends ConversationLike {
64
+ id: string;
65
+ title: string | null;
66
+ summary: string | null;
67
+ tags: string | null;
68
+ metadata: string | null;
69
+ }
70
+
71
+ /** Python's `_clean_title`: collapse whitespace, require non-empty, cap 160. */
72
+ export function cleanTitle(title: string): string {
73
+ const clean = title.trim().split(/\s+/u).filter(Boolean).join(" ");
74
+ if (!clean) throw new Error("title is required");
75
+ if (clean.length > 160) throw new Error("title must be at most 160 characters");
76
+ return clean;
77
+ }
78
+
79
+ /**
80
+ * Python's `_clean_summary`: collapse whitespace inside each blank-line-separated
81
+ * paragraph, drop empty paragraphs, rejoin with a blank line, then truncate to
82
+ * 1000 characters and right-strip.
83
+ */
84
+ export function cleanSummary(summary: string): string {
85
+ const paragraphs = summary
86
+ .trim()
87
+ .split("\n\n")
88
+ .filter((part) => part.trim())
89
+ .map((part) => part.split(/\s+/u).filter(Boolean).join(" "));
90
+ let clean = paragraphs.join("\n\n");
91
+ if (clean.length > 1000) clean = clean.slice(0, 1000).replace(/\s+$/u, "");
92
+ return clean;
93
+ }
94
+
95
+ /** Python's `_looks_like_artifact`: digits, hashes, and CSS sizes are not tags. */
96
+ export function looksLikeArtifact(term: string): boolean {
97
+ // Python's `str.isdigit()` is Unicode-aware (superscripts included); the last
98
+ // clause already rejects anything containing an ASCII digit, so the practical
99
+ // gate here is the same.
100
+ if (/^\d+$/u.test(term)) return true;
101
+ if (/^[0-9a-f]{7,}$/u.test(term)) return true;
102
+ if (/^\d+px$/u.test(term)) return true;
103
+ return /\d/u.test(term);
104
+ }
105
+
106
+ /** Python's `_title_metadata`. */
107
+ export function titleMetadata(
108
+ conversation: ConversationLike,
109
+ options: { source: string; status: string; previousTitle: string | null },
110
+ ): Record<string, unknown> {
111
+ const metadata = metadataDict(conversation.metadata);
112
+ if (options.previousTitle && options.previousTitle !== metadata.previous_title) {
113
+ metadata.previous_title = options.previousTitle;
114
+ }
115
+ metadata.title_source = options.source;
116
+ metadata.title_status = options.status;
117
+ return metadata;
118
+ }
119
+
120
+ export function metadataDict(raw: string | null | undefined): Record<string, unknown> {
121
+ if (!raw) return {};
122
+ try {
123
+ const value: unknown = JSON.parse(raw);
124
+ return value !== null && typeof value === "object" && !Array.isArray(value)
125
+ ? (value as Record<string, unknown>)
126
+ : {};
127
+ } catch {
128
+ return {};
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Generate and apply the safe updates the lifecycle report exposes.
134
+ *
135
+ * KNOWN WASTED CALL, preserved for parity (registered for Debt Review): when
136
+ * the tags action is apply/regenerate, no summary was generated, and the
137
+ * summary decision is `refine_candidate`, Python calls `suggest_summary` a
138
+ * second time and passes the result to `_suggest_tags` — which declares the
139
+ * parameter and never reads it. That is a real LLM call whose output is
140
+ * discarded. Porting it preserves the call sequence the ledger records; it is
141
+ * named here so DS8 does not price it as intentional.
142
+ */
143
+ export async function applyGeneratedMetadataLifecycle(
144
+ db: WritableDatabase,
145
+ conversationId: string,
146
+ options: CloseTailDeps & { source?: string; profileName?: string },
147
+ ): Promise<MetadataLifecycleApplyReport> {
148
+ const source = options.source ?? "metadata_lifecycle_apply";
149
+ const profileName = options.profileName ?? "manual_safe";
150
+
151
+ const conversation = getConversationForTitleOperation(db, conversationId);
152
+ const messages = getMessages(db, conversation.id);
153
+ const metadata = metadataDict(conversation.metadata);
154
+ const dryRun = dryRunMetadataLifecycle(conversation, messages, metadata, {
155
+ titleNeedsImprovement,
156
+ });
157
+ const fields = dryRun.fields;
158
+ const profile = metadataExecutionProfile(profileName);
159
+ const actions: Record<string, string> = Object.fromEntries(
160
+ Object.entries(fields).map(([field, report]) => [
161
+ field,
162
+ metadataProfileAction(profile, field, report as FieldReport),
163
+ ]),
164
+ );
165
+
166
+ const suggest = buildSuggesters(conversation, messages, options);
167
+
168
+ let generatedTitle: string | null = null;
169
+ let generatedSummary: string | null = null;
170
+ let generatedTags: string[] | null = null;
171
+
172
+ if (actions.title === "apply" || actions.title === "regenerate") {
173
+ generatedTitle = await suggest.title();
174
+ }
175
+ if (actions.summary === "apply" || actions.summary === "regenerate") {
176
+ generatedSummary = await suggest.summary();
177
+ }
178
+ if (actions.tags === "apply" || actions.tags === "regenerate") {
179
+ let tagSourceSummary = generatedSummary;
180
+ if (tagSourceSummary === null && fields.summary.decision === "refine_candidate") {
181
+ // The discarded call documented above. Kept so the ledger sequence matches.
182
+ tagSourceSummary = await suggest.summary();
183
+ }
184
+ generatedTags = await suggest.tags(tagSourceSummary);
185
+ }
186
+
187
+ if (!profile.forceRegenerate) {
188
+ throw new Error(
189
+ `metadata execution profile '${profileName}' is not force_regenerate; ` +
190
+ "the non-force apply path (apply_metadata_lifecycle) is not ported yet " +
191
+ "— close_time is the profile the close tail uses.",
192
+ );
193
+ }
194
+
195
+ const report = applyForceGeneratedMetadataLifecycle(db, conversation, {
196
+ dryRun,
197
+ actions,
198
+ title: generatedTitle,
199
+ summary: generatedSummary,
200
+ tags: generatedTags,
201
+ source,
202
+ });
203
+ return { ...report, profile: profile.name, actions };
204
+ }
205
+
206
+ /** Python's `_apply_force_generated_metadata_lifecycle`. */
207
+ function applyForceGeneratedMetadataLifecycle(
208
+ db: WritableDatabase,
209
+ conversation: ConversationRow,
210
+ input: {
211
+ dryRun: unknown;
212
+ actions: Record<string, string>;
213
+ title: string | null;
214
+ summary: string | null;
215
+ tags: string[] | null;
216
+ source: string;
217
+ },
218
+ ): Omit<MetadataLifecycleApplyReport, "profile" | "actions"> {
219
+ const { actions, source } = input;
220
+ // Rebound below by the title branch, exactly as Python rebinds `metadata`
221
+ // to `_title_metadata(...)`'s fresh dict — later branches mutate the new one.
222
+ let metadata = metadataDict(conversation.metadata);
223
+ const updates: Record<string, string> = {};
224
+ const changed: Record<string, unknown> = {};
225
+ const skipped: Record<string, string> = {};
226
+
227
+ if (actions.title === "preserve_manual") {
228
+ skipped.title = "manual_lock_preserved";
229
+ } else if (actions.title === "regenerate" && input.title) {
230
+ const clean = cleanTitle(input.title);
231
+ updates.title = clean;
232
+ metadata = titleMetadata(conversation, {
233
+ source,
234
+ status: "generated",
235
+ previousTitle: conversation.title,
236
+ });
237
+ changed.title = clean;
238
+ } else if (actions.title === "regenerate") {
239
+ skipped.title = "generation_failed";
240
+ } else {
241
+ skipped.title = actions.title as string;
242
+ }
243
+
244
+ if (actions.summary === "regenerate" && metadata.summary_source === "manual") {
245
+ skipped.summary = "manual_summary_preserved";
246
+ } else if (actions.summary === "regenerate" && input.summary) {
247
+ const clean = cleanSummary(input.summary);
248
+ if (clean) {
249
+ updates.summary = clean;
250
+ metadata.summary_status = "generated";
251
+ metadata.summary_source = source;
252
+ changed.summary = clean;
253
+ } else {
254
+ skipped.summary = "blank_value";
255
+ }
256
+ } else if (actions.summary === "regenerate") {
257
+ skipped.summary = "generation_failed";
258
+ } else {
259
+ skipped.summary = actions.summary as string;
260
+ }
261
+
262
+ if (actions.tags === "regenerate" && metadata.tags_source === "manual") {
263
+ skipped.tags = "manual_tags_preserved";
264
+ } else if (actions.tags === "regenerate" && input.tags && input.tags.length > 0) {
265
+ // `pythonJsonDumps`, not `JSON.stringify`: Python writes `["a", "b"]` with a
266
+ // space after the comma, and these bytes land in a TEXT column that both
267
+ // cores read and compare.
268
+ updates.tags = pythonJsonDumps(input.tags);
269
+ metadata.tags_status = "generated";
270
+ metadata.tags_source = source;
271
+ changed.tags = input.tags;
272
+ } else if (actions.tags === "regenerate") {
273
+ skipped.tags = "generation_failed";
274
+ } else {
275
+ skipped.tags = actions.tags as string;
276
+ }
277
+
278
+ if (Object.keys(changed).length > 0) {
279
+ metadata.metadata_lifecycle_version = 1;
280
+ metadata.last_metadata_update_source = source;
281
+ updates.metadata = pythonJsonDumps(metadata);
282
+ updateConversation(db, conversation.id, updates);
283
+ }
284
+
285
+ return {
286
+ conversation_id: conversation.id,
287
+ mode: "apply",
288
+ mutated: Object.keys(changed).length > 0,
289
+ changed,
290
+ skipped,
291
+ dry_run: input.dryRun,
292
+ };
293
+ }
294
+
295
+ /**
296
+ * Python's `maybe_generate_title`: improve one title when it is safe to replace.
297
+ *
298
+ * Distinct from the close tail's finalization: this is the startup-maintenance
299
+ * path, it touches only the title, and it returns whether the title actually
300
+ * changed so `retitle_pending_conversations` can count real work. Provider
301
+ * failure leaves the conversation untouched, exactly as Python's bare `except`
302
+ * does.
303
+ */
304
+ export async function maybeGenerateTitle(
305
+ db: WritableDatabase,
306
+ conversationId: string,
307
+ deps: CloseTailDeps & { source?: string },
308
+ ): Promise<boolean> {
309
+ const source = deps.source ?? "llm_auto";
310
+ const conversation = getConversationForTitleOperation(db, conversationId);
311
+ if (!titleNeedsImprovement(conversation)) return false;
312
+
313
+ const messages = getMessages(db, conversation.id);
314
+ if (!messagesAreTitleable(messages)) return false;
315
+
316
+ try {
317
+ const suggestion = await generateConversationTitle(deps.llm, messages, {
318
+ userName: deps.userName ?? "User",
319
+ onLlmCall: (response, prompt) =>
320
+ deps.onLlmCall?.("conversation_title", response, conversation.id, prompt),
321
+ });
322
+ if (!suggestion) return false;
323
+ const clean = cleanTitle(suggestion);
324
+ const metadata = titleMetadata(conversation, {
325
+ source,
326
+ status: "generated",
327
+ previousTitle: conversation.title,
328
+ });
329
+ updateConversation(db, conversation.id, {
330
+ title: clean,
331
+ metadata: pythonJsonDumps(metadata),
332
+ });
333
+ return clean !== conversation.title;
334
+ } catch {
335
+ return false;
336
+ }
337
+ }
338
+
339
+ /** Python's `finalize_metadata_on_close`. */
340
+ export async function finalizeMetadataOnClose(
341
+ db: WritableDatabase,
342
+ conversationId: string,
343
+ deps: CloseTailDeps,
344
+ ): Promise<MetadataLifecycleApplyReport> {
345
+ return applyGeneratedMetadataLifecycle(db, conversationId, {
346
+ ...deps,
347
+ source: "close_time_metadata_finalization",
348
+ profileName: "close_time",
349
+ });
350
+ }
351
+
352
+ /**
353
+ * Build the `CloseHooks` that `logger.ts`'s `endConversation` invokes.
354
+ *
355
+ * The ordering contract (`ended_at` -> extraction -> `finally` finalize) lives
356
+ * in exactly one place, the logger's close seam that US5 slice A built and
357
+ * tested. This module supplies the two tail implementations that seam was
358
+ * designed to receive; it deliberately does NOT re-implement the ordering,
359
+ * because two copies of that contract would eventually disagree.
360
+ */
361
+ export function createCloseHooks(
362
+ deps: CloseTailDeps & {
363
+ runExtraction?: (db: WritableDatabase, conversationId: string) => Promise<void> | void;
364
+ },
365
+ ): {
366
+ runExtraction?: (db: WritableDatabase, conversationId: string) => Promise<void> | void;
367
+ finalizeMetadata: (db: WritableDatabase, conversationId: string) => Promise<void>;
368
+ } {
369
+ return {
370
+ runExtraction: deps.runExtraction,
371
+ finalizeMetadata: async (db, conversationId) => {
372
+ await finalizeMetadataOnClose(db, conversationId, deps);
373
+ },
374
+ };
375
+ }
376
+
377
+ function buildSuggesters(
378
+ conversation: ConversationRow,
379
+ messages: ExtractionMessage[],
380
+ deps: CloseTailDeps,
381
+ ) {
382
+ const userName = deps.userName ?? "User";
383
+ const record = (role: string) => (response: LlmResponse, prompt: string) =>
384
+ deps.onLlmCall?.(role, response, conversation.id, prompt);
385
+
386
+ return {
387
+ async title(): Promise<string | null> {
388
+ // Python's `suggest_title` raises ValueError on no messages or an empty
389
+ // suggestion, and the caller maps that to None.
390
+ if (messages.length === 0) return null;
391
+ const suggestion = await generateConversationTitle(deps.llm, messages, {
392
+ userName,
393
+ onLlmCall: record("conversation_title"),
394
+ });
395
+ return suggestion ? suggestion : null;
396
+ },
397
+ async summary(): Promise<string | null> {
398
+ if (messages.length === 0) return null;
399
+ const suggestion = await generateConversationSummary(deps.llm, messages, {
400
+ userName,
401
+ onLlmCall: record("conversation_summary"),
402
+ });
403
+ const clean = cleanSummary(suggestion);
404
+ return clean ? clean : null;
405
+ },
406
+ async tags(_generatedSummary: string | null): Promise<string[]> {
407
+ // `_generatedSummary` is accepted and ignored, mirroring Python's
408
+ // `_suggest_tags(conversation_id, generated_summary)`.
409
+ const tags = await generateConversationTags(deps.llm, messages, {
410
+ userName,
411
+ onLlmCall: record("conversation_tags"),
412
+ });
413
+ return tags.filter((tag) => !looksLikeArtifact(tag));
414
+ },
415
+ };
416
+ }
417
+
418
+ function getConversationForTitleOperation(
419
+ db: WritableDatabase,
420
+ conversationId: string,
421
+ ): ConversationRow {
422
+ if (typeof conversationId !== "string" || !conversationId.trim()) {
423
+ throw new Error("conversationId is required");
424
+ }
425
+ const row =
426
+ (db
427
+ .prepare("SELECT id, title, summary, tags, metadata FROM conversations WHERE id = ?")
428
+ .get(conversationId) as Record<string, unknown> | undefined) ??
429
+ (db
430
+ .prepare(
431
+ "SELECT id, title, summary, tags, metadata FROM conversations WHERE id LIKE ? " +
432
+ "ORDER BY started_at DESC LIMIT 1",
433
+ )
434
+ .get(`${conversationId}%`) as Record<string, unknown> | undefined);
435
+ if (!row) throw new Error(`Conversation '${conversationId}' not found`);
436
+ return {
437
+ id: String(row.id),
438
+ title: typeof row.title === "string" ? row.title : null,
439
+ summary: typeof row.summary === "string" ? row.summary : null,
440
+ tags: typeof row.tags === "string" ? row.tags : null,
441
+ metadata: typeof row.metadata === "string" ? row.metadata : null,
442
+ };
443
+ }
444
+
445
+ function getMessages(
446
+ db: WritableDatabase,
447
+ conversationId: string,
448
+ ): (ExtractionMessage & MessageLike)[] {
449
+ const rows = db
450
+ .prepare("SELECT role, content FROM messages WHERE conversation_id = ? ORDER BY created_at")
451
+ .all(conversationId) as Record<string, unknown>[];
452
+ return rows.map((row) => ({ role: String(row.role), content: String(row.content ?? "") }));
453
+ }
454
+
455
+ function updateConversation(
456
+ db: WritableDatabase,
457
+ conversationId: string,
458
+ updates: Record<string, string>,
459
+ ): void {
460
+ const columns = Object.keys(updates);
461
+ if (columns.length === 0) return;
462
+ const assignments = columns.map((column) => `${column} = ?`).join(", ");
463
+ db.prepare(`UPDATE conversations SET ${assignments} WHERE id = ?`).run(
464
+ ...columns.map((column) => updates[column] as string),
465
+ conversationId,
466
+ );
467
+ }