@jamesaphoenix/tx-core 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (289) hide show
  1. package/dist/db.d.ts +42 -0
  2. package/dist/db.d.ts.map +1 -0
  3. package/dist/db.js +46 -0
  4. package/dist/db.js.map +1 -0
  5. package/dist/errors.d.ts +231 -0
  6. package/dist/errors.d.ts.map +1 -0
  7. package/dist/errors.js +139 -0
  8. package/dist/errors.js.map +1 -0
  9. package/dist/id.d.ts +6 -0
  10. package/dist/id.d.ts.map +1 -0
  11. package/dist/id.js +21 -0
  12. package/dist/id.js.map +1 -0
  13. package/dist/index.d.ts +25 -0
  14. package/dist/index.d.ts.map +1 -0
  15. package/dist/index.js +56 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/layer.d.ts +50 -0
  18. package/dist/layer.d.ts.map +1 -0
  19. package/dist/layer.js +155 -0
  20. package/dist/layer.js.map +1 -0
  21. package/dist/mappers/anchor.d.ts +14 -0
  22. package/dist/mappers/anchor.d.ts.map +1 -0
  23. package/dist/mappers/anchor.js +38 -0
  24. package/dist/mappers/anchor.js.map +1 -0
  25. package/dist/mappers/attempt.d.ts +15 -0
  26. package/dist/mappers/attempt.d.ts.map +1 -0
  27. package/dist/mappers/attempt.js +23 -0
  28. package/dist/mappers/attempt.js.map +1 -0
  29. package/dist/mappers/candidate.d.ts +23 -0
  30. package/dist/mappers/candidate.d.ts.map +1 -0
  31. package/dist/mappers/candidate.js +53 -0
  32. package/dist/mappers/candidate.js.map +1 -0
  33. package/dist/mappers/claim.d.ts +30 -0
  34. package/dist/mappers/claim.d.ts.map +1 -0
  35. package/dist/mappers/claim.js +32 -0
  36. package/dist/mappers/claim.js.map +1 -0
  37. package/dist/mappers/deduplication.d.ts +39 -0
  38. package/dist/mappers/deduplication.d.ts.map +1 -0
  39. package/dist/mappers/deduplication.js +53 -0
  40. package/dist/mappers/deduplication.js.map +1 -0
  41. package/dist/mappers/edge.d.ts +10 -0
  42. package/dist/mappers/edge.d.ts.map +1 -0
  43. package/dist/mappers/edge.js +19 -0
  44. package/dist/mappers/edge.js.map +1 -0
  45. package/dist/mappers/file-learning.d.ts +14 -0
  46. package/dist/mappers/file-learning.d.ts.map +1 -0
  47. package/dist/mappers/file-learning.js +75 -0
  48. package/dist/mappers/file-learning.js.map +1 -0
  49. package/dist/mappers/index.d.ts +17 -0
  50. package/dist/mappers/index.d.ts.map +1 -0
  51. package/dist/mappers/index.js +30 -0
  52. package/dist/mappers/index.js.map +1 -0
  53. package/dist/mappers/learning.d.ts +19 -0
  54. package/dist/mappers/learning.d.ts.map +1 -0
  55. package/dist/mappers/learning.js +41 -0
  56. package/dist/mappers/learning.js.map +1 -0
  57. package/dist/mappers/orchestrator-state.d.ts +33 -0
  58. package/dist/mappers/orchestrator-state.d.ts.map +1 -0
  59. package/dist/mappers/orchestrator-state.js +34 -0
  60. package/dist/mappers/orchestrator-state.js.map +1 -0
  61. package/dist/mappers/run.d.ts +32 -0
  62. package/dist/mappers/run.d.ts.map +1 -0
  63. package/dist/mappers/run.js +64 -0
  64. package/dist/mappers/run.js.map +1 -0
  65. package/dist/mappers/task.d.ts +23 -0
  66. package/dist/mappers/task.d.ts.map +1 -0
  67. package/dist/mappers/task.js +54 -0
  68. package/dist/mappers/task.js.map +1 -0
  69. package/dist/mappers/tracked-project.d.ts +15 -0
  70. package/dist/mappers/tracked-project.d.ts.map +1 -0
  71. package/dist/mappers/tracked-project.js +23 -0
  72. package/dist/mappers/tracked-project.js.map +1 -0
  73. package/dist/mappers/worker.d.ts +33 -0
  74. package/dist/mappers/worker.d.ts.map +1 -0
  75. package/dist/mappers/worker.js +35 -0
  76. package/dist/mappers/worker.js.map +1 -0
  77. package/dist/repo/anchor-repo.d.ts +52 -0
  78. package/dist/repo/anchor-repo.d.ts.map +1 -0
  79. package/dist/repo/anchor-repo.js +204 -0
  80. package/dist/repo/anchor-repo.js.map +1 -0
  81. package/dist/repo/attempt-repo.d.ts +25 -0
  82. package/dist/repo/attempt-repo.d.ts.map +1 -0
  83. package/dist/repo/attempt-repo.js +78 -0
  84. package/dist/repo/attempt-repo.js.map +1 -0
  85. package/dist/repo/candidate-repo.d.ts +16 -0
  86. package/dist/repo/candidate-repo.d.ts.map +1 -0
  87. package/dist/repo/candidate-repo.js +143 -0
  88. package/dist/repo/candidate-repo.js.map +1 -0
  89. package/dist/repo/claim-repo.d.ts +17 -0
  90. package/dist/repo/claim-repo.d.ts.map +1 -0
  91. package/dist/repo/claim-repo.js +62 -0
  92. package/dist/repo/claim-repo.js.map +1 -0
  93. package/dist/repo/deduplication-repo.d.ts +37 -0
  94. package/dist/repo/deduplication-repo.d.ts.map +1 -0
  95. package/dist/repo/deduplication-repo.js +133 -0
  96. package/dist/repo/deduplication-repo.js.map +1 -0
  97. package/dist/repo/dep-repo.d.ts +19 -0
  98. package/dist/repo/dep-repo.d.ts.map +1 -0
  99. package/dist/repo/dep-repo.js +104 -0
  100. package/dist/repo/dep-repo.js.map +1 -0
  101. package/dist/repo/edge-repo.d.ts +26 -0
  102. package/dist/repo/edge-repo.d.ts.map +1 -0
  103. package/dist/repo/edge-repo.js +227 -0
  104. package/dist/repo/edge-repo.js.map +1 -0
  105. package/dist/repo/file-learning-repo.d.ts +17 -0
  106. package/dist/repo/file-learning-repo.d.ts.map +1 -0
  107. package/dist/repo/file-learning-repo.js +60 -0
  108. package/dist/repo/file-learning-repo.js.map +1 -0
  109. package/dist/repo/index.d.ts +18 -0
  110. package/dist/repo/index.d.ts.map +1 -0
  111. package/dist/repo/index.js +18 -0
  112. package/dist/repo/index.js.map +1 -0
  113. package/dist/repo/learning-repo.d.ts +31 -0
  114. package/dist/repo/learning-repo.d.ts.map +1 -0
  115. package/dist/repo/learning-repo.js +165 -0
  116. package/dist/repo/learning-repo.js.map +1 -0
  117. package/dist/repo/orchestrator-state-repo.d.ts +27 -0
  118. package/dist/repo/orchestrator-state-repo.d.ts.map +1 -0
  119. package/dist/repo/orchestrator-state-repo.js +96 -0
  120. package/dist/repo/orchestrator-state-repo.js.map +1 -0
  121. package/dist/repo/run-repo.d.ts +31 -0
  122. package/dist/repo/run-repo.d.ts.map +1 -0
  123. package/dist/repo/run-repo.js +132 -0
  124. package/dist/repo/run-repo.js.map +1 -0
  125. package/dist/repo/task-repo.d.ts +21 -0
  126. package/dist/repo/task-repo.d.ts.map +1 -0
  127. package/dist/repo/task-repo.js +169 -0
  128. package/dist/repo/task-repo.js.map +1 -0
  129. package/dist/repo/tracked-project-repo.d.ts +16 -0
  130. package/dist/repo/tracked-project-repo.d.ts.map +1 -0
  131. package/dist/repo/tracked-project-repo.js +54 -0
  132. package/dist/repo/tracked-project-repo.js.map +1 -0
  133. package/dist/repo/worker-repo.d.ts +19 -0
  134. package/dist/repo/worker-repo.d.ts.map +1 -0
  135. package/dist/repo/worker-repo.js +72 -0
  136. package/dist/repo/worker-repo.js.map +1 -0
  137. package/dist/schemas/index.d.ts +8 -0
  138. package/dist/schemas/index.d.ts.map +1 -0
  139. package/dist/schemas/index.js +7 -0
  140. package/dist/schemas/index.js.map +1 -0
  141. package/dist/schemas/sync.d.ts +296 -0
  142. package/dist/schemas/sync.d.ts.map +1 -0
  143. package/dist/schemas/sync.js +146 -0
  144. package/dist/schemas/sync.js.map +1 -0
  145. package/dist/schemas/worker.d.ts +77 -0
  146. package/dist/schemas/worker.d.ts.map +1 -0
  147. package/dist/schemas/worker.js +80 -0
  148. package/dist/schemas/worker.js.map +1 -0
  149. package/dist/services/anchor-service.d.ts +147 -0
  150. package/dist/services/anchor-service.d.ts.map +1 -0
  151. package/dist/services/anchor-service.js +540 -0
  152. package/dist/services/anchor-service.js.map +1 -0
  153. package/dist/services/anchor-verification.d.ts +94 -0
  154. package/dist/services/anchor-verification.d.ts.map +1 -0
  155. package/dist/services/anchor-verification.js +617 -0
  156. package/dist/services/anchor-verification.js.map +1 -0
  157. package/dist/services/ast-grep-service.d.ts +58 -0
  158. package/dist/services/ast-grep-service.d.ts.map +1 -0
  159. package/dist/services/ast-grep-service.js +356 -0
  160. package/dist/services/ast-grep-service.js.map +1 -0
  161. package/dist/services/attempt-service.d.ts +24 -0
  162. package/dist/services/attempt-service.d.ts.map +1 -0
  163. package/dist/services/attempt-service.js +55 -0
  164. package/dist/services/attempt-service.js.map +1 -0
  165. package/dist/services/auto-sync-service.d.ts +56 -0
  166. package/dist/services/auto-sync-service.d.ts.map +1 -0
  167. package/dist/services/auto-sync-service.js +66 -0
  168. package/dist/services/auto-sync-service.js.map +1 -0
  169. package/dist/services/candidate-extractor-service.d.ts +56 -0
  170. package/dist/services/candidate-extractor-service.d.ts.map +1 -0
  171. package/dist/services/candidate-extractor-service.js +365 -0
  172. package/dist/services/candidate-extractor-service.js.map +1 -0
  173. package/dist/services/claim-service.d.ts +52 -0
  174. package/dist/services/claim-service.d.ts.map +1 -0
  175. package/dist/services/claim-service.js +134 -0
  176. package/dist/services/claim-service.js.map +1 -0
  177. package/dist/services/daemon-service.d.ts +214 -0
  178. package/dist/services/daemon-service.d.ts.map +1 -0
  179. package/dist/services/daemon-service.js +522 -0
  180. package/dist/services/daemon-service.js.map +1 -0
  181. package/dist/services/deduplication-service.d.ts +67 -0
  182. package/dist/services/deduplication-service.d.ts.map +1 -0
  183. package/dist/services/deduplication-service.js +145 -0
  184. package/dist/services/deduplication-service.js.map +1 -0
  185. package/dist/services/dep-service.d.ts +14 -0
  186. package/dist/services/dep-service.d.ts.map +1 -0
  187. package/dist/services/dep-service.js +34 -0
  188. package/dist/services/dep-service.js.map +1 -0
  189. package/dist/services/diversifier-service.d.ts +46 -0
  190. package/dist/services/diversifier-service.d.ts.map +1 -0
  191. package/dist/services/diversifier-service.js +197 -0
  192. package/dist/services/diversifier-service.js.map +1 -0
  193. package/dist/services/edge-service.d.ts +78 -0
  194. package/dist/services/edge-service.d.ts.map +1 -0
  195. package/dist/services/edge-service.js +158 -0
  196. package/dist/services/edge-service.js.map +1 -0
  197. package/dist/services/embedding-service.d.ts +138 -0
  198. package/dist/services/embedding-service.d.ts.map +1 -0
  199. package/dist/services/embedding-service.js +318 -0
  200. package/dist/services/embedding-service.js.map +1 -0
  201. package/dist/services/feedback-tracker.d.ts +64 -0
  202. package/dist/services/feedback-tracker.d.ts.map +1 -0
  203. package/dist/services/feedback-tracker.js +110 -0
  204. package/dist/services/feedback-tracker.js.map +1 -0
  205. package/dist/services/file-learning-service.d.ts +17 -0
  206. package/dist/services/file-learning-service.d.ts.map +1 -0
  207. package/dist/services/file-learning-service.js +41 -0
  208. package/dist/services/file-learning-service.js.map +1 -0
  209. package/dist/services/file-watcher-service.d.ts +141 -0
  210. package/dist/services/file-watcher-service.d.ts.map +1 -0
  211. package/dist/services/file-watcher-service.js +278 -0
  212. package/dist/services/file-watcher-service.js.map +1 -0
  213. package/dist/services/graph-expansion.d.ts +155 -0
  214. package/dist/services/graph-expansion.d.ts.map +1 -0
  215. package/dist/services/graph-expansion.js +466 -0
  216. package/dist/services/graph-expansion.js.map +1 -0
  217. package/dist/services/hierarchy-service.d.ts +16 -0
  218. package/dist/services/hierarchy-service.d.ts.map +1 -0
  219. package/dist/services/hierarchy-service.js +66 -0
  220. package/dist/services/hierarchy-service.js.map +1 -0
  221. package/dist/services/index.d.ts +36 -0
  222. package/dist/services/index.d.ts.map +1 -0
  223. package/dist/services/index.js +36 -0
  224. package/dist/services/index.js.map +1 -0
  225. package/dist/services/learning-service.d.ts +39 -0
  226. package/dist/services/learning-service.d.ts.map +1 -0
  227. package/dist/services/learning-service.js +151 -0
  228. package/dist/services/learning-service.js.map +1 -0
  229. package/dist/services/migration-service.d.ts +67 -0
  230. package/dist/services/migration-service.d.ts.map +1 -0
  231. package/dist/services/migration-service.js +144 -0
  232. package/dist/services/migration-service.js.map +1 -0
  233. package/dist/services/orchestrator-service.d.ts +52 -0
  234. package/dist/services/orchestrator-service.d.ts.map +1 -0
  235. package/dist/services/orchestrator-service.js +203 -0
  236. package/dist/services/orchestrator-service.js.map +1 -0
  237. package/dist/services/promotion-service.d.ts +67 -0
  238. package/dist/services/promotion-service.d.ts.map +1 -0
  239. package/dist/services/promotion-service.js +151 -0
  240. package/dist/services/promotion-service.js.map +1 -0
  241. package/dist/services/query-expansion-service.d.ts +55 -0
  242. package/dist/services/query-expansion-service.d.ts.map +1 -0
  243. package/dist/services/query-expansion-service.js +174 -0
  244. package/dist/services/query-expansion-service.js.map +1 -0
  245. package/dist/services/ready-service.d.ts +16 -0
  246. package/dist/services/ready-service.d.ts.map +1 -0
  247. package/dist/services/ready-service.js +70 -0
  248. package/dist/services/ready-service.js.map +1 -0
  249. package/dist/services/reranker-service.d.ts +51 -0
  250. package/dist/services/reranker-service.d.ts.map +1 -0
  251. package/dist/services/reranker-service.js +128 -0
  252. package/dist/services/reranker-service.js.map +1 -0
  253. package/dist/services/retriever-service.d.ts +49 -0
  254. package/dist/services/retriever-service.d.ts.map +1 -0
  255. package/dist/services/retriever-service.js +419 -0
  256. package/dist/services/retriever-service.js.map +1 -0
  257. package/dist/services/score-service.d.ts +43 -0
  258. package/dist/services/score-service.d.ts.map +1 -0
  259. package/dist/services/score-service.js +82 -0
  260. package/dist/services/score-service.js.map +1 -0
  261. package/dist/services/swarm-verification.d.ts +104 -0
  262. package/dist/services/swarm-verification.d.ts.map +1 -0
  263. package/dist/services/swarm-verification.js +400 -0
  264. package/dist/services/swarm-verification.js.map +1 -0
  265. package/dist/services/sync-service.d.ts +115 -0
  266. package/dist/services/sync-service.d.ts.map +1 -0
  267. package/dist/services/sync-service.js +350 -0
  268. package/dist/services/sync-service.js.map +1 -0
  269. package/dist/services/task-service.d.ts +22 -0
  270. package/dist/services/task-service.d.ts.map +1 -0
  271. package/dist/services/task-service.js +221 -0
  272. package/dist/services/task-service.js.map +1 -0
  273. package/dist/services/worker-process.d.ts +41 -0
  274. package/dist/services/worker-process.d.ts.map +1 -0
  275. package/dist/services/worker-process.js +280 -0
  276. package/dist/services/worker-process.js.map +1 -0
  277. package/dist/services/worker-service.d.ts +74 -0
  278. package/dist/services/worker-service.d.ts.map +1 -0
  279. package/dist/services/worker-service.js +148 -0
  280. package/dist/services/worker-service.js.map +1 -0
  281. package/dist/utils/glob.d.ts +15 -0
  282. package/dist/utils/glob.d.ts.map +1 -0
  283. package/dist/utils/glob.js +27 -0
  284. package/dist/utils/glob.js.map +1 -0
  285. package/dist/utils/math.d.ts +6 -0
  286. package/dist/utils/math.d.ts.map +1 -0
  287. package/dist/utils/math.js +21 -0
  288. package/dist/utils/math.js.map +1 -0
  289. package/package.json +72 -0
@@ -0,0 +1,419 @@
1
+ import { Context, Effect, Layer, Option } from "effect";
2
+ import { LearningRepository } from "../repo/learning-repo.js";
3
+ import { EmbeddingService } from "./embedding-service.js";
4
+ import { QueryExpansionService } from "./query-expansion-service.js";
5
+ import { RerankerService } from "./reranker-service.js";
6
+ import { GraphExpansionService } from "./graph-expansion.js";
7
+ import { FeedbackTrackerService } from "./feedback-tracker.js";
8
+ import { cosineSimilarity } from "../utils/math.js";
9
+ /** RRF constant - standard value from the original paper */
10
+ const RRF_K = 60;
11
+ /** Default weights for recency (used as boost on top of RRF) */
12
+ const DEFAULT_RECENCY_WEIGHT = 0.1;
13
+ const MAX_AGE_DAYS = 30;
14
+ /** Boost weights for outcome and frequency */
15
+ const OUTCOME_BOOST = 0.05;
16
+ const FREQUENCY_BOOST = 0.02;
17
+ /** Position-aware bonuses for items ranking highly in any retrieval system */
18
+ const TOP_1_BONUS = 0.05; // Bonus for #1 rank in any system
19
+ const TOP_3_BONUS = 0.02; // Bonus for top 3 in any system
20
+ /** Feedback boost weight - scales the 0-1 feedback score */
21
+ const FEEDBACK_BOOST = 0.05;
22
+ /**
23
+ * RetrieverService provides pluggable retrieval for learnings.
24
+ *
25
+ * Design: PRD-015 specifies retrieval should be pluggable with good defaults.
26
+ * Users can swap out the default BM25+vector+RRF pipeline for their own
27
+ * implementation (Pinecone, Weaviate, Chroma, etc.).
28
+ */
29
+ export class RetrieverService extends Context.Tag("RetrieverService")() {
30
+ }
31
+ /**
32
+ * Calculate recency score (0-1) based on age in days.
33
+ * Newer learnings get higher scores.
34
+ */
35
+ const calculateRecencyScore = (createdAt) => {
36
+ const ageMs = Date.now() - createdAt.getTime();
37
+ const ageDays = ageMs / (1000 * 60 * 60 * 24);
38
+ return Math.max(0, 1 - ageDays / MAX_AGE_DAYS);
39
+ };
40
+ /**
41
+ * Compute vector similarity scores and return ranked results.
42
+ * Rank is 1-indexed (1 = best match).
43
+ */
44
+ const computeVectorRanking = (learnings, queryEmbedding) => {
45
+ if (!queryEmbedding) {
46
+ return [];
47
+ }
48
+ const withScores = learnings
49
+ .filter(l => l.embedding !== null)
50
+ .map(learning => {
51
+ const similarity = cosineSimilarity(queryEmbedding, learning.embedding);
52
+ // Normalize cosine similarity from [-1, 1] to [0, 1]
53
+ const score = (similarity + 1) / 2;
54
+ return { learning, score };
55
+ })
56
+ .sort((a, b) => b.score - a.score);
57
+ // Add 1-indexed ranks
58
+ return withScores.map((item, idx) => ({
59
+ ...item,
60
+ rank: idx + 1
61
+ }));
62
+ };
63
+ /**
64
+ * Reciprocal Rank Fusion (RRF) score calculation.
65
+ * Formula: RRF(d) = Σ 1/(k + rank_i(d))
66
+ *
67
+ * k is a constant (typically 60) that determines how much to weight
68
+ * items that appear in multiple lists vs items that rank highly in one list.
69
+ *
70
+ * @param k - RRF constant (default 60)
71
+ * @param ranks - Array of ranks (1-indexed, 0 means not present in that list)
72
+ */
73
+ const rrfScore = (k, ...ranks) => {
74
+ return ranks.reduce((sum, rank) => {
75
+ if (rank === 0)
76
+ return sum; // Not present in this list
77
+ return sum + 1 / (k + rank);
78
+ }, 0);
79
+ };
80
+ /**
81
+ * Combine BM25 and vector search results using Reciprocal Rank Fusion (RRF).
82
+ *
83
+ * RRF is a robust method for combining ranked lists that:
84
+ * 1. Does not require score normalization
85
+ * 2. Works well when combining different retrieval systems
86
+ * 3. Is robust to outliers and different score distributions
87
+ *
88
+ * The final relevance score combines:
89
+ * - RRF score from BM25 and vector rankings
90
+ * - Recency boost for newer learnings
91
+ * - Outcome boost for learnings marked as helpful
92
+ * - Frequency boost for frequently retrieved learnings
93
+ */
94
+ const computeRRFScoring = (bm25Results, vectorRanking) => {
95
+ // Build lookup maps for quick access
96
+ const bm25Map = new Map();
97
+ bm25Results.forEach((result, idx) => {
98
+ bm25Map.set(result.learning.id, { score: result.score, rank: idx + 1 });
99
+ });
100
+ const vectorMap = new Map();
101
+ vectorRanking.forEach(item => {
102
+ vectorMap.set(item.learning.id, { score: item.score, rank: item.rank });
103
+ });
104
+ // Collect all unique learnings from both sources
105
+ const allLearnings = new Map();
106
+ for (const result of bm25Results) {
107
+ allLearnings.set(result.learning.id, result.learning);
108
+ }
109
+ for (const item of vectorRanking) {
110
+ allLearnings.set(item.learning.id, item.learning);
111
+ }
112
+ // Compute RRF scores for all candidates
113
+ const candidates = [];
114
+ for (const [id, learning] of allLearnings) {
115
+ const bm25Info = bm25Map.get(id);
116
+ const vectorInfo = vectorMap.get(id);
117
+ const bm25Rank = bm25Info?.rank ?? 0;
118
+ const vectorRank = vectorInfo?.rank ?? 0;
119
+ const bm25Score = bm25Info?.score ?? 0;
120
+ const vectorScore = vectorInfo?.score ?? 0;
121
+ const recencyScore = calculateRecencyScore(learning.createdAt);
122
+ const rrf = rrfScore(RRF_K, bm25Rank, vectorRank);
123
+ candidates.push({
124
+ learning,
125
+ bm25Score,
126
+ bm25Rank,
127
+ vectorScore,
128
+ vectorRank,
129
+ rrfScore: rrf,
130
+ recencyScore
131
+ });
132
+ }
133
+ // Sort by RRF score (descending)
134
+ return candidates.sort((a, b) => b.rrfScore - a.rrfScore);
135
+ };
136
+ /**
137
+ * Calculate position-aware bonus based on best rank across retrieval systems.
138
+ * Items ranking #1 in any system get a larger bonus; top 3 get a smaller one.
139
+ *
140
+ * @param ranks - Array of ranks (1-indexed, 0 means not present in that system)
141
+ * @returns Position bonus to add to the score
142
+ */
143
+ const calculatePositionBonus = (...ranks) => {
144
+ // Filter out zeros (not present in that ranking)
145
+ const validRanks = ranks.filter(r => r > 0);
146
+ if (validRanks.length === 0)
147
+ return 0;
148
+ const bestRank = Math.min(...validRanks);
149
+ if (bestRank === 1)
150
+ return TOP_1_BONUS; // #1 in any system
151
+ if (bestRank <= 3)
152
+ return TOP_3_BONUS; // Top 3 in any system
153
+ return 0;
154
+ };
155
+ /**
156
+ * Convert RRF candidates to final LearningWithScore results.
157
+ * Applies additional boosts for recency, outcome, frequency, position, and feedback.
158
+ *
159
+ * @param candidates - RRF-scored candidates
160
+ * @param recencyWeight - Weight for recency boost
161
+ * @param feedbackScores - Optional map of learning ID to feedback score (0-1)
162
+ */
163
+ const applyFinalScoring = (candidates, recencyWeight, feedbackScores) => {
164
+ return candidates.map(candidate => {
165
+ const { learning, bm25Score, bm25Rank, vectorScore, vectorRank, rrfScore: rrf, recencyScore } = candidate;
166
+ // Outcome boost: if learning has been marked helpful, boost it
167
+ const outcomeBoost = learning.outcomeScore !== null
168
+ ? OUTCOME_BOOST * learning.outcomeScore
169
+ : 0;
170
+ // Frequency boost: learnings that have been retrieved more get a small boost
171
+ const frequencyBoost = FREQUENCY_BOOST * Math.log(1 + learning.usageCount);
172
+ // Position-aware bonus: reward items that rank highly in any retrieval system
173
+ const positionBonus = calculatePositionBonus(bm25Rank, vectorRank);
174
+ // Feedback boost: learnings that were helpful in past runs get boosted
175
+ // feedbackScore is 0-1 with 0.5 being neutral (no feedback)
176
+ const feedbackScore = feedbackScores?.get(learning.id) ?? 0.5;
177
+ const feedbackBoost = FEEDBACK_BOOST * feedbackScore;
178
+ // Final relevance score: RRF as base + boosts
179
+ // RRF score range is [0, 2/k] for two lists, normalize to [0, 1] range
180
+ // Max possible RRF = 2 * 1/(k+1) ≈ 0.0328 for k=60
181
+ // Normalize: multiply by (k+1)/2 to get ~[0, 1]
182
+ const normalizedRRF = rrf * (RRF_K + 1) / 2;
183
+ const relevanceScore = normalizedRRF +
184
+ recencyWeight * recencyScore +
185
+ outcomeBoost +
186
+ frequencyBoost +
187
+ positionBonus +
188
+ feedbackBoost;
189
+ return {
190
+ ...learning,
191
+ relevanceScore,
192
+ bm25Score,
193
+ vectorScore,
194
+ recencyScore,
195
+ rrfScore: rrf,
196
+ bm25Rank,
197
+ vectorRank,
198
+ feedbackScore
199
+ };
200
+ }).sort((a, b) => b.relevanceScore - a.relevanceScore);
201
+ };
202
+ /**
203
+ * Noop fallback - returns empty results.
204
+ * Used when retrieval is disabled or for testing without full pipeline.
205
+ */
206
+ export const RetrieverServiceNoop = Layer.succeed(RetrieverService, {
207
+ search: (_query, _options) => Effect.succeed([]),
208
+ isAvailable: () => Effect.succeed(false)
209
+ });
210
+ /**
211
+ * Live implementation with BM25 + vector + RRF pipeline.
212
+ * Uses the default hybrid search with:
213
+ * - Query expansion via LLM
214
+ * - BM25 full-text search
215
+ * - Vector similarity (when embeddings available)
216
+ * - RRF fusion
217
+ * - Optional LLM re-ranking
218
+ * - Recency/outcome/frequency boosts
219
+ */
220
+ export const RetrieverServiceLive = Layer.effect(RetrieverService, Effect.gen(function* () {
221
+ const learningRepo = yield* LearningRepository;
222
+ const embeddingService = yield* EmbeddingService;
223
+ const queryExpansionService = yield* QueryExpansionService;
224
+ const rerankerService = yield* RerankerService;
225
+ // GraphExpansionService is optional - graceful degradation when not available
226
+ const graphExpansionServiceOption = yield* Effect.serviceOption(GraphExpansionService);
227
+ const graphExpansionService = Option.getOrNull(graphExpansionServiceOption);
228
+ // FeedbackTrackerService is optional - graceful degradation when not available
229
+ const feedbackTrackerServiceOption = yield* Effect.serviceOption(FeedbackTrackerService);
230
+ const feedbackTrackerService = Option.getOrNull(feedbackTrackerServiceOption);
231
+ // Load recency weight from config
232
+ const recencyWeightStr = yield* learningRepo.getConfig("recency_weight");
233
+ const recencyWeight = recencyWeightStr ? parseFloat(recencyWeightStr) : DEFAULT_RECENCY_WEIGHT;
234
+ /**
235
+ * Apply LLM re-ranking to scored learnings with position-aware blending.
236
+ * Re-ranking uses a specialized model to improve precision.
237
+ * Position-aware blending gives bonuses to items that rank highly
238
+ * across multiple retrieval systems (BM25, vector, AND reranker).
239
+ * Gracefully degrades if reranker is unavailable.
240
+ */
241
+ const applyReranking = (query, learnings, rerankerWeight = 0.3) => Effect.gen(function* () {
242
+ // Check if reranker is available
243
+ const isAvailable = yield* rerankerService.isAvailable();
244
+ if (!isAvailable || learnings.length === 0) {
245
+ return learnings;
246
+ }
247
+ // Extract document contents for reranking
248
+ const documents = learnings.map(l => l.content);
249
+ // Get reranker scores (graceful degradation on error)
250
+ const reranked = yield* Effect.catchAll(rerankerService.rerank(query, documents), () => Effect.succeed(null));
251
+ if (!reranked) {
252
+ return learnings;
253
+ }
254
+ // Create maps of content to reranker score and rank
255
+ const rerankerScores = new Map();
256
+ const rerankerRanks = new Map();
257
+ reranked.forEach((result, idx) => {
258
+ rerankerScores.set(result.document, result.score);
259
+ rerankerRanks.set(result.document, idx + 1); // 1-indexed rank
260
+ });
261
+ // Blend reranker scores with existing relevance scores using position-aware bonuses
262
+ // Formula: final = (1 - weight) * existing + weight * reranker + positionBonus
263
+ return learnings.map(learning => {
264
+ const rerankerScore = rerankerScores.get(learning.content) ?? 0;
265
+ const rerankerRank = rerankerRanks.get(learning.content) ?? 0;
266
+ // Calculate position bonus across all three systems (BM25, vector, reranker)
267
+ const positionBonus = calculatePositionBonus(learning.bm25Rank, learning.vectorRank, rerankerRank);
268
+ // Weighted blend plus position-aware bonus
269
+ const blendedScore = (1 - rerankerWeight) * learning.relevanceScore +
270
+ rerankerWeight * rerankerScore +
271
+ positionBonus;
272
+ return {
273
+ ...learning,
274
+ relevanceScore: blendedScore,
275
+ rerankerScore // Add reranker score to output
276
+ };
277
+ }).sort((a, b) => b.relevanceScore - a.relevanceScore);
278
+ });
279
+ /**
280
+ * Perform BM25 search across multiple queries and merge results.
281
+ * Uses RRF to combine rankings from each query.
282
+ */
283
+ const multiQueryBM25Search = (queries, limit) => Effect.gen(function* () {
284
+ // Search for each query
285
+ const allResults = [];
286
+ for (const query of queries) {
287
+ const results = yield* learningRepo.bm25Search(query, limit);
288
+ allResults.push([...results]);
289
+ }
290
+ // Merge results using best rank across all queries
291
+ const learningRanks = new Map();
292
+ for (const results of allResults) {
293
+ results.forEach((result, idx) => {
294
+ const rank = idx + 1;
295
+ const existing = learningRanks.get(result.learning.id);
296
+ if (!existing || rank < existing.bestRank) {
297
+ learningRanks.set(result.learning.id, {
298
+ learning: result.learning,
299
+ bestRank: rank,
300
+ bestScore: result.score
301
+ });
302
+ }
303
+ });
304
+ }
305
+ // Convert to BM25Result format, sorted by best rank
306
+ const merged = [...learningRanks.values()]
307
+ .sort((a, b) => a.bestRank - b.bestRank)
308
+ .map(item => ({ learning: item.learning, score: item.bestScore }));
309
+ return merged;
310
+ });
311
+ /**
312
+ * Apply graph expansion to seed learnings and merge with existing results.
313
+ * Gracefully degrades if expansion fails or GraphExpansionService is unavailable.
314
+ */
315
+ const applyGraphExpansion = (seeds, options) => Effect.gen(function* () {
316
+ const graphOpts = options.graphExpansion;
317
+ // Skip if not enabled, no seeds, or GraphExpansionService unavailable
318
+ if (!graphOpts?.enabled || seeds.length === 0 || !graphExpansionService) {
319
+ return seeds;
320
+ }
321
+ // Convert top-k seeds to SeedLearning format for expansion
322
+ const seedCount = Math.min(seeds.length, 10); // Default top-k seeds
323
+ const seedLearnings = seeds.slice(0, seedCount).map(s => ({
324
+ learning: s,
325
+ score: s.relevanceScore
326
+ }));
327
+ // Perform graph expansion (graceful degradation on error)
328
+ const expansionResult = yield* Effect.catchAll(graphExpansionService.expand(seedLearnings, {
329
+ depth: graphOpts.depth ?? 2,
330
+ decayFactor: graphOpts.decayFactor ?? 0.7,
331
+ maxNodes: graphOpts.maxNodes ?? 100,
332
+ edgeTypes: graphOpts.edgeTypes
333
+ }), () => Effect.succeed(null));
334
+ if (!expansionResult) {
335
+ // Graph expansion failed, return original seeds with hops=0
336
+ return seeds.map(s => ({
337
+ ...s,
338
+ expansionHops: 0,
339
+ expansionPath: [s.id],
340
+ sourceEdge: null
341
+ }));
342
+ }
343
+ // Create a set of seed IDs to track which learnings are direct matches
344
+ const seedIds = new Set(seeds.map(s => s.id));
345
+ // Mark seed learnings with expansion metadata (hops=0)
346
+ const seedsWithMeta = seeds.map(s => ({
347
+ ...s,
348
+ expansionHops: 0,
349
+ expansionPath: [s.id],
350
+ sourceEdge: null
351
+ }));
352
+ // Convert expanded learnings to LearningWithScore format
353
+ // Only include learnings that aren't already in the seed set (avoid duplicates)
354
+ const expandedWithScores = expansionResult.expanded
355
+ .filter(e => !seedIds.has(e.learning.id))
356
+ .map(e => ({
357
+ ...e.learning,
358
+ relevanceScore: e.decayedScore,
359
+ bm25Score: 0,
360
+ vectorScore: 0,
361
+ recencyScore: calculateRecencyScore(e.learning.createdAt),
362
+ rrfScore: 0,
363
+ bm25Rank: 0,
364
+ vectorRank: 0,
365
+ expansionHops: e.hops,
366
+ expansionPath: e.path,
367
+ sourceEdge: e.sourceEdge
368
+ }));
369
+ // Merge seeds and expanded, sort by relevance score
370
+ const merged = [...seedsWithMeta, ...expandedWithScores]
371
+ .sort((a, b) => b.relevanceScore - a.relevanceScore);
372
+ return merged;
373
+ });
374
+ return {
375
+ search: (query, options) => Effect.gen(function* () {
376
+ const { limit = 10, minScore = 0.1 } = options ?? {};
377
+ // Expand query using LLM (graceful degradation - returns original if unavailable)
378
+ const expansionResult = yield* Effect.catchAll(queryExpansionService.expand(query), () => Effect.succeed({ original: query, expanded: [query], wasExpanded: false }));
379
+ // Get BM25 search results across all expanded queries (ranked list 1)
380
+ const bm25Results = yield* multiQueryBM25Search(expansionResult.expanded, limit * 3);
381
+ // Try to get query embedding for vector search (graceful degradation)
382
+ // Use original query for embedding since expanded queries may be noisier
383
+ const queryEmbedding = yield* Effect.option(embeddingService.embed(query));
384
+ const queryEmbeddingValue = Option.getOrNull(queryEmbedding);
385
+ // Get all learnings that have embeddings for vector ranking
386
+ const learningsWithEmbeddings = yield* learningRepo.findWithEmbeddings(limit * 3);
387
+ // Compute vector ranking (ranked list 2)
388
+ const vectorRanking = computeVectorRanking(learningsWithEmbeddings, queryEmbeddingValue);
389
+ // Combine using RRF
390
+ const candidates = computeRRFScoring(bm25Results, vectorRanking);
391
+ // Fetch feedback scores for all candidates in a single batch (graceful degradation)
392
+ let feedbackScores;
393
+ if (feedbackTrackerService && candidates.length > 0) {
394
+ const learningIds = candidates.map(c => c.learning.id);
395
+ feedbackScores = yield* Effect.catchAll(feedbackTrackerService.getFeedbackScores(learningIds), () => Effect.succeed(new Map(learningIds.map(id => [id, 0.5]))));
396
+ }
397
+ // Apply final scoring with boosts (including feedback)
398
+ const scored = applyFinalScoring(candidates, recencyWeight, feedbackScores);
399
+ // Apply graph expansion if enabled (after initial RRF scoring)
400
+ const withGraphExpansion = yield* applyGraphExpansion(scored, options ?? {});
401
+ // Apply LLM re-ranking to top candidates for improved precision
402
+ // Only re-rank a reasonable number of candidates to balance quality vs latency
403
+ const topCandidates = withGraphExpansion.slice(0, Math.min(limit * 2, 20));
404
+ const reranked = yield* applyReranking(query, topCandidates);
405
+ // Filter by minimum score and limit
406
+ return reranked
407
+ .filter(r => r.relevanceScore >= minScore)
408
+ .slice(0, limit);
409
+ }),
410
+ isAvailable: () => Effect.succeed(true)
411
+ };
412
+ }));
413
+ /**
414
+ * Auto-detecting layer that always uses Live since BM25 is always available.
415
+ * The Live implementation gracefully degrades vector search when embeddings
416
+ * are unavailable, so Auto just delegates to Live.
417
+ */
418
+ export const RetrieverServiceAuto = RetrieverServiceLive;
419
+ //# sourceMappingURL=retriever-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retriever-service.js","sourceRoot":"","sources":["../../src/services/retriever-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ import { Context, Effect, Layer } from "effect";
2
+ import { TaskRepository } from "../repo/task-repo.js";
3
+ import { DependencyRepository } from "../repo/dep-repo.js";
4
+ import { TaskNotFoundError, DatabaseError } from "../errors.js";
5
+ import { HierarchyService } from "./hierarchy-service.js";
6
+ import type { Task, TaskId } from "@jamesaphoenix/tx-types";
7
+ /**
8
+ * Score breakdown showing each adjustment factor (for CLI display)
9
+ */
10
+ export interface ScoreBreakdown {
11
+ readonly baseScore: number;
12
+ readonly blockingBonus: number;
13
+ readonly blockingCount: number;
14
+ readonly ageBonus: number;
15
+ readonly ageHours: number;
16
+ readonly depthPenalty: number;
17
+ readonly depth: number;
18
+ readonly blockedPenalty: number;
19
+ readonly finalScore: number;
20
+ }
21
+ declare const ScoreService_base: Context.TagClass<ScoreService, "ScoreService", {
22
+ /**
23
+ * Calculate the final score with dynamic adjustments
24
+ */
25
+ readonly calculate: (task: Task) => Effect.Effect<number, DatabaseError>;
26
+ /**
27
+ * Calculate score for a task by ID
28
+ */
29
+ readonly calculateById: (id: TaskId) => Effect.Effect<number, TaskNotFoundError | DatabaseError>;
30
+ /**
31
+ * Get detailed score breakdown for display
32
+ */
33
+ readonly getBreakdown: (task: Task) => Effect.Effect<ScoreBreakdown, DatabaseError>;
34
+ /**
35
+ * Get score breakdown by task ID
36
+ */
37
+ readonly getBreakdownById: (id: TaskId) => Effect.Effect<ScoreBreakdown, TaskNotFoundError | DatabaseError>;
38
+ }>;
39
+ export declare class ScoreService extends ScoreService_base {
40
+ }
41
+ export declare const ScoreServiceLive: Layer.Layer<ScoreService, never, TaskRepository | DependencyRepository | HierarchyService>;
42
+ export {};
43
+ //# sourceMappingURL=score-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"score-service.d.ts","sourceRoot":"","sources":["../../src/services/score-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAA;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AACrD,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AAC1D,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAC/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AACzD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAA;AAE3D;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAA;IAC/B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAC5B;;IAKG;;OAEG;wBACiB,CAAC,IAAI,EAAE,IAAI,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC;IAExE;;OAEG;4BACqB,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,iBAAiB,GAAG,aAAa,CAAC;IAEhG;;OAEG;2BACoB,CAAC,IAAI,EAAE,IAAI,KAAK,MAAM,CAAC,MAAM,CAAC,cAAc,EAAE,aAAa,CAAC;IAEnF;;OAEG;+BACwB,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,cAAc,EAAE,iBAAiB,GAAG,aAAa,CAAC;;AArB/G,qBAAa,YAAa,SAAQ,iBAuB/B;CAAG;AAEN,eAAO,MAAM,gBAAgB,4FAoG5B,CAAA"}
@@ -0,0 +1,82 @@
1
+ import { Context, Effect, Layer } from "effect";
2
+ import { TaskRepository } from "../repo/task-repo.js";
3
+ import { DependencyRepository } from "../repo/dep-repo.js";
4
+ import { TaskNotFoundError } from "../errors.js";
5
+ import { HierarchyService } from "./hierarchy-service.js";
6
+ export class ScoreService extends Context.Tag("ScoreService")() {
7
+ }
8
+ export const ScoreServiceLive = Layer.effect(ScoreService, Effect.gen(function* () {
9
+ const taskRepo = yield* TaskRepository;
10
+ const depRepo = yield* DependencyRepository;
11
+ const hierarchySvc = yield* HierarchyService;
12
+ const computeBreakdown = (task, blockingCount, depth) => {
13
+ // Base score from DB
14
+ const baseScore = task.score;
15
+ // Blocking bonus: +25 per task this task blocks
16
+ const blockingBonus = blockingCount * 25;
17
+ // Age bonus: old tasks shouldn't rot
18
+ const ageMs = Date.now() - task.createdAt.getTime();
19
+ const ageHours = ageMs / (1000 * 60 * 60);
20
+ let ageBonus = 0;
21
+ if (ageHours > 48) {
22
+ ageBonus = 100;
23
+ }
24
+ else if (ageHours > 24) {
25
+ ageBonus = 50;
26
+ }
27
+ // Depth penalty: prefer root tasks over deep subtasks
28
+ const depthPenalty = depth * 10;
29
+ // Blocked status penalty: blocked tasks should not be prioritized
30
+ const blockedPenalty = task.status === "blocked" ? 1000 : 0;
31
+ // Final calculation
32
+ const finalScore = baseScore + blockingBonus + ageBonus - depthPenalty - blockedPenalty;
33
+ return {
34
+ baseScore,
35
+ blockingBonus,
36
+ blockingCount,
37
+ ageBonus,
38
+ ageHours: Math.floor(ageHours),
39
+ depthPenalty,
40
+ depth,
41
+ blockedPenalty,
42
+ finalScore
43
+ };
44
+ };
45
+ const getTaskContext = (task) => Effect.gen(function* () {
46
+ // Get how many tasks this task blocks
47
+ const blockingIds = yield* depRepo.getBlockingIds(task.id);
48
+ const blockingCount = blockingIds.length;
49
+ // Get depth in hierarchy
50
+ const depth = yield* hierarchySvc.getDepth(task.id).pipe(Effect.catchTag("TaskNotFoundError", () => Effect.succeed(0)));
51
+ return { blockingCount, depth };
52
+ });
53
+ return {
54
+ calculate: (task) => Effect.gen(function* () {
55
+ const ctx = yield* getTaskContext(task);
56
+ const breakdown = computeBreakdown(task, ctx.blockingCount, ctx.depth);
57
+ return breakdown.finalScore;
58
+ }),
59
+ calculateById: (id) => Effect.gen(function* () {
60
+ const task = yield* taskRepo.findById(id);
61
+ if (!task) {
62
+ return yield* Effect.fail(new TaskNotFoundError({ id }));
63
+ }
64
+ const ctx = yield* getTaskContext(task);
65
+ const breakdown = computeBreakdown(task, ctx.blockingCount, ctx.depth);
66
+ return breakdown.finalScore;
67
+ }),
68
+ getBreakdown: (task) => Effect.gen(function* () {
69
+ const ctx = yield* getTaskContext(task);
70
+ return computeBreakdown(task, ctx.blockingCount, ctx.depth);
71
+ }),
72
+ getBreakdownById: (id) => Effect.gen(function* () {
73
+ const task = yield* taskRepo.findById(id);
74
+ if (!task) {
75
+ return yield* Effect.fail(new TaskNotFoundError({ id }));
76
+ }
77
+ const ctx = yield* getTaskContext(task);
78
+ return computeBreakdown(task, ctx.blockingCount, ctx.depth);
79
+ })
80
+ };
81
+ }));
82
+ //# sourceMappingURL=score-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"score-service.js","sourceRoot":"","sources":["../../src/services/score-service.ts"],"names":[],"mappings":"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"}