cursor-recursive-rag 0.2.0-alpha.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (210) hide show
  1. package/README.md +179 -203
  2. package/dist/adapters/llm/anthropic.d.ts +27 -0
  3. package/dist/adapters/llm/anthropic.d.ts.map +1 -0
  4. package/dist/adapters/llm/anthropic.js +287 -0
  5. package/dist/adapters/llm/anthropic.js.map +1 -0
  6. package/dist/adapters/llm/base.d.ts +62 -0
  7. package/dist/adapters/llm/base.d.ts.map +1 -0
  8. package/dist/adapters/llm/base.js +140 -0
  9. package/dist/adapters/llm/base.js.map +1 -0
  10. package/dist/adapters/llm/deepseek.d.ts +24 -0
  11. package/dist/adapters/llm/deepseek.d.ts.map +1 -0
  12. package/dist/adapters/llm/deepseek.js +228 -0
  13. package/dist/adapters/llm/deepseek.js.map +1 -0
  14. package/dist/adapters/llm/groq.d.ts +25 -0
  15. package/dist/adapters/llm/groq.d.ts.map +1 -0
  16. package/dist/adapters/llm/groq.js +265 -0
  17. package/dist/adapters/llm/groq.js.map +1 -0
  18. package/dist/adapters/llm/index.d.ts +62 -0
  19. package/dist/adapters/llm/index.d.ts.map +1 -0
  20. package/dist/adapters/llm/index.js +380 -0
  21. package/dist/adapters/llm/index.js.map +1 -0
  22. package/dist/adapters/llm/ollama.d.ts +23 -0
  23. package/dist/adapters/llm/ollama.d.ts.map +1 -0
  24. package/dist/adapters/llm/ollama.js +261 -0
  25. package/dist/adapters/llm/ollama.js.map +1 -0
  26. package/dist/adapters/llm/openai.d.ts +22 -0
  27. package/dist/adapters/llm/openai.d.ts.map +1 -0
  28. package/dist/adapters/llm/openai.js +232 -0
  29. package/dist/adapters/llm/openai.js.map +1 -0
  30. package/dist/adapters/llm/openrouter.d.ts +27 -0
  31. package/dist/adapters/llm/openrouter.d.ts.map +1 -0
  32. package/dist/adapters/llm/openrouter.js +305 -0
  33. package/dist/adapters/llm/openrouter.js.map +1 -0
  34. package/dist/adapters/vector/index.d.ts.map +1 -1
  35. package/dist/adapters/vector/index.js +8 -0
  36. package/dist/adapters/vector/index.js.map +1 -1
  37. package/dist/adapters/vector/redis-native.d.ts +35 -0
  38. package/dist/adapters/vector/redis-native.d.ts.map +1 -0
  39. package/dist/adapters/vector/redis-native.js +170 -0
  40. package/dist/adapters/vector/redis-native.js.map +1 -0
  41. package/dist/cli/commands/chat.d.ts +4 -0
  42. package/dist/cli/commands/chat.d.ts.map +1 -0
  43. package/dist/cli/commands/chat.js +374 -0
  44. package/dist/cli/commands/chat.js.map +1 -0
  45. package/dist/cli/commands/maintenance.d.ts +4 -0
  46. package/dist/cli/commands/maintenance.d.ts.map +1 -0
  47. package/dist/cli/commands/maintenance.js +237 -0
  48. package/dist/cli/commands/maintenance.js.map +1 -0
  49. package/dist/cli/commands/rules.d.ts +9 -0
  50. package/dist/cli/commands/rules.d.ts.map +1 -0
  51. package/dist/cli/commands/rules.js +639 -0
  52. package/dist/cli/commands/rules.js.map +1 -0
  53. package/dist/cli/commands/setup.js +5 -4
  54. package/dist/cli/commands/setup.js.map +1 -1
  55. package/dist/cli/index.js +6 -0
  56. package/dist/cli/index.js.map +1 -1
  57. package/dist/config/memoryConfig.d.ts +427 -0
  58. package/dist/config/memoryConfig.d.ts.map +1 -0
  59. package/dist/config/memoryConfig.js +258 -0
  60. package/dist/config/memoryConfig.js.map +1 -0
  61. package/dist/config/rulesConfig.d.ts +486 -0
  62. package/dist/config/rulesConfig.d.ts.map +1 -0
  63. package/dist/config/rulesConfig.js +345 -0
  64. package/dist/config/rulesConfig.js.map +1 -0
  65. package/dist/dashboard/coreTools.d.ts +14 -0
  66. package/dist/dashboard/coreTools.d.ts.map +1 -0
  67. package/dist/dashboard/coreTools.js +413 -0
  68. package/dist/dashboard/coreTools.js.map +1 -0
  69. package/dist/dashboard/public/index.html +1982 -13
  70. package/dist/dashboard/server.d.ts +1 -8
  71. package/dist/dashboard/server.d.ts.map +1 -1
  72. package/dist/dashboard/server.js +846 -13
  73. package/dist/dashboard/server.js.map +1 -1
  74. package/dist/dashboard/toolRegistry.d.ts +192 -0
  75. package/dist/dashboard/toolRegistry.d.ts.map +1 -0
  76. package/dist/dashboard/toolRegistry.js +322 -0
  77. package/dist/dashboard/toolRegistry.js.map +1 -0
  78. package/dist/proxy/index.d.ts +1 -1
  79. package/dist/proxy/index.d.ts.map +1 -1
  80. package/dist/proxy/index.js +9 -6
  81. package/dist/proxy/index.js.map +1 -1
  82. package/dist/server/index.js +21 -0
  83. package/dist/server/index.js.map +1 -1
  84. package/dist/server/tools/crawl.d.ts.map +1 -1
  85. package/dist/server/tools/crawl.js +8 -0
  86. package/dist/server/tools/crawl.js.map +1 -1
  87. package/dist/server/tools/index.d.ts.map +1 -1
  88. package/dist/server/tools/index.js +19 -1
  89. package/dist/server/tools/index.js.map +1 -1
  90. package/dist/server/tools/ingest.d.ts.map +1 -1
  91. package/dist/server/tools/ingest.js +5 -0
  92. package/dist/server/tools/ingest.js.map +1 -1
  93. package/dist/server/tools/memory.d.ts +250 -0
  94. package/dist/server/tools/memory.d.ts.map +1 -0
  95. package/dist/server/tools/memory.js +472 -0
  96. package/dist/server/tools/memory.js.map +1 -0
  97. package/dist/server/tools/recursive-query.d.ts.map +1 -1
  98. package/dist/server/tools/recursive-query.js +6 -0
  99. package/dist/server/tools/recursive-query.js.map +1 -1
  100. package/dist/server/tools/search.d.ts.map +1 -1
  101. package/dist/server/tools/search.js +6 -0
  102. package/dist/server/tools/search.js.map +1 -1
  103. package/dist/services/activity-log.d.ts +10 -0
  104. package/dist/services/activity-log.d.ts.map +1 -0
  105. package/dist/services/activity-log.js +53 -0
  106. package/dist/services/activity-log.js.map +1 -0
  107. package/dist/services/categoryManager.d.ts +110 -0
  108. package/dist/services/categoryManager.d.ts.map +1 -0
  109. package/dist/services/categoryManager.js +549 -0
  110. package/dist/services/categoryManager.js.map +1 -0
  111. package/dist/services/contextEnvironment.d.ts +206 -0
  112. package/dist/services/contextEnvironment.d.ts.map +1 -0
  113. package/dist/services/contextEnvironment.js +481 -0
  114. package/dist/services/contextEnvironment.js.map +1 -0
  115. package/dist/services/conversationProcessor.d.ts +99 -0
  116. package/dist/services/conversationProcessor.d.ts.map +1 -0
  117. package/dist/services/conversationProcessor.js +311 -0
  118. package/dist/services/conversationProcessor.js.map +1 -0
  119. package/dist/services/cursorChatReader.d.ts +129 -0
  120. package/dist/services/cursorChatReader.d.ts.map +1 -0
  121. package/dist/services/cursorChatReader.js +419 -0
  122. package/dist/services/cursorChatReader.js.map +1 -0
  123. package/dist/services/decayCalculator.d.ts +85 -0
  124. package/dist/services/decayCalculator.d.ts.map +1 -0
  125. package/dist/services/decayCalculator.js +182 -0
  126. package/dist/services/decayCalculator.js.map +1 -0
  127. package/dist/services/enhancedVectorStore.d.ts +102 -0
  128. package/dist/services/enhancedVectorStore.d.ts.map +1 -0
  129. package/dist/services/enhancedVectorStore.js +245 -0
  130. package/dist/services/enhancedVectorStore.js.map +1 -0
  131. package/dist/services/hybridScorer.d.ts +120 -0
  132. package/dist/services/hybridScorer.d.ts.map +1 -0
  133. package/dist/services/hybridScorer.js +334 -0
  134. package/dist/services/hybridScorer.js.map +1 -0
  135. package/dist/services/knowledgeExtractor.d.ts +45 -0
  136. package/dist/services/knowledgeExtractor.d.ts.map +1 -0
  137. package/dist/services/knowledgeExtractor.js +436 -0
  138. package/dist/services/knowledgeExtractor.js.map +1 -0
  139. package/dist/services/knowledgeStorage.d.ts +102 -0
  140. package/dist/services/knowledgeStorage.d.ts.map +1 -0
  141. package/dist/services/knowledgeStorage.js +383 -0
  142. package/dist/services/knowledgeStorage.js.map +1 -0
  143. package/dist/services/maintenanceScheduler.d.ts +89 -0
  144. package/dist/services/maintenanceScheduler.d.ts.map +1 -0
  145. package/dist/services/maintenanceScheduler.js +479 -0
  146. package/dist/services/maintenanceScheduler.js.map +1 -0
  147. package/dist/services/memoryMetadataStore.d.ts +62 -0
  148. package/dist/services/memoryMetadataStore.d.ts.map +1 -0
  149. package/dist/services/memoryMetadataStore.js +570 -0
  150. package/dist/services/memoryMetadataStore.js.map +1 -0
  151. package/dist/services/recursiveRetrieval.d.ts +122 -0
  152. package/dist/services/recursiveRetrieval.d.ts.map +1 -0
  153. package/dist/services/recursiveRetrieval.js +443 -0
  154. package/dist/services/recursiveRetrieval.js.map +1 -0
  155. package/dist/services/relationshipGraph.d.ts +77 -0
  156. package/dist/services/relationshipGraph.d.ts.map +1 -0
  157. package/dist/services/relationshipGraph.js +411 -0
  158. package/dist/services/relationshipGraph.js.map +1 -0
  159. package/dist/services/rlmSafeguards.d.ts +273 -0
  160. package/dist/services/rlmSafeguards.d.ts.map +1 -0
  161. package/dist/services/rlmSafeguards.js +705 -0
  162. package/dist/services/rlmSafeguards.js.map +1 -0
  163. package/dist/services/rulesAnalyzer.d.ts +119 -0
  164. package/dist/services/rulesAnalyzer.d.ts.map +1 -0
  165. package/dist/services/rulesAnalyzer.js +768 -0
  166. package/dist/services/rulesAnalyzer.js.map +1 -0
  167. package/dist/services/rulesMerger.d.ts +75 -0
  168. package/dist/services/rulesMerger.d.ts.map +1 -0
  169. package/dist/services/rulesMerger.js +404 -0
  170. package/dist/services/rulesMerger.js.map +1 -0
  171. package/dist/services/rulesParser.d.ts +127 -0
  172. package/dist/services/rulesParser.d.ts.map +1 -0
  173. package/dist/services/rulesParser.js +594 -0
  174. package/dist/services/rulesParser.js.map +1 -0
  175. package/dist/services/smartChunker.d.ts +110 -0
  176. package/dist/services/smartChunker.d.ts.map +1 -0
  177. package/dist/services/smartChunker.js +520 -0
  178. package/dist/services/smartChunker.js.map +1 -0
  179. package/dist/types/categories.d.ts +105 -0
  180. package/dist/types/categories.d.ts.map +1 -0
  181. package/dist/types/categories.js +108 -0
  182. package/dist/types/categories.js.map +1 -0
  183. package/dist/types/extractedKnowledge.d.ts +233 -0
  184. package/dist/types/extractedKnowledge.d.ts.map +1 -0
  185. package/dist/types/extractedKnowledge.js +56 -0
  186. package/dist/types/extractedKnowledge.js.map +1 -0
  187. package/dist/types/index.d.ts +9 -2
  188. package/dist/types/index.d.ts.map +1 -1
  189. package/dist/types/index.js +12 -1
  190. package/dist/types/index.js.map +1 -1
  191. package/dist/types/llmProvider.d.ts +282 -0
  192. package/dist/types/llmProvider.d.ts.map +1 -0
  193. package/dist/types/llmProvider.js +48 -0
  194. package/dist/types/llmProvider.js.map +1 -0
  195. package/dist/types/memory.d.ts +227 -0
  196. package/dist/types/memory.d.ts.map +1 -0
  197. package/dist/types/memory.js +76 -0
  198. package/dist/types/memory.js.map +1 -0
  199. package/dist/types/relationships.d.ts +167 -0
  200. package/dist/types/relationships.d.ts.map +1 -0
  201. package/dist/types/relationships.js +106 -0
  202. package/dist/types/relationships.js.map +1 -0
  203. package/dist/types/rulesOptimizer.d.ts +345 -0
  204. package/dist/types/rulesOptimizer.d.ts.map +1 -0
  205. package/dist/types/rulesOptimizer.js +22 -0
  206. package/dist/types/rulesOptimizer.js.map +1 -0
  207. package/docs/cursor-recursive-rag-memory-spec.md +4569 -0
  208. package/docs/cursor-recursive-rag-tasks.md +1355 -0
  209. package/package.json +6 -3
  210. package/restart-rag.sh +16 -0
@@ -0,0 +1,705 @@
1
+ /**
2
+ * RLM Safeguards - Anti-Pattern Mitigations
3
+ *
4
+ * Based on the Recursive Language Models paper's Negative Results (Appendix A),
5
+ * this service implements safeguards to prevent common anti-patterns:
6
+ *
7
+ * 1. Model-specific configurations (not one-size-fits-all)
8
+ * 2. Capability detection for code execution ability
9
+ * 3. Token budget management (reserve for answers)
10
+ * 4. Multi-signal termination detection (not just tags)
11
+ * 5. Sub-call throttling and caching
12
+ * 6. Circuit breaker for runaway trajectories
13
+ * 7. Model prior-based pre-filtering
14
+ */
15
+ import { createHash } from 'crypto';
16
+ export const DEFAULT_MODEL_CAPABILITIES = {
17
+ codeExecution: 'good',
18
+ contextWindow: 128000,
19
+ outputTokens: 4096,
20
+ supportsStreaming: true,
21
+ supportsToolUse: true,
22
+ };
23
+ export const MODEL_CONFIGS = {
24
+ 'gpt-4': {
25
+ maxSubCalls: 100,
26
+ maxIterations: 20,
27
+ warnOnExcessiveCalls: false,
28
+ costMultiplier: 1.0,
29
+ requiresExtraWarnings: false,
30
+ preferredChunkSize: 10,
31
+ },
32
+ 'gpt-4o': {
33
+ maxSubCalls: 100,
34
+ maxIterations: 20,
35
+ warnOnExcessiveCalls: false,
36
+ costMultiplier: 0.5,
37
+ requiresExtraWarnings: false,
38
+ preferredChunkSize: 15,
39
+ },
40
+ 'gpt-4o-mini': {
41
+ maxSubCalls: 80,
42
+ maxIterations: 15,
43
+ warnOnExcessiveCalls: false,
44
+ costMultiplier: 0.1,
45
+ requiresExtraWarnings: false,
46
+ preferredChunkSize: 12,
47
+ },
48
+ 'claude-3-opus': {
49
+ maxSubCalls: 100,
50
+ maxIterations: 20,
51
+ warnOnExcessiveCalls: false,
52
+ costMultiplier: 1.5,
53
+ requiresExtraWarnings: false,
54
+ preferredChunkSize: 10,
55
+ },
56
+ 'claude-3-sonnet': {
57
+ maxSubCalls: 80,
58
+ maxIterations: 15,
59
+ warnOnExcessiveCalls: false,
60
+ costMultiplier: 0.3,
61
+ requiresExtraWarnings: false,
62
+ preferredChunkSize: 12,
63
+ },
64
+ 'claude-3-haiku': {
65
+ maxSubCalls: 60,
66
+ maxIterations: 12,
67
+ warnOnExcessiveCalls: true,
68
+ costMultiplier: 0.05,
69
+ requiresExtraWarnings: false,
70
+ preferredChunkSize: 8,
71
+ },
72
+ 'qwen': {
73
+ maxSubCalls: 50,
74
+ maxIterations: 10,
75
+ warnOnExcessiveCalls: true,
76
+ costMultiplier: 0.2,
77
+ requiresExtraWarnings: true,
78
+ preferredChunkSize: 8,
79
+ },
80
+ 'llama': {
81
+ maxSubCalls: 40,
82
+ maxIterations: 8,
83
+ warnOnExcessiveCalls: true,
84
+ costMultiplier: 0.1,
85
+ requiresExtraWarnings: true,
86
+ preferredChunkSize: 6,
87
+ },
88
+ 'local': {
89
+ maxSubCalls: 20,
90
+ maxIterations: 5,
91
+ warnOnExcessiveCalls: true,
92
+ costMultiplier: 0.01,
93
+ requiresExtraWarnings: true,
94
+ preferredChunkSize: 5,
95
+ },
96
+ 'default': {
97
+ maxSubCalls: 50,
98
+ maxIterations: 10,
99
+ warnOnExcessiveCalls: true,
100
+ costMultiplier: 0.5,
101
+ requiresExtraWarnings: false,
102
+ preferredChunkSize: 10,
103
+ },
104
+ };
105
+ export const MODEL_CAPABILITIES = {
106
+ 'gpt-4': {
107
+ codeExecution: 'excellent',
108
+ contextWindow: 128000,
109
+ outputTokens: 4096,
110
+ supportsStreaming: true,
111
+ supportsToolUse: true,
112
+ },
113
+ 'gpt-4o': {
114
+ codeExecution: 'excellent',
115
+ contextWindow: 128000,
116
+ outputTokens: 4096,
117
+ supportsStreaming: true,
118
+ supportsToolUse: true,
119
+ },
120
+ 'gpt-4o-mini': {
121
+ codeExecution: 'good',
122
+ contextWindow: 128000,
123
+ outputTokens: 4096,
124
+ supportsStreaming: true,
125
+ supportsToolUse: true,
126
+ },
127
+ 'claude-3-opus': {
128
+ codeExecution: 'excellent',
129
+ contextWindow: 200000,
130
+ outputTokens: 4096,
131
+ supportsStreaming: true,
132
+ supportsToolUse: true,
133
+ },
134
+ 'claude-3-sonnet': {
135
+ codeExecution: 'excellent',
136
+ contextWindow: 200000,
137
+ outputTokens: 4096,
138
+ supportsStreaming: true,
139
+ supportsToolUse: true,
140
+ },
141
+ 'claude-3-haiku': {
142
+ codeExecution: 'good',
143
+ contextWindow: 200000,
144
+ outputTokens: 4096,
145
+ supportsStreaming: true,
146
+ supportsToolUse: true,
147
+ },
148
+ 'qwen': {
149
+ codeExecution: 'good',
150
+ contextWindow: 32000,
151
+ outputTokens: 2048,
152
+ supportsStreaming: true,
153
+ supportsToolUse: false,
154
+ },
155
+ 'llama': {
156
+ codeExecution: 'limited',
157
+ contextWindow: 8192,
158
+ outputTokens: 2048,
159
+ supportsStreaming: true,
160
+ supportsToolUse: false,
161
+ },
162
+ 'local': {
163
+ codeExecution: 'limited',
164
+ contextWindow: 4096,
165
+ outputTokens: 1024,
166
+ supportsStreaming: false,
167
+ supportsToolUse: false,
168
+ },
169
+ };
170
+ /**
171
+ * Get model configuration, with fallback to default
172
+ */
173
+ export function getModelConfig(modelName) {
174
+ // Normalize model name
175
+ const normalized = modelName.toLowerCase();
176
+ // Find matching config
177
+ for (const [key, config] of Object.entries(MODEL_CONFIGS)) {
178
+ if (normalized.includes(key)) {
179
+ return config;
180
+ }
181
+ }
182
+ return MODEL_CONFIGS['default'];
183
+ }
184
+ /**
185
+ * Get model capabilities, with fallback to default
186
+ */
187
+ export function getModelCapabilities(modelName) {
188
+ const normalized = modelName.toLowerCase();
189
+ for (const [key, capabilities] of Object.entries(MODEL_CAPABILITIES)) {
190
+ if (normalized.includes(key)) {
191
+ return capabilities;
192
+ }
193
+ }
194
+ return DEFAULT_MODEL_CAPABILITIES;
195
+ }
196
+ /**
197
+ * Choose retrieval strategy based on model capabilities and query complexity
198
+ */
199
+ export function chooseRetrievalStrategy(capabilities, queryComplexity) {
200
+ // Models without coding ability can't use recursive REPL approach
201
+ if (capabilities.codeExecution === 'none' || capabilities.codeExecution === 'limited') {
202
+ return queryComplexity === 'simple' ? 'direct' : 'iterative';
203
+ }
204
+ // Good code execution - use iterative for moderate, recursive for complex
205
+ if (capabilities.codeExecution === 'good') {
206
+ if (queryComplexity === 'simple')
207
+ return 'direct';
208
+ if (queryComplexity === 'moderate')
209
+ return 'iterative';
210
+ return 'recursive';
211
+ }
212
+ // Excellent code execution - use recursive for anything complex
213
+ if (queryComplexity === 'simple')
214
+ return 'direct';
215
+ return 'recursive';
216
+ }
217
+ /**
218
+ * Token Budget Manager
219
+ * Reserves output tokens for final answers to prevent running out
220
+ */
221
+ export class TokenBudgetManager {
222
+ budget;
223
+ constructor(modelName, reserveRatio = 0.25) {
224
+ const capabilities = getModelCapabilities(modelName);
225
+ const reserved = Math.floor(capabilities.outputTokens * reserveRatio);
226
+ this.budget = {
227
+ totalOutputTokens: capabilities.outputTokens,
228
+ reservedForAnswer: reserved,
229
+ maxThinkingTokens: capabilities.outputTokens - reserved,
230
+ currentUsed: 0,
231
+ };
232
+ }
233
+ canUseTokens(count) {
234
+ return this.budget.currentUsed + count <= this.budget.maxThinkingTokens;
235
+ }
236
+ useTokens(count) {
237
+ this.budget.currentUsed += count;
238
+ }
239
+ getRemainingThinkingTokens() {
240
+ return this.budget.maxThinkingTokens - this.budget.currentUsed;
241
+ }
242
+ getReservedForAnswer() {
243
+ return this.budget.reservedForAnswer;
244
+ }
245
+ getBudget() {
246
+ return { ...this.budget };
247
+ }
248
+ reset() {
249
+ this.budget.currentUsed = 0;
250
+ }
251
+ }
252
+ /**
253
+ * Multi-Signal Termination Detector
254
+ * Uses multiple signals to determine when to terminate, not just tags
255
+ */
256
+ export class TerminationDetector {
257
+ signals;
258
+ requiredSignals;
259
+ constructor(requiredSignals = 2) {
260
+ this.requiredSignals = requiredSignals;
261
+ this.signals = {
262
+ explicitTag: false,
263
+ confidenceStatement: false,
264
+ noMoreActions: false,
265
+ answerValidation: false,
266
+ iterationLimitReached: false,
267
+ budgetExhausted: false,
268
+ };
269
+ }
270
+ /**
271
+ * Check if response contains explicit termination tags
272
+ */
273
+ checkExplicitTag(response) {
274
+ const patterns = [
275
+ /\bFINAL\s*\(/i,
276
+ /\bFINAL_VAR\s*\(/i,
277
+ /\bANSWER\s*:/i,
278
+ /"type"\s*:\s*"answer"/i,
279
+ ];
280
+ this.signals.explicitTag = patterns.some(p => p.test(response));
281
+ return this.signals.explicitTag;
282
+ }
283
+ /**
284
+ * Check if response expresses confidence in the answer
285
+ */
286
+ checkConfidenceStatement(response) {
287
+ const patterns = [
288
+ /\b(I am confident|I believe|The answer is|Based on the context)/i,
289
+ /\b(sufficient information|enough information|can conclude)/i,
290
+ /\b(definitively|conclusively|clearly shows)/i,
291
+ ];
292
+ this.signals.confidenceStatement = patterns.some(p => p.test(response));
293
+ return this.signals.confidenceStatement;
294
+ }
295
+ /**
296
+ * Check if response doesn't request more operations
297
+ */
298
+ checkNoMoreActions(response) {
299
+ const actionPatterns = [
300
+ /"type"\s*:\s*"(peek|filter|chunk|subQuery|search)"/i,
301
+ /\b(let me|I will|I need to|next I should)\b/i,
302
+ /\b(examine|investigate|look at|check)\b.*\b(more|further|additional)\b/i,
303
+ ];
304
+ this.signals.noMoreActions = !actionPatterns.some(p => p.test(response));
305
+ return this.signals.noMoreActions;
306
+ }
307
+ /**
308
+ * Validate that answer is actually an answer, not a plan
309
+ */
310
+ validateAnswer(answer, query) {
311
+ // Check answer isn't just a plan/thought
312
+ const planPatterns = [
313
+ /^(I will|Let me|I should|I need to|First,|Next,|Then,)/i,
314
+ /^(To answer this|To find out|To determine)/i,
315
+ /^(Looking at|Examining|Checking)/i,
316
+ ];
317
+ if (planPatterns.some(p => p.test(answer.trim()))) {
318
+ this.signals.answerValidation = false;
319
+ return { valid: false, reason: 'Answer appears to be a plan, not a result' };
320
+ }
321
+ // Check answer isn't too short
322
+ if (answer.trim().length < 20) {
323
+ this.signals.answerValidation = false;
324
+ return { valid: false, reason: 'Answer is too short to be meaningful' };
325
+ }
326
+ // Check answer contains some substance
327
+ const hasSubstance = answer.match(/\b(is|are|was|were|can|could|should|would|found|discovered|determined)\b/i);
328
+ if (!hasSubstance) {
329
+ this.signals.answerValidation = false;
330
+ return { valid: false, reason: 'Answer lacks conclusive statements' };
331
+ }
332
+ this.signals.answerValidation = true;
333
+ return { valid: true, confidence: 0.8 };
334
+ }
335
+ /**
336
+ * Set iteration limit reached
337
+ */
338
+ setIterationLimitReached() {
339
+ this.signals.iterationLimitReached = true;
340
+ }
341
+ /**
342
+ * Set budget exhausted
343
+ */
344
+ setBudgetExhausted() {
345
+ this.signals.budgetExhausted = true;
346
+ }
347
+ /**
348
+ * Check if we should terminate based on multiple signals
349
+ */
350
+ shouldTerminate() {
351
+ // Forced termination conditions
352
+ if (this.signals.iterationLimitReached) {
353
+ return { terminate: true, reason: 'Iteration limit reached', confidence: 1.0 };
354
+ }
355
+ if (this.signals.budgetExhausted) {
356
+ return { terminate: true, reason: 'Budget exhausted', confidence: 1.0 };
357
+ }
358
+ // Count positive signals
359
+ const positiveSignals = [
360
+ this.signals.explicitTag,
361
+ this.signals.confidenceStatement,
362
+ this.signals.noMoreActions,
363
+ this.signals.answerValidation,
364
+ ].filter(Boolean).length;
365
+ if (positiveSignals >= this.requiredSignals) {
366
+ return {
367
+ terminate: true,
368
+ reason: `${positiveSignals} termination signals detected`,
369
+ confidence: positiveSignals / 4,
370
+ };
371
+ }
372
+ return { terminate: false, reason: 'Insufficient termination signals', confidence: positiveSignals / 4 };
373
+ }
374
+ /**
375
+ * Get current signals
376
+ */
377
+ getSignals() {
378
+ return { ...this.signals };
379
+ }
380
+ /**
381
+ * Reset signals
382
+ */
383
+ reset() {
384
+ this.signals = {
385
+ explicitTag: false,
386
+ confidenceStatement: false,
387
+ noMoreActions: false,
388
+ answerValidation: false,
389
+ iterationLimitReached: false,
390
+ budgetExhausted: false,
391
+ };
392
+ }
393
+ }
394
+ /**
395
+ * Circuit Breaker for runaway trajectories
396
+ * Prevents excessive failures from consuming resources
397
+ */
398
+ export class CircuitBreaker {
399
+ failures = 0;
400
+ lastFailure = null;
401
+ state = 'closed';
402
+ threshold;
403
+ resetTimeMs;
404
+ constructor(threshold = 3, resetTimeMs = 60000) {
405
+ this.threshold = threshold;
406
+ this.resetTimeMs = resetTimeMs;
407
+ }
408
+ async execute(operation) {
409
+ // Check if circuit is open
410
+ if (this.state === 'open') {
411
+ if (this.lastFailure && Date.now() - this.lastFailure.getTime() > this.resetTimeMs) {
412
+ this.state = 'half-open';
413
+ }
414
+ else {
415
+ throw new Error('Circuit breaker is open - too many recent failures');
416
+ }
417
+ }
418
+ try {
419
+ const result = await operation();
420
+ this.onSuccess();
421
+ return result;
422
+ }
423
+ catch (error) {
424
+ this.onFailure();
425
+ throw error;
426
+ }
427
+ }
428
+ onSuccess() {
429
+ this.failures = 0;
430
+ this.state = 'closed';
431
+ }
432
+ onFailure() {
433
+ this.failures++;
434
+ this.lastFailure = new Date();
435
+ if (this.failures >= this.threshold) {
436
+ this.state = 'open';
437
+ }
438
+ }
439
+ getState() {
440
+ return this.state;
441
+ }
442
+ getFailures() {
443
+ return this.failures;
444
+ }
445
+ reset() {
446
+ this.failures = 0;
447
+ this.lastFailure = null;
448
+ this.state = 'closed';
449
+ }
450
+ }
451
+ /**
452
+ * Sub-Call Throttler with Caching
453
+ * Prevents excessive sub-calls and caches results
454
+ */
455
+ export class SubCallThrottler {
456
+ callCounts = new Map();
457
+ cache = new Map();
458
+ maxCallsPerKey;
459
+ cacheTtlMs;
460
+ constructor(maxCallsPerKey = 3, cacheTtlMs = 300000) {
461
+ this.maxCallsPerKey = maxCallsPerKey;
462
+ this.cacheTtlMs = cacheTtlMs;
463
+ }
464
+ async throttledCall(key, llm, prompt, options) {
465
+ // Check cache first
466
+ const cacheKey = this.hashPrompt(prompt);
467
+ const cached = this.cache.get(cacheKey);
468
+ if (cached && Date.now() - cached.timestamp < this.cacheTtlMs) {
469
+ return cached.result;
470
+ }
471
+ // Check call count for this key
472
+ const count = this.callCounts.get(key) ?? 0;
473
+ if (count >= this.maxCallsPerKey) {
474
+ throw new Error(`Maximum sub-calls (${this.maxCallsPerKey}) exceeded for key: ${key}`);
475
+ }
476
+ // Make the call
477
+ this.callCounts.set(key, count + 1);
478
+ const result = await llm.invoke(prompt, options);
479
+ // Cache the result
480
+ this.cache.set(cacheKey, { result, timestamp: Date.now() });
481
+ return result;
482
+ }
483
+ hashPrompt(prompt) {
484
+ return createHash('md5').update(prompt).digest('hex').substring(0, 16);
485
+ }
486
+ getCallCount(key) {
487
+ return this.callCounts.get(key) ?? 0;
488
+ }
489
+ getCacheSize() {
490
+ return this.cache.size;
491
+ }
492
+ clearCache() {
493
+ this.cache.clear();
494
+ }
495
+ reset() {
496
+ this.callCounts.clear();
497
+ this.cache.clear();
498
+ }
499
+ }
500
+ /**
501
+ * Model Prior-Based Pre-Filter
502
+ * Filters context using patterns before semantic analysis
503
+ */
504
+ export class PriorBasedFilter {
505
+ /**
506
+ * Pre-filter chunks using keyword patterns
507
+ */
508
+ filterByKeywords(chunks, keywords, options) {
509
+ if (keywords.length === 0)
510
+ return chunks;
511
+ const flags = options?.caseSensitive ? '' : 'i';
512
+ const patterns = keywords.map(k => new RegExp(k, flags));
513
+ return chunks.filter(chunk => {
514
+ if (options?.matchAll) {
515
+ return patterns.every(p => p.test(chunk.content));
516
+ }
517
+ return patterns.some(p => p.test(chunk.content));
518
+ });
519
+ }
520
+ /**
521
+ * Extract keywords from query using heuristics
522
+ */
523
+ extractQueryKeywords(query) {
524
+ const keywords = [];
525
+ const queryLower = query.toLowerCase();
526
+ // Common technical terms
527
+ const technicalPatterns = [
528
+ /\b(error|exception|bug|issue|problem)\b/gi,
529
+ /\b(function|method|class|component|module)\b/gi,
530
+ /\b(api|endpoint|route|handler)\b/gi,
531
+ /\b(database|query|sql|mongodb)\b/gi,
532
+ /\b(auth|authentication|authorization|login)\b/gi,
533
+ /\b(test|testing|unit|integration)\b/gi,
534
+ ];
535
+ for (const pattern of technicalPatterns) {
536
+ const matches = query.match(pattern);
537
+ if (matches) {
538
+ keywords.push(...matches.map(m => m.toLowerCase()));
539
+ }
540
+ }
541
+ // Extract quoted strings
542
+ const quotedMatches = query.match(/"([^"]+)"/g);
543
+ if (quotedMatches) {
544
+ keywords.push(...quotedMatches.map(m => m.replace(/"/g, '')));
545
+ }
546
+ // Extract capitalized words (likely class/function names)
547
+ const capitalizedMatches = query.match(/\b[A-Z][a-z]+(?:[A-Z][a-z]+)+\b/g);
548
+ if (capitalizedMatches) {
549
+ keywords.push(...capitalizedMatches);
550
+ }
551
+ return [...new Set(keywords)];
552
+ }
553
+ /**
554
+ * Smart pre-filter that combines multiple strategies
555
+ */
556
+ smartPreFilter(chunks, query, maxChunks = 50) {
557
+ // Extract keywords from query
558
+ const keywords = this.extractQueryKeywords(query);
559
+ if (keywords.length === 0) {
560
+ // No keywords - return top chunks by importance
561
+ return [...chunks]
562
+ .sort((a, b) => b.importance - a.importance)
563
+ .slice(0, maxChunks);
564
+ }
565
+ // Filter by keywords
566
+ let filtered = this.filterByKeywords(chunks, keywords);
567
+ // If too few results, relax to any match
568
+ if (filtered.length < 5 && keywords.length > 1) {
569
+ filtered = this.filterByKeywords(chunks, keywords, { matchAll: false });
570
+ }
571
+ // If still no results, fall back to importance-based selection
572
+ if (filtered.length === 0) {
573
+ return [...chunks]
574
+ .sort((a, b) => b.importance - a.importance)
575
+ .slice(0, maxChunks);
576
+ }
577
+ // Sort by importance and limit
578
+ return [...filtered]
579
+ .sort((a, b) => b.importance - a.importance)
580
+ .slice(0, maxChunks);
581
+ }
582
+ }
583
+ /**
584
+ * RLM Safeguards Coordinator
585
+ * Combines all safeguards into a single interface
586
+ */
587
+ export class RLMSafeguards {
588
+ tokenBudget;
589
+ terminationDetector;
590
+ circuitBreaker;
591
+ throttler;
592
+ priorFilter;
593
+ modelConfig;
594
+ modelCapabilities;
595
+ constructor(modelName, options) {
596
+ this.modelConfig = getModelConfig(modelName);
597
+ this.modelCapabilities = getModelCapabilities(modelName);
598
+ this.tokenBudget = new TokenBudgetManager(modelName);
599
+ this.terminationDetector = new TerminationDetector(options?.requiredTerminationSignals ?? 2);
600
+ this.circuitBreaker = new CircuitBreaker(options?.circuitBreakerThreshold ?? 3, options?.circuitBreakerResetMs ?? 60000);
601
+ this.throttler = new SubCallThrottler(options?.maxSubCallsPerKey ?? this.modelConfig.maxSubCalls, options?.cacheTtlMs ?? 300000);
602
+ this.priorFilter = new PriorBasedFilter();
603
+ }
604
+ /**
605
+ * Choose appropriate retrieval strategy
606
+ */
607
+ chooseStrategy(queryComplexity) {
608
+ return chooseRetrievalStrategy(this.modelCapabilities, queryComplexity);
609
+ }
610
+ /**
611
+ * Pre-filter chunks before processing
612
+ */
613
+ preFilter(chunks, query, maxChunks) {
614
+ return this.priorFilter.smartPreFilter(chunks, query, maxChunks ?? this.modelConfig.preferredChunkSize * 5);
615
+ }
616
+ /**
617
+ * Execute a sub-call with all safeguards
618
+ */
619
+ async safeSubCall(key, llm, prompt, options) {
620
+ // Check circuit breaker
621
+ return this.circuitBreaker.execute(async () => {
622
+ // Check token budget
623
+ const estimatedTokens = Math.ceil(prompt.length / 4);
624
+ if (!this.tokenBudget.canUseTokens(estimatedTokens)) {
625
+ throw new Error('Token budget exceeded');
626
+ }
627
+ // Throttled and cached call
628
+ const result = await this.throttler.throttledCall(key, llm, prompt, options);
629
+ // Record token usage
630
+ this.tokenBudget.useTokens(estimatedTokens + Math.ceil(result.length / 4));
631
+ return result;
632
+ });
633
+ }
634
+ /**
635
+ * Check response and update termination signals
636
+ */
637
+ checkResponse(response, query) {
638
+ this.terminationDetector.checkExplicitTag(response);
639
+ this.terminationDetector.checkConfidenceStatement(response);
640
+ this.terminationDetector.checkNoMoreActions(response);
641
+ // If it looks like an answer, validate it
642
+ if (this.terminationDetector.getSignals().noMoreActions) {
643
+ this.terminationDetector.validateAnswer(response, query);
644
+ }
645
+ return this.terminationDetector.shouldTerminate();
646
+ }
647
+ /**
648
+ * Mark iteration limit reached
649
+ */
650
+ markIterationLimitReached() {
651
+ this.terminationDetector.setIterationLimitReached();
652
+ }
653
+ /**
654
+ * Mark budget exhausted
655
+ */
656
+ markBudgetExhausted() {
657
+ this.terminationDetector.setBudgetExhausted();
658
+ }
659
+ /**
660
+ * Get model configuration
661
+ */
662
+ getModelConfig() {
663
+ return { ...this.modelConfig };
664
+ }
665
+ /**
666
+ * Get model capabilities
667
+ */
668
+ getModelCapabilities() {
669
+ return { ...this.modelCapabilities };
670
+ }
671
+ /**
672
+ * Get current state
673
+ */
674
+ getState() {
675
+ return {
676
+ tokenBudget: this.tokenBudget.getBudget(),
677
+ terminationSignals: this.terminationDetector.getSignals(),
678
+ circuitBreakerState: this.circuitBreaker.getState(),
679
+ cacheSize: this.throttler.getCacheSize(),
680
+ };
681
+ }
682
+ /**
683
+ * Reset all state
684
+ */
685
+ reset() {
686
+ this.tokenBudget.reset();
687
+ this.terminationDetector.reset();
688
+ this.circuitBreaker.reset();
689
+ this.throttler.reset();
690
+ }
691
+ }
692
+ let instance = null;
693
+ export function createRLMSafeguards(modelName, options) {
694
+ return new RLMSafeguards(modelName, options);
695
+ }
696
+ export function getRLMSafeguards(modelName = 'default', options) {
697
+ if (!instance) {
698
+ instance = new RLMSafeguards(modelName, options);
699
+ }
700
+ return instance;
701
+ }
702
+ export function resetRLMSafeguards() {
703
+ instance = null;
704
+ }
705
+ //# sourceMappingURL=rlmSafeguards.js.map