bikky 0.3.1 → 0.3.3

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 (342) hide show
  1. package/README.md +124 -35
  2. package/dist/cli.d.ts +1 -0
  3. package/dist/cli.d.ts.map +1 -1
  4. package/dist/cli.js +7 -1
  5. package/dist/cli.js.map +1 -1
  6. package/dist/config.d.ts +22 -3
  7. package/dist/config.d.ts.map +1 -1
  8. package/dist/config.js +61 -6
  9. package/dist/config.js.map +1 -1
  10. package/dist/config.test.js +17 -9
  11. package/dist/config.test.js.map +1 -1
  12. package/dist/daemon/capture-policy.d.ts +95 -0
  13. package/dist/daemon/capture-policy.d.ts.map +1 -0
  14. package/dist/daemon/capture-policy.js +139 -0
  15. package/dist/daemon/capture-policy.js.map +1 -0
  16. package/dist/daemon/capture-policy.test.d.ts +2 -0
  17. package/dist/daemon/capture-policy.test.d.ts.map +1 -0
  18. package/dist/daemon/capture-policy.test.js +46 -0
  19. package/dist/daemon/capture-policy.test.js.map +1 -0
  20. package/dist/daemon/consolidation.d.ts.map +1 -1
  21. package/dist/daemon/consolidation.js +84 -98
  22. package/dist/daemon/consolidation.js.map +1 -1
  23. package/dist/daemon/episode-summary.d.ts +72 -0
  24. package/dist/daemon/episode-summary.d.ts.map +1 -0
  25. package/dist/daemon/episode-summary.js +208 -0
  26. package/dist/daemon/episode-summary.js.map +1 -0
  27. package/dist/daemon/episode-summary.test.d.ts +2 -0
  28. package/dist/daemon/episode-summary.test.d.ts.map +1 -0
  29. package/dist/daemon/episode-summary.test.js +101 -0
  30. package/dist/daemon/episode-summary.test.js.map +1 -0
  31. package/dist/daemon/extraction.d.ts +25 -0
  32. package/dist/daemon/extraction.d.ts.map +1 -1
  33. package/dist/daemon/extraction.js +244 -124
  34. package/dist/daemon/extraction.js.map +1 -1
  35. package/dist/daemon/extraction.test.d.ts +2 -0
  36. package/dist/daemon/extraction.test.d.ts.map +1 -0
  37. package/dist/daemon/extraction.test.js +106 -0
  38. package/dist/daemon/extraction.test.js.map +1 -0
  39. package/dist/daemon/loop.d.ts.map +1 -1
  40. package/dist/daemon/loop.js +8 -6
  41. package/dist/daemon/loop.js.map +1 -1
  42. package/dist/daemon/qdrant.d.ts +59 -8
  43. package/dist/daemon/qdrant.d.ts.map +1 -1
  44. package/dist/daemon/qdrant.js +74 -23
  45. package/dist/daemon/qdrant.js.map +1 -1
  46. package/dist/daemon/qdrant.test.js +2 -2
  47. package/dist/daemon/qdrant.test.js.map +1 -1
  48. package/dist/daemon/relations.d.ts +6 -1
  49. package/dist/daemon/relations.d.ts.map +1 -1
  50. package/dist/daemon/relations.js +44 -63
  51. package/dist/daemon/relations.js.map +1 -1
  52. package/dist/daemon/session-index.d.ts +60 -0
  53. package/dist/daemon/session-index.d.ts.map +1 -0
  54. package/dist/daemon/session-index.js +136 -0
  55. package/dist/daemon/session-index.js.map +1 -0
  56. package/dist/daemon/session-index.test.d.ts +2 -0
  57. package/dist/daemon/session-index.test.d.ts.map +1 -0
  58. package/dist/daemon/session-index.test.js +54 -0
  59. package/dist/daemon/session-index.test.js.map +1 -0
  60. package/dist/daemon/session-summary.d.ts +69 -0
  61. package/dist/daemon/session-summary.d.ts.map +1 -0
  62. package/dist/daemon/session-summary.js +200 -0
  63. package/dist/daemon/session-summary.js.map +1 -0
  64. package/dist/daemon/session-summary.test.d.ts +2 -0
  65. package/dist/daemon/session-summary.test.d.ts.map +1 -0
  66. package/dist/daemon/session-summary.test.js +160 -0
  67. package/dist/daemon/session-summary.test.js.map +1 -0
  68. package/dist/daemon/staleness.test.d.ts +7 -0
  69. package/dist/daemon/staleness.test.d.ts.map +1 -0
  70. package/dist/daemon/staleness.test.js +128 -0
  71. package/dist/daemon/staleness.test.js.map +1 -0
  72. package/dist/daemon/workstream-summary.d.ts +61 -0
  73. package/dist/daemon/workstream-summary.d.ts.map +1 -0
  74. package/dist/daemon/workstream-summary.js +220 -0
  75. package/dist/daemon/workstream-summary.js.map +1 -0
  76. package/dist/daemon/workstream-summary.test.d.ts +2 -0
  77. package/dist/daemon/workstream-summary.test.d.ts.map +1 -0
  78. package/dist/daemon/workstream-summary.test.js +86 -0
  79. package/dist/daemon/workstream-summary.test.js.map +1 -0
  80. package/dist/lib/qdrant-client.d.ts +6 -1
  81. package/dist/lib/qdrant-client.d.ts.map +1 -1
  82. package/dist/lib/qdrant-client.js +3 -4
  83. package/dist/lib/qdrant-client.js.map +1 -1
  84. package/dist/lib/qdrant-client.test.js +21 -2
  85. package/dist/lib/qdrant-client.test.js.map +1 -1
  86. package/dist/lifecycle.test.d.ts +8 -0
  87. package/dist/lifecycle.test.d.ts.map +1 -0
  88. package/dist/lifecycle.test.js +74 -0
  89. package/dist/lifecycle.test.js.map +1 -0
  90. package/dist/llm/embedding/index.d.ts +42 -0
  91. package/dist/llm/embedding/index.d.ts.map +1 -0
  92. package/dist/llm/embedding/index.js +78 -0
  93. package/dist/llm/embedding/index.js.map +1 -0
  94. package/dist/llm/embedding/index.test.d.ts +8 -0
  95. package/dist/llm/embedding/index.test.d.ts.map +1 -0
  96. package/dist/llm/embedding/index.test.js +100 -0
  97. package/dist/llm/embedding/index.test.js.map +1 -0
  98. package/dist/llm/embedding/providers/bedrock.d.ts +16 -0
  99. package/dist/llm/embedding/providers/bedrock.d.ts.map +1 -0
  100. package/dist/llm/embedding/providers/bedrock.js +90 -0
  101. package/dist/llm/embedding/providers/bedrock.js.map +1 -0
  102. package/dist/llm/embedding/providers/bedrock.test.d.ts +2 -0
  103. package/dist/llm/embedding/providers/bedrock.test.d.ts.map +1 -0
  104. package/dist/llm/embedding/providers/bedrock.test.js +24 -0
  105. package/dist/llm/embedding/providers/bedrock.test.js.map +1 -0
  106. package/dist/llm/embedding/providers/index.d.ts +9 -0
  107. package/dist/llm/embedding/providers/index.d.ts.map +1 -0
  108. package/dist/llm/embedding/providers/index.js +9 -0
  109. package/dist/llm/embedding/providers/index.js.map +1 -0
  110. package/dist/llm/embedding/providers/ollama.d.ts +6 -0
  111. package/dist/llm/embedding/providers/ollama.d.ts.map +1 -0
  112. package/dist/llm/embedding/providers/ollama.js +39 -0
  113. package/dist/llm/embedding/providers/ollama.js.map +1 -0
  114. package/dist/llm/embedding/providers/ollama.test.d.ts +2 -0
  115. package/dist/llm/embedding/providers/ollama.test.d.ts.map +1 -0
  116. package/dist/llm/embedding/providers/ollama.test.js +54 -0
  117. package/dist/llm/embedding/providers/ollama.test.js.map +1 -0
  118. package/dist/llm/embedding/providers/openai.d.ts +6 -0
  119. package/dist/llm/embedding/providers/openai.d.ts.map +1 -0
  120. package/dist/llm/embedding/providers/openai.js +44 -0
  121. package/dist/llm/embedding/providers/openai.js.map +1 -0
  122. package/dist/llm/embedding/providers/openai.test.d.ts +2 -0
  123. package/dist/llm/embedding/providers/openai.test.d.ts.map +1 -0
  124. package/dist/llm/embedding/providers/openai.test.js +48 -0
  125. package/dist/llm/embedding/providers/openai.test.js.map +1 -0
  126. package/dist/llm/embedding/providers/portkey.d.ts +15 -0
  127. package/dist/llm/embedding/providers/portkey.d.ts.map +1 -0
  128. package/dist/llm/embedding/providers/portkey.js +58 -0
  129. package/dist/llm/embedding/providers/portkey.js.map +1 -0
  130. package/dist/llm/embedding/providers/portkey.test.d.ts +2 -0
  131. package/dist/llm/embedding/providers/portkey.test.d.ts.map +1 -0
  132. package/dist/llm/embedding/providers/portkey.test.js +56 -0
  133. package/dist/llm/embedding/providers/portkey.test.js.map +1 -0
  134. package/dist/llm/embedding/registry.d.ts +14 -0
  135. package/dist/llm/embedding/registry.d.ts.map +1 -0
  136. package/dist/llm/embedding/registry.js +27 -0
  137. package/dist/llm/embedding/registry.js.map +1 -0
  138. package/dist/llm/embedding/registry.test.d.ts +7 -0
  139. package/dist/llm/embedding/registry.test.d.ts.map +1 -0
  140. package/dist/llm/embedding/registry.test.js +68 -0
  141. package/dist/llm/embedding/registry.test.js.map +1 -0
  142. package/dist/llm/embedding/types.d.ts +55 -0
  143. package/dist/llm/embedding/types.d.ts.map +1 -0
  144. package/dist/llm/embedding/types.js +12 -0
  145. package/dist/llm/embedding/types.js.map +1 -0
  146. package/dist/llm/errors.d.ts +95 -0
  147. package/dist/llm/errors.d.ts.map +1 -0
  148. package/dist/llm/errors.js +164 -0
  149. package/dist/llm/errors.js.map +1 -0
  150. package/dist/llm/errors.test.d.ts +2 -0
  151. package/dist/llm/errors.test.d.ts.map +1 -0
  152. package/dist/llm/errors.test.js +103 -0
  153. package/dist/llm/errors.test.js.map +1 -0
  154. package/dist/llm/fetch.d.ts +39 -0
  155. package/dist/llm/fetch.d.ts.map +1 -0
  156. package/dist/llm/fetch.js +52 -0
  157. package/dist/llm/fetch.js.map +1 -0
  158. package/dist/llm/index.d.ts +6 -3
  159. package/dist/llm/index.d.ts.map +1 -1
  160. package/dist/llm/index.js +2 -2
  161. package/dist/llm/index.js.map +1 -1
  162. package/dist/llm/inference/index.d.ts +39 -0
  163. package/dist/llm/inference/index.d.ts.map +1 -0
  164. package/dist/llm/inference/index.js +118 -0
  165. package/dist/llm/inference/index.js.map +1 -0
  166. package/dist/llm/inference/index.test.d.ts +6 -0
  167. package/dist/llm/inference/index.test.d.ts.map +1 -0
  168. package/dist/llm/inference/index.test.js +109 -0
  169. package/dist/llm/inference/index.test.js.map +1 -0
  170. package/dist/llm/inference/providers/bedrock.d.ts +18 -0
  171. package/dist/llm/inference/providers/bedrock.d.ts.map +1 -0
  172. package/dist/llm/inference/providers/bedrock.js +105 -0
  173. package/dist/llm/inference/providers/bedrock.js.map +1 -0
  174. package/dist/llm/inference/providers/bedrock.test.d.ts +2 -0
  175. package/dist/llm/inference/providers/bedrock.test.d.ts.map +1 -0
  176. package/dist/llm/inference/providers/bedrock.test.js +21 -0
  177. package/dist/llm/inference/providers/bedrock.test.js.map +1 -0
  178. package/dist/llm/inference/providers/index.d.ts +10 -0
  179. package/dist/llm/inference/providers/index.d.ts.map +1 -0
  180. package/dist/llm/inference/providers/index.js +10 -0
  181. package/dist/llm/inference/providers/index.js.map +1 -0
  182. package/dist/llm/inference/providers/ollama.d.ts +8 -0
  183. package/dist/llm/inference/providers/ollama.d.ts.map +1 -0
  184. package/dist/llm/inference/providers/ollama.js +63 -0
  185. package/dist/llm/inference/providers/ollama.js.map +1 -0
  186. package/dist/llm/inference/providers/ollama.test.d.ts +2 -0
  187. package/dist/llm/inference/providers/ollama.test.d.ts.map +1 -0
  188. package/dist/llm/inference/providers/ollama.test.js +57 -0
  189. package/dist/llm/inference/providers/ollama.test.js.map +1 -0
  190. package/dist/llm/inference/providers/openai.d.ts +11 -0
  191. package/dist/llm/inference/providers/openai.d.ts.map +1 -0
  192. package/dist/llm/inference/providers/openai.js +73 -0
  193. package/dist/llm/inference/providers/openai.js.map +1 -0
  194. package/dist/llm/inference/providers/openai.test.d.ts +2 -0
  195. package/dist/llm/inference/providers/openai.test.d.ts.map +1 -0
  196. package/dist/llm/inference/providers/openai.test.js +46 -0
  197. package/dist/llm/inference/providers/openai.test.js.map +1 -0
  198. package/dist/llm/inference/providers/portkey.d.ts +13 -0
  199. package/dist/llm/inference/providers/portkey.d.ts.map +1 -0
  200. package/dist/llm/inference/providers/portkey.js +80 -0
  201. package/dist/llm/inference/providers/portkey.js.map +1 -0
  202. package/dist/llm/inference/providers/portkey.test.d.ts +2 -0
  203. package/dist/llm/inference/providers/portkey.test.d.ts.map +1 -0
  204. package/dist/llm/inference/providers/portkey.test.js +48 -0
  205. package/dist/llm/inference/providers/portkey.test.js.map +1 -0
  206. package/dist/llm/inference/registry.d.ts +15 -0
  207. package/dist/llm/inference/registry.d.ts.map +1 -0
  208. package/dist/llm/inference/registry.js +28 -0
  209. package/dist/llm/inference/registry.js.map +1 -0
  210. package/dist/llm/inference/registry.test.d.ts +6 -0
  211. package/dist/llm/inference/registry.test.d.ts.map +1 -0
  212. package/dist/llm/inference/registry.test.js +63 -0
  213. package/dist/llm/inference/registry.test.js.map +1 -0
  214. package/dist/llm/inference/types.d.ts +84 -0
  215. package/dist/llm/inference/types.d.ts.map +1 -0
  216. package/dist/llm/inference/types.js +9 -0
  217. package/dist/llm/inference/types.js.map +1 -0
  218. package/dist/llm/telemetry.d.ts +25 -0
  219. package/dist/llm/telemetry.d.ts.map +1 -0
  220. package/dist/llm/telemetry.js +43 -0
  221. package/dist/llm/telemetry.js.map +1 -0
  222. package/dist/llm/telemetry.test.d.ts +5 -0
  223. package/dist/llm/telemetry.test.d.ts.map +1 -0
  224. package/dist/llm/telemetry.test.js +89 -0
  225. package/dist/llm/telemetry.test.js.map +1 -0
  226. package/dist/llm/types.d.ts +4 -37
  227. package/dist/llm/types.d.ts.map +1 -1
  228. package/dist/llm/types.js +4 -1
  229. package/dist/llm/types.js.map +1 -1
  230. package/dist/logger.d.ts +18 -3
  231. package/dist/logger.d.ts.map +1 -1
  232. package/dist/logger.js +102 -20
  233. package/dist/logger.js.map +1 -1
  234. package/dist/logger.test.d.ts +5 -0
  235. package/dist/logger.test.d.ts.map +1 -0
  236. package/dist/logger.test.js +103 -0
  237. package/dist/logger.test.js.map +1 -0
  238. package/dist/mcp/api.d.ts +15 -1
  239. package/dist/mcp/api.d.ts.map +1 -1
  240. package/dist/mcp/api.js +44 -19
  241. package/dist/mcp/api.js.map +1 -1
  242. package/dist/mcp/api.test.d.ts +6 -0
  243. package/dist/mcp/api.test.d.ts.map +1 -0
  244. package/dist/mcp/api.test.js +130 -0
  245. package/dist/mcp/api.test.js.map +1 -0
  246. package/dist/mcp/helpers.d.ts +1 -0
  247. package/dist/mcp/helpers.d.ts.map +1 -1
  248. package/dist/mcp/helpers.js +62 -6
  249. package/dist/mcp/helpers.js.map +1 -1
  250. package/dist/mcp/helpers.test.js +71 -10
  251. package/dist/mcp/helpers.test.js.map +1 -1
  252. package/dist/mcp/index.d.ts +7 -1
  253. package/dist/mcp/index.d.ts.map +1 -1
  254. package/dist/mcp/index.js +46 -21
  255. package/dist/mcp/index.js.map +1 -1
  256. package/dist/mcp/taxonomy.d.ts +251 -31
  257. package/dist/mcp/taxonomy.d.ts.map +1 -1
  258. package/dist/mcp/taxonomy.js +603 -171
  259. package/dist/mcp/taxonomy.js.map +1 -1
  260. package/dist/mcp/taxonomy.test.d.ts +1 -1
  261. package/dist/mcp/taxonomy.test.js +141 -302
  262. package/dist/mcp/taxonomy.test.js.map +1 -1
  263. package/dist/mcp/tools.d.ts +1 -1
  264. package/dist/mcp/tools.d.ts.map +1 -1
  265. package/dist/mcp/tools.integration.itest.d.ts +23 -0
  266. package/dist/mcp/tools.integration.itest.d.ts.map +1 -0
  267. package/dist/mcp/tools.integration.itest.js +172 -0
  268. package/dist/mcp/tools.integration.itest.js.map +1 -0
  269. package/dist/mcp/tools.js +422 -357
  270. package/dist/mcp/tools.js.map +1 -1
  271. package/dist/mcp/tools.test.d.ts +16 -0
  272. package/dist/mcp/tools.test.d.ts.map +1 -0
  273. package/dist/mcp/tools.test.js +472 -0
  274. package/dist/mcp/tools.test.js.map +1 -0
  275. package/dist/mcp/types.d.ts +63 -8
  276. package/dist/mcp/types.d.ts.map +1 -1
  277. package/dist/prompts/brief.d.ts +19 -0
  278. package/dist/prompts/brief.d.ts.map +1 -0
  279. package/dist/prompts/brief.js +67 -0
  280. package/dist/prompts/brief.js.map +1 -0
  281. package/dist/prompts/contradiction.d.ts +24 -0
  282. package/dist/prompts/contradiction.d.ts.map +1 -0
  283. package/dist/prompts/contradiction.js +73 -0
  284. package/dist/prompts/contradiction.js.map +1 -0
  285. package/dist/prompts/distill.d.ts +21 -0
  286. package/dist/prompts/distill.d.ts.map +1 -0
  287. package/dist/prompts/distill.js +92 -0
  288. package/dist/prompts/distill.js.map +1 -0
  289. package/dist/prompts/episode-summary.d.ts +15 -0
  290. package/dist/prompts/episode-summary.d.ts.map +1 -0
  291. package/dist/prompts/episode-summary.js +60 -0
  292. package/dist/prompts/episode-summary.js.map +1 -0
  293. package/dist/prompts/extraction.d.ts +14 -0
  294. package/dist/prompts/extraction.d.ts.map +1 -0
  295. package/dist/prompts/extraction.js +110 -0
  296. package/dist/prompts/extraction.js.map +1 -0
  297. package/dist/prompts/index.d.ts +52 -0
  298. package/dist/prompts/index.d.ts.map +1 -0
  299. package/dist/prompts/index.js +104 -0
  300. package/dist/prompts/index.js.map +1 -0
  301. package/dist/prompts/prompts.test.d.ts +8 -0
  302. package/dist/prompts/prompts.test.d.ts.map +1 -0
  303. package/dist/prompts/prompts.test.js +140 -0
  304. package/dist/prompts/prompts.test.js.map +1 -0
  305. package/dist/prompts/relations.d.ts +17 -0
  306. package/dist/prompts/relations.d.ts.map +1 -0
  307. package/dist/prompts/relations.js +72 -0
  308. package/dist/prompts/relations.js.map +1 -0
  309. package/dist/prompts/workstream-summary.d.ts +17 -0
  310. package/dist/prompts/workstream-summary.d.ts.map +1 -0
  311. package/dist/prompts/workstream-summary.js +72 -0
  312. package/dist/prompts/workstream-summary.js.map +1 -0
  313. package/dist/render.d.ts +41 -0
  314. package/dist/render.d.ts.map +1 -0
  315. package/dist/render.js +185 -0
  316. package/dist/render.js.map +1 -0
  317. package/dist/render.test.d.ts +8 -0
  318. package/dist/render.test.d.ts.map +1 -0
  319. package/dist/render.test.js +243 -0
  320. package/dist/render.test.js.map +1 -0
  321. package/docs/diagrams/architecture.svg +87 -0
  322. package/docs/diagrams/team-memory.svg +250 -0
  323. package/docs/screenshots/dashboard.png +0 -0
  324. package/docs/screenshots/graph.png +0 -0
  325. package/docs/screenshots/memory.png +0 -0
  326. package/package.json +12 -3
  327. package/dist/llm/embedding.d.ts +0 -13
  328. package/dist/llm/embedding.d.ts.map +0 -1
  329. package/dist/llm/embedding.js +0 -127
  330. package/dist/llm/embedding.js.map +0 -1
  331. package/dist/llm/embedding.test.d.ts +0 -8
  332. package/dist/llm/embedding.test.d.ts.map +0 -1
  333. package/dist/llm/embedding.test.js +0 -117
  334. package/dist/llm/embedding.test.js.map +0 -1
  335. package/dist/llm/inference.d.ts +0 -12
  336. package/dist/llm/inference.d.ts.map +0 -1
  337. package/dist/llm/inference.js +0 -146
  338. package/dist/llm/inference.js.map +0 -1
  339. package/dist/llm/inference.test.d.ts +0 -8
  340. package/dist/llm/inference.test.d.ts.map +0 -1
  341. package/dist/llm/inference.test.js +0 -117
  342. package/dist/llm/inference.test.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telemetry.test.d.ts","sourceRoot":"","sources":["../../src/llm/telemetry.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Tests for LLM telemetry — JSONL append, rotation, error swallowing.
3
+ */
4
+ import { describe, it, beforeEach, afterEach } from "node:test";
5
+ import assert from "node:assert/strict";
6
+ import fs from "node:fs";
7
+ import path from "node:path";
8
+ import os from "node:os";
9
+ import { estimateTokens, writeTelemetry } from "./telemetry.js";
10
+ describe("llm/telemetry", () => {
11
+ let dir;
12
+ let file;
13
+ const noopLog = () => undefined;
14
+ beforeEach(() => {
15
+ dir = fs.mkdtempSync(path.join(os.tmpdir(), "bikky-telemetry-"));
16
+ file = path.join(dir, "llm.jsonl");
17
+ process.env.BIKKY_LLM_LOG = file;
18
+ delete process.env.BIKKY_LLM_LOG_MAX_BYTES;
19
+ });
20
+ afterEach(() => {
21
+ delete process.env.BIKKY_LLM_LOG;
22
+ delete process.env.BIKKY_LLM_LOG_MAX_BYTES;
23
+ fs.rmSync(dir, { recursive: true, force: true });
24
+ });
25
+ function makeRecord(overrides = {}) {
26
+ return {
27
+ ts: new Date().toISOString(),
28
+ prompt: "extraction",
29
+ model: "gpt-4o-mini",
30
+ provider: "openai",
31
+ ok: true,
32
+ latency_ms: 123,
33
+ tokens_in_est: 10,
34
+ tokens_out_est: 20,
35
+ ...overrides,
36
+ };
37
+ }
38
+ describe("estimateTokens", () => {
39
+ it("returns ceil(len/4) for non-empty input", () => {
40
+ assert.equal(estimateTokens(""), 0);
41
+ assert.equal(estimateTokens("abcd"), 1);
42
+ assert.equal(estimateTokens("abcde"), 2);
43
+ assert.equal(estimateTokens("a".repeat(100)), 25);
44
+ });
45
+ });
46
+ describe("writeTelemetry", () => {
47
+ it("appends a JSONL record to the configured log path", async () => {
48
+ await writeTelemetry(makeRecord({ prompt: "first" }), noopLog);
49
+ await writeTelemetry(makeRecord({ prompt: "second" }), noopLog);
50
+ const lines = fs.readFileSync(file, "utf-8").trim().split("\n");
51
+ assert.equal(lines.length, 2);
52
+ const first = JSON.parse(lines[0]);
53
+ const second = JSON.parse(lines[1]);
54
+ assert.equal(first.prompt, "first");
55
+ assert.equal(second.prompt, "second");
56
+ assert.equal(first.provider, "openai");
57
+ });
58
+ it("creates the parent directory if missing", async () => {
59
+ const nested = path.join(dir, "a", "b", "c", "llm.jsonl");
60
+ process.env.BIKKY_LLM_LOG = nested;
61
+ await writeTelemetry(makeRecord(), noopLog);
62
+ assert.ok(fs.existsSync(nested));
63
+ });
64
+ it("rotates to .1 when the file exceeds max bytes", async () => {
65
+ process.env.BIKKY_LLM_LOG_MAX_BYTES = "200";
66
+ // Seed an oversize file
67
+ fs.writeFileSync(file, "x".repeat(500));
68
+ await writeTelemetry(makeRecord(), noopLog);
69
+ assert.ok(fs.existsSync(`${file}.1`), "rotated file should exist");
70
+ const active = fs.readFileSync(file, "utf-8");
71
+ // Active file should only contain the new record after rotation
72
+ assert.equal(active.trim().split("\n").length, 1);
73
+ });
74
+ it("never throws when the path is unwritable; warns once via the logger", async () => {
75
+ // Point at a path that cannot be created (parent is a file, not a dir)
76
+ const blocker = path.join(dir, "blocker");
77
+ fs.writeFileSync(blocker, "not-a-dir");
78
+ process.env.BIKKY_LLM_LOG = path.join(blocker, "llm.jsonl");
79
+ const warnings = [];
80
+ const log = (_level, msg) => { warnings.push(String(msg)); };
81
+ await writeTelemetry(makeRecord(), log);
82
+ await writeTelemetry(makeRecord(), log);
83
+ // Module-level `warned` flag may already be set from a previous test;
84
+ // assert at most one warning was added in this call sequence.
85
+ assert.ok(warnings.length <= 1, "should warn at most once per process");
86
+ });
87
+ });
88
+ });
89
+ //# sourceMappingURL=telemetry.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telemetry.test.js","sourceRoot":"","sources":["../../src/llm/telemetry.test.ts"],"names":[],"mappings":"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"}
@@ -1,41 +1,8 @@
1
1
  /**
2
- * Shared types for embedding and inference providers.
2
+ * Cross-cutting types not specific to embedding or inference.
3
+ * Embedding types live in ./embedding/types.ts.
4
+ * Inference types (ChatCompletionOpts, ResponseFormat, JsonSchemaSpec, etc.)
5
+ * live in ./inference/types.ts.
3
6
  */
4
7
  export type LogFn = (level: string, ...args: unknown[]) => void;
5
- export interface EmbeddingProviderConfig {
6
- provider: "ollama" | "openai" | "bedrock";
7
- baseUrl: string;
8
- model: string;
9
- dimensions: number;
10
- apiKey: string | null;
11
- }
12
- export interface InferenceProviderConfig {
13
- provider: "ollama" | "openai" | "bedrock";
14
- ollama_url: string;
15
- ollama_model: string;
16
- openai_api_key: string | null;
17
- openai_model: string;
18
- bedrock_region: string;
19
- bedrock_model: string;
20
- }
21
- export interface ChatCompletionOpts {
22
- messages: Array<{
23
- role: string;
24
- content: string;
25
- }>;
26
- temperature?: number;
27
- max_tokens?: number;
28
- response_format?: ResponseFormat;
29
- }
30
- export type ResponseFormat = {
31
- type: "json_object";
32
- } | {
33
- type: "json_schema";
34
- json_schema: JsonSchemaSpec;
35
- };
36
- export interface JsonSchemaSpec {
37
- name: string;
38
- strict?: boolean;
39
- schema: Record<string, unknown>;
40
- }
41
8
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/llm/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,MAAM,KAAK,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;AAIhE,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAID,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;IAC1C,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,cAAc,CAAC;CAClC;AAED,MAAM,MAAM,cAAc,GACtB;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,GACvB;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,WAAW,EAAE,cAAc,CAAA;CAAE,CAAC;AAEzD,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/llm/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,MAAM,KAAK,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC"}
package/dist/llm/types.js CHANGED
@@ -1,5 +1,8 @@
1
1
  /**
2
- * Shared types for embedding and inference providers.
2
+ * Cross-cutting types not specific to embedding or inference.
3
+ * Embedding types live in ./embedding/types.ts.
4
+ * Inference types (ChatCompletionOpts, ResponseFormat, JsonSchemaSpec, etc.)
5
+ * live in ./inference/types.ts.
3
6
  */
4
7
  export {};
5
8
  //# sourceMappingURL=types.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/llm/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/llm/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
package/dist/logger.d.ts CHANGED
@@ -1,11 +1,26 @@
1
1
  /**
2
- * Simple rotating file logger.
3
- * Writes to file only — stdout/stderr are reserved for MCP stdio transport.
2
+ * Structured logger backed by pino.
3
+ *
4
+ * Output is JSON-lines written to a rotating log file. We do NOT log to
5
+ * stdout/stderr because the MCP server reserves stdio for its transport.
6
+ *
7
+ * The exported `LogFn` signature is preserved for backward compatibility:
8
+ * callers continue to invoke `log("INFO", "msg", ...args)` and pino emits
9
+ * a structured record `{level, time, name, msg, ...extra}`. New code can
10
+ * pass a plain object as the first arg to attach structured fields:
11
+ *
12
+ * log("INFO", { event: "embed_request", provider: "openai" }, "ok");
13
+ *
14
+ * We rotate the file in-process (no worker threads) by checking size after
15
+ * each write and recreating the pino destination when the threshold is
16
+ * exceeded.
4
17
  */
5
18
  export type LogLevel = "DEBUG" | "INFO" | "WARN" | "ERROR";
6
- export type LogFn = (level: LogLevel, ...args: unknown[]) => void;
19
+ export type LogFn = (level: LogLevel | string, ...args: unknown[]) => void;
7
20
  interface LoggerOpts {
21
+ /** Bytes before rotation (default 2MB). */
8
22
  maxSize?: number;
23
+ /** Rotated files to keep (default 3). */
9
24
  maxFiles?: number;
10
25
  }
11
26
  export declare function createLogger(name: string, filePath: string, opts?: LoggerOpts): LogFn;
@@ -1 +1 @@
1
- {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAC3D,MAAM,MAAM,KAAK,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;AAElE,UAAU,UAAU;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE,UAAe,GACpB,KAAK,CAgCP"}
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAMH,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAC3D,MAAM,MAAM,KAAK,GAAG,CAAC,KAAK,EAAE,QAAQ,GAAG,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;AAE3E,UAAU,UAAU;IAClB,2CAA2C;IAC3C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yCAAyC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAyCD,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE,UAAe,GACpB,KAAK,CAsEP"}
package/dist/logger.js CHANGED
@@ -1,41 +1,123 @@
1
1
  /**
2
- * Simple rotating file logger.
3
- * Writes to file only — stdout/stderr are reserved for MCP stdio transport.
2
+ * Structured logger backed by pino.
3
+ *
4
+ * Output is JSON-lines written to a rotating log file. We do NOT log to
5
+ * stdout/stderr because the MCP server reserves stdio for its transport.
6
+ *
7
+ * The exported `LogFn` signature is preserved for backward compatibility:
8
+ * callers continue to invoke `log("INFO", "msg", ...args)` and pino emits
9
+ * a structured record `{level, time, name, msg, ...extra}`. New code can
10
+ * pass a plain object as the first arg to attach structured fields:
11
+ *
12
+ * log("INFO", { event: "embed_request", provider: "openai" }, "ok");
13
+ *
14
+ * We rotate the file in-process (no worker threads) by checking size after
15
+ * each write and recreating the pino destination when the threshold is
16
+ * exceeded.
4
17
  */
5
18
  import fs from "node:fs";
6
19
  import path from "node:path";
20
+ import pino from "pino";
21
+ const LEVEL_MAP = {
22
+ DEBUG: "debug",
23
+ INFO: "info",
24
+ WARN: "warn",
25
+ ERROR: "error",
26
+ debug: "debug",
27
+ info: "info",
28
+ warn: "warn",
29
+ error: "error",
30
+ };
31
+ function buildPino(name, filePath) {
32
+ const dest = pino.destination({ dest: filePath, sync: true, append: true, mkdir: true });
33
+ return pino({
34
+ name,
35
+ level: "debug",
36
+ // Omit pid / hostname — every line carries `name` instead.
37
+ base: { name },
38
+ timestamp: pino.stdTimeFunctions.isoTime,
39
+ formatters: {
40
+ // Emit human-readable level strings ("info") rather than numeric codes.
41
+ level(label) {
42
+ return { level: label };
43
+ },
44
+ },
45
+ }, dest);
46
+ }
47
+ function safeSize(filePath) {
48
+ try {
49
+ return fs.statSync(filePath).size;
50
+ }
51
+ catch {
52
+ return 0;
53
+ }
54
+ }
7
55
  export function createLogger(name, filePath, opts = {}) {
8
56
  const maxSize = opts.maxSize ?? 2 * 1024 * 1024;
9
57
  const maxFiles = opts.maxFiles ?? 3;
10
58
  fs.mkdirSync(path.dirname(filePath), { recursive: true });
11
- let fd = fs.openSync(filePath, "a");
12
- let currentSize = 0;
13
- try {
14
- currentSize = fs.fstatSync(fd).size;
15
- }
16
- catch { /* new file */ }
59
+ let logger = buildPino(name, filePath);
60
+ let writtenSinceCheck = 0;
17
61
  function rotate() {
18
- fs.closeSync(fd);
62
+ try {
63
+ logger.flush();
64
+ }
65
+ catch {
66
+ /* ignore — pino sync mode flushes on every write */
67
+ }
19
68
  for (let i = maxFiles - 1; i >= 1; i--) {
20
69
  const from = i === 1 ? filePath : `${filePath}.${i - 1}`;
21
70
  const to = `${filePath}.${i}`;
22
71
  try {
23
72
  fs.renameSync(from, to);
24
73
  }
25
- catch { /* missing file is fine */ }
74
+ catch {
75
+ /* missing file is fine */
76
+ }
26
77
  }
27
- fd = fs.openSync(filePath, "w");
28
- currentSize = 0;
78
+ logger = buildPino(name, filePath);
79
+ writtenSinceCheck = 0;
29
80
  }
30
- return (level, ...args) => {
31
- const ts = new Date().toISOString().replace("T", " ").slice(0, 19);
32
- const msg = args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ");
33
- const line = `[${ts}] [${name}] ${level}: ${msg}\n`;
34
- const buf = Buffer.from(line);
35
- fs.writeSync(fd, buf);
36
- currentSize += buf.length;
37
- if (currentSize > maxSize)
81
+ function maybeRotate() {
82
+ // Cheap heuristic: only stat the file once we've written enough bytes
83
+ // to plausibly cross the threshold. Avoids a stat on every log line.
84
+ if (writtenSinceCheck < 4096)
85
+ return;
86
+ writtenSinceCheck = 0;
87
+ if (safeSize(filePath) > maxSize)
38
88
  rotate();
89
+ }
90
+ return (level, ...args) => {
91
+ const lvl = LEVEL_MAP[level] ?? "info";
92
+ const fn = logger[lvl].bind(logger);
93
+ let approxBytes = 0;
94
+ const first = args[0];
95
+ if (first !== null &&
96
+ typeof first === "object" &&
97
+ !Array.isArray(first) &&
98
+ !(first instanceof Error)) {
99
+ // Structured form: log("INFO", {fields}, "msg", ...)
100
+ const rest = args.slice(1);
101
+ const msg = rest
102
+ .map((a) => (typeof a === "string" ? a : a instanceof Error ? a.message : JSON.stringify(a)))
103
+ .join(" ");
104
+ fn(first, msg);
105
+ try {
106
+ approxBytes = JSON.stringify(first).length + msg.length;
107
+ }
108
+ catch {
109
+ approxBytes = msg.length;
110
+ }
111
+ }
112
+ else {
113
+ const msg = args
114
+ .map((a) => (typeof a === "string" ? a : a instanceof Error ? a.message : JSON.stringify(a)))
115
+ .join(" ");
116
+ fn(msg);
117
+ approxBytes = msg.length;
118
+ }
119
+ writtenSinceCheck += approxBytes + 80; // +80 for JSON envelope (level/time/name/...)
120
+ maybeRotate();
39
121
  };
40
122
  }
41
123
  //# sourceMappingURL=logger.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAU7B,MAAM,UAAU,YAAY,CAC1B,IAAY,EACZ,QAAgB,EAChB,OAAmB,EAAE;IAErB,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;IAChD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;IAEpC,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE1D,IAAI,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IACpC,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,IAAI,CAAC;QAAC,WAAW,GAAG,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC,CAAC,cAAc,CAAC,CAAC;IAErE,SAAS,MAAM;QACb,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QACjB,KAAK,IAAI,CAAC,GAAG,QAAQ,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YACvC,MAAM,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YACzD,MAAM,EAAE,GAAG,GAAG,QAAQ,IAAI,CAAC,EAAE,CAAC;YAC9B,IAAI,CAAC;gBAAC,EAAE,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,0BAA0B,CAAC,CAAC;QACvE,CAAC;QACD,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;QAChC,WAAW,GAAG,CAAC,CAAC;IAClB,CAAC;IAED,OAAO,CAAC,KAAe,EAAE,GAAG,IAAe,EAAQ,EAAE;QACnD,MAAM,EAAE,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACnE,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CACzB,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAC9C,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACZ,MAAM,IAAI,GAAG,IAAI,EAAE,MAAM,IAAI,KAAK,KAAK,KAAK,GAAG,IAAI,CAAC;QACpD,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9B,EAAE,CAAC,SAAS,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;QACtB,WAAW,IAAI,GAAG,CAAC,MAAM,CAAC;QAC1B,IAAI,WAAW,GAAG,OAAO;YAAE,MAAM,EAAE,CAAC;IACtC,CAAC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Tests for the pino-backed structured file logger.
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=logger.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.test.d.ts","sourceRoot":"","sources":["../src/logger.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Tests for the pino-backed structured file logger.
3
+ */
4
+ import { describe, it, beforeEach, afterEach } from "node:test";
5
+ import assert from "node:assert/strict";
6
+ import fs from "node:fs";
7
+ import path from "node:path";
8
+ import os from "node:os";
9
+ import { createLogger } from "./logger.js";
10
+ function readLines(file) {
11
+ return fs
12
+ .readFileSync(file, "utf-8")
13
+ .trim()
14
+ .split("\n")
15
+ .filter((l) => l.length > 0)
16
+ .map((l) => JSON.parse(l));
17
+ }
18
+ describe("logger (pino-backed)", () => {
19
+ let dir;
20
+ let file;
21
+ beforeEach(() => {
22
+ dir = fs.mkdtempSync(path.join(os.tmpdir(), "bikky-logger-"));
23
+ file = path.join(dir, "nested", "test.log");
24
+ });
25
+ afterEach(() => {
26
+ fs.rmSync(dir, { recursive: true, force: true });
27
+ });
28
+ it("creates the parent directory and writes a JSON line with name + msg", () => {
29
+ const log = createLogger("svc", file);
30
+ log("INFO", "hello", { a: 1 });
31
+ const lines = readLines(file);
32
+ assert.equal(lines.length, 1);
33
+ assert.equal(lines[0].level, "info");
34
+ assert.equal(lines[0].name, "svc");
35
+ assert.equal(lines[0].msg, 'hello {"a":1}');
36
+ assert.match(lines[0].time, /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/);
37
+ });
38
+ it("maps DEBUG/INFO/WARN/ERROR levels to pino lowercase labels", () => {
39
+ const log = createLogger("svc", file);
40
+ log("DEBUG", "d");
41
+ log("INFO", "i");
42
+ log("WARN", "w");
43
+ log("ERROR", "e");
44
+ const lines = readLines(file);
45
+ assert.equal(lines.length, 4);
46
+ assert.deepEqual(lines.map((l) => [l.level, l.msg]), [
47
+ ["debug", "d"],
48
+ ["info", "i"],
49
+ ["warn", "w"],
50
+ ["error", "e"],
51
+ ]);
52
+ });
53
+ it("merges a leading object arg as structured fields", () => {
54
+ const log = createLogger("svc", file);
55
+ log("INFO", { event: "embed_request", provider: "openai" }, "ok");
56
+ const lines = readLines(file);
57
+ assert.equal(lines.length, 1);
58
+ assert.equal(lines[0].event, "embed_request");
59
+ assert.equal(lines[0].provider, "openai");
60
+ assert.equal(lines[0].msg, "ok");
61
+ });
62
+ it("serialises non-string args into msg via JSON.stringify", () => {
63
+ const log = createLogger("svc", file);
64
+ log("INFO", "msg", [1, 2], 42);
65
+ const lines = readLines(file);
66
+ assert.equal(lines[0].msg, "msg [1,2] 42");
67
+ });
68
+ it("renders Error args via .message rather than {}", () => {
69
+ const log = createLogger("svc", file);
70
+ log("ERROR", "boom", new Error("kaboom"));
71
+ const lines = readLines(file);
72
+ assert.match(lines[0].msg, /boom kaboom/);
73
+ });
74
+ it("rotates when the file exceeds maxSize", () => {
75
+ const log = createLogger("svc", file, { maxSize: 200, maxFiles: 3 });
76
+ for (let i = 0; i < 50; i++)
77
+ log("INFO", "x".repeat(80));
78
+ assert.ok(fs.existsSync(file), "active log file exists");
79
+ assert.ok(fs.existsSync(`${file}.1`), "rotated file .1 exists");
80
+ const active = fs.statSync(file).size;
81
+ const rotated = fs.statSync(`${file}.1`).size;
82
+ assert.ok(active <= rotated);
83
+ });
84
+ it("caps the number of rotated files at maxFiles", () => {
85
+ const log = createLogger("svc", file, { maxSize: 100, maxFiles: 2 });
86
+ for (let i = 0; i < 100; i++)
87
+ log("INFO", "x".repeat(80));
88
+ assert.ok(fs.existsSync(`${file}.1`));
89
+ assert.ok(!fs.existsSync(`${file}.3`), "should not keep .3");
90
+ });
91
+ it("appends to an existing log file", () => {
92
+ fs.mkdirSync(path.dirname(file), { recursive: true });
93
+ fs.writeFileSync(file, '{"pre":"existing"}\n');
94
+ const log = createLogger("svc", file);
95
+ log("INFO", "after");
96
+ const contents = fs.readFileSync(file, "utf-8");
97
+ assert.ok(contents.startsWith('{"pre":"existing"}\n'));
98
+ const lines = contents.trim().split("\n");
99
+ const last = JSON.parse(lines[lines.length - 1]);
100
+ assert.equal(last.msg, "after");
101
+ });
102
+ });
103
+ //# sourceMappingURL=logger.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.test.js","sourceRoot":"","sources":["../src/logger.test.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAChE,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AAEzB,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAU3C,SAAS,SAAS,CAAC,IAAY;IAC7B,OAAO,EAAE;SACN,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC;SAC3B,IAAI,EAAE;SACN,KAAK,CAAC,IAAI,CAAC;SACX,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;SAC3B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAY,CAAC,CAAC;AAC1C,CAAC;AAED,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;IACpC,IAAI,GAAW,CAAC;IAChB,IAAI,IAAY,CAAC;IAEjB,UAAU,CAAC,GAAG,EAAE;QACd,GAAG,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,eAAe,CAAC,CAAC,CAAC;QAC9D,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qEAAqE,EAAE,GAAG,EAAE;QAC7E,MAAM,GAAG,GAAG,YAAY,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACtC,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QAE/B,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;QAC9B,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAC9B,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QACtC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACpC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;QAC7C,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,IAAI,EAAE,sCAAsC,CAAC,CAAC;IACvE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4DAA4D,EAAE,GAAG,EAAE;QACpE,MAAM,GAAG,GAAG,YAAY,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACtC,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QAClB,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QACjB,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QACjB,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QAElB,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;QAC9B,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAC9B,MAAM,CAAC,SAAS,CACd,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,EAClC;YACE,CAAC,OAAO,EAAE,GAAG,CAAC;YACd,CAAC,MAAM,EAAE,GAAG,CAAC;YACb,CAAC,MAAM,EAAE,GAAG,CAAC;YACb,CAAC,OAAO,EAAE,GAAG,CAAC;SACf,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kDAAkD,EAAE,GAAG,EAAE;QAC1D,MAAM,GAAG,GAAG,YAAY,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACtC,GAAG,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,eAAe,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,IAAI,CAAC,CAAC;QAElE,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;QAC9B,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAC9B,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,KAAK,EAAE,eAAe,CAAC,CAAC;QAC/C,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAC3C,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,MAAM,GAAG,GAAG,YAAY,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACtC,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAE/B,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;QAC9B,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACxD,MAAM,GAAG,GAAG,YAAY,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACtC,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;QAE1C,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;QAC9B,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,MAAM,GAAG,GAAG,YAAY,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC;QACrE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE;YAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;QAEzD,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,wBAAwB,CAAC,CAAC;QACzD,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,IAAI,IAAI,CAAC,EAAE,wBAAwB,CAAC,CAAC;QAChE,MAAM,MAAM,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC;QACtC,MAAM,OAAO,GAAG,EAAE,CAAC,QAAQ,CAAC,GAAG,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC;QAC9C,MAAM,CAAC,EAAE,CAAC,MAAM,IAAI,OAAO,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACtD,MAAM,GAAG,GAAG,YAAY,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC;QACrE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE;YAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;QAE1D,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC;QACtC,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,IAAI,IAAI,CAAC,EAAE,oBAAoB,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;QACzC,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACtD,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,sBAAsB,CAAC,CAAC;QAE/C,MAAM,GAAG,GAAG,YAAY,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACtC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAErB,MAAM,QAAQ,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAChD,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,sBAAsB,CAAC,CAAC,CAAC;QACvD,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC1C,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAE,CAAY,CAAC;QAC7D,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
package/dist/mcp/api.d.ts CHANGED
@@ -4,7 +4,8 @@
4
4
  */
5
5
  import type { QdrantFilter, QdrantGetResult, QdrantScrollResult, QdrantSearchResult } from "./types.js";
6
6
  import { embed, getEmbeddingDimensions, getEmbeddingConfig, initEmbedding } from "../llm/index.js";
7
- export type { EmbeddingProviderConfig } from "../llm/index.js";
7
+ import type { ChatCompletionOpts } from "../llm/index.js";
8
+ export type { ResolvedEmbeddingConfig } from "../llm/index.js";
8
9
  export { embed, getEmbeddingDimensions, getEmbeddingConfig, initEmbedding };
9
10
  export declare const MEMORY_DIR: string;
10
11
  export declare function getCollection(): string;
@@ -12,11 +13,24 @@ export declare function setCollection(name: string): void;
12
13
  export declare let qdrantUrl: string | null;
13
14
  export declare let qdrantApiKey: string | null;
14
15
  export declare let ready: boolean;
16
+ /**
17
+ * Reason the system is not ready, when known. Set by `setSetupError` from the
18
+ * MCP boot path if `initEmbedding`/`ensureCollection` fails. Surfaced in
19
+ * `requireReady()` and `get_setup_status` so users see an actionable message
20
+ * instead of a generic "setup_required".
21
+ */
22
+ export declare let setupError: string | null;
15
23
  export declare function setQdrantUrl(v: string | null): void;
16
24
  export declare function setQdrantApiKey(v: string | null): void;
17
25
  export declare function setReady(v: boolean): void;
26
+ export declare function setSetupError(v: string | null): void;
18
27
  export declare const log: import("../logger.js").LogFn;
19
28
  export declare function chatComplete(systemPrompt: string, userPrompt: string): Promise<string>;
29
+ /**
30
+ * Run a pre-rendered prompt (from src/prompts/*) through the LLM. The
31
+ * RenderedPrompt already carries messages, response_format, temperature, etc.
32
+ */
33
+ export declare function chatCompleteRendered(opts: ChatCompletionOpts): Promise<string>;
20
34
  export declare function qdrantReq<T>(method: string, urlPath: string, body?: unknown): Promise<T>;
21
35
  export declare function ensureCollection(indexes: Array<{
22
36
  field_name: string;
@@ -1 +1 @@
1
- {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/mcp/api.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACxG,OAAO,EAAE,KAAK,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,aAAa,EAA2B,MAAM,iBAAiB,CAAC;AAC5H,YAAY,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,KAAK,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,aAAa,EAAE,CAAC;AAU5E,eAAO,MAAM,UAAU,QAAY,CAAC;AAGpC,wBAAgB,aAAa,IAAI,MAAM,CAA2B;AAClE,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAGhD;AAWD,eAAO,IAAI,SAAS,EAAE,MAAM,GAAG,IAAW,CAAC;AAC3C,eAAO,IAAI,YAAY,EAAE,MAAM,GAAG,IAAW,CAAC;AAC9C,eAAO,IAAI,KAAK,SAAQ,CAAC;AAIzB,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAGnD;AACD,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAGtD;AACD,wBAAgB,QAAQ,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI,CAAe;AAMzD,eAAO,MAAM,GAAG,8BAGd,CAAC;AA0BH,wBAAsB,YAAY,CAAC,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA6B5F;AAgBD,wBAAsB,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAE9F;AAED,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,KAAK,CAAC;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAGlH;AAED,wBAAsB,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAInH;AAED,wBAAsB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,YAAY,GAAG,SAAS,EAAE,KAAK,SAAI,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAO7H;AAED,wBAAsB,YAAY,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,SAAK,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAMhG;AAED,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAKxG;AAED,wBAAsB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,eAAe,CAAC,CAK7E"}
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/mcp/api.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACxG,OAAO,EAAE,KAAK,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,aAAa,EAA2B,MAAM,iBAAiB,CAAC;AAC5H,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAC1D,YAAY,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,KAAK,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,aAAa,EAAE,CAAC;AAU5E,eAAO,MAAM,UAAU,QAAY,CAAC;AAGpC,wBAAgB,aAAa,IAAI,MAAM,CAA2B;AAClE,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAGhD;AAWD,eAAO,IAAI,SAAS,EAAE,MAAM,GAAG,IAAW,CAAC;AAC3C,eAAO,IAAI,YAAY,EAAE,MAAM,GAAG,IAAW,CAAC;AAC9C,eAAO,IAAI,KAAK,SAAQ,CAAC;AACzB;;;;;GAKG;AACH,eAAO,IAAI,UAAU,EAAE,MAAM,GAAG,IAAW,CAAC;AAI5C,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAGnD;AACD,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAGtD;AACD,wBAAgB,QAAQ,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI,CAAe;AACzD,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAoB;AAMzE,eAAO,MAAM,GAAG,8BAGd,CAAC;AA0BH,wBAAsB,YAAY,CAAC,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAa5F;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,CAKpF;AAqCD,wBAAsB,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAE9F;AAED,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,KAAK,CAAC;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAGlH;AAED,wBAAsB,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAInH;AAED,wBAAsB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,YAAY,GAAG,SAAS,EAAE,KAAK,SAAI,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAO7H;AAED,wBAAsB,YAAY,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,SAAK,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAMhG;AAED,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAKxG;AAED,wBAAsB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,eAAe,CAAC,CAK7E"}
package/dist/mcp/api.js CHANGED
@@ -28,6 +28,13 @@ let llmInitialized = false;
28
28
  export let qdrantUrl = null;
29
29
  export let qdrantApiKey = null;
30
30
  export let ready = false;
31
+ /**
32
+ * Reason the system is not ready, when known. Set by `setSetupError` from the
33
+ * MCP boot path if `initEmbedding`/`ensureCollection` fails. Surfaced in
34
+ * `requireReady()` and `get_setup_status` so users see an actionable message
35
+ * instead of a generic "setup_required".
36
+ */
37
+ export let setupError = null;
31
38
  let client = null;
32
39
  export function setQdrantUrl(v) {
33
40
  qdrantUrl = v ? v.replace(/\/+$/, "") : v;
@@ -38,6 +45,7 @@ export function setQdrantApiKey(v) {
38
45
  rebuildClient();
39
46
  }
40
47
  export function setReady(v) { ready = v; }
48
+ export function setSetupError(v) { setupError = v; }
41
49
  // ---------------------------------------------------------------------------
42
50
  // Logging (to file only — stdout/stderr are MCP stdio transport)
43
51
  // ---------------------------------------------------------------------------
@@ -48,7 +56,7 @@ export const log = createLogger("memory-mcp", path.join(LOG_DIR, "mcp.log"), {
48
56
  // Adapter to bridge QdrantClient's QdrantLogFn signature to our file logger.
49
57
  const qdrantLogAdapter = (level, msg) => log(level, msg);
50
58
  function rebuildClient() {
51
- if (qdrantUrl && qdrantApiKey && collectionName) {
59
+ if (qdrantUrl && collectionName) {
52
60
  const cfg = loadConfig();
53
61
  client = new QdrantClient({
54
62
  url: qdrantUrl,
@@ -68,22 +76,7 @@ function rebuildClient() {
68
76
  // LLM Chat Completion (for distillation)
69
77
  // ---------------------------------------------------------------------------
70
78
  export async function chatComplete(systemPrompt, userPrompt) {
71
- if (!llmInitialized) {
72
- const cfg = loadConfig();
73
- initLLM({
74
- config: {
75
- provider: cfg.llm.provider,
76
- ollama_url: cfg.llm.base_url,
77
- ollama_model: cfg.llm.model,
78
- openai_api_key: cfg.llm.api_key ?? null,
79
- openai_model: cfg.llm.model,
80
- bedrock_region: cfg.llm.bedrock_region,
81
- bedrock_model: cfg.llm.model,
82
- },
83
- logger: log,
84
- });
85
- llmInitialized = true;
86
- }
79
+ ensureLLMInitialized();
87
80
  const result = await chatCompletion({
88
81
  messages: [
89
82
  { role: "system", content: systemPrompt },
@@ -96,13 +89,45 @@ export async function chatComplete(systemPrompt, userPrompt) {
96
89
  throw new Error("LLM chat completion failed");
97
90
  return result;
98
91
  }
92
+ /**
93
+ * Run a pre-rendered prompt (from src/prompts/*) through the LLM. The
94
+ * RenderedPrompt already carries messages, response_format, temperature, etc.
95
+ */
96
+ export async function chatCompleteRendered(opts) {
97
+ ensureLLMInitialized();
98
+ const result = await chatCompletion(opts);
99
+ if (!result)
100
+ throw new Error("LLM chat completion failed");
101
+ return result;
102
+ }
103
+ function ensureLLMInitialized() {
104
+ if (llmInitialized)
105
+ return;
106
+ const cfg = loadConfig();
107
+ initLLM({
108
+ config: {
109
+ provider: cfg.llm.provider,
110
+ model: cfg.llm.model,
111
+ baseUrl: cfg.llm.base_url,
112
+ apiKey: cfg.llm.api_key ?? null,
113
+ fallback: cfg.llm.fallback_provider ?? null,
114
+ extra: cfg.llm.extra ?? {},
115
+ timeoutMs: cfg.llm.timeout_ms,
116
+ retries: cfg.llm.retries,
117
+ retryBaseDelayMs: cfg.llm.retry_base_delay_ms,
118
+ },
119
+ logger: log,
120
+ });
121
+ llmInitialized = true;
122
+ }
99
123
  // ---------------------------------------------------------------------------
100
124
  // Qdrant REST Client
101
125
  // ---------------------------------------------------------------------------
102
126
  function getClient() {
103
127
  if (!client) {
104
- throw new Error("Qdrant client not initialized — credentials missing. " +
105
- "Use configure_credentials or set QDRANT_URL + QDRANT_API_KEY.");
128
+ throw new Error("Qdrant client not initialized — URL missing. " +
129
+ "Use configure_credentials or set QDRANT_URL " +
130
+ "(QDRANT_API_KEY is optional for local / self-hosted instances).");
106
131
  }
107
132
  return client;
108
133
  }