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,410 @@
1
+ /**
2
+ * The one transport-selection precedence for provider-backed command families
3
+ * (CV22.DS8.US1, generalized by CV22.DS8.US3 / CR077).
4
+ *
5
+ * Sixteen leaves reach a provider. Each one needs the same decision: a
6
+ * deterministic replay fixture for CI and the smokes, or the live provider.
7
+ * Deriving that per leaf is how the US11 defect happened -- `LLM_ROLES` had a
8
+ * type and a hand-maintained guard that drifted apart, and the unit tests
9
+ * still passed. One list, no drift.
10
+ *
11
+ * Until CV22.DS10.TS5 there was a third answer, an operational revert to
12
+ * Python (`<revertVar>=0`), and it outranked everything else. It left with the
13
+ * engine it reverted to (decision D3). A leftover variable is inert, and
14
+ * `runtime diagnose` says so.
15
+ *
16
+ * CR077: a family may need MORE THAN ONE replay fixture (the close tail needs
17
+ * an LLM fixture and an embedding fixture; `mirror load --query` and the
18
+ * cultivation family are the same shape). The spec used to name one variable,
19
+ * so the "both or neither" rule lived privately in `loggerRuntime` -- and the
20
+ * router could report `replay` for an invocation the runtime then refused.
21
+ * The spec now carries the whole rule and `incomplete_replay` is a mode, so
22
+ * routing and the runtime agree by construction.
23
+ */
24
+
25
+ export type ProviderTransportMode = "replay" | "incomplete_replay" | "live";
26
+
27
+ /**
28
+ * Read-only env view. Indexed access is deliberate: families name their
29
+ * variables as data, so a typed per-variable interface would have to be
30
+ * extended for every family US3 flips.
31
+ */
32
+ export type ProviderTransportEnv = Readonly<Record<string, string | undefined>>;
33
+
34
+ /** The provider kinds a replay fixture can answer for. */
35
+ export type ProviderKind = "llm" | "embedding" | "credits";
36
+
37
+ /**
38
+ * Which fixture variable answers for which provider kind.
39
+ *
40
+ * Keyed rather than a bare list because the family provider factory has to
41
+ * know what each fixture IS, not only that it is required. The "all or none"
42
+ * rule derives from the declared set either way.
43
+ */
44
+ export type ProviderReplaySpec = Partial<Record<ProviderKind, string>>;
45
+
46
+ export interface ProviderTransportSpec {
47
+ /** Replay fixture variables this family needs. All of them, or none. */
48
+ replay?: ProviderReplaySpec;
49
+ /** Reason recorded when the live provider is selected. */
50
+ liveReason?: string;
51
+ }
52
+
53
+ export interface ProviderTransportDecision {
54
+ mode: ProviderTransportMode;
55
+ /** Route-decision reason, surfaced in the front-door log. Never a payload. */
56
+ reason: string;
57
+ /** Set only in `replay` mode: the fixture path for each declared kind. */
58
+ replayPaths?: Readonly<ProviderReplaySpec>;
59
+ /** Set only in `incomplete_replay`: the fixtures that must also be set. */
60
+ missingReplayVars?: readonly string[];
61
+ /** Set only in `incomplete_replay`: the fixtures that already are. */
62
+ presentReplayVars?: readonly string[];
63
+ }
64
+
65
+ /**
66
+ * Resolve which transport answers a provider-backed command.
67
+ *
68
+ * Precedence, highest first:
69
+ *
70
+ * 1. **replay** -- every declared fixture variable is set. Deterministic, no
71
+ * network, no spend. This is what CI and the smokes use. It deliberately
72
+ * does not require `MIRROR_TS_EXTERNAL_ROUTES`: that gate was DS5's safety
73
+ * catch while replay was the PRODUCTION route, and after the live cutover
74
+ * replay is a test transport.
75
+ * 2. **incomplete_replay** -- some declared fixtures are set and some are not.
76
+ * Never live: someone who set one variable meant to replay, and running
77
+ * half a fixture would spend real money. The only honest answer names the
78
+ * missing half.
79
+ * 3. **live** -- the DS8 default. An unconfigured install reaches the provider
80
+ * through TypeScript.
81
+ *
82
+ * An exported-but-empty fixture variable is treated as absent, because that
83
+ * shell accident should not become a file-not-found later.
84
+ */
85
+ export function resolveProviderTransport(
86
+ env: ProviderTransportEnv,
87
+ spec: ProviderTransportSpec,
88
+ ): ProviderTransportDecision {
89
+ const declared = declaredReplayVars(spec);
90
+ const present = declared.filter(([, variable]) => Boolean(env[variable]));
91
+
92
+ if (declared.length > 0 && present.length === declared.length) {
93
+ const replayPaths: ProviderReplaySpec = {};
94
+ for (const [kind, variable] of declared) replayPaths[kind] = env[variable];
95
+ return {
96
+ mode: "replay",
97
+ reason: `${declared.map(([, variable]) => variable).join(" + ")} replay transport`,
98
+ replayPaths,
99
+ };
100
+ }
101
+
102
+ if (present.length > 0) {
103
+ const missingReplayVars = declared
104
+ .filter(([, variable]) => !env[variable])
105
+ .map(([, variable]) => variable);
106
+ const presentReplayVars = present.map(([, variable]) => variable);
107
+ return {
108
+ mode: "incomplete_replay",
109
+ reason:
110
+ `incomplete replay fixture: ${presentReplayVars.join(", ")} set, ` +
111
+ `${missingReplayVars.join(", ")} missing`,
112
+ missingReplayVars,
113
+ presentReplayVars,
114
+ };
115
+ }
116
+
117
+ return { mode: "live", reason: spec.liveReason ?? "live provider" };
118
+ }
119
+
120
+ /** The declared fixture variables, in a stable kind order for stable reasons. */
121
+ function declaredReplayVars(spec: ProviderTransportSpec): [ProviderKind, string][] {
122
+ const order: ProviderKind[] = ["llm", "embedding", "credits"];
123
+ return order
124
+ .filter((kind) => Boolean(spec.replay?.[kind]))
125
+ .map((kind) => [kind, spec.replay?.[kind] as string]);
126
+ }
127
+
128
+ /**
129
+ * A command whose provider work belongs to families it does not own
130
+ * (CV22.DS7.US8 item 18b).
131
+ *
132
+ * `build load` is the case that forced this. It owns no provider seam: its two
133
+ * embeddings ARE the search family, and the previous conversation's close tail
134
+ * IS the conversation-tail family -- both already live in production since DS8.
135
+ * Its fixtures are the owner's; the composed families only say whether replay
136
+ * was intended somewhere, so a half-configured harness refuses instead of
137
+ * reaching the live provider through fixtures nobody set.
138
+ */
139
+ export interface ComposedProviderTransportSpec {
140
+ /** Named in every composed reason, so the log says which command composed. */
141
+ readonly label: string;
142
+ /** The family whose fixtures the command's providers are actually built from. */
143
+ readonly owner: ProviderTransportSpec;
144
+ /**
145
+ * Families whose work this command performs. Consulted for replay intent --
146
+ * never for fixtures, because a composing command runs every seam off the
147
+ * owner's.
148
+ */
149
+ readonly composes: readonly ProviderTransportSpec[];
150
+ }
151
+
152
+ /**
153
+ * Resolve one transport decision for a command that composes several families.
154
+ *
155
+ * Precedence, extending `resolveProviderTransport` rather than replacing it:
156
+ *
157
+ * 1. **any incomplete fixture refuses** -- including a composed family's. Its
158
+ * own pair rule cannot be honoured by a runtime that never sees it.
159
+ * 2. **replay intent anywhere requires the OWNER's complete fixture set.** A
160
+ * harness that configured the search family for replay and then ran `build
161
+ * load` would reach the live provider through fixtures nobody set -- a
162
+ * silent charge -- so the only honest answer names the fixtures missing
163
+ * here.
164
+ * 3. otherwise the owner's decision stands, unchanged.
165
+ *
166
+ * (A fourth rule, "any revert wins", sat on top until CV22.DS10.TS5 deleted
167
+ * the reverts with the engine they reverted to.)
168
+ *
169
+ * With an empty `composes` the result is byte-identical to the single-family
170
+ * decision: the composition is data, not a second precedence to drift from.
171
+ */
172
+ export function resolveComposedProviderTransport(
173
+ env: ProviderTransportEnv,
174
+ spec: ComposedProviderTransportSpec,
175
+ ): ProviderTransportDecision {
176
+ const owner = resolveProviderTransport(env, spec.owner);
177
+ const composed = spec.composes.map((family) => resolveProviderTransport(env, family));
178
+
179
+ if (owner.mode === "incomplete_replay") return owner;
180
+ const incomplete = composed.find((decision) => decision.mode === "incomplete_replay");
181
+ if (incomplete) return composedReason(incomplete, spec.label);
182
+
183
+ const replaying = spec.composes.filter((_, index) => composed[index]?.mode === "replay");
184
+ if (owner.mode === "live" && replaying.length > 0) {
185
+ const missingReplayVars = declaredReplayVars(spec.owner).map(([, variable]) => variable);
186
+ // A composition whose owner declares no fixture has nothing to name, and
187
+ // nothing of its own to replay either: the owner's decision stands.
188
+ if (missingReplayVars.length > 0) {
189
+ const presentReplayVars = replaying.flatMap((family) =>
190
+ declaredReplayVars(family)
191
+ .map(([, variable]) => variable)
192
+ .filter((variable) => Boolean(env[variable])),
193
+ );
194
+ return composedReason(
195
+ {
196
+ mode: "incomplete_replay",
197
+ reason:
198
+ `incomplete replay fixture: ${presentReplayVars.join(", ")} set, ` +
199
+ `${missingReplayVars.join(", ")} missing`,
200
+ missingReplayVars,
201
+ presentReplayVars,
202
+ },
203
+ spec.label,
204
+ );
205
+ }
206
+ }
207
+
208
+ return owner;
209
+ }
210
+
211
+ /** Same decision, with the composing command named for the front-door log. */
212
+ function composedReason(
213
+ decision: ProviderTransportDecision,
214
+ label: string,
215
+ ): ProviderTransportDecision {
216
+ return { ...decision, reason: `${decision.reason} (composed by ${label})` };
217
+ }
218
+
219
+ /**
220
+ * Exactly one half of a multi-fixture family's replay configuration is set.
221
+ *
222
+ * Someone who set one variable meant to replay, so the only honest answer is
223
+ * to stop and name the missing half. (Until CV22.DS10.TS5 a PLAIN family sent
224
+ * this case to the Python fallback, which has no replay transport and would
225
+ * have called the live provider; only compositions refused. With the fallback
226
+ * gone every family refuses.)
227
+ *
228
+ * Lived in `loggerRuntime` until CR077; it is the whole family rule's error,
229
+ * so it belongs beside the rule.
230
+ */
231
+ export class ReplayFixtureIncompleteError extends Error {
232
+ constructor(decision: ProviderTransportDecision) {
233
+ const missing = (decision.missingReplayVars ?? []).join(", ");
234
+ const present = (decision.presentReplayVars ?? []).join(", ");
235
+ super(
236
+ `${present} is set but ${missing} is not. This command needs EVERY replay ` +
237
+ "fixture its family declares; running with part of one would reach the live " +
238
+ `provider and spend real money. Set ${missing}, or unset ${present} to run live ` +
239
+ "deliberately.",
240
+ );
241
+ this.name = "ReplayFixtureIncompleteError";
242
+ }
243
+ }
244
+
245
+ // --- Family transport specs ------------------------------------------------
246
+ //
247
+ // Each provider-backed family names its variables here, in one place, rather
248
+ // than in the route that happens to consume them: `routing.ts` decides the
249
+ // route and the provider factory builds the providers, and both must read the
250
+ // same spec or they can disagree about which transport is live.
251
+
252
+ /**
253
+ * Fresh semantic search (CV22.DS8.US1).
254
+ *
255
+ * `MIRROR_TS_EXTERNAL_ROUTES` is deliberately absent: it was DS5's safety gate
256
+ * while replay was the PRODUCTION route for this leaf, and after the live
257
+ * cutover replay is a test transport.
258
+ */
259
+ export const SEARCH_TRANSPORT: ProviderTransportSpec = {
260
+ replay: { embedding: "MIRROR_TS_SEARCH_EMBEDDING_REPLAY" },
261
+ liveReason: "DS8.US1 fresh semantic search live",
262
+ };
263
+
264
+ /**
265
+ * The conversation close tail (CV22.DS8.US2): title, tags, summary, memory and
266
+ * task extraction, and their embeddings.
267
+ *
268
+ * Two fixtures back this family, not one -- the pair rule that motivated
269
+ * CR077. It now lives in the spec, so the router cannot report `replay` for an
270
+ * invocation the runtime refuses.
271
+ */
272
+ export const CONVERSATION_TAIL_TRANSPORT: ProviderTransportSpec = {
273
+ replay: {
274
+ llm: "MIRROR_TS_CONVERSATION_LLM_REPLAY",
275
+ embedding: "MIRROR_TS_CONVERSATION_EMBEDDING_REPLAY",
276
+ },
277
+ liveReason: "DS8.US2 conversation close tail live",
278
+ };
279
+
280
+ /**
281
+ * `soul harvest save` (CV22.DS8.US3) -- the one leaf of the Soul command that
282
+ * crosses the provider seam, and only through the embedding: the save supplies
283
+ * title, layer, and tags, so the journal classifier is unreachable.
284
+ */
285
+ export const SOUL_HARVEST_TRANSPORT: ProviderTransportSpec = {
286
+ replay: { embedding: "MIRROR_TS_SOUL_EMBEDDING_REPLAY" },
287
+ liveReason: "DS8.US3 soul harvest save live",
288
+ };
289
+
290
+ /**
291
+ * `consult credits` and `consult ask` (CV22.DS8.US3).
292
+ *
293
+ * TWO specs for one family, because the fixtures are genuinely per leaf:
294
+ * `credits` needs only the credits fixture, while `ask` needs the chat fixture
295
+ * AND the credits one (it fetches the call's real cost, then prints the
296
+ * balance). With the credits fixture alone, `credits` replays and `ask`
297
+ * refuses -- it must not go live for the half nobody configured.
298
+ */
299
+ export const CONSULT_CREDITS_TRANSPORT: ProviderTransportSpec = {
300
+ replay: { credits: "MIRROR_TS_CREDITS_REPLAY" },
301
+ liveReason: "DS8.US3 consult credits live",
302
+ };
303
+
304
+ export const CONSULT_ASK_TRANSPORT: ProviderTransportSpec = {
305
+ replay: { llm: "MIRROR_TS_CONSULT_LLM_REPLAY", credits: "MIRROR_TS_CREDITS_REPLAY" },
306
+ liveReason: "DS8.US3 consult ask live",
307
+ };
308
+
309
+ /**
310
+ * `mirror load --query` (CV22.DS8.US3): the reception classifier plus the
311
+ * query's own embedding for attachment and journey search.
312
+ *
313
+ * `MEMORY_RECEPTION=0` skips the classifier; the embedding half stays, which is
314
+ * why both fixtures are declared.
315
+ */
316
+ export const MIRROR_QUERY_TRANSPORT: ProviderTransportSpec = {
317
+ replay: {
318
+ llm: "MIRROR_TS_MIRROR_LLM_REPLAY",
319
+ embedding: "MIRROR_TS_MIRROR_EMBEDDING_REPLAY",
320
+ },
321
+ liveReason: "DS8.US3 mirror load --query live",
322
+ };
323
+
324
+ /**
325
+ * `consolidate scan` and `shadow scan` -- the two cultivation leaves that send
326
+ * a PROMPT. Everything around the call landed in CV22.DS8.US3; the flip
327
+ * itself waited for CV22.DS8.TS2, because until then TypeScript sent a fenced
328
+ * Markdown dump of the memories with no task statement and no JSON output
329
+ * contract -- invisible under replay, which answers by role and never reads
330
+ * the prompt. The real templates now travel with the call and their assembled
331
+ * bytes are digest-pinned against the Python oracle (`cultivation/propose.ts`).
332
+ */
333
+ export const CULTIVATION_SCAN_TRANSPORT: ProviderTransportSpec = {
334
+ replay: { llm: "MIRROR_TS_CULTIVATION_LLM_REPLAY" },
335
+ liveReason: "DS8.TS2 cultivation scan live",
336
+ };
337
+
338
+ /**
339
+ * `consolidate apply` (CV22.DS8.US3).
340
+ *
341
+ * A SEPARATE spec, and not blocked by TS2: apply sends no prompt at all. A
342
+ * `merge` embeds the merged content and an `identity_update` makes no provider
343
+ * call whatsoever, so the only fixture it can need is the embedding one. The
344
+ * plan and the burn-down ledger both said "the three cultivation leaves wait
345
+ * for TS2"; measured, it is two.
346
+ */
347
+ export const CULTIVATION_APPLY_TRANSPORT: ProviderTransportSpec = {
348
+ replay: { embedding: "MIRROR_TS_CULTIVATION_EMBEDDING_REPLAY" },
349
+ liveReason: "DS8.US3 consolidate apply live",
350
+ };
351
+
352
+ /** `journal` (CV22.DS8.US3): classify the entry, then embed the memory. */
353
+ export const JOURNAL_TRANSPORT: ProviderTransportSpec = {
354
+ replay: {
355
+ llm: "MIRROR_TS_JOURNAL_LLM_REPLAY",
356
+ embedding: "MIRROR_TS_JOURNAL_EMBEDDING_REPLAY",
357
+ },
358
+ liveReason: "DS8.US3 journal live",
359
+ };
360
+
361
+ /** `week plan` (CV22.DS8.US3). `week save` and `week view` make no provider call. */
362
+ export const WEEK_PLAN_TRANSPORT: ProviderTransportSpec = {
363
+ replay: { llm: "MIRROR_TS_WEEK_LLM_REPLAY" },
364
+ liveReason: "DS8.US3 week plan live",
365
+ };
366
+
367
+ /**
368
+ * `descriptor generate` (CV22.DS8.US3). One call per persona AND per journey
369
+ * when no `--layer/--key` narrows it, so the front-door log records `calls=N`.
370
+ */
371
+ export const DESCRIPTOR_TRANSPORT: ProviderTransportSpec = {
372
+ replay: { llm: "MIRROR_TS_DESCRIPTOR_LLM_REPLAY" },
373
+ liveReason: "DS8.US3 descriptor generate live",
374
+ };
375
+
376
+ /**
377
+ * `build load` (CV22.DS7.US8) -- the one leaf of the 27 that crosses the
378
+ * provider seam, and the only family here that owns no seam of its own.
379
+ *
380
+ * Two fixtures, because `load` reaches the model twice over: the embedding for
381
+ * its two searches (scoped and global, the same query), and the LLM plus
382
+ * embedding of the previous conversation's close tail, which `switchConversation`
383
+ * runs on the way in. Every seam inside the command is built from THESE
384
+ * variables, which is why an incomplete set here refuses even when another
385
+ * family's fixtures are present.
386
+ */
387
+ export const BUILD_LOAD_TRANSPORT: ProviderTransportSpec = {
388
+ replay: {
389
+ llm: "MIRROR_TS_BUILD_LLM_REPLAY",
390
+ embedding: "MIRROR_TS_BUILD_EMBEDDING_REPLAY",
391
+ },
392
+ liveReason: "DS7.US8 build load live",
393
+ };
394
+
395
+ /**
396
+ * The three specs `build load` resolves BEFORE it prints a byte (item 18b).
397
+ *
398
+ * Four surfaces -- transition card, entry surface, identity context, and the
399
+ * banner on stderr -- print ahead of the first provider call, so a refusal
400
+ * decided later would come after all of them in the Navigator's terminal.
401
+ *
402
+ * The composed pair is what makes a half-configured harness refuse: replay
403
+ * intent on the search or conversation-tail family requires this command's
404
+ * own fixtures, rather than reaching the live provider through ones nobody set.
405
+ */
406
+ export const BUILD_LOAD_COMPOSITION: ComposedProviderTransportSpec = {
407
+ label: "build load",
408
+ owner: BUILD_LOAD_TRANSPORT,
409
+ composes: [SEARCH_TRANSPORT, CONVERSATION_TAIL_TRANSPORT],
410
+ };
@@ -0,0 +1,253 @@
1
+ // Reversible UTF-8/Windows mojibake repair for Mirror user text.
2
+ //
3
+ // Port of `src/memory/cli/repair_encoding.py` (CV22.DS7.TS1). Older
4
+ // Windows/local-runtime combinations stored UTF-8 text after decoding it as a
5
+ // legacy code page, producing "é" where "é" was meant. The repair only touches
6
+ // two-code-point sequences that round-trip cleanly: re-encode through Latin-1
7
+ // (first pass) or Windows-1252 (second pass), then decode strictly as UTF-8;
8
+ // anything that fails either step is left exactly as it was. Legitimate text
9
+ // such as "Âncora" therefore survives, which is the whole safety argument.
10
+ //
11
+ // Parity notes, each pinned by the golden:
12
+ // - `.` matches one CODE POINT (`u` + `s` flags), never a UTF-16 unit.
13
+ // - Python's `bytes.decode("utf-8")` is strict; so is a fatal TextDecoder.
14
+ // - Windows-1252 has five undefined bytes and a repertoire outside Latin-1;
15
+ // a code point it cannot encode makes the pass leave the chunk unchanged,
16
+ // exactly like Python's UnicodeEncodeError branch.
17
+ // - The preview collapses whitespace with Python's `str.split()` set, which
18
+ // is not JavaScript's `\s`, and caps by code point, not `.length`.
19
+
20
+ import { type Database, type WritableDatabase, withTransaction } from "#db/database.ts";
21
+ import { pythonWhitespaceRegex } from "#util/pythonText.ts";
22
+
23
+ /** One repairable cell: where it is, what it holds, what it becomes. */
24
+ export interface RepairHit {
25
+ table: string;
26
+ column: string;
27
+ row_id: number;
28
+ before: string;
29
+ after: string;
30
+ }
31
+
32
+ // UTF-8 bytes decoded as Latin-1: "é", "ç", "ã", "º", ...
33
+ const LATIN1_UTF8_PAIR = /[\u00c2-\u00c3][\u0080-\u00bf]/gu;
34
+ // UTF-8 bytes decoded as Windows-1252: "Ó", "É", ... (any following code point).
35
+ const CP1252_UTF8_PAIR = /[\u00c2-\u00c3]./gsu;
36
+
37
+ /**
38
+ * User-text columns that can be scanned without touching arbitrary extension
39
+ * tables. Missing tables/columns are ignored for older database versions.
40
+ * Order matters: it is the oracle's scan order, hence the hit order.
41
+ */
42
+ const TEXT_TARGETS: readonly (readonly [string, readonly string[]])[] = [
43
+ ["identity", ["content"]],
44
+ ["attachments", ["content"]],
45
+ ["memories", ["content", "summary", "source", "journey", "layer"]],
46
+ ["messages", ["content"]],
47
+ ["conversations", ["title", "summary", "journey", "persona"]],
48
+ ["memory_access_log", ["access_context"]],
49
+ ["tasks", ["title", "description", "journey", "status", "notes"]],
50
+ ];
51
+
52
+ /**
53
+ * Windows-1252 code points for bytes 0x80–0x9F. `undefined` marks the five
54
+ * bytes (0x81, 0x8D, 0x8F, 0x90, 0x9D) the code page leaves unassigned;
55
+ * every other byte maps to itself (0x00–0x7F, 0xA0–0xFF), as in Latin-1.
56
+ */
57
+ const CP1252_HIGH_BLOCK: readonly (number | undefined)[] = [
58
+ 0x20ac,
59
+ undefined,
60
+ 0x201a,
61
+ 0x0192,
62
+ 0x201e,
63
+ 0x2026,
64
+ 0x2020,
65
+ 0x2021,
66
+ 0x02c6,
67
+ 0x2030,
68
+ 0x0160,
69
+ 0x2039,
70
+ 0x0152,
71
+ undefined,
72
+ 0x017d,
73
+ undefined,
74
+ undefined,
75
+ 0x2018,
76
+ 0x2019,
77
+ 0x201c,
78
+ 0x201d,
79
+ 0x2022,
80
+ 0x2013,
81
+ 0x2014,
82
+ 0x02dc,
83
+ 0x2122,
84
+ 0x0161,
85
+ 0x203a,
86
+ 0x0153,
87
+ undefined,
88
+ 0x017e,
89
+ 0x0178,
90
+ ];
91
+
92
+ const CP1252_BYTE_FOR_CODE_POINT: ReadonlyMap<number, number> = new Map(
93
+ CP1252_HIGH_BLOCK.flatMap((codePoint, offset) =>
94
+ codePoint === undefined ? [] : [[codePoint, 0x80 + offset] as const],
95
+ ),
96
+ );
97
+
98
+ const STRICT_UTF8 = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true });
99
+
100
+ /** Encode one code point as Latin-1, or `null` when it does not fit. */
101
+ function latin1Byte(codePoint: number): number | null {
102
+ return codePoint <= 0xff ? codePoint : null;
103
+ }
104
+
105
+ /** Encode one code point as Windows-1252, or `null` when the code page lacks it. */
106
+ function cp1252Byte(codePoint: number): number | null {
107
+ if (codePoint < 0x80 || (codePoint >= 0xa0 && codePoint <= 0xff)) return codePoint;
108
+ return CP1252_BYTE_FOR_CODE_POINT.get(codePoint) ?? null;
109
+ }
110
+
111
+ /**
112
+ * Re-encode `chunk` byte-per-code-point through `encodeByte` and decode the
113
+ * result strictly as UTF-8. Returns the chunk unchanged when either step
114
+ * fails — the same "leave it alone" contract as Python's `_decode_pair`.
115
+ */
116
+ function decodePair(chunk: string, encodeByte: (codePoint: number) => number | null): string {
117
+ const bytes: number[] = [];
118
+ for (const char of chunk) {
119
+ const byte = encodeByte(char.codePointAt(0) as number);
120
+ if (byte === null) return chunk;
121
+ bytes.push(byte);
122
+ }
123
+ try {
124
+ return STRICT_UTF8.decode(Uint8Array.from(bytes));
125
+ } catch {
126
+ return chunk;
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Return `text` with reversible mojibake repaired. Only two-code-point
132
+ * sequences that decode as UTF-8 after re-encoding through Latin-1 or
133
+ * Windows-1252 change; everything else — including legitimate Portuguese like
134
+ * "Âncora" — stays as it is.
135
+ */
136
+ export function repairText(text: string): string {
137
+ const latin1Pass = text.replace(LATIN1_UTF8_PAIR, (chunk) => decodePair(chunk, latin1Byte));
138
+ return latin1Pass.replace(CP1252_UTF8_PAIR, (chunk) => decodePair(chunk, cp1252Byte));
139
+ }
140
+
141
+ export function hasRepairableMojibake(text: string): boolean {
142
+ return repairText(text) !== text;
143
+ }
144
+
145
+ /**
146
+ * Python's `str.isspace()` set — what `str.split()` with no separator splits
147
+ * on. Differs from JavaScript `\s`: it includes U+001C–U+001F and excludes
148
+ * U+FEFF. Shared with the extension migration checksum, which depends on the
149
+ * same distinction (CV22.DS7.TS3).
150
+ */
151
+ const PYTHON_WHITESPACE = pythonWhitespaceRegex();
152
+
153
+ /**
154
+ * Port of `_preview`: collapse whitespace runs to single spaces, trim, and cap
155
+ * at `limit` code points with a trailing ellipsis.
156
+ */
157
+ export function previewText(text: string, limit = 80): string {
158
+ const compact = text
159
+ .split(PYTHON_WHITESPACE)
160
+ .filter((part) => part !== "")
161
+ .join(" ");
162
+ const codePoints = Array.from(compact);
163
+ if (codePoints.length > limit) {
164
+ return `${codePoints.slice(0, limit - 1).join("")}…`;
165
+ }
166
+ return compact;
167
+ }
168
+
169
+ function existingColumns(db: Database, table: string): Set<string> {
170
+ try {
171
+ return new Set(
172
+ db
173
+ .prepare(`PRAGMA table_info("${table}")`)
174
+ .all()
175
+ .map((row) => String(row.name)),
176
+ );
177
+ } catch {
178
+ return new Set();
179
+ }
180
+ }
181
+
182
+ function scanTable(db: Database, table: string, columns: readonly string[]): RepairHit[] {
183
+ const existing = existingColumns(db, table);
184
+ const selected = columns.filter((column) => existing.has(column));
185
+ if (selected.length === 0) return [];
186
+
187
+ const quoted = selected.map((column) => `"${column}"`).join(", ");
188
+ let rows: Record<string, unknown>[];
189
+ try {
190
+ // Same statement as the oracle, deliberately without ORDER BY: same
191
+ // engine, same plan, same rowid order. The golden pins it.
192
+ rows = db.prepare(`SELECT _rowid_ AS __rowid__, ${quoted} FROM "${table}"`).all();
193
+ } catch {
194
+ return [];
195
+ }
196
+
197
+ const hits: RepairHit[] = [];
198
+ for (const row of rows) {
199
+ const rowId = Number(row.__rowid__);
200
+ for (const column of selected) {
201
+ const value = row[column];
202
+ if (typeof value !== "string") continue;
203
+ const fixed = repairText(value);
204
+ if (fixed !== value) {
205
+ hits.push({ table, column, row_id: rowId, before: value, after: fixed });
206
+ }
207
+ }
208
+ }
209
+ return hits;
210
+ }
211
+
212
+ /** Scan every target table/column and return the ordered list of repairable cells. */
213
+ export function scanDatabase(db: Database): RepairHit[] {
214
+ return TEXT_TARGETS.flatMap(([table, columns]) => scanTable(db, table, columns));
215
+ }
216
+
217
+ /** Apply every hit by `_rowid_` in one transaction; returns the count applied. */
218
+ export function applyRepairs(db: WritableDatabase, hits: readonly RepairHit[]): number {
219
+ return withTransaction(db, () => {
220
+ for (const hit of hits) {
221
+ db.prepare(`UPDATE "${hit.table}" SET "${hit.column}" = ? WHERE _rowid_ = ?`).run(
222
+ hit.after,
223
+ hit.row_id,
224
+ );
225
+ }
226
+ return hits.length;
227
+ });
228
+ }
229
+
230
+ export interface ScanReportOptions {
231
+ dbPath: string;
232
+ hits: readonly RepairHit[];
233
+ /** Maximum preview rows; negative values behave as zero, like `max(limit, 0)`. */
234
+ limit: number;
235
+ }
236
+
237
+ /** Render the scan section of the report exactly as the oracle prints it. */
238
+ export function renderScanReport({ dbPath, hits, limit }: ScanReportOptions): string {
239
+ const shown = Math.max(limit, 0);
240
+ const lines = [`Database: ${dbPath}`, `Repairable mojibake hits: ${hits.length}`];
241
+ for (const hit of hits.slice(0, shown)) {
242
+ lines.push(
243
+ `- ${hit.table}.${hit.column} row=${hit.row_id}: ${previewText(hit.before)} -> ${previewText(hit.after)}`,
244
+ );
245
+ }
246
+ if (hits.length > shown) lines.push(`… ${hits.length - shown} more`);
247
+ return `${lines.join("\n")}\n`;
248
+ }
249
+
250
+ /** Render the trailing line of a successful `--apply`. */
251
+ export function renderApplyReport(applied: number): string {
252
+ return `Applied repairs: ${applied}\n`;
253
+ }