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,205 @@
1
+ import type { Database, WritableDatabase } from "#db/database.ts";
2
+
3
+ export const GLOBAL_STICKY_DEFAULTS_SESSION_ID = "__global_sticky_defaults__";
4
+ export const GLOBAL_OPERATING_MODE_SESSION_ID = "__global_operating_mode__";
5
+
6
+ export interface RuntimeSessionRow {
7
+ sessionId: string;
8
+ conversationId: string | null;
9
+ interface: string | null;
10
+ mirrorActive: boolean;
11
+ persona: string | null;
12
+ journey: string | null;
13
+ hookInjected: boolean;
14
+ active: boolean;
15
+ startedAt: string;
16
+ updatedAt: string;
17
+ closedAt: string | null;
18
+ metadata: string | null;
19
+ }
20
+
21
+ export interface RuntimeSessionPatch {
22
+ conversationId?: string | null;
23
+ interface?: string | null;
24
+ mirrorActive?: boolean;
25
+ persona?: string | null;
26
+ journey?: string | null;
27
+ hookInjected?: boolean;
28
+ active?: boolean;
29
+ closedAt?: string | null;
30
+ metadata?: string | null;
31
+ }
32
+
33
+ export function getRuntimeSession(db: Database, sessionId: string): RuntimeSessionRow | null {
34
+ const row = db.prepare("SELECT * FROM runtime_sessions WHERE session_id = ?").get(sessionId);
35
+ if (!row) return null;
36
+ return {
37
+ sessionId: String(row.session_id),
38
+ conversationId: nullableString(row.conversation_id),
39
+ interface: nullableString(row.interface),
40
+ mirrorActive: Boolean(row.mirror_active),
41
+ persona: nullableString(row.persona),
42
+ journey: nullableString(row.journey),
43
+ hookInjected: Boolean(row.hook_injected),
44
+ active: Boolean(row.active),
45
+ startedAt: String(row.started_at),
46
+ updatedAt: String(row.updated_at),
47
+ closedAt: nullableString(row.closed_at),
48
+ metadata: nullableString(row.metadata),
49
+ };
50
+ }
51
+
52
+ export function upsertRuntimeSession(
53
+ db: WritableDatabase,
54
+ sessionId: string,
55
+ patch: RuntimeSessionPatch,
56
+ nowIso: string,
57
+ ): RuntimeSessionRow {
58
+ const existing = getRuntimeSession(db, sessionId);
59
+ const value = <K extends keyof RuntimeSessionPatch>(
60
+ key: K,
61
+ fallback: RuntimeSessionRow[keyof RuntimeSessionRow],
62
+ ) => (patch[key] === undefined ? fallback : patch[key]);
63
+ const row: RuntimeSessionRow = {
64
+ sessionId,
65
+ conversationId: value("conversationId", existing?.conversationId ?? null) as string | null,
66
+ interface: value("interface", existing?.interface ?? null) as string | null,
67
+ mirrorActive: value("mirrorActive", existing?.mirrorActive ?? false) as boolean,
68
+ persona: value("persona", existing?.persona ?? null) as string | null,
69
+ journey: value("journey", existing?.journey ?? null) as string | null,
70
+ hookInjected: value("hookInjected", existing?.hookInjected ?? false) as boolean,
71
+ active: value("active", existing?.active ?? true) as boolean,
72
+ startedAt: existing?.startedAt ?? nowIso,
73
+ updatedAt: nowIso,
74
+ closedAt: value("closedAt", existing?.closedAt ?? null) as string | null,
75
+ metadata: value("metadata", existing?.metadata ?? null) as string | null,
76
+ };
77
+ db.prepare(
78
+ `INSERT INTO runtime_sessions
79
+ (session_id, conversation_id, interface, mirror_active, persona, journey,
80
+ hook_injected, active, started_at, updated_at, closed_at, metadata)
81
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
82
+ ON CONFLICT(session_id) DO UPDATE SET
83
+ conversation_id = excluded.conversation_id,
84
+ interface = excluded.interface,
85
+ mirror_active = excluded.mirror_active,
86
+ persona = excluded.persona,
87
+ journey = excluded.journey,
88
+ hook_injected = excluded.hook_injected,
89
+ active = excluded.active,
90
+ updated_at = excluded.updated_at,
91
+ closed_at = excluded.closed_at,
92
+ metadata = excluded.metadata`,
93
+ ).run(
94
+ row.sessionId,
95
+ row.conversationId,
96
+ row.interface,
97
+ Number(row.mirrorActive),
98
+ row.persona,
99
+ row.journey,
100
+ Number(row.hookInjected),
101
+ Number(row.active),
102
+ row.startedAt,
103
+ row.updatedAt,
104
+ row.closedAt,
105
+ row.metadata,
106
+ );
107
+ return row;
108
+ }
109
+
110
+ /**
111
+ * Python `compare_and_swap_runtime_session_metadata` (CV22.DS7.US8 plateau 2).
112
+ *
113
+ * Replace `metadata` only while it still equals the caller's observed value, and
114
+ * report whether the swap happened. The match is STRING EQUALITY on the stored
115
+ * metadata, which is what makes the delivery cursor's serialization a contract
116
+ * rather than a detail: a cursor written with different bytes for the same state
117
+ * cannot be swapped, so a cursor the Python engine wrote -- every database from
118
+ * before CV22.DS10.TS5 carries them -- would fail its first TypeScript write.
119
+ *
120
+ * The WHERE clause also requires `active = 1`, so a cleared row can never be
121
+ * swapped back into place.
122
+ */
123
+ export function compareAndSwapRuntimeSessionMetadata(
124
+ db: WritableDatabase,
125
+ sessionId: string,
126
+ options: { expectedMetadata: string; metadata: string },
127
+ nowIso: string,
128
+ ): boolean {
129
+ const result = db
130
+ .prepare(
131
+ `UPDATE runtime_sessions
132
+ SET metadata = ?, updated_at = ?
133
+ WHERE session_id = ? AND active = 1 AND metadata = ?`,
134
+ )
135
+ .run(options.metadata, nowIso, sessionId, options.expectedMetadata);
136
+ return Number(result.changes) === 1;
137
+ }
138
+
139
+ /**
140
+ * The session a caller was TOLD about: `--session-id`, else `MIRROR_SESSION_ID`.
141
+ * `null` means nobody named one. It never guesses, so it is the only resolver a
142
+ * caller may bind a journey or write project state on (CR008).
143
+ */
144
+ export function resolveNamedRuntimeSessionId(
145
+ explicitSessionId: string | null,
146
+ environmentSessionId: string | null = null,
147
+ ): string | null {
148
+ if (explicitSessionId?.trim()) return explicitSessionId;
149
+ if (environmentSessionId?.trim()) return environmentSessionId.trim();
150
+ return null;
151
+ }
152
+
153
+ /**
154
+ * A named session if there is one, else a GUESS: the most recently updated active
155
+ * row that is not a global pseudo-session.
156
+ *
157
+ * The guess is usually the active window, because the Pi extension logs each
158
+ * prompt under its own session id before any tool runs. It is wrong under
159
+ * interleaving (another window's prompt in between), and delivery-cursor rows
160
+ * qualify, so a lifecycle write can make a cursor "the session". Builder binding
161
+ * stopped using it in CR008; the callers that still do are CR100's.
162
+ */
163
+ export function resolveRuntimeSessionId(
164
+ db: Database,
165
+ explicitSessionId: string | null,
166
+ environmentSessionId: string | null = null,
167
+ ): string | null {
168
+ return (
169
+ resolveNamedRuntimeSessionId(explicitSessionId, environmentSessionId) ??
170
+ guessRuntimeSessionId(db)
171
+ );
172
+ }
173
+
174
+ function guessRuntimeSessionId(db: Database): string | null {
175
+ const row = db
176
+ .prepare(
177
+ `SELECT session_id FROM runtime_sessions
178
+ WHERE active = 1
179
+ AND session_id NOT IN (?, ?)
180
+ AND interface IS NOT NULL
181
+ AND interface != 'global_defaults'
182
+ ORDER BY updated_at DESC
183
+ LIMIT 1`,
184
+ )
185
+ .get(GLOBAL_OPERATING_MODE_SESSION_ID, GLOBAL_STICKY_DEFAULTS_SESSION_ID);
186
+ return row ? String(row.session_id) : null;
187
+ }
188
+
189
+ export function decodeMetadata(raw: string | null): Record<string, unknown> {
190
+ if (!raw) return {};
191
+ try {
192
+ const value: unknown = JSON.parse(raw);
193
+ return isRecord(value) ? value : {};
194
+ } catch {
195
+ return {};
196
+ }
197
+ }
198
+
199
+ function nullableString(value: unknown): string | null {
200
+ return typeof value === "string" ? value : null;
201
+ }
202
+
203
+ function isRecord(value: unknown): value is Record<string, unknown> {
204
+ return typeof value === "object" && value !== null && !Array.isArray(value);
205
+ }
@@ -0,0 +1,148 @@
1
+ import type { Database, WritableDatabase } from "#db/database.ts";
2
+ import {
3
+ decodeMetadata,
4
+ GLOBAL_OPERATING_MODE_SESSION_ID,
5
+ getRuntimeSession,
6
+ upsertRuntimeSession,
7
+ } from "#mirror/runtimeSession.ts";
8
+ import { pythonJsonDumps } from "#util/pyGenerators.ts";
9
+
10
+ export const OPERATING_MODE_METADATA_KEY = "operating_mode";
11
+
12
+ // `runtime_sessions.metadata` is written by BOTH cores for as long as any
13
+ // command that touches it is still on Python -- Soul (DS7.US6) shares the very
14
+ // same row and object. Python writes it with `json.dumps(..., ensure_ascii=
15
+ // False)`, whose separators are `", "` and `": "`; `JSON.stringify` writes
16
+ // neither and escapes nothing, so the two cores stored different bytes for
17
+ // identical state until DS7.US6 corrected it. Nothing failed, because both the
18
+ // mirror-state golden and the write-parity harness compare metadata by VALUE --
19
+ // which is exactly why the divergence survived. The bytes are pinned directly
20
+ // by `operating-mode-metadata.golden.json`.
21
+
22
+ /** Port of `MODE_ICONS`: the glyph each lens is announced with. */
23
+ export const MODE_ICONS: Readonly<Record<string, string>> = {
24
+ "Mirror Mode": "◌",
25
+ "Builder Mode": "■",
26
+ "Explorer Mode": "△",
27
+ "Soul Mode": "☾",
28
+ };
29
+
30
+ /** Port of `OperatingModeState.label`: the icon, then the mode, or just the mode. */
31
+ export function modeLabel(state: OperatingModeState): string {
32
+ const icon = MODE_ICONS[state.mode];
33
+ return icon ? `${icon} ${state.mode}` : state.mode;
34
+ }
35
+
36
+ export interface OperatingModeState {
37
+ mode: string;
38
+ journey: string | null;
39
+ }
40
+
41
+ export function activateOperatingMode(
42
+ db: WritableDatabase,
43
+ input: { mode: string; journey?: string | null; sessionId?: string | null },
44
+ nowIso: string,
45
+ ): OperatingModeState {
46
+ const mode = input.mode.trim();
47
+ if (!mode) throw new Error("mode must not be empty");
48
+ const journey = input.journey?.trim() || null;
49
+ const state = { mode, journey };
50
+ const payload = { active_mode: mode, active_journey: journey };
51
+ if (input.sessionId) {
52
+ const session = getRuntimeSession(db, input.sessionId);
53
+ const metadata = decodeMetadata(session?.metadata ?? null);
54
+ metadata[OPERATING_MODE_METADATA_KEY] = payload;
55
+ upsertRuntimeSession(
56
+ db,
57
+ input.sessionId,
58
+ { metadata: pythonJsonDumps(metadata), active: true },
59
+ nowIso,
60
+ );
61
+ } else {
62
+ upsertRuntimeSession(
63
+ db,
64
+ GLOBAL_OPERATING_MODE_SESSION_ID,
65
+ { metadata: pythonJsonDumps(payload), active: true },
66
+ nowIso,
67
+ );
68
+ }
69
+ return state;
70
+ }
71
+
72
+ export function deactivateOperatingMode(
73
+ db: WritableDatabase,
74
+ sessionId: string | null,
75
+ nowIso: string,
76
+ ): void {
77
+ if (!sessionId) {
78
+ upsertRuntimeSession(
79
+ db,
80
+ GLOBAL_OPERATING_MODE_SESSION_ID,
81
+ { metadata: null, active: false },
82
+ nowIso,
83
+ );
84
+ return;
85
+ }
86
+ const session = getRuntimeSession(db, sessionId);
87
+ if (!session) return;
88
+ const metadata = decodeMetadata(session.metadata);
89
+ delete metadata[OPERATING_MODE_METADATA_KEY];
90
+ upsertRuntimeSession(
91
+ db,
92
+ sessionId,
93
+ { metadata: Object.keys(metadata).length > 0 ? pythonJsonDumps(metadata) : null },
94
+ nowIso,
95
+ );
96
+ }
97
+
98
+ /**
99
+ * The mode stamped on ONE session row, with no fallback (CR008).
100
+ *
101
+ * A caller that binds on the answer must read this, not `getActiveOperatingMode`:
102
+ * that one falls back to the global row, which whichever window loaded last
103
+ * wrote.
104
+ */
105
+ export function getSessionOperatingMode(
106
+ db: Database,
107
+ sessionId: string,
108
+ ): OperatingModeState | null {
109
+ const session = getRuntimeSession(db, sessionId);
110
+ return stateFromPayload(decodeMetadata(session?.metadata ?? null)[OPERATING_MODE_METADATA_KEY]);
111
+ }
112
+
113
+ /** The session's own mode, else the global row: what the status line displays. */
114
+ export function getActiveOperatingMode(
115
+ db: Database,
116
+ sessionId: string | null,
117
+ ): OperatingModeState | null {
118
+ if (sessionId) {
119
+ const state = getSessionOperatingMode(db, sessionId);
120
+ if (state) return state;
121
+ }
122
+ const global = getRuntimeSession(db, GLOBAL_OPERATING_MODE_SESSION_ID);
123
+ if (!global?.active) return null;
124
+ return stateFromPayload(decodeMetadata(global.metadata));
125
+ }
126
+
127
+ function stateFromPayload(payload: unknown): OperatingModeState | null {
128
+ if (typeof payload !== "object" || payload === null || Array.isArray(payload)) return null;
129
+ const record = payload as Record<string, unknown>;
130
+ const mode = typeof record.active_mode === "string" ? record.active_mode.trim() : "";
131
+ if (!mode) return null;
132
+ const journey =
133
+ typeof record.active_journey === "string" && record.active_journey.trim()
134
+ ? record.active_journey.trim()
135
+ : null;
136
+ return { mode, journey };
137
+ }
138
+
139
+ export function renderModeActivation(state: OperatingModeState): string {
140
+ return state.journey
141
+ ? `Activated ${state.mode} for ${state.journey}\n`
142
+ : `Activated ${state.mode}\n`;
143
+ }
144
+
145
+ export function renderModeStatus(state: OperatingModeState | null): string {
146
+ if (!state) return "Mirror Mode\n";
147
+ return state.journey ? `${state.mode} · ${state.journey}\n` : `${state.mode}\n`;
148
+ }
@@ -0,0 +1,115 @@
1
+ /**
2
+ * What actually happened on one provider-backed call (CV22.DS8.US3).
3
+ *
4
+ * Three leaves in this story swallow every failure, faithfully: Python's
5
+ * `propose_consolidation` returns `None`, `propose_shadow_observations`
6
+ * returns `[]`, and `reception` returns an empty result — for a transport
7
+ * error, for output the parser rejects, and for an honest "nothing to say"
8
+ * alike. Reproducing that is correct parity, and it means a live run can
9
+ * report "no proposals found", cost real money, and look identical to a
10
+ * healthy run that genuinely found nothing.
11
+ *
12
+ * So the swallow stays, and the DIAGNOSIS travels beside it: one outcome per
13
+ * call, category only, into the front-door log. That is what lets the live
14
+ * smoke read outcomes instead of counting rows — a zero-proposal run that
15
+ * passed a count-based check is exactly the shape of the US2 false start
16
+ * ("zero live calls, clean report") arriving one story later
17
+ * (CV22.DS8.US3 plan review, ai-engineer).
18
+ *
19
+ * One taxonomy for every surface rather than a per-surface vocabulary. The
20
+ * plan sketched `proposed|no_action|…` for cultivation and `ok|empty|…` for
21
+ * reception; two vocabularies is two maps to keep in step, which is the
22
+ * `LLM_ROLES` shape. The log line already names the surface, so the outcome
23
+ * only has to name the SHAPE of what happened.
24
+ */
25
+
26
+ import { ProviderConfigError } from "#providers/config.ts";
27
+ import { LlmTransportError } from "#providers/openrouter.ts";
28
+
29
+ export type ProviderCallOutcome =
30
+ /** The model answered, the parser accepted it, and it carried a result. */
31
+ | "answered"
32
+ /** The model answered and the parser accepted it, but there was no result. */
33
+ | "empty"
34
+ /** A response arrived that the parser could not use. A PROMPT-layer signal. */
35
+ | "parse_failed"
36
+ /** No usable response arrived. A TRANSPORT-layer signal; `kind` narrows it. */
37
+ | "transport_failed";
38
+
39
+ export interface ProviderCallReport {
40
+ outcome: ProviderCallOutcome;
41
+ /** The AI-18 taxonomy class, only for `transport_failed`. Never a message. */
42
+ kind?: string;
43
+ }
44
+
45
+ export type OnProviderCallOutcome = (report: ProviderCallReport) => void;
46
+
47
+ /**
48
+ * Reduce a provider failure to its CLASS. Only the class travels: provider
49
+ * messages can echo request content, and the front-door log forbids payloads.
50
+ */
51
+ export function classifyProviderError(error: unknown): string {
52
+ if (error instanceof ProviderConfigError) return "config";
53
+ if (error instanceof LlmTransportError) return error.kind;
54
+ return "unknown";
55
+ }
56
+
57
+ /** `outcome=… kind=…`, the content-free form the front-door log carries. */
58
+ export function formatCallOutcome(surface: string, report: ProviderCallReport): string {
59
+ const kind = report.kind ? ` kind=${report.kind}` : "";
60
+ return `${surface} outcome=${report.outcome}${kind}`;
61
+ }
62
+
63
+ /**
64
+ * Collect per-call reports and render one summary line per invocation.
65
+ *
66
+ * `calls=N` is the fan-out number: `descriptor generate` without
67
+ * `--layer/--key` makes one call per persona AND per journey, and
68
+ * `consolidate scan` one per cluster. Neither is bounded by anything but an
69
+ * argument, so a runaway should be visible after the fact without reading the
70
+ * ledger (CV22.DS8.US3 plan review, security).
71
+ */
72
+ export class CallOutcomeTally {
73
+ private readonly reports: ProviderCallReport[] = [];
74
+
75
+ record(report: ProviderCallReport): void {
76
+ this.reports.push(report);
77
+ }
78
+
79
+ get calls(): number {
80
+ return this.reports.length;
81
+ }
82
+
83
+ /** `calls=3 answered=2 parse_failed=1`, in a stable outcome order. */
84
+ summary(): string {
85
+ const order: ProviderCallOutcome[] = ["answered", "empty", "parse_failed", "transport_failed"];
86
+ const counts = order
87
+ .map(
88
+ (outcome) => [outcome, this.reports.filter((r) => r.outcome === outcome).length] as const,
89
+ )
90
+ .filter(([, count]) => count > 0)
91
+ .map(([outcome, count]) => `${outcome}=${count}`);
92
+ return [`calls=${this.calls}`, ...counts].join(" ");
93
+ }
94
+
95
+ /**
96
+ * `timeout=2 rate_limit=1` -- the transport-failure classes behind a
97
+ * `transport_failed=N`, or `""` when there were none. Kept apart from
98
+ * `summary()` so that line's pinned shape stays stable; the first live
99
+ * `consolidate scan` (CV22.DS8.TS2) reported `transport_failed=2` and
100
+ * nothing else, and the kind is the one field that separates a slow
101
+ * provider from a rate limit from a bad body.
102
+ */
103
+ failureKinds(): string {
104
+ const counts = new Map<string, number>();
105
+ for (const report of this.reports) {
106
+ if (report.outcome !== "transport_failed") continue;
107
+ const kind = report.kind ?? "unknown";
108
+ counts.set(kind, (counts.get(kind) ?? 0) + 1);
109
+ }
110
+ return [...counts.entries()]
111
+ .sort(([a], [b]) => a.localeCompare(b))
112
+ .map(([kind, count]) => `${kind}=${count}`)
113
+ .join(" ");
114
+ }
115
+ }