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,203 @@
1
+ // TS journey writes — the write path of create_journey and set_project_path.
2
+ //
3
+ // A journey is an identity row (layer "journey"). Its metadata is serialized in
4
+ // the canonical JSON form (plain JSON.stringify) chosen in CV22.DS6.US1, which
5
+ // retired the transition-only Python json.dumps byte-mimicry (pyJson.ts) once TS
6
+ // owned schema custody. Existing rows written in the old Python dialects still
7
+ // read back fine — every reader JSON.parses — and converge to the canonical form
8
+ // on their next write. The project_path is normalized by the caller against the
9
+ // real filesystem (Path.resolve equivalent), so we take it pre-normalized
10
+ // (injected) — the same way id and now are injected.
11
+
12
+ import { type WritableDatabase, withTransaction } from "#db/database.ts";
13
+ import {
14
+ type IdentityRow,
15
+ updateIdentityMetadata,
16
+ upsertIdentity,
17
+ } from "#identity/identityStore.ts";
18
+ import { resolveParentJourney } from "#journey/parentJourney.ts";
19
+ import { type JourneyParentRow, validateParentJourney } from "#journey/validateParentJourney.ts";
20
+
21
+ export const JOURNEY_LAYER = "journey";
22
+
23
+ /** Raised when a journey slug has no identity row to update. */
24
+ export class JourneyNotFoundError extends Error {
25
+ readonly slug: string;
26
+ constructor(slug: string) {
27
+ super(`journey not found: ${slug}`);
28
+ this.slug = slug;
29
+ }
30
+ }
31
+
32
+ export interface JourneyFields {
33
+ /** Already normalized (Path.resolve equivalent) when present. */
34
+ projectPath?: string | null;
35
+ syncFile?: string | null;
36
+ icon?: string | null;
37
+ color?: string | null;
38
+ parentJourney?: string | null;
39
+ }
40
+
41
+ export interface CreateJourneyInput extends JourneyFields {
42
+ id: string;
43
+ slug: string;
44
+ content: string;
45
+ }
46
+
47
+ /** Read the complete parent graph through CR050's metadata-only resolver. */
48
+ function journeyParentRows(db: WritableDatabase): JourneyParentRow[] {
49
+ return db
50
+ .prepare("SELECT key, metadata FROM identity WHERE layer = ? ORDER BY key")
51
+ .all(JOURNEY_LAYER)
52
+ .map((row) => ({
53
+ key: typeof row.key === "string" ? row.key : "",
54
+ parentJourney: resolveParentJourney({
55
+ metadata: typeof row.metadata === "string" ? row.metadata : null,
56
+ }),
57
+ }));
58
+ }
59
+
60
+ /** Python's tolerant metadata-object boundary for parent updates. */
61
+ function metadataObject(raw: unknown): Record<string, unknown> {
62
+ if (typeof raw !== "string" || raw.length === 0) return {};
63
+ try {
64
+ const parsed: unknown = JSON.parse(raw);
65
+ return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)
66
+ ? { ...(parsed as Record<string, unknown>) }
67
+ : {};
68
+ } catch {
69
+ return {};
70
+ }
71
+ }
72
+
73
+ function normalizeParentJourney(value: string | null | undefined): string {
74
+ const clean = (value ?? "").trim();
75
+ if (clean.length > 500) throw new Error("parent_journey must be at most 500 characters");
76
+ return clean;
77
+ }
78
+
79
+ /**
80
+ * Select the non-empty metadata fields in a fixed order (trim; keep only
81
+ * non-empty), mirroring Python's _metadata_from_fields. The fixed order makes
82
+ * the canonical JSON.stringify output deterministic and gives set_project_path a
83
+ * stable key order to update in place.
84
+ */
85
+ export function journeyMetadata(fields: JourneyFields): Record<string, string> {
86
+ const ordered: [string, string | null | undefined][] = [
87
+ ["project_path", fields.projectPath],
88
+ ["sync_file", fields.syncFile],
89
+ ["icon", fields.icon],
90
+ ["color", fields.color],
91
+ ["parent_journey", fields.parentJourney],
92
+ ];
93
+ const metadata: Record<string, string> = {};
94
+ for (const [key, value] of ordered) {
95
+ const clean = key === "parent_journey" ? normalizeParentJourney(value) : (value ?? "").trim();
96
+ if (clean) {
97
+ metadata[key] = clean;
98
+ }
99
+ }
100
+ return metadata;
101
+ }
102
+
103
+ /**
104
+ * Port of create_journey's write: compose metadata, serialize, upsert identity,
105
+ * and atomically mirror `parent_journey` into the first-class column in the
106
+ * SAME transaction (CV22.DS6.US3 rider, activated here). Both statements
107
+ * commit or roll back together — a failure between them must never leave the
108
+ * JSON and the column disagreeing (see reinforcement.ts's `logAccess` for the
109
+ * same `withTransaction` idiom applied to a different two-statement write).
110
+ *
111
+ * `setParentJourney` below is the dedicated move seam. Python's
112
+ * `JourneyService.update_metadata_fields` can still change or remove a parent
113
+ * and does not know the column exists — that path remains live through the
114
+ * Python Workspace/web backend. CR050 therefore keeps metadata as mixed-engine
115
+ * semantic authority and treats column writes as projection maintenance.
116
+ */
117
+ export function createJourney(
118
+ db: WritableDatabase,
119
+ input: CreateJourneyInput,
120
+ nowIso: string,
121
+ ): void {
122
+ const metadata = journeyMetadata(input);
123
+ const metadataJson = Object.keys(metadata).length > 0 ? JSON.stringify(metadata) : null;
124
+ const parentJourney = metadata.parent_journey ?? null;
125
+ const row: IdentityRow = {
126
+ id: input.id,
127
+ layer: JOURNEY_LAYER,
128
+ key: input.slug,
129
+ content: input.content,
130
+ version: "1.0.0",
131
+ metadata: metadataJson,
132
+ };
133
+ withTransaction(db, () => {
134
+ if (parentJourney) {
135
+ validateParentJourney(input.slug, parentJourney, journeyParentRows(db));
136
+ }
137
+ upsertIdentity(db, row, nowIso);
138
+ db.prepare("UPDATE identity SET parent_journey = ? WHERE layer = ? AND key = ?").run(
139
+ parentJourney,
140
+ JOURNEY_LAYER,
141
+ input.slug,
142
+ );
143
+ });
144
+ }
145
+
146
+ /**
147
+ * Move an existing journey, or make it a root, without changing any other
148
+ * identity field. Graph read, full-ancestry validation, metadata update, and
149
+ * migration 017 projection maintenance share one immediate transaction so a
150
+ * concurrent writer cannot invalidate the checked ancestry before commit.
151
+ */
152
+ export function setParentJourney(
153
+ db: WritableDatabase,
154
+ slug: string,
155
+ parentJourney: string | null,
156
+ nowIso: string,
157
+ ): void {
158
+ const cleanParent = normalizeParentJourney(parentJourney);
159
+ withTransaction(db, () => {
160
+ const row = db
161
+ .prepare("SELECT metadata FROM identity WHERE layer = ? AND key = ?")
162
+ .get(JOURNEY_LAYER, slug);
163
+ if (row === undefined) throw new JourneyNotFoundError(slug);
164
+
165
+ const parentRows = journeyParentRows(db);
166
+ validateParentJourney(slug, cleanParent || null, parentRows);
167
+
168
+ const metadata = metadataObject(row.metadata);
169
+ if (cleanParent) metadata.parent_journey = cleanParent;
170
+ else delete metadata.parent_journey;
171
+ updateIdentityMetadata(db, JOURNEY_LAYER, slug, JSON.stringify(metadata), nowIso);
172
+ db.prepare("UPDATE identity SET parent_journey = ? WHERE layer = ? AND key = ?").run(
173
+ cleanParent || null,
174
+ JOURNEY_LAYER,
175
+ slug,
176
+ );
177
+ });
178
+ }
179
+
180
+ /**
181
+ * Port of set_project_path's write: read the journey metadata, set project_path
182
+ * (already normalized), re-serialize in the canonical JSON form, update.
183
+ * Preserves the existing key order and updates project_path in place when present.
184
+ */
185
+ export function setProjectPath(
186
+ db: WritableDatabase,
187
+ slug: string,
188
+ normalizedProjectPath: string,
189
+ nowIso: string,
190
+ ): void {
191
+ const row = db
192
+ .prepare("SELECT metadata FROM identity WHERE layer = ? AND key = ?")
193
+ .get(JOURNEY_LAYER, slug);
194
+ if (row === undefined) {
195
+ throw new JourneyNotFoundError(slug);
196
+ }
197
+ const existing =
198
+ typeof row.metadata === "string" && row.metadata.length > 0
199
+ ? (JSON.parse(row.metadata) as Record<string, unknown>)
200
+ : {};
201
+ const meta: Record<string, unknown> = { ...existing, project_path: normalizedProjectPath };
202
+ updateIdentityMetadata(db, JOURNEY_LAYER, slug, JSON.stringify(meta), nowIso);
203
+ }
@@ -0,0 +1,41 @@
1
+ // Mixed-engine parent-journey resolution (RS008/CR050).
2
+ //
3
+ // Python v0.31.9 stores the semantic parent in identity.metadata and remains
4
+ // runtime authority for the unported Workspace/web parent writer. TS migration
5
+ // 017's `identity.parent_journey` column is therefore only a derived projection
6
+ // while both engines can write: Python can move or unparent a journey without
7
+ // updating that column. Reading the column as authority or fallback would then
8
+ // resurrect stale structure.
9
+ //
10
+ // Every supported TS parent writer still updates metadata and the projection
11
+ // atomically. A future column-authority flip requires every parent writer to be
12
+ // on TS plus a separate decision; until then all TS readers use this one
13
+ // metadata-only resolver and column/metadata disagreement is drift to diagnose,
14
+ // never something a read silently repairs.
15
+
16
+ export interface ParentJourneySource {
17
+ /** Migration 017's derived projection. Not semantic authority during mixed-engine operation. */
18
+ parent_journey?: string | null;
19
+ /** The semantic journey metadata written by both Python and TS. */
20
+ metadata?: string | null;
21
+ }
22
+
23
+ /** Parse the parent journey out of JSON metadata; "" on malformed/non-object
24
+ * metadata or a non-string value, matching the Python reader's tolerance. */
25
+ function resolveParentJourneyFromMetadata(metadata: string | null | undefined): string {
26
+ if (!metadata) return "";
27
+ let payload: unknown;
28
+ try {
29
+ payload = JSON.parse(metadata);
30
+ } catch {
31
+ return "";
32
+ }
33
+ if (payload === null || typeof payload !== "object") return "";
34
+ const parent = (payload as Record<string, unknown>).parent_journey;
35
+ return typeof parent === "string" ? parent : "";
36
+ }
37
+
38
+ /** Resolve a journey's parent from the mixed-engine semantic authority. */
39
+ export function resolveParentJourney(source: ParentJourneySource): string {
40
+ return resolveParentJourneyFromMetadata(source.metadata);
41
+ }
@@ -0,0 +1,55 @@
1
+ // Port of `JourneyService._validate_parent_journey` (src/memory/services/journey.py).
2
+ //
3
+ // Pure over resolved journey rows — the DB seam supplies them, keeping this the
4
+ // decision core. Walks complete ancestry in Python's exact order and with
5
+ // Python's exact messages, so the front door and every parent write route
6
+ // reject an invalid parent identically to the Python oracle. Parent information
7
+ // is read from mixed-engine-authoritative metadata (CR050) via
8
+ // `resolveParentJourney`, the same resolver the listing sort uses, so validation
9
+ // and listing can never disagree.
10
+
11
+ /** Raised when a proposed parent_journey violates a hierarchy rule. */
12
+ export class ParentJourneyValidationError extends Error {}
13
+
14
+ /** One journey with its CR050 metadata-authoritative parent; "" when it has none. */
15
+ export interface JourneyParentRow {
16
+ key: string;
17
+ parentJourney: string;
18
+ }
19
+
20
+ /**
21
+ * Validate a proposed `parentJourney` for `journey` against all known journey
22
+ * rows. No-op when the parent is empty. Otherwise enforce the released
23
+ * arbitrary-depth boundary: the proposed parent must exist, its full ancestry
24
+ * must not reach the journey being moved, and that ancestry must not already
25
+ * contain a cycle. A missing legacy ancestor safely ends the walk, matching
26
+ * Python's tolerant read behavior.
27
+ */
28
+ export function validateParentJourney(
29
+ journey: string | null,
30
+ parentJourney: string | null,
31
+ rows: readonly JourneyParentRow[],
32
+ ): void {
33
+ if (!parentJourney) return;
34
+ if (journey && parentJourney === journey) {
35
+ throw new ParentJourneyValidationError("parent_journey cannot be the journey itself");
36
+ }
37
+ const byKey = new Map(rows.map((row) => [row.key, row]));
38
+ let current = byKey.get(parentJourney);
39
+ if (!current) {
40
+ throw new ParentJourneyValidationError(`Parent journey '${parentJourney}' not found`);
41
+ }
42
+
43
+ const visited = new Set<string>();
44
+ while (current) {
45
+ if (journey && current.key === journey) {
46
+ throw new ParentJourneyValidationError("parent_journey would create a cycle");
47
+ }
48
+ if (visited.has(current.key)) {
49
+ throw new ParentJourneyValidationError("Parent lineage contains an existing cycle");
50
+ }
51
+ visited.add(current.key);
52
+ if (!current.parentJourney) return;
53
+ current = byKey.get(current.parentJourney);
54
+ }
55
+ }
@@ -0,0 +1,151 @@
1
+ // The guard boundary around the wired MCP registry (CV22.DS9.TS1).
2
+ //
3
+ // Placement is the whole design. Validation lives in a WRAPPER rather than inside the
4
+ // tools, because the parity golden calls the tool functions directly: the tools stay
5
+ // Python-faithful -- `recall_conversation` with `limit=0` still returns the whole
6
+ // transcript, as the oracle does and `mcp-tools.golden.json` records -- while the registry
7
+ // a client reaches refuses the same arguments. DS9's D4 asked for the divergence to be
8
+ // visible on both sides rather than one silently overwriting the other, and this is what
9
+ // makes that possible.
10
+ //
11
+ // Both controls the threat model assigns to TS1 sit here:
12
+ //
13
+ // * argument bounds (items 1-2, exfiltration shapes) -- `limit=0` returning an entire
14
+ // transcript is the cheapest one the surface offers;
15
+ // * the spend decision (item 3, denial of wallet) -- applied only to the tools that can
16
+ // reach a provider, because a wallet guard has no business refusing a free read.
17
+
18
+ import type { Database } from "#db/database.ts";
19
+ import { decideSpend, PAID_TOOLS, readSpendState, type SpendPolicy } from "./guards.ts";
20
+ import type { Tool, ToolRegistry } from "./registry.ts";
21
+ import { buildRegistry } from "./registry.ts";
22
+
23
+ /** Per-tool argument bounds. A table, not a JSON-Schema validator: seven declarations
24
+ * do not earn one, and the rules a schema cannot express (the caps) are the point. */
25
+ interface ArgumentRules {
26
+ /** Inclusive upper bound for `limit`, when the tool takes one. */
27
+ limitMax?: number;
28
+ /** Inclusive upper bound for the length of `query`, when the tool takes one. */
29
+ queryMaxLength?: number;
30
+ }
31
+
32
+ const QUERY_MAX_LENGTH = 4_000;
33
+
34
+ const RULES: Record<string, ArgumentRules> = {
35
+ // Caps chosen an order of magnitude above any plausible agent request, so they bound
36
+ // abuse without ever shaping legitimate use.
37
+ search_memories: { limitMax: 50, queryMaxLength: QUERY_MAX_LENGTH },
38
+ list_conversations: { limitMax: 100 },
39
+ recall_conversation: { limitMax: 200 },
40
+ mirror_context: { queryMaxLength: QUERY_MAX_LENGTH },
41
+ detect_persona: { queryMaxLength: QUERY_MAX_LENGTH },
42
+ };
43
+
44
+ export interface GuardOptions {
45
+ db: Database;
46
+ policy: SpendPolicy;
47
+ /** Where the refusal line goes. Defaults to stderr, the client's log for this server. */
48
+ warn?: (line: string) => void;
49
+ /** Injectable clock for the window, used by tests. */
50
+ now?: () => Date;
51
+ }
52
+
53
+ /**
54
+ * Python's `int(...)` tolerance, bounded.
55
+ *
56
+ * `int("5")` and `int(5)` both succeed in Python and clients do send the string form, so
57
+ * that stays. `int(5.9)` also succeeds -- it truncates -- but a fractional limit from an
58
+ * agent is a mistake worth naming rather than silently rounding, and no oracle behaviour
59
+ * depends on it reaching the tool: the boundary refuses before the tool is entered.
60
+ */
61
+ function boundedLimit(value: unknown, max: number): number | null {
62
+ const numeric =
63
+ typeof value === "number"
64
+ ? value
65
+ : typeof value === "string" && value.trim() !== ""
66
+ ? Number(value)
67
+ : Number.NaN;
68
+ if (!Number.isInteger(numeric) || numeric < 1 || numeric > max) return null;
69
+ return numeric;
70
+ }
71
+
72
+ function received(value: unknown): string {
73
+ // Numbers and short scalars are safe to quote back; agent-authored TEXT is not -- the
74
+ // refusal travels into the model's context and into the client's log (RS005).
75
+ if (typeof value === "number" || typeof value === "boolean" || value === null)
76
+ return String(value);
77
+ if (typeof value === "string") return Number.isNaN(Number(value)) ? "a non-numeric value" : value;
78
+ return "a value of the wrong type";
79
+ }
80
+
81
+ /**
82
+ * Validate one call's arguments. Throws the refusal, which `protocol.ts` turns into an
83
+ * `isError` result -- a readable failure the session survives, never a protocol error.
84
+ *
85
+ * Argument refusals are deliberately NOT terminal: unlike a spend refusal, the right
86
+ * response is to correct the value and call again, so the text states the valid range and
87
+ * says nothing about retrying.
88
+ */
89
+ function validateArguments(tool: string, args: unknown): void {
90
+ const rules = RULES[tool];
91
+ if (!rules) return;
92
+ const record =
93
+ args !== null && typeof args === "object" && !Array.isArray(args)
94
+ ? (args as Record<string, unknown>)
95
+ : {};
96
+
97
+ if (rules.limitMax !== undefined && record.limit !== undefined && record.limit !== null) {
98
+ if (boundedLimit(record.limit, rules.limitMax) === null) {
99
+ throw new Error(
100
+ `${tool}: limit must be an integer from 1 to ${rules.limitMax} (received ${received(record.limit)})`,
101
+ );
102
+ }
103
+ }
104
+
105
+ if (rules.queryMaxLength !== undefined && typeof record.query === "string") {
106
+ // Embedding cost is proportional to tokens, so an unbounded query is unbounded spend
107
+ // in a single call -- the rate guard alone would not catch it.
108
+ const length = [...record.query].length;
109
+ if (length > rules.queryMaxLength) {
110
+ throw new Error(
111
+ `${tool}: query must be at most ${rules.queryMaxLength} characters (received ${length})`,
112
+ );
113
+ }
114
+ }
115
+ }
116
+
117
+ /**
118
+ * Wrap a wired registry with argument validation and, for the provider-crossing tools, the
119
+ * spend decision.
120
+ *
121
+ * `tools/list` is untouched: the declarations a client caches are the contract US1 froze,
122
+ * and a guard is server behaviour, not a change to what the surface offers.
123
+ */
124
+ export function guardedRegistry(inner: ToolRegistry, options: GuardOptions): ToolRegistry {
125
+ const warn = options.warn ?? ((line: string) => process.stderr.write(`${line}\n`));
126
+ const now = options.now ?? (() => new Date());
127
+
128
+ const guard = (tool: Tool): Tool => ({
129
+ ...tool,
130
+ handler: async (args: unknown) => {
131
+ validateArguments(tool.name, args);
132
+ if (PAID_TOOLS.includes(tool.name)) {
133
+ const decision = decideSpend(
134
+ tool.name,
135
+ readSpendState(options.db, options.policy, now()),
136
+ options.policy,
137
+ );
138
+ if (!decision.allow) {
139
+ // Metadata only: the tool and the reason, never the arguments.
140
+ warn(`guard refused tool=${tool.name} reason=${decision.reason}`);
141
+ throw new Error(decision.text);
142
+ }
143
+ }
144
+ return await tool.handler(args);
145
+ },
146
+ });
147
+
148
+ const list = inner.list.map(guard);
149
+ const extras = [...inner.byName.values()].filter((tool) => !inner.list.includes(tool)).map(guard);
150
+ return buildRegistry(list, extras);
151
+ }