mark-improving-agent 2.2.4 → 2.2.5
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.
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.2.
|
|
1
|
+
2.2.5
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-Agent Peer Review System
|
|
3
|
+
*
|
|
4
|
+
* Enables cross-model peer review of agent decisions and outputs.
|
|
5
|
+
* Based on agentic-fleet-hub's peer review architecture.
|
|
6
|
+
*
|
|
7
|
+
* Key features:
|
|
8
|
+
* - Multiple agents review each other's work
|
|
9
|
+
* - Consensus-based approval
|
|
10
|
+
* - Dissent tracking for quality improvement
|
|
11
|
+
* - Reputation-weighted voting
|
|
12
|
+
*
|
|
13
|
+
* @module core/collaboration
|
|
14
|
+
* @fileoverview Cross-model peer review for agent outputs
|
|
15
|
+
*/
|
|
16
|
+
import { randomUUID } from 'crypto';
|
|
17
|
+
import { createLogger } from '../../utils/logger.js';
|
|
18
|
+
const logger = createLogger('PeerReview');
|
|
19
|
+
/**
|
|
20
|
+
* Default reviewers
|
|
21
|
+
*/
|
|
22
|
+
const DEFAULT_REVIEWERS = [
|
|
23
|
+
{
|
|
24
|
+
id: 'reviewer-logic',
|
|
25
|
+
name: 'Logic Reviewer',
|
|
26
|
+
model: 'claude-opus',
|
|
27
|
+
specialties: ['reasoning', 'logic', 'consistency'],
|
|
28
|
+
reputation: 0.95,
|
|
29
|
+
reviewsCompleted: 0,
|
|
30
|
+
approvalRate: 0,
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
id: 'reviewer-safety',
|
|
34
|
+
name: 'Safety Reviewer',
|
|
35
|
+
model: 'claude-opus',
|
|
36
|
+
specialties: ['safety', 'ethics', 'harm prevention'],
|
|
37
|
+
reputation: 0.98,
|
|
38
|
+
reviewsCompleted: 0,
|
|
39
|
+
approvalRate: 0,
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
id: 'reviewer-quality',
|
|
43
|
+
name: 'Quality Reviewer',
|
|
44
|
+
model: 'claude-sonnet',
|
|
45
|
+
specialties: ['code quality', 'documentation', 'best practices'],
|
|
46
|
+
reputation: 0.92,
|
|
47
|
+
reviewsCompleted: 0,
|
|
48
|
+
approvalRate: 0,
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
id: 'reviewer-creativity',
|
|
52
|
+
name: 'Creativity Reviewer',
|
|
53
|
+
model: 'claude-haiku',
|
|
54
|
+
specialties: ['innovation', 'alternatives', 'creative solutions'],
|
|
55
|
+
reputation: 0.88,
|
|
56
|
+
reviewsCompleted: 0,
|
|
57
|
+
approvalRate: 0,
|
|
58
|
+
},
|
|
59
|
+
];
|
|
60
|
+
export function createPeerReviewSystem(options) {
|
|
61
|
+
const consensusThreshold = options?.consensusThreshold ?? 0.7;
|
|
62
|
+
const requiredReviewers = options?.requiredReviewers ?? 3;
|
|
63
|
+
const enableArbitration = options?.enableArbitration ?? true;
|
|
64
|
+
// State
|
|
65
|
+
const items = new Map();
|
|
66
|
+
const reviewers = new Map(DEFAULT_REVIEWERS.map(r => [r.id, r]));
|
|
67
|
+
const sessions = new Map();
|
|
68
|
+
const reviewHistory = [];
|
|
69
|
+
// Stats
|
|
70
|
+
let totalReviewTime = 0;
|
|
71
|
+
let consensusCount = 0;
|
|
72
|
+
let revisionCount = 0;
|
|
73
|
+
function submitItem(submitterId, content, type, context, options) {
|
|
74
|
+
const item = {
|
|
75
|
+
id: randomUUID(),
|
|
76
|
+
submitterId,
|
|
77
|
+
content,
|
|
78
|
+
type,
|
|
79
|
+
context: context ?? {},
|
|
80
|
+
status: 'pending',
|
|
81
|
+
votes: [],
|
|
82
|
+
createdAt: Date.now(),
|
|
83
|
+
consensusThreshold: options?.consensusThreshold ?? consensusThreshold,
|
|
84
|
+
requiredReviewers: options?.requiredReviewers ?? requiredReviewers,
|
|
85
|
+
};
|
|
86
|
+
items.set(item.id, item);
|
|
87
|
+
logger.info(`Review item submitted: ${item.id} by ${submitterId}`);
|
|
88
|
+
// Auto-assign reviewers
|
|
89
|
+
assignReviewers(item.id);
|
|
90
|
+
return item;
|
|
91
|
+
}
|
|
92
|
+
function registerReviewer(reviewer) {
|
|
93
|
+
reviewers.set(reviewer.id, {
|
|
94
|
+
...reviewer,
|
|
95
|
+
reviewsCompleted: 0,
|
|
96
|
+
approvalRate: 0,
|
|
97
|
+
});
|
|
98
|
+
logger.info(`Reviewer registered: ${reviewer.name}`);
|
|
99
|
+
}
|
|
100
|
+
function getReviewersForType(contentType) {
|
|
101
|
+
const relevantReviewers = Array.from(reviewers.values()).filter(r => r.specialties.some(s => contentType === 'code' ? s.includes('code') :
|
|
102
|
+
contentType === 'decision' ? s.includes('reasoning') || s.includes('logic') :
|
|
103
|
+
contentType === 'response' ? s.includes('ethics') || s.includes('safety') :
|
|
104
|
+
true));
|
|
105
|
+
// Return up to 4 reviewers
|
|
106
|
+
return relevantReviewers.slice(0, 4);
|
|
107
|
+
}
|
|
108
|
+
function assignReviewers(itemId) {
|
|
109
|
+
const item = items.get(itemId);
|
|
110
|
+
if (!item || item.status !== 'pending')
|
|
111
|
+
return null;
|
|
112
|
+
const availableReviewers = getReviewersForType(item.type)
|
|
113
|
+
.filter(r => r.id !== item.submitterId)
|
|
114
|
+
.slice(0, item.requiredReviewers);
|
|
115
|
+
if (availableReviewers.length < 2) {
|
|
116
|
+
// Use default reviewers if no specialty match
|
|
117
|
+
const defaults = Array.from(reviewers.values())
|
|
118
|
+
.filter(r => r.id !== item.submitterId)
|
|
119
|
+
.slice(0, item.requiredReviewers);
|
|
120
|
+
availableReviewers.push(...defaults);
|
|
121
|
+
}
|
|
122
|
+
const session = {
|
|
123
|
+
itemId,
|
|
124
|
+
phase: 'review',
|
|
125
|
+
assignedReviewers: availableReviewers.map(r => r.id),
|
|
126
|
+
completedReviews: 0,
|
|
127
|
+
consensusReached: false,
|
|
128
|
+
};
|
|
129
|
+
sessions.set(itemId, session);
|
|
130
|
+
item.status = 'in_review';
|
|
131
|
+
logger.info(`Assigned ${availableReviewers.length} reviewers to ${itemId}`);
|
|
132
|
+
return item;
|
|
133
|
+
}
|
|
134
|
+
function submitVote(itemId, reviewerId, decision, feedback, confidence) {
|
|
135
|
+
const item = items.get(itemId);
|
|
136
|
+
const session = sessions.get(itemId);
|
|
137
|
+
const reviewer = reviewers.get(reviewerId);
|
|
138
|
+
if (!item || !session || !reviewer) {
|
|
139
|
+
logger.warn(`Invalid vote submission: item=${itemId}, reviewer=${reviewerId}`);
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
// Check if reviewer is assigned
|
|
143
|
+
if (!session.assignedReviewers.includes(reviewerId)) {
|
|
144
|
+
logger.warn(`Reviewer ${reviewerId} not assigned to ${itemId}`);
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
// Check if reviewer already voted
|
|
148
|
+
if (item.votes.some(v => v.reviewerId === reviewerId)) {
|
|
149
|
+
logger.warn(`Reviewer ${reviewerId} already voted on ${itemId}`);
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
const vote = {
|
|
153
|
+
reviewerId,
|
|
154
|
+
role: session.completedReviews === 0 ? 'primary' : 'secondary',
|
|
155
|
+
decision,
|
|
156
|
+
confidence: Math.max(0, Math.min(1, confidence)),
|
|
157
|
+
feedback,
|
|
158
|
+
timestamp: Date.now(),
|
|
159
|
+
};
|
|
160
|
+
item.votes.push(vote);
|
|
161
|
+
session.completedReviews++;
|
|
162
|
+
reviewHistory.push(vote);
|
|
163
|
+
// Update reviewer stats
|
|
164
|
+
reviewer.reviewsCompleted++;
|
|
165
|
+
logger.info(`Vote submitted: ${reviewerId} -> ${decision} on ${itemId}`);
|
|
166
|
+
// Check consensus
|
|
167
|
+
const consensus = checkConsensus(itemId);
|
|
168
|
+
if (consensus.reached && consensus.decision) {
|
|
169
|
+
item.status = consensus.decision;
|
|
170
|
+
item.completedAt = Date.now();
|
|
171
|
+
session.consensusReached = true;
|
|
172
|
+
session.decision = consensus.decision === 'approved' ? 'approved' :
|
|
173
|
+
consensus.decision === 'rejected' ? 'rejected' : 'revision_requested';
|
|
174
|
+
if (consensus.decision === 'revision_requested') {
|
|
175
|
+
revisionCount++;
|
|
176
|
+
}
|
|
177
|
+
consensusCount++;
|
|
178
|
+
}
|
|
179
|
+
return vote;
|
|
180
|
+
}
|
|
181
|
+
function checkConsensus(itemId) {
|
|
182
|
+
const item = items.get(itemId);
|
|
183
|
+
if (!item)
|
|
184
|
+
return { reached: false };
|
|
185
|
+
const requiredApprovals = Math.ceil(item.requiredReviewers * item.consensusThreshold);
|
|
186
|
+
const votes = item.votes;
|
|
187
|
+
// Need minimum votes
|
|
188
|
+
if (votes.length < item.requiredReviewers) {
|
|
189
|
+
return { reached: false };
|
|
190
|
+
}
|
|
191
|
+
// Count decisions
|
|
192
|
+
const approvals = votes.filter(v => v.decision === 'approve').length;
|
|
193
|
+
const rejections = votes.filter(v => v.decision === 'reject').length;
|
|
194
|
+
const revisions = votes.filter(v => v.decision === 'revision').length;
|
|
195
|
+
// Reputation-weighted voting
|
|
196
|
+
let weightedApprovals = 0;
|
|
197
|
+
let totalWeight = 0;
|
|
198
|
+
for (const vote of votes) {
|
|
199
|
+
const reviewer = reviewers.get(vote.reviewerId);
|
|
200
|
+
if (reviewer) {
|
|
201
|
+
const weight = reviewer.reputation * vote.confidence;
|
|
202
|
+
totalWeight += weight;
|
|
203
|
+
if (vote.decision === 'approve') {
|
|
204
|
+
weightedApprovals += weight;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
const weightedApprovalRate = totalWeight > 0 ? weightedApprovals / totalWeight : 0;
|
|
209
|
+
if (weightedApprovalRate >= item.consensusThreshold) {
|
|
210
|
+
return { reached: true, decision: 'approved' };
|
|
211
|
+
}
|
|
212
|
+
if (rejections > item.requiredReviewers / 2) {
|
|
213
|
+
// Check for revision option before outright rejection
|
|
214
|
+
if (enableArbitration && revisions > 0) {
|
|
215
|
+
return { reached: true, decision: 'revision_requested' };
|
|
216
|
+
}
|
|
217
|
+
return { reached: true, decision: 'rejected' };
|
|
218
|
+
}
|
|
219
|
+
// No consensus yet
|
|
220
|
+
return { reached: false };
|
|
221
|
+
}
|
|
222
|
+
function getItemStatus(itemId) {
|
|
223
|
+
return items.get(itemId);
|
|
224
|
+
}
|
|
225
|
+
function getPendingReviews(reviewerId) {
|
|
226
|
+
return Array.from(items.values()).filter(item => {
|
|
227
|
+
const session = sessions.get(item.id);
|
|
228
|
+
return (item.status === 'in_review' &&
|
|
229
|
+
session?.assignedReviewers.includes(reviewerId) &&
|
|
230
|
+
!item.votes.some(v => v.reviewerId === reviewerId));
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
function getStats() {
|
|
234
|
+
const allItems = Array.from(items.values());
|
|
235
|
+
const completedItems = allItems.filter(i => i.completedAt);
|
|
236
|
+
const avgReviewTime = completedItems.length > 0
|
|
237
|
+
? totalReviewTime / completedItems.length
|
|
238
|
+
: 0;
|
|
239
|
+
return {
|
|
240
|
+
itemsReviewed: completedItems.length,
|
|
241
|
+
approvalRate: completedItems.length > 0
|
|
242
|
+
? completedItems.filter(i => i.status === 'approved').length / completedItems.length
|
|
243
|
+
: 0,
|
|
244
|
+
avgReviewTime,
|
|
245
|
+
consensusRate: completedItems.length > 0
|
|
246
|
+
? consensusCount / completedItems.length
|
|
247
|
+
: 0,
|
|
248
|
+
revisionRate: completedItems.length > 0
|
|
249
|
+
? revisionCount / completedItems.length
|
|
250
|
+
: 0,
|
|
251
|
+
dissentCount: reviewHistory.filter(v => v.decision !== 'approve').length,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
return {
|
|
255
|
+
submitItem,
|
|
256
|
+
registerReviewer,
|
|
257
|
+
getReviewersForType,
|
|
258
|
+
assignReviewers,
|
|
259
|
+
submitVote,
|
|
260
|
+
checkConsensus,
|
|
261
|
+
getItemStatus,
|
|
262
|
+
getPendingReviews,
|
|
263
|
+
getStats,
|
|
264
|
+
};
|
|
265
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hybrid Memory Search
|
|
3
|
+
*
|
|
4
|
+
* Combines vector similarity search with BM25 keyword matching and knowledge graph traversal
|
|
5
|
+
* Based on Dakera AI's hybrid search architecture (87.8% LoCoMo accuracy)
|
|
6
|
+
*
|
|
7
|
+
* @module core/memory
|
|
8
|
+
* @fileoverview Hybrid search combining vector + BM25 + knowledge graph for superior recall
|
|
9
|
+
*/
|
|
10
|
+
import { createLogger } from '../../utils/logger.js';
|
|
11
|
+
const logger = createLogger('HybridSearch');
|
|
12
|
+
/**
|
|
13
|
+
* Default hybrid search configuration
|
|
14
|
+
*/
|
|
15
|
+
export const DEFAULT_HYBRID_CONFIG = {
|
|
16
|
+
vectorWeight: 0.5,
|
|
17
|
+
bm25Weight: 0.3,
|
|
18
|
+
kgWeight: 0.2,
|
|
19
|
+
bm25: { k1: 1.5, b: 0.75 },
|
|
20
|
+
minScore: 0.1,
|
|
21
|
+
maxResults: 20,
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Tokenizer for BM25
|
|
25
|
+
*/
|
|
26
|
+
function tokenize(text) {
|
|
27
|
+
return text
|
|
28
|
+
.toLowerCase()
|
|
29
|
+
.replace(/[^\w\s]/g, ' ')
|
|
30
|
+
.split(/\s+/)
|
|
31
|
+
.filter(token => token.length > 2);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Create a BM25 index from memory entries
|
|
35
|
+
*/
|
|
36
|
+
export function createBM25Index(entries) {
|
|
37
|
+
const index = {
|
|
38
|
+
docLengths: new Map(),
|
|
39
|
+
termDocFreq: new Map(),
|
|
40
|
+
avgDocLength: 0,
|
|
41
|
+
totalDocs: entries.length,
|
|
42
|
+
invertedIndex: new Map(),
|
|
43
|
+
};
|
|
44
|
+
let totalLength = 0;
|
|
45
|
+
for (const entry of entries) {
|
|
46
|
+
const tokens = tokenize(entry.content);
|
|
47
|
+
const docLength = tokens.length;
|
|
48
|
+
index.docLengths.set(entry.id, docLength);
|
|
49
|
+
totalLength += docLength;
|
|
50
|
+
// Count term frequencies
|
|
51
|
+
const termFreq = new Map();
|
|
52
|
+
for (const token of tokens) {
|
|
53
|
+
termFreq.set(token, (termFreq.get(token) ?? 0) + 1);
|
|
54
|
+
if (!index.invertedIndex.has(token)) {
|
|
55
|
+
index.invertedIndex.set(token, new Map());
|
|
56
|
+
}
|
|
57
|
+
const posting = index.invertedIndex.get(token);
|
|
58
|
+
posting.set(entry.id, termFreq.get(token));
|
|
59
|
+
}
|
|
60
|
+
// Update document frequency
|
|
61
|
+
for (const token of new Set(tokens)) {
|
|
62
|
+
index.termDocFreq.set(token, (index.termDocFreq.get(token) ?? 0) + 1);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
index.avgDocLength = totalLength / Math.max(entries.length, 1);
|
|
66
|
+
return index;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Calculate BM25 score for a single document
|
|
70
|
+
*/
|
|
71
|
+
export function bm25Score(index, docId, queryTokens, config) {
|
|
72
|
+
const docLength = index.docLengths.get(docId) ?? 0;
|
|
73
|
+
let score = 0;
|
|
74
|
+
for (const token of queryTokens) {
|
|
75
|
+
const tf = index.invertedIndex.get(token)?.get(docId) ?? 0;
|
|
76
|
+
if (tf === 0)
|
|
77
|
+
continue;
|
|
78
|
+
const df = index.termDocFreq.get(token) ?? 0;
|
|
79
|
+
if (df === 0)
|
|
80
|
+
continue;
|
|
81
|
+
const idf = Math.log((index.totalDocs - df + 0.5) / (df + 0.5) + 1);
|
|
82
|
+
const tfComponent = (tf * (config.k1 + 1)) / (tf + config.k1 * (1 - config.b + config.b * (docLength / index.avgDocLength)));
|
|
83
|
+
score += idf * tfComponent;
|
|
84
|
+
}
|
|
85
|
+
return score;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Normalize BM25 scores to 0-1 range
|
|
89
|
+
*/
|
|
90
|
+
export function normalizeBM25Scores(scores) {
|
|
91
|
+
const maxScore = Math.max(...Array.from(scores.values()), 1);
|
|
92
|
+
const normalized = new Map();
|
|
93
|
+
for (const [docId, score] of scores) {
|
|
94
|
+
normalized.set(docId, score / maxScore);
|
|
95
|
+
}
|
|
96
|
+
return normalized;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Create a hybrid search engine
|
|
100
|
+
*/
|
|
101
|
+
export function createHybridSearchEngine(initialEntries = [], config = {}) {
|
|
102
|
+
const fullConfig = {
|
|
103
|
+
...DEFAULT_HYBRID_CONFIG,
|
|
104
|
+
...config,
|
|
105
|
+
bm25: { ...DEFAULT_HYBRID_CONFIG.bm25, ...config.bm25 },
|
|
106
|
+
};
|
|
107
|
+
let bm25Index = createBM25Index(initialEntries);
|
|
108
|
+
function rebuildIndex(entries) {
|
|
109
|
+
logger.info(`Rebuilding BM25 index with ${entries.length} documents`);
|
|
110
|
+
bm25Index = createBM25Index(entries);
|
|
111
|
+
}
|
|
112
|
+
function search(query, entries, options) {
|
|
113
|
+
const searchConfig = {
|
|
114
|
+
...fullConfig,
|
|
115
|
+
...options?.config,
|
|
116
|
+
};
|
|
117
|
+
// Tokenize query for BM25
|
|
118
|
+
const queryTokens = tokenize(query);
|
|
119
|
+
// Calculate BM25 scores for all entries
|
|
120
|
+
const bm25Scores = new Map();
|
|
121
|
+
for (const entry of entries) {
|
|
122
|
+
const score = bm25Score(bm25Index, entry.id, queryTokens, searchConfig.bm25);
|
|
123
|
+
if (score > 0) {
|
|
124
|
+
bm25Scores.set(entry.id, score);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
const normalizedBM25 = normalizeBM25Scores(bm25Scores);
|
|
128
|
+
// Calculate KG scores based on connection count
|
|
129
|
+
const kgScores = new Map();
|
|
130
|
+
if (options?.kgConnections) {
|
|
131
|
+
const maxConnections = Math.max(...Array.from(options.kgConnections.values()).map(arr => arr.length), 1);
|
|
132
|
+
for (const entry of entries) {
|
|
133
|
+
const connections = options.kgConnections.get(entry.id)?.length ?? 0;
|
|
134
|
+
kgScores.set(entry.id, connections / maxConnections);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
// Combine scores
|
|
138
|
+
const combinedResults = new Map();
|
|
139
|
+
for (const entry of entries) {
|
|
140
|
+
const vectorScore = options?.vectorScores?.get(entry.id) ?? 0;
|
|
141
|
+
const bm25Score = normalizedBM25.get(entry.id) ?? 0;
|
|
142
|
+
const kgScore = kgScores.get(entry.id) ?? 0;
|
|
143
|
+
// Weighted combination
|
|
144
|
+
const combinedScore = (vectorScore * searchConfig.vectorWeight) +
|
|
145
|
+
(bm25Score * searchConfig.bm25Weight) +
|
|
146
|
+
(kgScore * searchConfig.kgWeight);
|
|
147
|
+
if (combinedScore >= searchConfig.minScore) {
|
|
148
|
+
combinedResults.set(entry.id, {
|
|
149
|
+
entry,
|
|
150
|
+
score: combinedScore,
|
|
151
|
+
combinedScore,
|
|
152
|
+
vectorScore,
|
|
153
|
+
bm25Score,
|
|
154
|
+
kgScore,
|
|
155
|
+
reason: 'semantic',
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
// Sort by combined score
|
|
160
|
+
const results = Array.from(combinedResults.values())
|
|
161
|
+
.sort((a, b) => b.combinedScore - a.combinedScore)
|
|
162
|
+
.slice(0, searchConfig.maxResults);
|
|
163
|
+
logger.debug(`Hybrid search for "${query}": ${results.length} results`);
|
|
164
|
+
return results;
|
|
165
|
+
}
|
|
166
|
+
function getStats() {
|
|
167
|
+
return {
|
|
168
|
+
indexedDocs: bm25Index.totalDocs,
|
|
169
|
+
avgDocLength: Math.round(bm25Index.avgDocLength),
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
return {
|
|
173
|
+
search,
|
|
174
|
+
rebuildIndex,
|
|
175
|
+
getStats,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
@@ -8,3 +8,4 @@ export * from './spaced-repetition.js';
|
|
|
8
8
|
export * from './hopfield-network.js';
|
|
9
9
|
export * from './adaptive-rag.js';
|
|
10
10
|
export { createContextFragmentationEngine } from './context-fragmentation.js';
|
|
11
|
+
export { createHybridSearchEngine, createBM25Index, bm25Score, normalizeBM25Scores, DEFAULT_HYBRID_CONFIG } from './hybrid-search.js';
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = '2.2.
|
|
1
|
+
export const VERSION = '2.2.5';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mark-improving-agent",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.5",
|
|
4
4
|
"description": "Self-evolving AI agent with permanent memory, identity continuity, and self-evolution — for AI agents that need to remember, learn, and evolve across sessions",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|