@unrdf/knowledge-engine 5.0.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.
- package/LICENSE +21 -0
- package/README.md +84 -0
- package/package.json +64 -0
- package/src/browser-shims.mjs +343 -0
- package/src/browser.mjs +910 -0
- package/src/canonicalize.mjs +414 -0
- package/src/condition-cache.mjs +109 -0
- package/src/condition-evaluator.mjs +722 -0
- package/src/dark-matter-core.mjs +742 -0
- package/src/define-hook.mjs +213 -0
- package/src/effect-sandbox-browser.mjs +283 -0
- package/src/effect-sandbox-worker.mjs +170 -0
- package/src/effect-sandbox.mjs +517 -0
- package/src/engines/index.mjs +11 -0
- package/src/engines/rdf-engine.mjs +299 -0
- package/src/file-resolver.mjs +387 -0
- package/src/hook-executor-batching.mjs +277 -0
- package/src/hook-executor.mjs +870 -0
- package/src/hook-management.mjs +150 -0
- package/src/index.mjs +93 -0
- package/src/ken-parliment.mjs +119 -0
- package/src/ken.mjs +149 -0
- package/src/knowledge-engine/builtin-rules.mjs +190 -0
- package/src/knowledge-engine/inference-engine.mjs +418 -0
- package/src/knowledge-engine/knowledge-engine.mjs +317 -0
- package/src/knowledge-engine/pattern-dsl.mjs +142 -0
- package/src/knowledge-engine/pattern-matcher.mjs +215 -0
- package/src/knowledge-engine/rules.mjs +184 -0
- package/src/knowledge-engine.mjs +319 -0
- package/src/knowledge-hook-engine.mjs +360 -0
- package/src/knowledge-hook-manager.mjs +469 -0
- package/src/knowledge-substrate-core.mjs +927 -0
- package/src/lite.mjs +222 -0
- package/src/lockchain-writer-browser.mjs +414 -0
- package/src/lockchain-writer.mjs +602 -0
- package/src/monitoring/andon-signals.mjs +775 -0
- package/src/observability.mjs +531 -0
- package/src/parse.mjs +290 -0
- package/src/performance-optimizer.mjs +678 -0
- package/src/policy-pack.mjs +572 -0
- package/src/query-cache.mjs +116 -0
- package/src/query-optimizer.mjs +1051 -0
- package/src/query.mjs +306 -0
- package/src/reason.mjs +350 -0
- package/src/resolution-layer.mjs +506 -0
- package/src/schemas.mjs +1063 -0
- package/src/security/error-sanitizer.mjs +257 -0
- package/src/security/path-validator.mjs +194 -0
- package/src/security/sandbox-restrictions.mjs +331 -0
- package/src/security-validator.mjs +389 -0
- package/src/store-cache.mjs +137 -0
- package/src/telemetry.mjs +167 -0
- package/src/transaction.mjs +810 -0
- package/src/utils/adaptive-monitor.mjs +746 -0
- package/src/utils/circuit-breaker.mjs +513 -0
- package/src/utils/edge-case-handler.mjs +503 -0
- package/src/utils/memory-manager.mjs +498 -0
- package/src/utils/ring-buffer.mjs +282 -0
- package/src/validate.mjs +319 -0
- package/src/validators/index.mjs +338 -0
|
@@ -0,0 +1,506 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Resolution Layer for Multi-Agent Coordination
|
|
3
|
+
* @module resolution-layer
|
|
4
|
+
*
|
|
5
|
+
* @description
|
|
6
|
+
* Implements multi-agent coordination and Delta resolution for swarm behavior.
|
|
7
|
+
* Handles competing proposals from multiple agents and resolves them into
|
|
8
|
+
* a single, consolidated Delta.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { randomUUID } from 'crypto';
|
|
12
|
+
import { z } from 'zod';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Schema for agent proposal
|
|
16
|
+
*/
|
|
17
|
+
const AgentProposalSchema = z.object({
|
|
18
|
+
id: z.string().uuid(),
|
|
19
|
+
agentId: z.string().min(1),
|
|
20
|
+
delta: z.object({
|
|
21
|
+
additions: z.array(z.any()),
|
|
22
|
+
removals: z.array(z.any()),
|
|
23
|
+
metadata: z.record(z.any()).optional(),
|
|
24
|
+
}),
|
|
25
|
+
confidence: z.number().min(0).max(1),
|
|
26
|
+
priority: z.number().int().min(0).max(100).default(50),
|
|
27
|
+
timestamp: z.number(),
|
|
28
|
+
metadata: z.record(z.any()).optional(),
|
|
29
|
+
dependencies: z.array(z.string()).optional(),
|
|
30
|
+
conflicts: z.array(z.string()).optional(),
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Schema for resolution strategy
|
|
35
|
+
*/
|
|
36
|
+
const ResolutionStrategySchema = z.object({
|
|
37
|
+
type: z.enum(['voting', 'merging', 'crdt', 'consensus', 'priority', 'random']),
|
|
38
|
+
parameters: z.record(z.any()).optional(),
|
|
39
|
+
timeout: z.number().int().positive().max(300000).default(30000),
|
|
40
|
+
quorum: z.number().min(0).max(1).default(0.5),
|
|
41
|
+
maxRetries: z.number().int().nonnegative().max(10).default(3),
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Schema for resolution result
|
|
46
|
+
*/
|
|
47
|
+
const ResolutionResultSchema = z.object({
|
|
48
|
+
id: z.string().uuid(),
|
|
49
|
+
strategy: z.string(),
|
|
50
|
+
proposals: z.array(AgentProposalSchema),
|
|
51
|
+
resolvedDelta: z.object({
|
|
52
|
+
additions: z.array(z.any()),
|
|
53
|
+
removals: z.array(z.any()),
|
|
54
|
+
metadata: z.record(z.any()).optional(),
|
|
55
|
+
}),
|
|
56
|
+
confidence: z.number().min(0).max(1),
|
|
57
|
+
consensus: z.boolean(),
|
|
58
|
+
conflicts: z
|
|
59
|
+
.array(
|
|
60
|
+
z.object({
|
|
61
|
+
type: z.enum(['addition', 'removal', 'metadata']),
|
|
62
|
+
proposals: z.array(z.string()),
|
|
63
|
+
resolution: z.string(),
|
|
64
|
+
})
|
|
65
|
+
)
|
|
66
|
+
.optional(),
|
|
67
|
+
timestamp: z.number(),
|
|
68
|
+
duration: z.number().nonnegative(),
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Resolution Layer for multi-agent coordination
|
|
73
|
+
*/
|
|
74
|
+
export class ResolutionLayer {
|
|
75
|
+
/**
|
|
76
|
+
* Create a new resolution layer
|
|
77
|
+
* @param {Object} [config] - Configuration
|
|
78
|
+
*/
|
|
79
|
+
constructor(config = {}) {
|
|
80
|
+
this.config = {
|
|
81
|
+
defaultStrategy: config.defaultStrategy || 'voting',
|
|
82
|
+
maxProposals: config.maxProposals || 100,
|
|
83
|
+
enableConflictDetection: config.enableConflictDetection !== false,
|
|
84
|
+
enableConsensus: config.enableConsensus !== false,
|
|
85
|
+
timeout: config.timeout || 30000,
|
|
86
|
+
...config,
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
this.proposals = new Map();
|
|
90
|
+
this.resolutionHistory = [];
|
|
91
|
+
this.agents = new Map();
|
|
92
|
+
this.strategies = new Map();
|
|
93
|
+
|
|
94
|
+
// Register default strategies
|
|
95
|
+
this._registerDefaultStrategies();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Register an agent
|
|
100
|
+
* @param {string} agentId - Agent identifier
|
|
101
|
+
* @param {Object} [metadata] - Agent metadata
|
|
102
|
+
*/
|
|
103
|
+
registerAgent(agentId, metadata = {}) {
|
|
104
|
+
this.agents.set(agentId, {
|
|
105
|
+
id: agentId,
|
|
106
|
+
metadata,
|
|
107
|
+
registeredAt: Date.now(),
|
|
108
|
+
proposalCount: 0,
|
|
109
|
+
lastActivity: Date.now(),
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Submit a proposal from an agent
|
|
115
|
+
* @param {string} agentId - Agent identifier
|
|
116
|
+
* @param {Object} delta - Proposed delta
|
|
117
|
+
* @param {Object} [options] - Proposal options
|
|
118
|
+
* @returns {Promise<string>} Proposal ID
|
|
119
|
+
*/
|
|
120
|
+
async submitProposal(agentId, delta, options = {}) {
|
|
121
|
+
// Validate agent
|
|
122
|
+
if (!this.agents.has(agentId)) {
|
|
123
|
+
this.registerAgent(agentId);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const proposal = {
|
|
127
|
+
id: randomUUID(),
|
|
128
|
+
agentId,
|
|
129
|
+
delta,
|
|
130
|
+
confidence: options.confidence || 0.5,
|
|
131
|
+
priority: options.priority || 50,
|
|
132
|
+
timestamp: Date.now(),
|
|
133
|
+
metadata: options.metadata || {},
|
|
134
|
+
dependencies: options.dependencies || [],
|
|
135
|
+
conflicts: options.conflicts || [],
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
// Validate proposal
|
|
139
|
+
const validatedProposal = AgentProposalSchema.parse(proposal);
|
|
140
|
+
|
|
141
|
+
// Store proposal
|
|
142
|
+
this.proposals.set(validatedProposal.id, validatedProposal);
|
|
143
|
+
|
|
144
|
+
// Update agent stats
|
|
145
|
+
const agent = this.agents.get(agentId);
|
|
146
|
+
agent.proposalCount++;
|
|
147
|
+
agent.lastActivity = Date.now();
|
|
148
|
+
|
|
149
|
+
return validatedProposal.id;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Resolve proposals using specified strategy
|
|
154
|
+
* @param {Array<string>} proposalIds - Proposal IDs to resolve
|
|
155
|
+
* @param {Object} [strategy] - Resolution strategy
|
|
156
|
+
* @returns {Promise<Object>} Resolution result
|
|
157
|
+
*/
|
|
158
|
+
async resolveProposals(proposalIds, strategy = {}) {
|
|
159
|
+
const startTime = Date.now();
|
|
160
|
+
const resolutionId = randomUUID();
|
|
161
|
+
|
|
162
|
+
try {
|
|
163
|
+
// Get proposals
|
|
164
|
+
const proposals = proposalIds.map(id => this.proposals.get(id)).filter(p => p !== undefined);
|
|
165
|
+
|
|
166
|
+
if (proposals.length === 0) {
|
|
167
|
+
throw new Error('No valid proposals found');
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Validate strategy
|
|
171
|
+
const validatedStrategy = ResolutionStrategySchema.parse({
|
|
172
|
+
type: strategy.type || this.config.defaultStrategy,
|
|
173
|
+
parameters: strategy.parameters || {},
|
|
174
|
+
timeout: strategy.timeout || this.config.timeout,
|
|
175
|
+
quorum: strategy.quorum || 0.5,
|
|
176
|
+
maxRetries: strategy.maxRetries || 3,
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
// Get resolution function
|
|
180
|
+
const resolutionFn = this.strategies.get(validatedStrategy.type);
|
|
181
|
+
if (!resolutionFn) {
|
|
182
|
+
throw new Error(`Unknown resolution strategy: ${validatedStrategy.type}`);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Resolve proposals
|
|
186
|
+
const resolvedDelta = await resolutionFn(proposals, validatedStrategy);
|
|
187
|
+
|
|
188
|
+
// Calculate consensus
|
|
189
|
+
const consensus = this._calculateConsensus(proposals, resolvedDelta);
|
|
190
|
+
|
|
191
|
+
// Detect conflicts
|
|
192
|
+
const conflicts = this.config.enableConflictDetection
|
|
193
|
+
? this._detectConflicts(proposals, resolvedDelta)
|
|
194
|
+
: [];
|
|
195
|
+
|
|
196
|
+
const result = {
|
|
197
|
+
id: resolutionId,
|
|
198
|
+
strategy: validatedStrategy.type,
|
|
199
|
+
proposals,
|
|
200
|
+
resolvedDelta,
|
|
201
|
+
confidence: this._calculateConfidence(proposals, resolvedDelta),
|
|
202
|
+
consensus,
|
|
203
|
+
conflicts,
|
|
204
|
+
timestamp: Date.now(),
|
|
205
|
+
duration: Date.now() - startTime,
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
// Validate result
|
|
209
|
+
const validatedResult = ResolutionResultSchema.parse(result);
|
|
210
|
+
|
|
211
|
+
// Store in history
|
|
212
|
+
this.resolutionHistory.push(validatedResult);
|
|
213
|
+
|
|
214
|
+
return validatedResult;
|
|
215
|
+
} catch (error) {
|
|
216
|
+
throw new Error(`Resolution failed: ${error.message}`);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Get all proposals for an agent
|
|
222
|
+
* @param {string} agentId - Agent identifier
|
|
223
|
+
* @returns {Array} Agent proposals
|
|
224
|
+
*/
|
|
225
|
+
getAgentProposals(agentId) {
|
|
226
|
+
return Array.from(this.proposals.values()).filter(p => p.agentId === agentId);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Get all active proposals
|
|
231
|
+
* @returns {Array} All proposals
|
|
232
|
+
*/
|
|
233
|
+
getAllProposals() {
|
|
234
|
+
return Array.from(this.proposals.values());
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Get resolution history
|
|
239
|
+
* @param {number} [limit] - Limit number of results
|
|
240
|
+
* @returns {Array} Resolution history
|
|
241
|
+
*/
|
|
242
|
+
getResolutionHistory(limit) {
|
|
243
|
+
const history = [...this.resolutionHistory].reverse();
|
|
244
|
+
return limit ? history.slice(0, limit) : history;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Get layer statistics
|
|
249
|
+
* @returns {Object} Statistics
|
|
250
|
+
*/
|
|
251
|
+
getStats() {
|
|
252
|
+
const proposals = Array.from(this.proposals.values());
|
|
253
|
+
const agents = Array.from(this.agents.values());
|
|
254
|
+
|
|
255
|
+
return {
|
|
256
|
+
config: this.config,
|
|
257
|
+
proposals: {
|
|
258
|
+
total: proposals.length,
|
|
259
|
+
byAgent: agents.reduce((acc, agent) => {
|
|
260
|
+
acc[agent.id] = agent.proposalCount;
|
|
261
|
+
return acc;
|
|
262
|
+
}, {}),
|
|
263
|
+
},
|
|
264
|
+
agents: {
|
|
265
|
+
total: agents.length,
|
|
266
|
+
active: agents.filter(a => Date.now() - a.lastActivity < 300000).length, // 5 minutes
|
|
267
|
+
},
|
|
268
|
+
resolutions: {
|
|
269
|
+
total: this.resolutionHistory.length,
|
|
270
|
+
strategies: this.resolutionHistory.reduce((acc, r) => {
|
|
271
|
+
acc[r.strategy] = (acc[r.strategy] || 0) + 1;
|
|
272
|
+
return acc;
|
|
273
|
+
}, {}),
|
|
274
|
+
},
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Register default resolution strategies
|
|
280
|
+
* @private
|
|
281
|
+
*/
|
|
282
|
+
_registerDefaultStrategies() {
|
|
283
|
+
// Voting strategy
|
|
284
|
+
this.strategies.set('voting', async (proposals, _strategy) => {
|
|
285
|
+
const votes = new Map();
|
|
286
|
+
|
|
287
|
+
for (const proposal of proposals) {
|
|
288
|
+
const weight = proposal.confidence * (proposal.priority / 100);
|
|
289
|
+
const key = JSON.stringify(proposal.delta);
|
|
290
|
+
votes.set(key, (votes.get(key) || 0) + weight);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Find highest voted delta
|
|
294
|
+
let bestDelta = null;
|
|
295
|
+
let bestWeight = 0;
|
|
296
|
+
|
|
297
|
+
for (const [deltaKey, weight] of votes) {
|
|
298
|
+
if (weight > bestWeight) {
|
|
299
|
+
bestWeight = weight;
|
|
300
|
+
bestDelta = JSON.parse(deltaKey);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
return bestDelta;
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
// Merging strategy
|
|
308
|
+
this.strategies.set('merging', async (proposals, _strategy) => {
|
|
309
|
+
const merged = { additions: [], removals: [], metadata: {} };
|
|
310
|
+
|
|
311
|
+
for (const proposal of proposals) {
|
|
312
|
+
merged.additions.push(...proposal.delta.additions);
|
|
313
|
+
merged.removals.push(...proposal.delta.removals);
|
|
314
|
+
Object.assign(merged.metadata, proposal.delta.metadata || {});
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// Remove duplicates
|
|
318
|
+
merged.additions = this._deduplicateQuads(merged.additions);
|
|
319
|
+
merged.removals = this._deduplicateQuads(merged.removals);
|
|
320
|
+
|
|
321
|
+
return merged;
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
// Priority strategy
|
|
325
|
+
this.strategies.set('priority', async (proposals, _strategy) => {
|
|
326
|
+
const sorted = proposals.sort((a, b) => b.priority - a.priority);
|
|
327
|
+
return sorted[0].delta;
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
// Random strategy
|
|
331
|
+
this.strategies.set('random', async (proposals, _strategy) => {
|
|
332
|
+
const randomIndex = Math.floor(Math.random() * proposals.length);
|
|
333
|
+
return proposals[randomIndex].delta;
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
// CRDT strategy (simplified)
|
|
337
|
+
this.strategies.set('crdt', async (proposals, _strategy) => {
|
|
338
|
+
const crdt = { additions: new Map(), removals: new Map() };
|
|
339
|
+
|
|
340
|
+
for (const proposal of proposals) {
|
|
341
|
+
// Add additions with timestamps
|
|
342
|
+
for (const quad of proposal.delta.additions) {
|
|
343
|
+
const key = this._quadToKey(quad);
|
|
344
|
+
const existing = crdt.additions.get(key);
|
|
345
|
+
if (!existing || proposal.timestamp > existing.timestamp) {
|
|
346
|
+
crdt.additions.set(key, {
|
|
347
|
+
quad,
|
|
348
|
+
timestamp: proposal.timestamp,
|
|
349
|
+
agent: proposal.agentId,
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Add removals with timestamps
|
|
355
|
+
for (const quad of proposal.delta.removals) {
|
|
356
|
+
const key = this._quadToKey(quad);
|
|
357
|
+
const existing = crdt.removals.get(key);
|
|
358
|
+
if (!existing || proposal.timestamp > existing.timestamp) {
|
|
359
|
+
crdt.removals.set(key, {
|
|
360
|
+
quad,
|
|
361
|
+
timestamp: proposal.timestamp,
|
|
362
|
+
agent: proposal.agentId,
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
return {
|
|
369
|
+
additions: Array.from(crdt.additions.values()).map(v => v.quad),
|
|
370
|
+
removals: Array.from(crdt.removals.values()).map(v => v.quad),
|
|
371
|
+
metadata: { strategy: 'crdt', timestamp: Date.now() },
|
|
372
|
+
};
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Calculate consensus
|
|
378
|
+
* @param {Array} proposals - Proposals
|
|
379
|
+
* @param {Object} resolvedDelta - Resolved delta
|
|
380
|
+
* @returns {boolean} Consensus achieved
|
|
381
|
+
* @private
|
|
382
|
+
*/
|
|
383
|
+
_calculateConsensus(proposals, resolvedDelta) {
|
|
384
|
+
if (proposals.length === 0) return false;
|
|
385
|
+
|
|
386
|
+
const resolvedKey = JSON.stringify(resolvedDelta);
|
|
387
|
+
let consensusCount = 0;
|
|
388
|
+
|
|
389
|
+
for (const proposal of proposals) {
|
|
390
|
+
const proposalKey = JSON.stringify(proposal.delta);
|
|
391
|
+
if (proposalKey === resolvedKey) {
|
|
392
|
+
consensusCount++;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
const consensusRatio = consensusCount / proposals.length;
|
|
397
|
+
return consensusRatio >= 0.5; // 50% consensus threshold
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Detect conflicts between proposals
|
|
402
|
+
* @param {Array} proposals - Proposals
|
|
403
|
+
* @param {Object} resolvedDelta - Resolved delta
|
|
404
|
+
* @returns {Array} Conflicts
|
|
405
|
+
* @private
|
|
406
|
+
*/
|
|
407
|
+
_detectConflicts(proposals, resolvedDelta) {
|
|
408
|
+
const conflicts = [];
|
|
409
|
+
|
|
410
|
+
// Check for conflicting additions/removals
|
|
411
|
+
const resolvedAdditions = new Set(resolvedDelta.additions.map(q => this._quadToKey(q)));
|
|
412
|
+
const resolvedRemovals = new Set(resolvedDelta.removals.map(q => this._quadToKey(q)));
|
|
413
|
+
|
|
414
|
+
for (const proposal of proposals) {
|
|
415
|
+
const proposalAdditions = new Set(proposal.delta.additions.map(q => this._quadToKey(q)));
|
|
416
|
+
const proposalRemovals = new Set(proposal.delta.removals.map(q => this._quadToKey(q)));
|
|
417
|
+
|
|
418
|
+
// Check for conflicts
|
|
419
|
+
for (const key of proposalAdditions) {
|
|
420
|
+
if (resolvedRemovals.has(key)) {
|
|
421
|
+
conflicts.push({
|
|
422
|
+
type: 'addition',
|
|
423
|
+
proposals: [proposal.id],
|
|
424
|
+
resolution: 'conflict detected',
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
for (const key of proposalRemovals) {
|
|
430
|
+
if (resolvedAdditions.has(key)) {
|
|
431
|
+
conflicts.push({
|
|
432
|
+
type: 'removal',
|
|
433
|
+
proposals: [proposal.id],
|
|
434
|
+
resolution: 'conflict detected',
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
return conflicts;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* Calculate confidence score
|
|
445
|
+
* @param {Array} proposals - Proposals
|
|
446
|
+
* @param {Object} resolvedDelta - Resolved delta
|
|
447
|
+
* @returns {number} Confidence score
|
|
448
|
+
* @private
|
|
449
|
+
*/
|
|
450
|
+
_calculateConfidence(proposals, _resolvedDelta) {
|
|
451
|
+
if (proposals.length === 0) return 0;
|
|
452
|
+
|
|
453
|
+
let totalConfidence = 0;
|
|
454
|
+
let weightSum = 0;
|
|
455
|
+
|
|
456
|
+
for (const proposal of proposals) {
|
|
457
|
+
const weight = proposal.priority / 100;
|
|
458
|
+
totalConfidence += proposal.confidence * weight;
|
|
459
|
+
weightSum += weight;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
return weightSum > 0 ? totalConfidence / weightSum : 0;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Deduplicate quads
|
|
467
|
+
* @param {Array} quads - Quads array
|
|
468
|
+
* @returns {Array} Deduplicated quads
|
|
469
|
+
* @private
|
|
470
|
+
*/
|
|
471
|
+
_deduplicateQuads(quads) {
|
|
472
|
+
const seen = new Set();
|
|
473
|
+
return quads.filter(quad => {
|
|
474
|
+
const key = this._quadToKey(quad);
|
|
475
|
+
if (seen.has(key)) {
|
|
476
|
+
return false;
|
|
477
|
+
}
|
|
478
|
+
seen.add(key);
|
|
479
|
+
return true;
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Convert quad to key
|
|
485
|
+
* @param {Object} quad - RDF quad
|
|
486
|
+
* @returns {string} Quad key
|
|
487
|
+
* @private
|
|
488
|
+
*/
|
|
489
|
+
_quadToKey(quad) {
|
|
490
|
+
return `${quad.subject?.value || ''}:${quad.predicate?.value || ''}:${quad.object?.value || ''}:${quad.graph?.value || ''}`;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* Create a resolution layer instance
|
|
496
|
+
* @param {Object} [config] - Configuration
|
|
497
|
+
* @returns {ResolutionLayer} Resolution layer
|
|
498
|
+
*/
|
|
499
|
+
export function createResolutionLayer(config = {}) {
|
|
500
|
+
return new ResolutionLayer(config);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
/**
|
|
504
|
+
* Default resolution layer instance
|
|
505
|
+
*/
|
|
506
|
+
export const defaultResolutionLayer = createResolutionLayer();
|