@soleri/core 2.10.0 → 2.12.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 (511) hide show
  1. package/dist/agency/agency-manager.d.ts +47 -0
  2. package/dist/agency/agency-manager.d.ts.map +1 -0
  3. package/dist/agency/agency-manager.js +281 -0
  4. package/dist/agency/agency-manager.js.map +1 -0
  5. package/dist/agency/index.d.ts +3 -0
  6. package/dist/agency/index.d.ts.map +1 -0
  7. package/dist/agency/index.js +2 -0
  8. package/dist/agency/index.js.map +1 -0
  9. package/dist/agency/types.d.ts +69 -0
  10. package/dist/agency/types.d.ts.map +1 -0
  11. package/dist/agency/types.js +5 -0
  12. package/dist/agency/types.js.map +1 -0
  13. package/dist/brain/brain.d.ts +0 -1
  14. package/dist/brain/brain.d.ts.map +1 -1
  15. package/dist/brain/brain.js +24 -0
  16. package/dist/brain/brain.js.map +1 -1
  17. package/dist/brain/intelligence.d.ts +5 -1
  18. package/dist/brain/intelligence.d.ts.map +1 -1
  19. package/dist/brain/intelligence.js +83 -0
  20. package/dist/brain/intelligence.js.map +1 -1
  21. package/dist/brain/types.d.ts +21 -0
  22. package/dist/brain/types.d.ts.map +1 -1
  23. package/dist/chat/agent-loop-types.d.ts +82 -0
  24. package/dist/chat/agent-loop-types.d.ts.map +1 -0
  25. package/dist/chat/agent-loop-types.js +8 -0
  26. package/dist/chat/agent-loop-types.js.map +1 -0
  27. package/dist/chat/agent-loop.d.ts +19 -0
  28. package/dist/chat/agent-loop.d.ts.map +1 -0
  29. package/dist/chat/agent-loop.js +261 -0
  30. package/dist/chat/agent-loop.js.map +1 -0
  31. package/dist/chat/auth-manager.d.ts +49 -0
  32. package/dist/chat/auth-manager.d.ts.map +1 -0
  33. package/dist/chat/auth-manager.js +152 -0
  34. package/dist/chat/auth-manager.js.map +1 -0
  35. package/dist/chat/browser-session.d.ts +86 -0
  36. package/dist/chat/browser-session.d.ts.map +1 -0
  37. package/dist/chat/browser-session.js +143 -0
  38. package/dist/chat/browser-session.js.map +1 -0
  39. package/dist/chat/cancellation.d.ts +54 -0
  40. package/dist/chat/cancellation.d.ts.map +1 -0
  41. package/dist/chat/cancellation.js +80 -0
  42. package/dist/chat/cancellation.js.map +1 -0
  43. package/dist/chat/chat-session.d.ts +86 -0
  44. package/dist/chat/chat-session.d.ts.map +1 -0
  45. package/dist/chat/chat-session.js +252 -0
  46. package/dist/chat/chat-session.js.map +1 -0
  47. package/dist/chat/file-handler.d.ts +63 -0
  48. package/dist/chat/file-handler.d.ts.map +1 -0
  49. package/dist/chat/file-handler.js +182 -0
  50. package/dist/chat/file-handler.js.map +1 -0
  51. package/dist/chat/fragment-buffer.d.ts +49 -0
  52. package/dist/chat/fragment-buffer.d.ts.map +1 -0
  53. package/dist/chat/fragment-buffer.js +130 -0
  54. package/dist/chat/fragment-buffer.js.map +1 -0
  55. package/dist/chat/index.d.ts +24 -0
  56. package/dist/chat/index.d.ts.map +1 -0
  57. package/dist/chat/index.js +15 -0
  58. package/dist/chat/index.js.map +1 -0
  59. package/dist/chat/mcp-bridge.d.ts +60 -0
  60. package/dist/chat/mcp-bridge.d.ts.map +1 -0
  61. package/dist/chat/mcp-bridge.js +111 -0
  62. package/dist/chat/mcp-bridge.js.map +1 -0
  63. package/dist/chat/notifications.d.ts +82 -0
  64. package/dist/chat/notifications.d.ts.map +1 -0
  65. package/dist/chat/notifications.js +119 -0
  66. package/dist/chat/notifications.js.map +1 -0
  67. package/dist/chat/output-compressor.d.ts +30 -0
  68. package/dist/chat/output-compressor.d.ts.map +1 -0
  69. package/dist/chat/output-compressor.js +95 -0
  70. package/dist/chat/output-compressor.js.map +1 -0
  71. package/dist/chat/queue.d.ts +91 -0
  72. package/dist/chat/queue.d.ts.map +1 -0
  73. package/dist/chat/queue.js +146 -0
  74. package/dist/chat/queue.js.map +1 -0
  75. package/dist/chat/response-chunker.d.ts +29 -0
  76. package/dist/chat/response-chunker.d.ts.map +1 -0
  77. package/dist/chat/response-chunker.js +163 -0
  78. package/dist/chat/response-chunker.js.map +1 -0
  79. package/dist/chat/self-update.d.ts +62 -0
  80. package/dist/chat/self-update.d.ts.map +1 -0
  81. package/dist/chat/self-update.js +90 -0
  82. package/dist/chat/self-update.js.map +1 -0
  83. package/dist/chat/types.d.ts +105 -0
  84. package/dist/chat/types.d.ts.map +1 -0
  85. package/dist/chat/types.js +8 -0
  86. package/dist/chat/types.js.map +1 -0
  87. package/dist/chat/voice.d.ts +39 -0
  88. package/dist/chat/voice.d.ts.map +1 -0
  89. package/dist/chat/voice.js +80 -0
  90. package/dist/chat/voice.js.map +1 -0
  91. package/dist/claudemd/compose.d.ts +31 -0
  92. package/dist/claudemd/compose.d.ts.map +1 -0
  93. package/dist/claudemd/compose.js +105 -0
  94. package/dist/claudemd/compose.js.map +1 -0
  95. package/dist/claudemd/index.d.ts +5 -0
  96. package/dist/claudemd/index.d.ts.map +1 -0
  97. package/dist/claudemd/index.js +3 -0
  98. package/dist/claudemd/index.js.map +1 -0
  99. package/dist/claudemd/inject.d.ts +31 -0
  100. package/dist/claudemd/inject.d.ts.map +1 -0
  101. package/dist/claudemd/inject.js +157 -0
  102. package/dist/claudemd/inject.js.map +1 -0
  103. package/dist/claudemd/types.d.ts +41 -0
  104. package/dist/claudemd/types.d.ts.map +1 -0
  105. package/dist/claudemd/types.js +5 -0
  106. package/dist/claudemd/types.js.map +1 -0
  107. package/dist/context/context-engine.d.ts +31 -0
  108. package/dist/context/context-engine.d.ts.map +1 -0
  109. package/dist/context/context-engine.js +245 -0
  110. package/dist/context/context-engine.js.map +1 -0
  111. package/dist/context/index.d.ts +3 -0
  112. package/dist/context/index.d.ts.map +1 -0
  113. package/dist/context/index.js +2 -0
  114. package/dist/context/index.js.map +1 -0
  115. package/dist/context/types.d.ts +54 -0
  116. package/dist/context/types.d.ts.map +1 -0
  117. package/dist/context/types.js +5 -0
  118. package/dist/context/types.js.map +1 -0
  119. package/dist/enforcement/adapters/claude-code.d.ts +18 -0
  120. package/dist/enforcement/adapters/claude-code.d.ts.map +1 -0
  121. package/dist/enforcement/adapters/claude-code.js +106 -0
  122. package/dist/enforcement/adapters/claude-code.js.map +1 -0
  123. package/dist/enforcement/adapters/index.d.ts +2 -0
  124. package/dist/enforcement/adapters/index.d.ts.map +1 -0
  125. package/dist/enforcement/adapters/index.js +2 -0
  126. package/dist/enforcement/adapters/index.js.map +1 -0
  127. package/dist/enforcement/index.d.ts +4 -0
  128. package/dist/enforcement/index.d.ts.map +1 -0
  129. package/dist/enforcement/index.js +3 -0
  130. package/dist/enforcement/index.js.map +1 -0
  131. package/dist/enforcement/registry.d.ts +23 -0
  132. package/dist/enforcement/registry.d.ts.map +1 -0
  133. package/dist/enforcement/registry.js +63 -0
  134. package/dist/enforcement/registry.js.map +1 -0
  135. package/dist/enforcement/types.d.ts +51 -0
  136. package/dist/enforcement/types.d.ts.map +1 -0
  137. package/dist/enforcement/types.js +8 -0
  138. package/dist/enforcement/types.js.map +1 -0
  139. package/dist/facades/facade-factory.d.ts +10 -3
  140. package/dist/facades/facade-factory.d.ts.map +1 -1
  141. package/dist/facades/facade-factory.js +94 -5
  142. package/dist/facades/facade-factory.js.map +1 -1
  143. package/dist/facades/types.d.ts +15 -1
  144. package/dist/facades/types.d.ts.map +1 -1
  145. package/dist/facades/types.js +6 -0
  146. package/dist/facades/types.js.map +1 -1
  147. package/dist/health/health-registry.d.ts +40 -0
  148. package/dist/health/health-registry.d.ts.map +1 -0
  149. package/dist/health/health-registry.js +134 -0
  150. package/dist/health/health-registry.js.map +1 -0
  151. package/dist/health/index.d.ts +5 -0
  152. package/dist/health/index.d.ts.map +1 -0
  153. package/dist/health/index.js +3 -0
  154. package/dist/health/index.js.map +1 -0
  155. package/dist/health/vault-integrity.d.ts +13 -0
  156. package/dist/health/vault-integrity.d.ts.map +1 -0
  157. package/dist/health/vault-integrity.js +49 -0
  158. package/dist/health/vault-integrity.js.map +1 -0
  159. package/dist/index.d.ts +67 -6
  160. package/dist/index.d.ts.map +1 -1
  161. package/dist/index.js +51 -3
  162. package/dist/index.js.map +1 -1
  163. package/dist/intake/intake-pipeline.d.ts +0 -7
  164. package/dist/intake/intake-pipeline.d.ts.map +1 -1
  165. package/dist/intake/intake-pipeline.js +1 -1
  166. package/dist/intake/intake-pipeline.js.map +1 -1
  167. package/dist/intelligence/loader.d.ts.map +1 -1
  168. package/dist/intelligence/loader.js +51 -5
  169. package/dist/intelligence/loader.js.map +1 -1
  170. package/dist/intelligence/types.d.ts +1 -0
  171. package/dist/intelligence/types.d.ts.map +1 -1
  172. package/dist/migrations/index.d.ts +6 -0
  173. package/dist/migrations/index.d.ts.map +1 -0
  174. package/dist/migrations/index.js +5 -0
  175. package/dist/migrations/index.js.map +1 -0
  176. package/dist/migrations/migration-runner.d.ts +51 -0
  177. package/dist/migrations/migration-runner.d.ts.map +1 -0
  178. package/dist/migrations/migration-runner.js +141 -0
  179. package/dist/migrations/migration-runner.js.map +1 -0
  180. package/dist/packs/index.d.ts +10 -0
  181. package/dist/packs/index.d.ts.map +1 -0
  182. package/dist/packs/index.js +8 -0
  183. package/dist/packs/index.js.map +1 -0
  184. package/dist/packs/lockfile.d.ts +97 -0
  185. package/dist/packs/lockfile.d.ts.map +1 -0
  186. package/dist/packs/lockfile.js +129 -0
  187. package/dist/packs/lockfile.js.map +1 -0
  188. package/dist/packs/pack-installer.d.ts +41 -0
  189. package/dist/packs/pack-installer.d.ts.map +1 -0
  190. package/dist/packs/pack-installer.js +253 -0
  191. package/dist/packs/pack-installer.js.map +1 -0
  192. package/dist/packs/resolver.d.ts +51 -0
  193. package/dist/packs/resolver.d.ts.map +1 -0
  194. package/dist/packs/resolver.js +195 -0
  195. package/dist/packs/resolver.js.map +1 -0
  196. package/dist/packs/types.d.ts +186 -0
  197. package/dist/packs/types.d.ts.map +1 -0
  198. package/dist/packs/types.js +69 -0
  199. package/dist/packs/types.js.map +1 -0
  200. package/dist/persistence/postgres-provider.d.ts +42 -7
  201. package/dist/persistence/postgres-provider.d.ts.map +1 -1
  202. package/dist/persistence/postgres-provider.js +187 -46
  203. package/dist/persistence/postgres-provider.js.map +1 -1
  204. package/dist/playbooks/index.d.ts +2 -0
  205. package/dist/playbooks/index.d.ts.map +1 -1
  206. package/dist/playbooks/index.js +2 -0
  207. package/dist/playbooks/index.js.map +1 -1
  208. package/dist/playbooks/playbook-executor.d.ts +100 -0
  209. package/dist/playbooks/playbook-executor.d.ts.map +1 -0
  210. package/dist/playbooks/playbook-executor.js +207 -0
  211. package/dist/playbooks/playbook-executor.js.map +1 -0
  212. package/dist/plugins/index.d.ts +7 -0
  213. package/dist/plugins/index.d.ts.map +1 -0
  214. package/dist/plugins/index.js +7 -0
  215. package/dist/plugins/index.js.map +1 -0
  216. package/dist/plugins/plugin-loader.d.ts +28 -0
  217. package/dist/plugins/plugin-loader.d.ts.map +1 -0
  218. package/dist/plugins/plugin-loader.js +150 -0
  219. package/dist/plugins/plugin-loader.js.map +1 -0
  220. package/dist/plugins/plugin-registry.d.ts +58 -0
  221. package/dist/plugins/plugin-registry.d.ts.map +1 -0
  222. package/dist/plugins/plugin-registry.js +157 -0
  223. package/dist/plugins/plugin-registry.js.map +1 -0
  224. package/dist/plugins/types.d.ts +180 -0
  225. package/dist/plugins/types.d.ts.map +1 -0
  226. package/dist/plugins/types.js +48 -0
  227. package/dist/plugins/types.js.map +1 -0
  228. package/dist/runtime/admin-extra-ops.d.ts.map +1 -1
  229. package/dist/runtime/admin-extra-ops.js +181 -8
  230. package/dist/runtime/admin-extra-ops.js.map +1 -1
  231. package/dist/runtime/capture-ops.d.ts.map +1 -1
  232. package/dist/runtime/capture-ops.js +106 -7
  233. package/dist/runtime/capture-ops.js.map +1 -1
  234. package/dist/runtime/deprecation.d.ts +33 -0
  235. package/dist/runtime/deprecation.d.ts.map +1 -0
  236. package/dist/runtime/deprecation.js +41 -0
  237. package/dist/runtime/deprecation.js.map +1 -0
  238. package/dist/runtime/domain-ops.d.ts.map +1 -1
  239. package/dist/runtime/domain-ops.js +21 -2
  240. package/dist/runtime/domain-ops.js.map +1 -1
  241. package/dist/runtime/facades/admin-facade.d.ts.map +1 -1
  242. package/dist/runtime/facades/admin-facade.js +12 -1
  243. package/dist/runtime/facades/admin-facade.js.map +1 -1
  244. package/dist/runtime/facades/agency-facade.d.ts +7 -0
  245. package/dist/runtime/facades/agency-facade.d.ts.map +1 -0
  246. package/dist/runtime/facades/agency-facade.js +103 -0
  247. package/dist/runtime/facades/agency-facade.js.map +1 -0
  248. package/dist/runtime/facades/brain-facade.d.ts.map +1 -1
  249. package/dist/runtime/facades/brain-facade.js +58 -0
  250. package/dist/runtime/facades/brain-facade.js.map +1 -1
  251. package/dist/runtime/facades/chat-facade.d.ts +7 -0
  252. package/dist/runtime/facades/chat-facade.d.ts.map +1 -0
  253. package/dist/runtime/facades/chat-facade.js +808 -0
  254. package/dist/runtime/facades/chat-facade.js.map +1 -0
  255. package/dist/runtime/facades/context-facade.d.ts +7 -0
  256. package/dist/runtime/facades/context-facade.d.ts.map +1 -0
  257. package/dist/runtime/facades/context-facade.js +45 -0
  258. package/dist/runtime/facades/context-facade.js.map +1 -0
  259. package/dist/runtime/facades/index.d.ts.map +1 -1
  260. package/dist/runtime/facades/index.js +18 -0
  261. package/dist/runtime/facades/index.js.map +1 -1
  262. package/dist/runtime/facades/vault-facade.d.ts.map +1 -1
  263. package/dist/runtime/facades/vault-facade.js +247 -1
  264. package/dist/runtime/facades/vault-facade.js.map +1 -1
  265. package/dist/runtime/feature-flags.d.ts +18 -0
  266. package/dist/runtime/feature-flags.d.ts.map +1 -0
  267. package/dist/runtime/feature-flags.js +90 -0
  268. package/dist/runtime/feature-flags.js.map +1 -0
  269. package/dist/runtime/orchestrate-ops.d.ts.map +1 -1
  270. package/dist/runtime/orchestrate-ops.js +13 -0
  271. package/dist/runtime/orchestrate-ops.js.map +1 -1
  272. package/dist/runtime/pack-ops.d.ts +9 -0
  273. package/dist/runtime/pack-ops.d.ts.map +1 -0
  274. package/dist/runtime/pack-ops.js +76 -0
  275. package/dist/runtime/pack-ops.js.map +1 -0
  276. package/dist/runtime/playbook-ops.d.ts +3 -7
  277. package/dist/runtime/playbook-ops.d.ts.map +1 -1
  278. package/dist/runtime/playbook-ops.js +101 -10
  279. package/dist/runtime/playbook-ops.js.map +1 -1
  280. package/dist/runtime/plugin-ops.d.ts +9 -0
  281. package/dist/runtime/plugin-ops.d.ts.map +1 -0
  282. package/dist/runtime/plugin-ops.js +235 -0
  283. package/dist/runtime/plugin-ops.js.map +1 -0
  284. package/dist/runtime/runtime.d.ts.map +1 -1
  285. package/dist/runtime/runtime.js +72 -5
  286. package/dist/runtime/runtime.js.map +1 -1
  287. package/dist/runtime/telemetry-ops.d.ts +10 -0
  288. package/dist/runtime/telemetry-ops.d.ts.map +1 -0
  289. package/dist/runtime/telemetry-ops.js +53 -0
  290. package/dist/runtime/telemetry-ops.js.map +1 -0
  291. package/dist/runtime/types.d.ts +35 -0
  292. package/dist/runtime/types.d.ts.map +1 -1
  293. package/dist/runtime/vault-sharing-ops.d.ts +13 -0
  294. package/dist/runtime/vault-sharing-ops.d.ts.map +1 -0
  295. package/dist/runtime/vault-sharing-ops.js +345 -0
  296. package/dist/runtime/vault-sharing-ops.js.map +1 -0
  297. package/dist/streams/index.d.ts +1 -1
  298. package/dist/streams/index.d.ts.map +1 -1
  299. package/dist/streams/index.js +1 -1
  300. package/dist/streams/index.js.map +1 -1
  301. package/dist/streams/replayable-stream.d.ts +13 -1
  302. package/dist/streams/replayable-stream.d.ts.map +1 -1
  303. package/dist/streams/replayable-stream.js +27 -3
  304. package/dist/streams/replayable-stream.js.map +1 -1
  305. package/dist/text/similarity.d.ts +0 -1
  306. package/dist/text/similarity.d.ts.map +1 -1
  307. package/dist/text/similarity.js +1 -1
  308. package/dist/text/similarity.js.map +1 -1
  309. package/dist/transport/http-server.d.ts +56 -0
  310. package/dist/transport/http-server.d.ts.map +1 -0
  311. package/dist/transport/http-server.js +210 -0
  312. package/dist/transport/http-server.js.map +1 -0
  313. package/dist/transport/index.d.ts +11 -0
  314. package/dist/transport/index.d.ts.map +1 -0
  315. package/dist/transport/index.js +10 -0
  316. package/dist/transport/index.js.map +1 -0
  317. package/dist/transport/lsp-server.d.ts +140 -0
  318. package/dist/transport/lsp-server.d.ts.map +1 -0
  319. package/dist/transport/lsp-server.js +239 -0
  320. package/dist/transport/lsp-server.js.map +1 -0
  321. package/dist/transport/rate-limiter.d.ts +35 -0
  322. package/dist/transport/rate-limiter.d.ts.map +1 -0
  323. package/dist/transport/rate-limiter.js +72 -0
  324. package/dist/transport/rate-limiter.js.map +1 -0
  325. package/dist/transport/session-manager.d.ts +49 -0
  326. package/dist/transport/session-manager.d.ts.map +1 -0
  327. package/dist/transport/session-manager.js +83 -0
  328. package/dist/transport/session-manager.js.map +1 -0
  329. package/dist/transport/token-auth.d.ts +29 -0
  330. package/dist/transport/token-auth.d.ts.map +1 -0
  331. package/dist/transport/token-auth.js +84 -0
  332. package/dist/transport/token-auth.js.map +1 -0
  333. package/dist/transport/types.d.ts +61 -0
  334. package/dist/transport/types.d.ts.map +1 -0
  335. package/dist/transport/types.js +5 -0
  336. package/dist/transport/types.js.map +1 -0
  337. package/dist/transport/ws-server.d.ts +78 -0
  338. package/dist/transport/ws-server.d.ts.map +1 -0
  339. package/dist/transport/ws-server.js +342 -0
  340. package/dist/transport/ws-server.js.map +1 -0
  341. package/dist/vault/git-vault-sync.d.ts +107 -0
  342. package/dist/vault/git-vault-sync.d.ts.map +1 -0
  343. package/dist/vault/git-vault-sync.js +251 -0
  344. package/dist/vault/git-vault-sync.js.map +1 -0
  345. package/dist/vault/knowledge-review.d.ts +67 -0
  346. package/dist/vault/knowledge-review.d.ts.map +1 -0
  347. package/dist/vault/knowledge-review.js +133 -0
  348. package/dist/vault/knowledge-review.js.map +1 -0
  349. package/dist/vault/obsidian-sync.d.ts +94 -0
  350. package/dist/vault/obsidian-sync.d.ts.map +1 -0
  351. package/dist/vault/obsidian-sync.js +247 -0
  352. package/dist/vault/obsidian-sync.js.map +1 -0
  353. package/dist/vault/scope-detector.d.ts +31 -0
  354. package/dist/vault/scope-detector.d.ts.map +1 -0
  355. package/dist/vault/scope-detector.js +182 -0
  356. package/dist/vault/scope-detector.js.map +1 -0
  357. package/dist/vault/vault-branching.d.ts +71 -0
  358. package/dist/vault/vault-branching.d.ts.map +1 -0
  359. package/dist/vault/vault-branching.js +180 -0
  360. package/dist/vault/vault-branching.js.map +1 -0
  361. package/dist/vault/vault-manager.d.ts +89 -0
  362. package/dist/vault/vault-manager.d.ts.map +1 -0
  363. package/dist/vault/vault-manager.js +199 -0
  364. package/dist/vault/vault-manager.js.map +1 -0
  365. package/dist/vault/vault-types.d.ts +30 -0
  366. package/dist/vault/vault-types.d.ts.map +1 -0
  367. package/dist/vault/vault-types.js +10 -0
  368. package/dist/vault/vault-types.js.map +1 -0
  369. package/dist/vault/vault.d.ts +10 -0
  370. package/dist/vault/vault.d.ts.map +1 -1
  371. package/dist/vault/vault.js +36 -3
  372. package/dist/vault/vault.js.map +1 -1
  373. package/package.json +1 -1
  374. package/src/__tests__/admin-extra-ops.test.ts +31 -11
  375. package/src/__tests__/agency-manager.test.ts +374 -0
  376. package/src/__tests__/agent-loop.test.ts +256 -0
  377. package/src/__tests__/capture-ops.test.ts +275 -0
  378. package/src/__tests__/chat-differentiators.test.ts +251 -0
  379. package/src/__tests__/chat-enhanced.test.ts +390 -0
  380. package/src/__tests__/chat-transport.test.ts +665 -0
  381. package/src/__tests__/claudemd.test.ts +282 -0
  382. package/src/__tests__/context-engine.test.ts +256 -0
  383. package/src/__tests__/core-ops.test.ts +97 -5
  384. package/src/__tests__/deprecation.test.ts +78 -0
  385. package/src/__tests__/enforcement.test.ts +153 -0
  386. package/src/__tests__/facade-factory.test.ts +271 -0
  387. package/src/__tests__/feature-flags.test.ts +138 -0
  388. package/src/__tests__/git-vault-sync.test.ts +230 -0
  389. package/src/__tests__/health-registry.test.ts +173 -0
  390. package/src/__tests__/knowledge-review.test.ts +104 -0
  391. package/src/__tests__/lsp-transport.test.ts +442 -0
  392. package/src/__tests__/migration-runner.test.ts +170 -0
  393. package/src/__tests__/normalize.test.ts +10 -0
  394. package/src/__tests__/obsidian-sync.test.ts +354 -0
  395. package/src/__tests__/pack-lockfile.test.ts +261 -0
  396. package/src/__tests__/pack-ops.test.ts +146 -0
  397. package/src/__tests__/pack-system.test.ts +423 -0
  398. package/src/__tests__/playbook-executor.test.ts +249 -0
  399. package/src/__tests__/playbook-ops-execution.test.ts +189 -0
  400. package/src/__tests__/plugin-ops.test.ts +411 -0
  401. package/src/__tests__/plugin-system.test.ts +509 -0
  402. package/src/__tests__/postgres-provider.test.ts +64 -6
  403. package/src/__tests__/replayable-stream.test.ts +112 -1
  404. package/src/__tests__/scope-detector.test.ts +121 -0
  405. package/src/__tests__/session-lifecycle.test.ts +259 -0
  406. package/src/__tests__/transport.test.ts +758 -0
  407. package/src/__tests__/vault-branching.test.ts +274 -0
  408. package/src/__tests__/vault-connect.test.ts +179 -0
  409. package/src/__tests__/vault-integrity.test.ts +71 -0
  410. package/src/__tests__/vault-manager.test.ts +238 -0
  411. package/src/__tests__/vault-scaling.test.ts +281 -0
  412. package/src/__tests__/vault-sharing.test.ts +270 -0
  413. package/src/__tests__/ws-transport.test.ts +479 -0
  414. package/src/agency/agency-manager.ts +326 -0
  415. package/src/agency/index.ts +13 -0
  416. package/src/agency/types.ts +88 -0
  417. package/src/brain/brain.ts +27 -11
  418. package/src/brain/intelligence.ts +103 -0
  419. package/src/brain/types.ts +26 -0
  420. package/src/chat/agent-loop-types.ts +99 -0
  421. package/src/chat/agent-loop.ts +357 -0
  422. package/src/chat/auth-manager.ts +171 -0
  423. package/src/chat/browser-session.ts +188 -0
  424. package/src/chat/cancellation.ts +99 -0
  425. package/src/chat/chat-session.ts +283 -0
  426. package/src/chat/file-handler.ts +230 -0
  427. package/src/chat/fragment-buffer.ts +160 -0
  428. package/src/chat/index.ts +72 -0
  429. package/src/chat/mcp-bridge.ts +135 -0
  430. package/src/chat/notifications.ts +164 -0
  431. package/src/chat/output-compressor.ts +116 -0
  432. package/src/chat/queue.ts +208 -0
  433. package/src/chat/response-chunker.ts +200 -0
  434. package/src/chat/self-update.ts +117 -0
  435. package/src/chat/types.ts +126 -0
  436. package/src/chat/voice.ts +134 -0
  437. package/src/claudemd/compose.ts +142 -0
  438. package/src/claudemd/index.ts +17 -0
  439. package/src/claudemd/inject.ts +170 -0
  440. package/src/claudemd/types.ts +45 -0
  441. package/src/context/context-engine.ts +302 -0
  442. package/src/context/index.ts +11 -0
  443. package/src/context/types.ts +69 -0
  444. package/src/enforcement/adapters/claude-code.ts +135 -0
  445. package/src/enforcement/adapters/index.ts +1 -0
  446. package/src/enforcement/index.ts +10 -0
  447. package/src/enforcement/registry.ts +82 -0
  448. package/src/enforcement/types.ts +56 -0
  449. package/src/facades/facade-factory.ts +138 -5
  450. package/src/facades/types.ts +21 -0
  451. package/src/health/health-registry.ts +165 -0
  452. package/src/health/index.ts +11 -0
  453. package/src/health/vault-integrity.ts +66 -0
  454. package/src/index.ts +294 -2
  455. package/src/intake/intake-pipeline.ts +1 -1
  456. package/src/intelligence/loader.ts +58 -5
  457. package/src/intelligence/types.ts +1 -0
  458. package/src/migrations/index.ts +6 -0
  459. package/src/migrations/migration-runner.ts +185 -0
  460. package/src/packs/index.ts +20 -0
  461. package/src/packs/lockfile.ts +180 -0
  462. package/src/packs/pack-installer.ts +289 -0
  463. package/src/packs/resolver.ts +237 -0
  464. package/src/packs/types.ts +125 -0
  465. package/src/persistence/postgres-provider.ts +211 -58
  466. package/src/playbooks/index.ts +11 -0
  467. package/src/playbooks/playbook-executor.ts +301 -0
  468. package/src/plugins/index.ts +19 -0
  469. package/src/plugins/plugin-loader.ts +183 -0
  470. package/src/plugins/plugin-registry.ts +187 -0
  471. package/src/plugins/types.ts +119 -0
  472. package/src/runtime/admin-extra-ops.ts +193 -8
  473. package/src/runtime/capture-ops.ts +113 -8
  474. package/src/runtime/deprecation.ts +58 -0
  475. package/src/runtime/domain-ops.ts +21 -2
  476. package/src/runtime/facades/admin-facade.ts +16 -1
  477. package/src/runtime/facades/agency-facade.ts +111 -0
  478. package/src/runtime/facades/brain-facade.ts +60 -0
  479. package/src/runtime/facades/chat-facade.ts +918 -0
  480. package/src/runtime/facades/context-facade.ts +55 -0
  481. package/src/runtime/facades/index.ts +22 -1
  482. package/src/runtime/facades/vault-facade.ts +261 -1
  483. package/src/runtime/feature-flags.ts +101 -0
  484. package/src/runtime/orchestrate-ops.ts +13 -0
  485. package/src/runtime/pack-ops.ts +85 -0
  486. package/src/runtime/playbook-ops.ts +113 -9
  487. package/src/runtime/plugin-ops.ts +258 -0
  488. package/src/runtime/runtime.ts +84 -5
  489. package/src/runtime/telemetry-ops.ts +57 -0
  490. package/src/runtime/types.ts +35 -0
  491. package/src/runtime/vault-sharing-ops.ts +372 -0
  492. package/src/streams/index.ts +1 -1
  493. package/src/streams/replayable-stream.ts +34 -3
  494. package/src/text/similarity.ts +1 -1
  495. package/src/transport/http-server.ts +269 -0
  496. package/src/transport/index.ts +48 -0
  497. package/src/transport/lsp-server.ts +401 -0
  498. package/src/transport/rate-limiter.ts +97 -0
  499. package/src/transport/session-manager.ts +120 -0
  500. package/src/transport/token-auth.ts +96 -0
  501. package/src/transport/types.ts +66 -0
  502. package/src/transport/ws-server.ts +415 -0
  503. package/src/vault/git-vault-sync.ts +318 -0
  504. package/src/vault/knowledge-review.ts +221 -0
  505. package/src/vault/obsidian-sync.ts +346 -0
  506. package/src/vault/scope-detector.ts +219 -0
  507. package/src/vault/vault-branching.ts +264 -0
  508. package/src/vault/vault-manager.ts +237 -0
  509. package/src/vault/vault-types.ts +50 -0
  510. package/src/vault/vault.ts +41 -3
  511. package/src/governance/index.ts +0 -18
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Notification Engine — pluggable background polling with debounce.
3
+ *
4
+ * Generic notification system for chat transports. Register check functions
5
+ * that run at configurable intervals. Built-in debounce prevents spam.
6
+ *
7
+ * Transport-specific delivery (Telegram, Discord, etc.) is handled via
8
+ * the onNotify callback.
9
+ */
10
+
11
+ // ─── Types ────────────────────────────────────────────────────────────
12
+
13
+ export interface NotificationCheck {
14
+ /** Unique check ID. */
15
+ id: string;
16
+ /** Human-readable label. */
17
+ label: string;
18
+ /** The check function. Returns null if nothing to notify, or a message string. */
19
+ check: () => Promise<string | null>;
20
+ /** Cooldown between notifications for this check (ms). Default: 4 hours. */
21
+ cooldownMs?: number;
22
+ /** Optional time window — only run during these hours (0-23). */
23
+ activeHours?: { start: number; end: number };
24
+ }
25
+
26
+ export interface NotificationEngineConfig {
27
+ /** Polling interval in ms. Default: 30 minutes. */
28
+ intervalMs?: number;
29
+ /** Callback to deliver a notification. */
30
+ onNotify: (checkId: string, message: string) => Promise<void>;
31
+ /** Default cooldown for checks that don't specify one. Default: 4 hours. */
32
+ defaultCooldownMs?: number;
33
+ }
34
+
35
+ export interface NotificationStats {
36
+ /** Number of registered checks. */
37
+ checks: number;
38
+ /** Whether the engine is running. */
39
+ running: boolean;
40
+ /** Total notifications sent. */
41
+ sent: number;
42
+ /** Last poll timestamp. */
43
+ lastPollAt: number | null;
44
+ }
45
+
46
+ // ─── Engine ───────────────────────────────────────────────────────────
47
+
48
+ const DEFAULT_INTERVAL_MS = 30 * 60 * 1000; // 30 minutes
49
+ const DEFAULT_COOLDOWN_MS = 4 * 60 * 60 * 1000; // 4 hours
50
+
51
+ export class NotificationEngine {
52
+ private checks = new Map<string, NotificationCheck>();
53
+ private lastNotified = new Map<string, number>();
54
+ private timer: ReturnType<typeof setInterval> | null = null;
55
+ private sent = 0;
56
+ private lastPollAt: number | null = null;
57
+ private readonly intervalMs: number;
58
+ private readonly defaultCooldownMs: number;
59
+ private readonly onNotify: (checkId: string, message: string) => Promise<void>;
60
+
61
+ constructor(config: NotificationEngineConfig) {
62
+ this.intervalMs = config.intervalMs ?? DEFAULT_INTERVAL_MS;
63
+ this.defaultCooldownMs = config.defaultCooldownMs ?? DEFAULT_COOLDOWN_MS;
64
+ this.onNotify = config.onNotify;
65
+ }
66
+
67
+ /**
68
+ * Register a notification check.
69
+ */
70
+ register(check: NotificationCheck): void {
71
+ this.checks.set(check.id, check);
72
+ }
73
+
74
+ /**
75
+ * Unregister a check.
76
+ */
77
+ unregister(id: string): boolean {
78
+ return this.checks.delete(id);
79
+ }
80
+
81
+ /**
82
+ * Start the polling loop. Runs an initial check after a short delay.
83
+ */
84
+ start(): void {
85
+ if (this.timer) return; // Already running
86
+
87
+ // Initial check after 10 seconds
88
+ setTimeout(() => this.poll(), 10_000);
89
+
90
+ this.timer = setInterval(() => this.poll(), this.intervalMs);
91
+ }
92
+
93
+ /**
94
+ * Stop the polling loop.
95
+ */
96
+ stop(): void {
97
+ if (this.timer) {
98
+ clearInterval(this.timer);
99
+ this.timer = null;
100
+ }
101
+ }
102
+
103
+ /**
104
+ * Run all checks once (called automatically by the polling loop).
105
+ */
106
+ async poll(): Promise<number> {
107
+ this.lastPollAt = Date.now();
108
+ let notified = 0;
109
+
110
+ for (const [id, check] of this.checks) {
111
+ try {
112
+ // Time window check
113
+ if (check.activeHours) {
114
+ const hour = new Date().getHours();
115
+ const { start, end } = check.activeHours;
116
+ if (start <= end) {
117
+ if (hour < start || hour >= end) continue;
118
+ } else {
119
+ // Wraps midnight (e.g., 22-6)
120
+ if (hour < start && hour >= end) continue;
121
+ }
122
+ }
123
+
124
+ // Cooldown check
125
+ if (!this.shouldNotify(id, check.cooldownMs ?? this.defaultCooldownMs)) {
126
+ continue;
127
+ }
128
+
129
+ const message = await check.check();
130
+ if (message) {
131
+ await this.onNotify(id, message);
132
+ this.lastNotified.set(id, Date.now());
133
+ this.sent++;
134
+ notified++;
135
+ }
136
+ } catch {
137
+ // Individual check failure is non-critical
138
+ }
139
+ }
140
+
141
+ return notified;
142
+ }
143
+
144
+ /**
145
+ * Get engine stats.
146
+ */
147
+ stats(): NotificationStats {
148
+ return {
149
+ checks: this.checks.size,
150
+ running: this.timer !== null,
151
+ sent: this.sent,
152
+ lastPollAt: this.lastPollAt,
153
+ };
154
+ }
155
+
156
+ /**
157
+ * Check if enough time has passed since last notification for this check.
158
+ */
159
+ private shouldNotify(checkId: string, cooldownMs: number): boolean {
160
+ const last = this.lastNotified.get(checkId);
161
+ if (!last) return true;
162
+ return Date.now() - last >= cooldownMs;
163
+ }
164
+ }
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Output Compressor — compress verbose tool outputs for chat contexts.
3
+ *
4
+ * Unlike Salvador's 30+ domain-specific compressors, this provides:
5
+ * 1. A generic JSON compressor (extracts key fields, truncates arrays)
6
+ * 2. A registry for domain-specific compressors (pluggable)
7
+ *
8
+ * Domain packs can register their own compressors for their tools.
9
+ */
10
+
11
+ import type { OutputCompressor } from './agent-loop-types.js';
12
+
13
+ const DEFAULT_MAX_LENGTH = 4000;
14
+ const DEFAULT_MAX_ARRAY_ITEMS = 5;
15
+ const DEFAULT_MAX_STRING_LENGTH = 500;
16
+
17
+ // ─── Compressor Registry ────────────────────────────────────────────
18
+
19
+ type ToolCompressor = (output: string, maxLength: number) => string;
20
+
21
+ const compressorRegistry = new Map<string, ToolCompressor>();
22
+
23
+ /**
24
+ * Register a compressor for a specific tool name (or prefix).
25
+ */
26
+ export function registerCompressor(toolNameOrPrefix: string, compressor: ToolCompressor): void {
27
+ compressorRegistry.set(toolNameOrPrefix, compressor);
28
+ }
29
+
30
+ /**
31
+ * Clear all registered compressors.
32
+ */
33
+ export function clearCompressors(): void {
34
+ compressorRegistry.clear();
35
+ }
36
+
37
+ // ─── Generic Compressor ─────────────────────────────────────────────
38
+
39
+ /**
40
+ * Create a generic output compressor. Uses registered compressors for known tools,
41
+ * falls back to JSON-aware truncation.
42
+ */
43
+ export function createOutputCompressor(options?: {
44
+ maxLength?: number;
45
+ maxArrayItems?: number;
46
+ maxStringLength?: number;
47
+ }): OutputCompressor {
48
+ const maxLength = options?.maxLength ?? DEFAULT_MAX_LENGTH;
49
+ const maxArrayItems = options?.maxArrayItems ?? DEFAULT_MAX_ARRAY_ITEMS;
50
+ const maxStringLength = options?.maxStringLength ?? DEFAULT_MAX_STRING_LENGTH;
51
+
52
+ return (toolName: string, output: string, overrideMax?: number) => {
53
+ const limit = overrideMax ?? maxLength;
54
+
55
+ // Check registered compressors (exact match, then prefix match)
56
+ const exact = compressorRegistry.get(toolName);
57
+ if (exact) return exact(output, limit);
58
+
59
+ for (const [prefix, compressor] of compressorRegistry) {
60
+ if (toolName.startsWith(prefix)) {
61
+ return compressor(output, limit);
62
+ }
63
+ }
64
+
65
+ // Already short enough
66
+ if (output.length <= limit) return output;
67
+
68
+ // Try JSON-aware compression
69
+ try {
70
+ const parsed = JSON.parse(output);
71
+ const compressed = compressValue(parsed, maxArrayItems, maxStringLength);
72
+ const result = JSON.stringify(compressed, null, 2);
73
+ if (result.length <= limit) return result;
74
+ return result.slice(0, limit) + '\n... (compressed, truncated)';
75
+ } catch {
76
+ // Not JSON — plain text truncation
77
+ return output.slice(0, limit) + '\n... (truncated)';
78
+ }
79
+ };
80
+ }
81
+
82
+ // ─── JSON Compression ───────────────────────────────────────────────
83
+
84
+ function compressValue(value: unknown, maxArrayItems: number, maxStringLength: number): unknown {
85
+ if (value === null || value === undefined) return value;
86
+
87
+ if (typeof value === 'string') {
88
+ if (value.length > maxStringLength) {
89
+ return value.slice(0, maxStringLength) + `... (+${value.length - maxStringLength} chars)`;
90
+ }
91
+ return value;
92
+ }
93
+
94
+ if (typeof value === 'number' || typeof value === 'boolean') return value;
95
+
96
+ if (Array.isArray(value)) {
97
+ const truncated = value
98
+ .slice(0, maxArrayItems)
99
+ .map((item) => compressValue(item, maxArrayItems, maxStringLength));
100
+ if (value.length > maxArrayItems) {
101
+ truncated.push(`... +${value.length - maxArrayItems} more`);
102
+ }
103
+ return truncated;
104
+ }
105
+
106
+ if (typeof value === 'object') {
107
+ const obj = value as Record<string, unknown>;
108
+ const result: Record<string, unknown> = {};
109
+ for (const [key, val] of Object.entries(obj)) {
110
+ result[key] = compressValue(val, maxArrayItems, maxStringLength);
111
+ }
112
+ return result;
113
+ }
114
+
115
+ return value;
116
+ }
@@ -0,0 +1,208 @@
1
+ /**
2
+ * Message Queue — disk-based message passing for zero-cost chat relay.
3
+ *
4
+ * The queue bot writes incoming messages to inbox/. Claude Code (or any
5
+ * processor) reads them, generates responses, and writes to outbox/.
6
+ * The outbox poller sends replies back through the chat transport.
7
+ *
8
+ * Directory structure:
9
+ * ~/.{agentId}/queue/
10
+ * inbox/ ← bot writes, processor reads
11
+ * outbox/ ← processor writes, bot reads and sends
12
+ */
13
+
14
+ import {
15
+ mkdirSync,
16
+ writeFileSync,
17
+ readFileSync,
18
+ readdirSync,
19
+ unlinkSync,
20
+ existsSync,
21
+ } from 'node:fs';
22
+ import { join } from 'node:path';
23
+
24
+ // ─── Types ────────────────────────────────────────────────────────────
25
+
26
+ export interface QueuedMessage {
27
+ /** Unique message ID: `{chatId}-{timestamp}`. */
28
+ id: string;
29
+ /** Chat/channel ID. */
30
+ chatId: string;
31
+ /** Sender user ID. */
32
+ userId: string;
33
+ /** Sender username (if available). */
34
+ username?: string;
35
+ /** Message type. */
36
+ type: 'text' | 'voice' | 'photo';
37
+ /** Text content (for text messages or transcribed voice). */
38
+ text?: string;
39
+ /** Voice metadata. */
40
+ voice?: { fileId: string; duration: number };
41
+ /** Photo metadata. */
42
+ photo?: { fileId: string; caption?: string };
43
+ /** ISO timestamp. */
44
+ timestamp: string;
45
+ }
46
+
47
+ export interface QueuedResponse {
48
+ /** Original message ID this responds to. */
49
+ messageId: string;
50
+ /** Target chat ID. */
51
+ chatId: string;
52
+ /** Response text. */
53
+ text: string;
54
+ /** ISO timestamp. */
55
+ timestamp: string;
56
+ }
57
+
58
+ export interface QueueConfig {
59
+ /** Base directory for queue (e.g., ~/.{agentId}/queue). */
60
+ queueDir: string;
61
+ }
62
+
63
+ // ─── Queue Manager ───────────────────────────────────────────────────
64
+
65
+ export class MessageQueue {
66
+ private readonly inboxDir: string;
67
+ private readonly outboxDir: string;
68
+
69
+ constructor(config: QueueConfig) {
70
+ this.inboxDir = join(config.queueDir, 'inbox');
71
+ this.outboxDir = join(config.queueDir, 'outbox');
72
+ mkdirSync(this.inboxDir, { recursive: true });
73
+ mkdirSync(this.outboxDir, { recursive: true });
74
+ }
75
+
76
+ /**
77
+ * Enqueue an incoming message to inbox.
78
+ */
79
+ enqueue(msg: Omit<QueuedMessage, 'id' | 'timestamp'>): QueuedMessage {
80
+ const timestamp = new Date().toISOString();
81
+ const id = `${msg.chatId}-${Date.now()}`;
82
+ const queued: QueuedMessage = { ...msg, id, timestamp };
83
+ const filename = `${id}.json`;
84
+ writeFileSync(join(this.inboxDir, filename), JSON.stringify(queued, null, 2), 'utf-8');
85
+ return queued;
86
+ }
87
+
88
+ /**
89
+ * Read all pending inbox messages, sorted by timestamp.
90
+ */
91
+ readInbox(): QueuedMessage[] {
92
+ const files = this.listJsonFiles(this.inboxDir);
93
+ const messages: QueuedMessage[] = [];
94
+
95
+ for (const file of files) {
96
+ try {
97
+ const raw = readFileSync(join(this.inboxDir, file), 'utf-8');
98
+ messages.push(JSON.parse(raw) as QueuedMessage);
99
+ } catch {
100
+ // Skip corrupt files
101
+ }
102
+ }
103
+
104
+ return messages.sort(
105
+ (a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime(),
106
+ );
107
+ }
108
+
109
+ /**
110
+ * Get the number of pending inbox messages.
111
+ */
112
+ inboxCount(): number {
113
+ return this.listJsonFiles(this.inboxDir).length;
114
+ }
115
+
116
+ /**
117
+ * Remove a message from inbox (after processing).
118
+ */
119
+ removeFromInbox(messageId: string): boolean {
120
+ const filename = `${messageId}.json`;
121
+ const filepath = join(this.inboxDir, filename);
122
+ if (!existsSync(filepath)) return false;
123
+ try {
124
+ unlinkSync(filepath);
125
+ return true;
126
+ } catch {
127
+ return false;
128
+ }
129
+ }
130
+
131
+ /**
132
+ * Write a response to outbox.
133
+ */
134
+ sendResponse(messageId: string, chatId: string, text: string): QueuedResponse {
135
+ const response: QueuedResponse = {
136
+ messageId,
137
+ chatId,
138
+ text,
139
+ timestamp: new Date().toISOString(),
140
+ };
141
+ const filename = `${chatId}-${Date.now()}.json`;
142
+ writeFileSync(join(this.outboxDir, filename), JSON.stringify(response, null, 2), 'utf-8');
143
+ // Remove from inbox
144
+ this.removeFromInbox(messageId);
145
+ return response;
146
+ }
147
+
148
+ /**
149
+ * Read and remove all pending outbox responses.
150
+ */
151
+ drainOutbox(): QueuedResponse[] {
152
+ const files = this.listJsonFiles(this.outboxDir);
153
+ const responses: QueuedResponse[] = [];
154
+
155
+ for (const file of files) {
156
+ const filepath = join(this.outboxDir, file);
157
+ try {
158
+ const raw = readFileSync(filepath, 'utf-8');
159
+ responses.push(JSON.parse(raw) as QueuedResponse);
160
+ unlinkSync(filepath);
161
+ } catch {
162
+ // Skip corrupt files
163
+ }
164
+ }
165
+
166
+ return responses.sort(
167
+ (a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime(),
168
+ );
169
+ }
170
+
171
+ /**
172
+ * Get outbox count.
173
+ */
174
+ outboxCount(): number {
175
+ return this.listJsonFiles(this.outboxDir).length;
176
+ }
177
+
178
+ /**
179
+ * Format inbox as readable text (for MCP tool output).
180
+ */
181
+ formatInbox(): string {
182
+ const messages = this.readInbox();
183
+ if (messages.length === 0) return 'No pending messages.';
184
+
185
+ return messages
186
+ .map((m) => {
187
+ const age = Math.round((Date.now() - new Date(m.timestamp).getTime()) / 1000);
188
+ const content =
189
+ m.type === 'text'
190
+ ? m.text
191
+ : m.type === 'voice'
192
+ ? `[Voice: ${m.voice?.duration}s]`
193
+ : `[Photo: ${m.photo?.caption ?? 'no caption'}]`;
194
+ return `[${m.id}] ${m.username ?? m.userId} (${age}s ago): ${content}`;
195
+ })
196
+ .join('\n');
197
+ }
198
+
199
+ private listJsonFiles(dir: string): string[] {
200
+ try {
201
+ return readdirSync(dir)
202
+ .filter((f) => f.endsWith('.json'))
203
+ .sort();
204
+ } catch {
205
+ return [];
206
+ }
207
+ }
208
+ }
@@ -0,0 +1,200 @@
1
+ /**
2
+ * Response Chunker — splits long agent responses for chat platforms.
3
+ *
4
+ * Two capabilities:
5
+ * 1. Markdown → HTML conversion (for Telegram-style HTML parse mode)
6
+ * 2. Smart chunking at natural boundaries (paragraphs, sentences, words)
7
+ *
8
+ * Ported from Salvador's response-chunker.ts with improvements:
9
+ * - Cleaner code block protection (no sentinel characters)
10
+ * - Supports multiple output formats (HTML, Markdown passthrough, plain)
11
+ * - Better heading handling
12
+ */
13
+
14
+ import type { ChunkConfig, MarkupFormat } from './types.js';
15
+
16
+ const DEFAULT_MAX_CHUNK_SIZE = 4000;
17
+
18
+ // ─── Public API ──────────────────────────────────────────────────────
19
+
20
+ /**
21
+ * Convert Markdown to the target format and split into chunks.
22
+ */
23
+ export function chunkResponse(text: string, config?: ChunkConfig): string[] {
24
+ const maxSize = config?.maxChunkSize ?? DEFAULT_MAX_CHUNK_SIZE;
25
+ const format = config?.format ?? 'html';
26
+
27
+ const converted = convertMarkup(text, format);
28
+
29
+ if (converted.length <= maxSize) return [converted];
30
+
31
+ return splitAtBoundaries(converted, maxSize);
32
+ }
33
+
34
+ /**
35
+ * Convert Markdown to the target markup format.
36
+ */
37
+ export function convertMarkup(text: string, format: MarkupFormat): string {
38
+ switch (format) {
39
+ case 'html':
40
+ return markdownToHtml(text);
41
+ case 'plain':
42
+ return markdownToPlain(text);
43
+ case 'markdown':
44
+ default:
45
+ return text;
46
+ }
47
+ }
48
+
49
+ // ─── Markdown → HTML ────────────────────────────────────────────────
50
+
51
+ /**
52
+ * Convert Markdown to Telegram-compatible HTML.
53
+ *
54
+ * Handles: headings, bold, italic, strikethrough, code, links,
55
+ * blockquotes, lists, and fenced code blocks.
56
+ */
57
+ export function markdownToHtml(text: string): string {
58
+ // Protect fenced code blocks first
59
+ const codeBlocks: string[] = [];
60
+ let processed = text.replace(/```(\w*)\n([\s\S]*?)```/g, (_match, lang, code) => {
61
+ const escaped = escapeHtml(code.trimEnd());
62
+ const langAttr = lang ? ` class="language-${escapeHtml(lang)}"` : '';
63
+ codeBlocks.push(`<pre><code${langAttr}>${escaped}</code></pre>`);
64
+ return `<<CODEBLOCK_${codeBlocks.length - 1}>>`;
65
+ });
66
+
67
+ // Inline code
68
+ processed = processed.replace(/`([^`]+)`/g, (_m, code) => `<code>${escapeHtml(code)}</code>`);
69
+
70
+ // Headings → bold
71
+ processed = processed.replace(/^#{1,6}\s+(.+)$/gm, '<b>$1</b>');
72
+
73
+ // Bold + italic
74
+ processed = processed.replace(/\*\*\*(.+?)\*\*\*/g, '<b><i>$1</i></b>');
75
+ // Bold
76
+ processed = processed.replace(/\*\*(.+?)\*\*/g, '<b>$1</b>');
77
+ // Italic
78
+ processed = processed.replace(/(?<!\*)\*([^*]+?)\*(?!\*)/g, '<i>$1</i>');
79
+ // Strikethrough
80
+ processed = processed.replace(/~~(.+?)~~/g, '<s>$1</s>');
81
+
82
+ // Links
83
+ processed = processed.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2">$1</a>');
84
+
85
+ // Blockquotes
86
+ processed = processed.replace(/^>\s?(.*)$/gm, '<blockquote>$1</blockquote>');
87
+ // Merge consecutive blockquotes
88
+ processed = processed.replace(/<\/blockquote>\n<blockquote>/g, '\n');
89
+
90
+ // Unordered lists
91
+ processed = processed.replace(/^[-*]\s+(.+)$/gm, '• $1');
92
+ // Ordered lists
93
+ processed = processed.replace(/^\d+\.\s+(.+)$/gm, '• $1');
94
+
95
+ // Horizontal rules
96
+ processed = processed.replace(/^---+$/gm, '─'.repeat(20));
97
+
98
+ // Restore code blocks
99
+ processed = processed.replace(/<<CODEBLOCK_(\d+)>>/g, (_m, i) => codeBlocks[Number(i)]);
100
+
101
+ return processed.trim();
102
+ }
103
+
104
+ // ─── Markdown → Plain ───────────────────────────────────────────────
105
+
106
+ function markdownToPlain(text: string): string {
107
+ let plain = text;
108
+
109
+ // Remove fenced code block markers (keep content)
110
+ plain = plain.replace(/```\w*\n?/g, '');
111
+
112
+ // Remove inline formatting
113
+ plain = plain.replace(/\*\*\*(.+?)\*\*\*/g, '$1');
114
+ plain = plain.replace(/\*\*(.+?)\*\*/g, '$1');
115
+ plain = plain.replace(/(?<!\*)\*([^*]+?)\*(?!\*)/g, '$1');
116
+ plain = plain.replace(/~~(.+?)~~/g, '$1');
117
+ plain = plain.replace(/`([^`]+)`/g, '$1');
118
+
119
+ // Headings → plain text
120
+ plain = plain.replace(/^#{1,6}\s+/gm, '');
121
+
122
+ // Links → text (URL)
123
+ plain = plain.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '$1 ($2)');
124
+
125
+ // Blockquotes
126
+ plain = plain.replace(/^>\s?/gm, '');
127
+
128
+ // Lists
129
+ plain = plain.replace(/^[-*]\s+/gm, '• ');
130
+
131
+ return plain.trim();
132
+ }
133
+
134
+ // ─── Chunking ───────────────────────────────────────────────────────
135
+
136
+ function splitAtBoundaries(text: string, maxSize: number): string[] {
137
+ const chunks: string[] = [];
138
+ let remaining = text;
139
+
140
+ while (remaining.length > maxSize) {
141
+ const cutPoint = findCutPoint(remaining, maxSize);
142
+ chunks.push(remaining.slice(0, cutPoint).trimEnd());
143
+ remaining = remaining.slice(cutPoint).trimStart();
144
+ }
145
+
146
+ if (remaining.length > 0) {
147
+ chunks.push(remaining.trimEnd());
148
+ }
149
+
150
+ return chunks;
151
+ }
152
+
153
+ /**
154
+ * Find the best cut point, prioritizing natural boundaries:
155
+ * 1. Paragraph break (double newline)
156
+ * 2. Line break
157
+ * 3. Sentence boundary (. ! ?)
158
+ * 4. Word boundary (space)
159
+ * 5. Hard cut at maxSize
160
+ */
161
+ function findCutPoint(text: string, maxSize: number): number {
162
+ const window = text.slice(0, maxSize);
163
+
164
+ // Try paragraph break (must be at least 30% into the chunk to be useful)
165
+ const minUseful = Math.floor(maxSize * 0.3);
166
+ const paragraphBreak = window.lastIndexOf('\n\n');
167
+ if (paragraphBreak >= minUseful) return paragraphBreak + 2;
168
+
169
+ // Try line break
170
+ const lineBreak = window.lastIndexOf('\n');
171
+ if (lineBreak >= minUseful) return lineBreak + 1;
172
+
173
+ // Try sentence boundary
174
+ const sentenceEnd = findLastSentenceEnd(window, minUseful);
175
+ if (sentenceEnd > 0) return sentenceEnd;
176
+
177
+ // Try word boundary
178
+ const spaceBreak = window.lastIndexOf(' ');
179
+ if (spaceBreak >= minUseful) return spaceBreak + 1;
180
+
181
+ // Hard cut
182
+ return maxSize;
183
+ }
184
+
185
+ function findLastSentenceEnd(text: string, minPos: number): number {
186
+ // Look for ". " or "! " or "? " from the end
187
+ for (let i = text.length - 1; i >= minPos; i--) {
188
+ const ch = text[i];
189
+ if ((ch === '.' || ch === '!' || ch === '?') && i + 1 < text.length && text[i + 1] === ' ') {
190
+ return i + 2;
191
+ }
192
+ }
193
+ return -1;
194
+ }
195
+
196
+ // ─── Helpers ────────────────────────────────────────────────────────
197
+
198
+ function escapeHtml(text: string): string {
199
+ return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
200
+ }