@holoscript/framework 6.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ALL-test-results.json +1 -0
- package/CHANGELOG.md +8 -0
- package/LICENSE +21 -0
- package/ROADMAP.md +175 -0
- package/dist/AgentManifest-CB4xM-Ma.d.cts +704 -0
- package/dist/AgentManifest-CB4xM-Ma.d.ts +704 -0
- package/dist/BehaviorTree-BrBFECv5.d.cts +103 -0
- package/dist/BehaviorTree-BrBFECv5.d.ts +103 -0
- package/dist/InvisibleWallet-BB6tFvRA.d.cts +1732 -0
- package/dist/InvisibleWallet-rtRrBOA8.d.ts +1732 -0
- package/dist/OrchestratorAgent-BvWgf9uw.d.cts +798 -0
- package/dist/OrchestratorAgent-Q_CbVTmO.d.ts +798 -0
- package/dist/agents/index.cjs +4790 -0
- package/dist/agents/index.d.cts +1788 -0
- package/dist/agents/index.d.ts +1788 -0
- package/dist/agents/index.js +4695 -0
- package/dist/ai/index.cjs +5347 -0
- package/dist/ai/index.d.cts +1753 -0
- package/dist/ai/index.d.ts +1753 -0
- package/dist/ai/index.js +5244 -0
- package/dist/behavior.cjs +449 -0
- package/dist/behavior.d.cts +130 -0
- package/dist/behavior.d.ts +130 -0
- package/dist/behavior.js +407 -0
- package/dist/economy/index.cjs +3659 -0
- package/dist/economy/index.d.cts +747 -0
- package/dist/economy/index.d.ts +747 -0
- package/dist/economy/index.js +3617 -0
- package/dist/implementations-D9T3un9D.d.cts +236 -0
- package/dist/implementations-D9T3un9D.d.ts +236 -0
- package/dist/index.cjs +24550 -0
- package/dist/index.d.cts +1729 -0
- package/dist/index.d.ts +1729 -0
- package/dist/index.js +24277 -0
- package/dist/learning/index.cjs +219 -0
- package/dist/learning/index.d.cts +104 -0
- package/dist/learning/index.d.ts +104 -0
- package/dist/learning/index.js +189 -0
- package/dist/negotiation/index.cjs +970 -0
- package/dist/negotiation/index.d.cts +610 -0
- package/dist/negotiation/index.d.ts +610 -0
- package/dist/negotiation/index.js +931 -0
- package/dist/skills/index.cjs +1118 -0
- package/dist/skills/index.d.cts +289 -0
- package/dist/skills/index.d.ts +289 -0
- package/dist/skills/index.js +1079 -0
- package/dist/swarm/index.cjs +5268 -0
- package/dist/swarm/index.d.cts +2433 -0
- package/dist/swarm/index.d.ts +2433 -0
- package/dist/swarm/index.js +5221 -0
- package/dist/training/index.cjs +2745 -0
- package/dist/training/index.d.cts +1734 -0
- package/dist/training/index.d.ts +1734 -0
- package/dist/training/index.js +2687 -0
- package/extract-failures.js +10 -0
- package/package.json +82 -0
- package/src/__tests__/bounty-marketplace.test.ts +374 -0
- package/src/__tests__/delegation.test.ts +144 -0
- package/src/__tests__/distributed-claimer.test.ts +147 -0
- package/src/__tests__/done-log-audit.test.ts +342 -0
- package/src/__tests__/framework.test.ts +865 -0
- package/src/__tests__/goal-synthesizer.test.ts +236 -0
- package/src/__tests__/presence.test.ts +223 -0
- package/src/__tests__/protocol-agent.test.ts +254 -0
- package/src/__tests__/revenue-splitter.test.ts +114 -0
- package/src/__tests__/scenario-driven-todo.test.ts +197 -0
- package/src/__tests__/self-improve.test.ts +349 -0
- package/src/__tests__/service-lifecycle.test.ts +237 -0
- package/src/__tests__/skill-router.test.ts +121 -0
- package/src/agents/AgentManifest.ts +493 -0
- package/src/agents/AgentRegistry.ts +475 -0
- package/src/agents/AgentTypes.ts +585 -0
- package/src/agents/AgentWalletRegistry.ts +83 -0
- package/src/agents/AuthenticatedCRDT.ts +388 -0
- package/src/agents/CapabilityMatcher.ts +453 -0
- package/src/agents/CrossRealityHandoff.ts +305 -0
- package/src/agents/CulturalMemory.ts +454 -0
- package/src/agents/FederatedRegistryAdapter.ts +429 -0
- package/src/agents/NormEngine.ts +450 -0
- package/src/agents/OrchestratorAgent.ts +414 -0
- package/src/agents/SkillWorkflowEngine.ts +472 -0
- package/src/agents/TaskDelegationService.ts +551 -0
- package/src/agents/__tests__/AgentManifest.prod.test.ts +134 -0
- package/src/agents/__tests__/AgentManifest.test.ts +182 -0
- package/src/agents/__tests__/AgentModule.test.ts +864 -0
- package/src/agents/__tests__/AgentRegistry.prod.test.ts +125 -0
- package/src/agents/__tests__/AgentRegistry.test.ts +148 -0
- package/src/agents/__tests__/AgentTypes.test.ts +534 -0
- package/src/agents/__tests__/AgentWalletRegistry.test.ts +152 -0
- package/src/agents/__tests__/AuthenticatedCRDT.test.ts +558 -0
- package/src/agents/__tests__/CapabilityMatcher.prod.test.ts +117 -0
- package/src/agents/__tests__/CapabilityMatcher.test.ts +178 -0
- package/src/agents/__tests__/CrossRealityHandoff.test.ts +402 -0
- package/src/agents/__tests__/CulturalMemory.test.ts +200 -0
- package/src/agents/__tests__/FederatedRegistryAdapter.test.ts +409 -0
- package/src/agents/__tests__/NormEngine.test.ts +276 -0
- package/src/agents/__tests__/OrchestratorAgent.test.ts +182 -0
- package/src/agents/__tests__/SkillWorkflowEngine.test.ts +357 -0
- package/src/agents/__tests__/TaskDelegationService.test.ts +446 -0
- package/src/agents/index.ts +107 -0
- package/src/agents/spatial-comms/Layer1RealTime.ts +621 -0
- package/src/agents/spatial-comms/Layer2A2A.ts +661 -0
- package/src/agents/spatial-comms/Layer3MCP.ts +651 -0
- package/src/agents/spatial-comms/ProtocolTypes.ts +543 -0
- package/src/agents/spatial-comms/SpatialCommClient.ts +483 -0
- package/src/agents/spatial-comms/__tests__/performance-benchmark.test.ts +465 -0
- package/src/agents/spatial-comms/examples/multi-agent-world-creation.ts +409 -0
- package/src/agents/spatial-comms/index.ts +66 -0
- package/src/ai/AIAdapter.ts +313 -0
- package/src/ai/AICopilot.ts +331 -0
- package/src/ai/AIOutputValidator.ts +203 -0
- package/src/ai/BTNodes.ts +239 -0
- package/src/ai/BehaviorSelector.ts +135 -0
- package/src/ai/BehaviorTree.ts +153 -0
- package/src/ai/Blackboard.ts +165 -0
- package/src/ai/GenerationAnalytics.ts +461 -0
- package/src/ai/GenerationCache.ts +265 -0
- package/src/ai/GoalPlanner.ts +165 -0
- package/src/ai/HoloScriptGenerator.ts +580 -0
- package/src/ai/InfluenceMap.ts +180 -0
- package/src/ai/NavMesh.ts +168 -0
- package/src/ai/PerceptionSystem.ts +178 -0
- package/src/ai/PromptTemplates.ts +453 -0
- package/src/ai/SemanticSearchService.ts +80 -0
- package/src/ai/StateMachine.ts +196 -0
- package/src/ai/SteeringBehavior.ts +150 -0
- package/src/ai/SteeringBehaviors.ts +244 -0
- package/src/ai/TrainingDataGenerator.ts +1082 -0
- package/src/ai/UtilityAI.ts +145 -0
- package/src/ai/__tests__/AIAdapter.prod.test.ts +259 -0
- package/src/ai/__tests__/AIAdapter.test.ts +109 -0
- package/src/ai/__tests__/AICopilot.prod.test.ts +341 -0
- package/src/ai/__tests__/AICopilot.test.ts +178 -0
- package/src/ai/__tests__/AIOutputValidator.prod.test.ts +226 -0
- package/src/ai/__tests__/AIOutputValidator.test.ts +138 -0
- package/src/ai/__tests__/BTNodes.prod.test.ts +391 -0
- package/src/ai/__tests__/BTNodes.test.ts +263 -0
- package/src/ai/__tests__/BehaviorSelector.prod.test.ts +129 -0
- package/src/ai/__tests__/BehaviorSelector.test.ts +132 -0
- package/src/ai/__tests__/BehaviorTree.prod.test.ts +266 -0
- package/src/ai/__tests__/BehaviorTree.test.ts +216 -0
- package/src/ai/__tests__/Blackboard.prod.test.ts +339 -0
- package/src/ai/__tests__/Blackboard.test.ts +183 -0
- package/src/ai/__tests__/GenerationAnalytics.prod.test.ts +141 -0
- package/src/ai/__tests__/GenerationAnalytics.test.ts +165 -0
- package/src/ai/__tests__/GenerationCache.prod.test.ts +144 -0
- package/src/ai/__tests__/GenerationCache.test.ts +171 -0
- package/src/ai/__tests__/GoalPlanner.prod.test.ts +189 -0
- package/src/ai/__tests__/GoalPlanner.test.ts +137 -0
- package/src/ai/__tests__/GoalPlannerDepth.prod.test.ts +217 -0
- package/src/ai/__tests__/HoloScriptGenerator.test.ts +125 -0
- package/src/ai/__tests__/InfluenceMap.prod.test.ts +146 -0
- package/src/ai/__tests__/InfluenceMap.test.ts +149 -0
- package/src/ai/__tests__/NavMesh.prod.test.ts +141 -0
- package/src/ai/__tests__/NavMesh.test.ts +159 -0
- package/src/ai/__tests__/PerceptionSystem.prod.test.ts +135 -0
- package/src/ai/__tests__/PerceptionSystem.test.ts +250 -0
- package/src/ai/__tests__/PromptTemplates.prod.test.ts +313 -0
- package/src/ai/__tests__/PromptTemplates.test.ts +146 -0
- package/src/ai/__tests__/SemanticSearch.test.ts +37 -0
- package/src/ai/__tests__/StateMachine.prod.test.ts +162 -0
- package/src/ai/__tests__/StateMachine.test.ts +163 -0
- package/src/ai/__tests__/SteeringBehavior.prod.test.ts +251 -0
- package/src/ai/__tests__/SteeringBehavior.test.ts +135 -0
- package/src/ai/__tests__/SteeringBehaviors.prod.test.ts +133 -0
- package/src/ai/__tests__/SteeringBehaviors.test.ts +151 -0
- package/src/ai/__tests__/TrainingDataGenerator.prod.test.ts +286 -0
- package/src/ai/__tests__/TrainingDataGenerator.test.ts +286 -0
- package/src/ai/__tests__/UtilityAI.prod.test.ts +207 -0
- package/src/ai/__tests__/UtilityAI.test.ts +155 -0
- package/src/ai/__tests__/adapters.prod.test.ts +263 -0
- package/src/ai/__tests__/adapters.test.ts +320 -0
- package/src/ai/adapters.ts +1585 -0
- package/src/ai/index.ts +130 -0
- package/src/behavior/BehaviorPresets.ts +140 -0
- package/src/behavior/BehaviorTree.ts +236 -0
- package/src/behavior/StateMachine.ts +176 -0
- package/src/behavior/StateTrait.ts +67 -0
- package/src/behavior/index.ts +8 -0
- package/src/behavior.ts +8 -0
- package/src/board/audit.ts +284 -0
- package/src/board/board-ops.ts +336 -0
- package/src/board/board-types.ts +302 -0
- package/src/board/index.ts +69 -0
- package/src/define-agent.ts +46 -0
- package/src/define-team.ts +33 -0
- package/src/delegation.ts +265 -0
- package/src/distributed-claimer.ts +228 -0
- package/src/economy/AgentBudgetEnforcer.ts +464 -0
- package/src/economy/BountyManager.ts +185 -0
- package/src/economy/CreatorRevenueAggregator.ts +460 -0
- package/src/economy/InvisibleWallet.ts +82 -0
- package/src/economy/KnowledgeMarketplace.ts +193 -0
- package/src/economy/PaymentWebhookService.ts +512 -0
- package/src/economy/RevenueSplitter.ts +156 -0
- package/src/economy/SubscriptionManager.ts +546 -0
- package/src/economy/UnifiedBudgetOptimizer.ts +635 -0
- package/src/economy/UsageMeter.ts +440 -0
- package/src/economy/_core-stubs.ts +219 -0
- package/src/economy/index.ts +100 -0
- package/src/economy/x402-facilitator.ts +1978 -0
- package/src/index.ts +348 -0
- package/src/knowledge/__tests__/knowledge-consolidator.test.ts +444 -0
- package/src/knowledge/__tests__/knowledge-store-vector.test.ts +291 -0
- package/src/knowledge/brain.ts +167 -0
- package/src/knowledge/consolidation.ts +581 -0
- package/src/knowledge/knowledge-consolidator.ts +510 -0
- package/src/knowledge/knowledge-store.ts +616 -0
- package/src/learning/MemoryConsolidator.ts +102 -0
- package/src/learning/MemoryScorer.ts +69 -0
- package/src/learning/ProceduralCompiler.ts +45 -0
- package/src/learning/SemanticClusterer.ts +66 -0
- package/src/learning/index.ts +8 -0
- package/src/llm/llm-adapter.ts +159 -0
- package/src/mesh/index.ts +309 -0
- package/src/negotiation/NegotiationProtocol.ts +694 -0
- package/src/negotiation/NegotiationTypes.ts +473 -0
- package/src/negotiation/VotingMechanisms.ts +691 -0
- package/src/negotiation/index.ts +49 -0
- package/src/protocol/goal-synthesizer.ts +317 -0
- package/src/protocol/implementations.ts +474 -0
- package/src/protocol/micro-phase-decomposer.ts +299 -0
- package/src/protocol/micro-step-decomposer.test.ts +306 -0
- package/src/protocol-agent.test.ts +353 -0
- package/src/protocol-agent.ts +670 -0
- package/src/self-improve/absorb-scanner.ts +252 -0
- package/src/self-improve/evolution-engine.ts +149 -0
- package/src/self-improve/framework-absorber.ts +214 -0
- package/src/self-improve/index.ts +50 -0
- package/src/self-improve/prompt-optimizer.ts +212 -0
- package/src/self-improve/test-generator.ts +175 -0
- package/src/skill-router.ts +186 -0
- package/src/skills/index.ts +5 -0
- package/src/skills/skill-md-bridge.ts +1699 -0
- package/src/swarm/ACOEngine.ts +261 -0
- package/src/swarm/CollectiveIntelligence.ts +383 -0
- package/src/swarm/ContributionSynthesizer.ts +481 -0
- package/src/swarm/LeaderElection.ts +393 -0
- package/src/swarm/PSOEngine.ts +206 -0
- package/src/swarm/QuorumPolicy.ts +173 -0
- package/src/swarm/SwarmCoordinator.ts +335 -0
- package/src/swarm/SwarmManager.ts +442 -0
- package/src/swarm/SwarmMembership.ts +456 -0
- package/src/swarm/VotingRound.ts +255 -0
- package/src/swarm/__tests__/ACOEngine.prod.test.ts +164 -0
- package/src/swarm/__tests__/ACOEngine.test.ts +117 -0
- package/src/swarm/__tests__/CollectiveIntelligence.prod.test.ts +296 -0
- package/src/swarm/__tests__/CollectiveIntelligence.test.ts +457 -0
- package/src/swarm/__tests__/ContributionSynthesizer.prod.test.ts +269 -0
- package/src/swarm/__tests__/ContributionSynthesizer.test.ts +254 -0
- package/src/swarm/__tests__/LeaderElection.prod.test.ts +196 -0
- package/src/swarm/__tests__/LeaderElection.test.ts +151 -0
- package/src/swarm/__tests__/PSOEngine.prod.test.ts +162 -0
- package/src/swarm/__tests__/PSOEngine.test.ts +106 -0
- package/src/swarm/__tests__/QuorumPolicy.prod.test.ts +216 -0
- package/src/swarm/__tests__/QuorumPolicy.test.ts +177 -0
- package/src/swarm/__tests__/SwarmCoordinator.prod.test.ts +186 -0
- package/src/swarm/__tests__/SwarmCoordinator.test.ts +167 -0
- package/src/swarm/__tests__/SwarmManager.prod.test.ts +308 -0
- package/src/swarm/__tests__/SwarmManager.test.ts +373 -0
- package/src/swarm/__tests__/SwarmMembership.prod.test.ts +273 -0
- package/src/swarm/__tests__/SwarmMembership.test.ts +264 -0
- package/src/swarm/__tests__/VotingRound.prod.test.ts +233 -0
- package/src/swarm/__tests__/VotingRound.test.ts +174 -0
- package/src/swarm/analytics/SwarmInspector.ts +476 -0
- package/src/swarm/analytics/SwarmMetrics.ts +449 -0
- package/src/swarm/analytics/__tests__/SwarmInspector.prod.test.ts +366 -0
- package/src/swarm/analytics/__tests__/SwarmInspector.test.ts +454 -0
- package/src/swarm/analytics/__tests__/SwarmMetrics.prod.test.ts +254 -0
- package/src/swarm/analytics/__tests__/SwarmMetrics.test.ts +370 -0
- package/src/swarm/analytics/index.ts +7 -0
- package/src/swarm/index.ts +69 -0
- package/src/swarm/messaging/BroadcastChannel.ts +509 -0
- package/src/swarm/messaging/GossipProtocol.ts +565 -0
- package/src/swarm/messaging/SwarmEventBus.ts +443 -0
- package/src/swarm/messaging/__tests__/BroadcastChannel.prod.test.ts +331 -0
- package/src/swarm/messaging/__tests__/BroadcastChannel.test.ts +333 -0
- package/src/swarm/messaging/__tests__/GossipProtocol.prod.test.ts +356 -0
- package/src/swarm/messaging/__tests__/GossipProtocol.test.ts +437 -0
- package/src/swarm/messaging/__tests__/SwarmEventBus.prod.test.ts +191 -0
- package/src/swarm/messaging/__tests__/SwarmEventBus.test.ts +247 -0
- package/src/swarm/messaging/index.ts +8 -0
- package/src/swarm/spatial/FlockingBehavior.ts +462 -0
- package/src/swarm/spatial/FormationController.ts +500 -0
- package/src/swarm/spatial/Vector3.ts +170 -0
- package/src/swarm/spatial/ZoneClaiming.ts +509 -0
- package/src/swarm/spatial/__tests__/FlockingBehavior.prod.test.ts +239 -0
- package/src/swarm/spatial/__tests__/FlockingBehavior.test.ts +298 -0
- package/src/swarm/spatial/__tests__/FormationController.prod.test.ts +240 -0
- package/src/swarm/spatial/__tests__/FormationController.test.ts +297 -0
- package/src/swarm/spatial/__tests__/Vector3.prod.test.ts +283 -0
- package/src/swarm/spatial/__tests__/Vector3.test.ts +224 -0
- package/src/swarm/spatial/__tests__/ZoneClaiming.prod.test.ts +246 -0
- package/src/swarm/spatial/__tests__/ZoneClaiming.test.ts +374 -0
- package/src/swarm/spatial/index.ts +28 -0
- package/src/team.ts +1245 -0
- package/src/training/LRScheduler.ts +377 -0
- package/src/training/QualityScoringPipeline.ts +139 -0
- package/src/training/SoftDedup.ts +461 -0
- package/src/training/SparsityMonitor.ts +685 -0
- package/src/training/SparsityMonitorTypes.ts +209 -0
- package/src/training/SpatialTrainingDataGenerator.ts +1526 -0
- package/src/training/SpatialTrainingDataTypes.ts +216 -0
- package/src/training/TrainingPipelineConfig.ts +215 -0
- package/src/training/constants.ts +94 -0
- package/src/training/index.ts +138 -0
- package/src/training/schema.ts +147 -0
- package/src/training/scripts/generate-novel-use-cases-dataset.ts +272 -0
- package/src/training/scripts/generate-spatial-dataset.ts +521 -0
- package/src/training/training/data/novel-use-cases.jsonl +153 -0
- package/src/training/training/data/spatial-reasoning-10k.jsonl +9354 -0
- package/src/training/trainingmonkey/TrainingMonkeyIntegration.ts +477 -0
- package/src/training/trainingmonkey/TrainingMonkeyTypes.ts +230 -0
- package/src/training/trainingmonkey/index.ts +26 -0
- package/src/training/trait-mappings.ts +157 -0
- package/src/types/core-stubs.d.ts +113 -0
- package/src/types.ts +304 -0
- package/test-output.txt +0 -0
- package/test-result.json +1 -0
- package/tsc-errors.txt +4 -0
- package/tsc_output.txt +0 -0
- package/tsconfig.json +14 -0
- package/tsup-learning-esm.config.ts +12 -0
- package/tsup.config.ts +21 -0
- package/typescript-errors-2.txt +0 -0
- package/typescript-errors.txt +22 -0
- package/vitest-log-utf8.txt +268 -0
- package/vitest-log.txt +0 -0
- package/vitest.config.ts +8 -0
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ContributionSynthesizer — Production Tests
|
|
3
|
+
*/
|
|
4
|
+
import { describe, it, expect } from 'vitest';
|
|
5
|
+
import { ContributionSynthesizer } from '../ContributionSynthesizer';
|
|
6
|
+
import type { IHiveContribution, IHiveSession } from '@holoscript/core';
|
|
7
|
+
|
|
8
|
+
function make(cfg = {}) {
|
|
9
|
+
return new ContributionSynthesizer(cfg);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
let _id = 0;
|
|
13
|
+
function c(type: IHiveContribution['type'], content: string, confidence = 0.8): IHiveContribution {
|
|
14
|
+
return { id: `c${++_id}`, agentId: 'a1', type, content, confidence, timestamp: Date.now() };
|
|
15
|
+
}
|
|
16
|
+
function session(contribs: IHiveContribution[]): IHiveSession {
|
|
17
|
+
return {
|
|
18
|
+
id: 'sess',
|
|
19
|
+
topic: 't',
|
|
20
|
+
goal: 'g',
|
|
21
|
+
initiator: 'a1',
|
|
22
|
+
status: 'active',
|
|
23
|
+
participants: ['a1'],
|
|
24
|
+
contributions: contribs,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
describe('ContributionSynthesizer — defaults', () => {
|
|
29
|
+
it('constructs without args', () => expect(() => make()).not.toThrow());
|
|
30
|
+
it('default minConfidenceThreshold=0.3', () => {
|
|
31
|
+
// contributions with confidence < 0.3 should be filtered
|
|
32
|
+
const s = make();
|
|
33
|
+
const sess = session([c('idea', 'low', 0.1), c('idea', 'ok', 0.5)]);
|
|
34
|
+
const r = s.synthesize(sess);
|
|
35
|
+
expect(r.metadata.totalContributions).toBe(2);
|
|
36
|
+
expect(r.metadata.ideaCount).toBe(1); // only 1 passes threshold
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
describe('ContributionSynthesizer — synthesize with empty session', () => {
|
|
41
|
+
it('empty session returns zero result', () => {
|
|
42
|
+
const s = make();
|
|
43
|
+
const r = s.synthesize(session([]));
|
|
44
|
+
expect(r.synthesizedContent).toBe('');
|
|
45
|
+
expect(r.confidence).toBe(0);
|
|
46
|
+
expect(r.metadata.totalContributions).toBe(0);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
describe('ContributionSynthesizer — method selection', () => {
|
|
51
|
+
it('hierarchical when solutions present', () => {
|
|
52
|
+
const s = make();
|
|
53
|
+
const sess = session([c('solution', 'use redis'), c('idea', 'try caching')]);
|
|
54
|
+
expect(s.synthesize(sess).synthesisMethod).toBe('hierarchical');
|
|
55
|
+
});
|
|
56
|
+
it('consensus when no solutions but has consensus items', () => {
|
|
57
|
+
const s = make({ preferSolutions: false });
|
|
58
|
+
const sess = session([c('consensus', 'agreed on approach')]);
|
|
59
|
+
expect(s.synthesize(sess).synthesisMethod).toBe('consensus');
|
|
60
|
+
});
|
|
61
|
+
it('weighted when 3+ ideas and no solutions/consensus', () => {
|
|
62
|
+
const s = make();
|
|
63
|
+
const sess = session([c('idea', 'idea1'), c('idea', 'idea2'), c('idea', 'idea3')]);
|
|
64
|
+
expect(s.synthesize(sess).synthesisMethod).toBe('weighted');
|
|
65
|
+
});
|
|
66
|
+
it('merge default when 1-2 ideas', () => {
|
|
67
|
+
const s = make();
|
|
68
|
+
const sess = session([c('idea', 'one idea')]);
|
|
69
|
+
expect(s.synthesize(sess).synthesisMethod).toBe('merge');
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
describe('ContributionSynthesizer — hierarchical synthesis', () => {
|
|
74
|
+
it('content includes solution section', () => {
|
|
75
|
+
const s = make();
|
|
76
|
+
const sess = session([c('solution', 'deploy redis cluster'), c('idea', 'optimize queries')]);
|
|
77
|
+
expect(s.synthesize(sess).synthesizedContent).toContain('Solutions');
|
|
78
|
+
});
|
|
79
|
+
it('sources contain solution contribution ids', () => {
|
|
80
|
+
const sol = c('solution', 'use redis');
|
|
81
|
+
const s = make();
|
|
82
|
+
const r = s.synthesize(session([sol]));
|
|
83
|
+
expect(r.sourceContributions).toContain(sol.id);
|
|
84
|
+
});
|
|
85
|
+
it('critiques section included when includeCritiques=true', () => {
|
|
86
|
+
const s = make({ includeCritiques: true });
|
|
87
|
+
const sess = session([c('solution', 'a sol'), c('critique', 'careful with latency')]);
|
|
88
|
+
expect(s.synthesize(sess).synthesizedContent).toContain('Considerations');
|
|
89
|
+
});
|
|
90
|
+
it('critiques excluded when includeCritiques=false', () => {
|
|
91
|
+
const s = make({ includeCritiques: false });
|
|
92
|
+
const sess = session([c('solution', 'deploy'), c('critique', 'latency risk')]);
|
|
93
|
+
const content = s.synthesize(sess).synthesizedContent;
|
|
94
|
+
expect(content).not.toContain('Considerations');
|
|
95
|
+
});
|
|
96
|
+
it('solutions sorted by confidence (highest first)', () => {
|
|
97
|
+
const low = c('solution', 'lower confidence', 0.4);
|
|
98
|
+
const high = c('solution', 'higher confidence', 0.9);
|
|
99
|
+
const s = make();
|
|
100
|
+
const content = s.synthesize(session([low, high])).synthesizedContent;
|
|
101
|
+
expect(content.indexOf('higher confidence')).toBeLessThan(content.indexOf('lower confidence'));
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
describe('ContributionSynthesizer — consensus synthesis', () => {
|
|
106
|
+
it('content includes Collective Consensus header', () => {
|
|
107
|
+
const s = make({ preferSolutions: false });
|
|
108
|
+
const sess = session([c('consensus', 'all agree on microservices')]);
|
|
109
|
+
expect(s.synthesize(sess).synthesizedContent).toContain('Collective Consensus');
|
|
110
|
+
});
|
|
111
|
+
it('confidence is average of included items', () => {
|
|
112
|
+
const s = make({ preferSolutions: false });
|
|
113
|
+
const sess = session([c('consensus', 'agree', 0.6), c('consensus', 'also agree', 0.8)]);
|
|
114
|
+
const r = s.synthesize(sess);
|
|
115
|
+
expect(r.confidence).toBeCloseTo(0.7, 5);
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
describe('ContributionSynthesizer — weighted synthesis', () => {
|
|
120
|
+
it('content includes Weighted Synthesis header', () => {
|
|
121
|
+
const s = make();
|
|
122
|
+
const sess = session([c('idea', 'i1'), c('idea', 'i2'), c('idea', 'i3')]);
|
|
123
|
+
expect(s.synthesize(sess).synthesizedContent).toContain('Weighted Synthesis');
|
|
124
|
+
});
|
|
125
|
+
it('shows confidence percentage for each idea', () => {
|
|
126
|
+
const s = make();
|
|
127
|
+
const sess = session([c('idea', 'i1', 0.8), c('idea', 'i2', 0.8), c('idea', 'i3', 0.8)]);
|
|
128
|
+
expect(s.synthesize(sess).synthesizedContent).toContain('80%');
|
|
129
|
+
});
|
|
130
|
+
it('maxSourcesPerSynthesis respected', () => {
|
|
131
|
+
const s = make({ maxSourcesPerSynthesis: 2 });
|
|
132
|
+
const sess = session([c('idea', 'i1'), c('idea', 'i2'), c('idea', 'i3'), c('idea', 'i4')]);
|
|
133
|
+
expect(s.synthesize(sess).sourceContributions).toHaveLength(2);
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
describe('ContributionSynthesizer — merge synthesis', () => {
|
|
138
|
+
it('content includes Merged Contributions header', () => {
|
|
139
|
+
const s = make();
|
|
140
|
+
const sess = session([c('idea', 'single idea')]);
|
|
141
|
+
expect(s.synthesize(sess).synthesizedContent).toContain('Merged Contributions');
|
|
142
|
+
});
|
|
143
|
+
it('sources contain all included contribution ids', () => {
|
|
144
|
+
const i1 = c('idea', 'first');
|
|
145
|
+
const i2 = c('critique', 'second');
|
|
146
|
+
const s = make();
|
|
147
|
+
const r = s.synthesize(session([i1, i2]));
|
|
148
|
+
expect(r.sourceContributions).toContain(i1.id);
|
|
149
|
+
expect(r.sourceContributions).toContain(i2.id);
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
describe('ContributionSynthesizer — synthesizeSubset', () => {
|
|
154
|
+
it('synthesizes list of contribs directly', () => {
|
|
155
|
+
const s = make();
|
|
156
|
+
const items = [c('idea', 'subset1'), c('idea', 'subset2'), c('idea', 'subset3')];
|
|
157
|
+
const r = s.synthesizeSubset(items);
|
|
158
|
+
expect(r.metadata.totalContributions).toBe(3);
|
|
159
|
+
});
|
|
160
|
+
it('empty subset → empty result', () => {
|
|
161
|
+
const r = make().synthesizeSubset([]);
|
|
162
|
+
expect(r.synthesizedContent).toBe('');
|
|
163
|
+
expect(r.confidence).toBe(0);
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
describe('ContributionSynthesizer — merge()', () => {
|
|
168
|
+
it('returns merged contribution with synthesizer agentId', () => {
|
|
169
|
+
const s = make();
|
|
170
|
+
const merged = s.merge(c('idea', 'idea A'), c('idea', 'idea B'));
|
|
171
|
+
expect(merged.agentId).toBe('synthesizer');
|
|
172
|
+
});
|
|
173
|
+
it('merged content contains both originals', () => {
|
|
174
|
+
const s = make();
|
|
175
|
+
const merged = s.merge(c('idea', 'alpha approach'), c('critique', 'beta concern'));
|
|
176
|
+
expect(merged.content).toContain('alpha approach');
|
|
177
|
+
expect(merged.content).toContain('beta concern');
|
|
178
|
+
});
|
|
179
|
+
it('merged confidence is average + 0.05 bonus, capped at 1', () => {
|
|
180
|
+
const s = make();
|
|
181
|
+
const m = s.merge(c('idea', 'x', 0.6), c('idea', 'y', 0.8));
|
|
182
|
+
expect(m.confidence).toBeCloseTo(0.75, 5); // (0.6+0.8)/2 + 0.05 = 0.75
|
|
183
|
+
});
|
|
184
|
+
it('merged confidence capped at 1', () => {
|
|
185
|
+
const s = make();
|
|
186
|
+
const m = s.merge(c('solution', 'x', 1.0), c('solution', 'y', 1.0));
|
|
187
|
+
expect(m.confidence).toBe(1);
|
|
188
|
+
});
|
|
189
|
+
it('higher type wins (solution > idea)', () => {
|
|
190
|
+
const s = make();
|
|
191
|
+
const m = s.merge(c('idea', 'x'), c('solution', 'y'));
|
|
192
|
+
expect(m.type).toBe('solution');
|
|
193
|
+
});
|
|
194
|
+
it('same type preserved', () => {
|
|
195
|
+
const s = make();
|
|
196
|
+
expect(s.merge(c('critique', 'a'), c('critique', 'b')).type).toBe('critique');
|
|
197
|
+
});
|
|
198
|
+
it('merged id contains both source ids', () => {
|
|
199
|
+
const a = c('idea', 'a');
|
|
200
|
+
const b = c('idea', 'b');
|
|
201
|
+
const s = make();
|
|
202
|
+
const m = s.merge(a, b);
|
|
203
|
+
expect(m.id).toContain(a.id);
|
|
204
|
+
expect(m.id).toContain(b.id);
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
describe('ContributionSynthesizer — findSimilar()', () => {
|
|
209
|
+
it('excludes self', () => {
|
|
210
|
+
const s = make();
|
|
211
|
+
const target = c('idea', 'machine learning optimization');
|
|
212
|
+
const result = s.findSimilar(target, [target]);
|
|
213
|
+
expect(result).toHaveLength(0);
|
|
214
|
+
});
|
|
215
|
+
it('finds high-overlap contributions', () => {
|
|
216
|
+
const s = make();
|
|
217
|
+
const target = c('idea', 'implement machine learning model training');
|
|
218
|
+
const similar = c('idea', 'implement machine learning model pipeline');
|
|
219
|
+
const different = c('idea', 'buy groceries from store');
|
|
220
|
+
const result = s.findSimilar(target, [target, similar, different]);
|
|
221
|
+
expect(result).toContainEqual(similar);
|
|
222
|
+
expect(result).not.toContainEqual(different);
|
|
223
|
+
});
|
|
224
|
+
it('custom threshold excludes below-threshold', () => {
|
|
225
|
+
const s = make();
|
|
226
|
+
const target = c('idea', 'abc def ghi');
|
|
227
|
+
const partial = c('idea', 'abc xyz');
|
|
228
|
+
const result = s.findSimilar(target, [target, partial], 0.9);
|
|
229
|
+
expect(result).toHaveLength(0); // partial has low jaccard
|
|
230
|
+
});
|
|
231
|
+
it('empty candidates returns empty', () => {
|
|
232
|
+
const s = make();
|
|
233
|
+
const target = c('idea', 'any content');
|
|
234
|
+
expect(s.findSimilar(target, [])).toHaveLength(0);
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
describe('ContributionSynthesizer — metadata', () => {
|
|
239
|
+
it('keyThemes extracts top words', () => {
|
|
240
|
+
const s = make();
|
|
241
|
+
const sess = session([
|
|
242
|
+
c('idea', 'redis caching redis performance redis'),
|
|
243
|
+
c('solution', 'redis cluster redis'),
|
|
244
|
+
]);
|
|
245
|
+
const r = s.synthesize(sess);
|
|
246
|
+
expect(r.metadata.keyThemes).toContain('redis');
|
|
247
|
+
});
|
|
248
|
+
it('averageConfidence correct', () => {
|
|
249
|
+
const s = make();
|
|
250
|
+
const sess = session([c('idea', 'x', 0.6), c('idea', 'y', 0.8)]);
|
|
251
|
+
const r = s.synthesize(sess);
|
|
252
|
+
expect(r.metadata.averageConfidence).toBeCloseTo(0.7, 5);
|
|
253
|
+
});
|
|
254
|
+
it('type counts correct', () => {
|
|
255
|
+
const s = make();
|
|
256
|
+
const sess = session([
|
|
257
|
+
c('idea', 'a'),
|
|
258
|
+
c('idea', 'b'),
|
|
259
|
+
c('critique', 'c'),
|
|
260
|
+
c('solution', 'd'),
|
|
261
|
+
c('consensus', 'e'),
|
|
262
|
+
]);
|
|
263
|
+
const r = s.synthesize(sess);
|
|
264
|
+
expect(r.metadata.ideaCount).toBe(2);
|
|
265
|
+
expect(r.metadata.critiqueCount).toBe(1);
|
|
266
|
+
expect(r.metadata.solutionCount).toBe(1);
|
|
267
|
+
expect(r.metadata.consensusCount).toBe(1);
|
|
268
|
+
});
|
|
269
|
+
});
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ContributionSynthesizer Tests
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
6
|
+
import { ContributionSynthesizer } from '../ContributionSynthesizer';
|
|
7
|
+
import type { IHiveContribution, IHiveSession } from '@holoscript/core';
|
|
8
|
+
|
|
9
|
+
describe('ContributionSynthesizer', () => {
|
|
10
|
+
let synthesizer: ContributionSynthesizer;
|
|
11
|
+
|
|
12
|
+
const makeContribution = (
|
|
13
|
+
id: string,
|
|
14
|
+
type: IHiveContribution['type'],
|
|
15
|
+
content: string,
|
|
16
|
+
confidence = 0.8
|
|
17
|
+
): IHiveContribution => ({
|
|
18
|
+
id,
|
|
19
|
+
agentId: `agent-${id}`,
|
|
20
|
+
timestamp: Date.now(),
|
|
21
|
+
type,
|
|
22
|
+
content,
|
|
23
|
+
confidence,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const makeSession = (contributions: IHiveContribution[]): IHiveSession => ({
|
|
27
|
+
id: 'test-session',
|
|
28
|
+
topic: 'Test Topic',
|
|
29
|
+
goal: 'Test Goal',
|
|
30
|
+
initiator: 'initiator',
|
|
31
|
+
status: 'active',
|
|
32
|
+
participants: ['initiator', 'agent-1', 'agent-2'],
|
|
33
|
+
contributions,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
beforeEach(() => {
|
|
37
|
+
synthesizer = new ContributionSynthesizer();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
describe('synthesize', () => {
|
|
41
|
+
it('should return empty result for no contributions', () => {
|
|
42
|
+
const session = makeSession([]);
|
|
43
|
+
const result = synthesizer.synthesize(session);
|
|
44
|
+
|
|
45
|
+
expect(result.synthesizedContent).toBe('');
|
|
46
|
+
expect(result.sourceContributions).toHaveLength(0);
|
|
47
|
+
expect(result.confidence).toBe(0);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should synthesize ideas using merge method', () => {
|
|
51
|
+
const session = makeSession([
|
|
52
|
+
makeContribution('c1', 'idea', 'Use distributed approach'),
|
|
53
|
+
makeContribution('c2', 'idea', 'Implement caching layer'),
|
|
54
|
+
]);
|
|
55
|
+
|
|
56
|
+
const result = synthesizer.synthesize(session);
|
|
57
|
+
|
|
58
|
+
expect(result.synthesizedContent).toContain('Use distributed approach');
|
|
59
|
+
expect(result.synthesizedContent).toContain('Implement caching layer');
|
|
60
|
+
expect(result.sourceContributions).toContain('c1');
|
|
61
|
+
expect(result.sourceContributions).toContain('c2');
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('should use weighted method for many ideas', () => {
|
|
65
|
+
const session = makeSession([
|
|
66
|
+
makeContribution('c1', 'idea', 'Idea one', 0.9),
|
|
67
|
+
makeContribution('c2', 'idea', 'Idea two', 0.7),
|
|
68
|
+
makeContribution('c3', 'idea', 'Idea three', 0.5),
|
|
69
|
+
]);
|
|
70
|
+
|
|
71
|
+
const result = synthesizer.synthesize(session);
|
|
72
|
+
|
|
73
|
+
expect(result.synthesisMethod).toBe('weighted');
|
|
74
|
+
expect(result.synthesizedContent).toContain('Weighted Synthesis');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should use hierarchical method when solutions present', () => {
|
|
78
|
+
const session = makeSession([
|
|
79
|
+
makeContribution('c1', 'solution', 'Final solution', 0.9),
|
|
80
|
+
makeContribution('c2', 'idea', 'Supporting idea', 0.7),
|
|
81
|
+
makeContribution('c3', 'consensus', 'Agreed point', 0.8),
|
|
82
|
+
]);
|
|
83
|
+
|
|
84
|
+
const result = synthesizer.synthesize(session);
|
|
85
|
+
|
|
86
|
+
expect(result.synthesisMethod).toBe('hierarchical');
|
|
87
|
+
expect(result.synthesizedContent).toContain('Solutions');
|
|
88
|
+
expect(result.metadata.solutionCount).toBe(1);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('should use consensus method when consensus items present', () => {
|
|
92
|
+
synthesizer = new ContributionSynthesizer({ preferSolutions: false });
|
|
93
|
+
const session = makeSession([
|
|
94
|
+
makeContribution('c1', 'consensus', 'We agree on this', 0.9),
|
|
95
|
+
makeContribution('c2', 'idea', 'Related idea', 0.7),
|
|
96
|
+
]);
|
|
97
|
+
|
|
98
|
+
const result = synthesizer.synthesize(session);
|
|
99
|
+
|
|
100
|
+
expect(result.synthesisMethod).toBe('consensus');
|
|
101
|
+
expect(result.synthesizedContent).toContain('Collective Consensus');
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('should filter by confidence threshold', () => {
|
|
105
|
+
synthesizer = new ContributionSynthesizer({ minConfidenceThreshold: 0.6 });
|
|
106
|
+
const session = makeSession([
|
|
107
|
+
makeContribution('c1', 'idea', 'High confidence idea', 0.9),
|
|
108
|
+
makeContribution('c2', 'idea', 'Low confidence idea', 0.3),
|
|
109
|
+
]);
|
|
110
|
+
|
|
111
|
+
const result = synthesizer.synthesize(session);
|
|
112
|
+
|
|
113
|
+
expect(result.sourceContributions).toContain('c1');
|
|
114
|
+
expect(result.sourceContributions).not.toContain('c2');
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('should include critiques when enabled', () => {
|
|
118
|
+
synthesizer = new ContributionSynthesizer({ includeCritiques: true });
|
|
119
|
+
const session = makeSession([
|
|
120
|
+
makeContribution('c1', 'solution', 'Main solution', 0.9),
|
|
121
|
+
makeContribution('c2', 'critique', 'Consider edge cases', 0.8),
|
|
122
|
+
]);
|
|
123
|
+
|
|
124
|
+
const result = synthesizer.synthesize(session);
|
|
125
|
+
|
|
126
|
+
expect(result.synthesizedContent).toContain('Considerations');
|
|
127
|
+
expect(result.metadata.critiqueCount).toBe(1);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('should compute metadata correctly', () => {
|
|
131
|
+
const session = makeSession([
|
|
132
|
+
makeContribution('c1', 'idea', 'Idea 1', 0.9),
|
|
133
|
+
makeContribution('c2', 'critique', 'Critique 1', 0.7),
|
|
134
|
+
makeContribution('c3', 'solution', 'Solution 1', 0.8),
|
|
135
|
+
makeContribution('c4', 'consensus', 'Consensus 1', 0.85),
|
|
136
|
+
]);
|
|
137
|
+
|
|
138
|
+
const result = synthesizer.synthesize(session);
|
|
139
|
+
|
|
140
|
+
expect(result.metadata.totalContributions).toBe(4);
|
|
141
|
+
expect(result.metadata.ideaCount).toBe(1);
|
|
142
|
+
expect(result.metadata.critiqueCount).toBe(1);
|
|
143
|
+
expect(result.metadata.solutionCount).toBe(1);
|
|
144
|
+
expect(result.metadata.consensusCount).toBe(1);
|
|
145
|
+
expect(result.metadata.averageConfidence).toBeCloseTo(0.8125, 2);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('should extract key themes', () => {
|
|
149
|
+
const session = makeSession([
|
|
150
|
+
makeContribution('c1', 'idea', 'Use distributed caching for performance', 0.9),
|
|
151
|
+
makeContribution('c2', 'idea', 'Distributed systems need caching', 0.8),
|
|
152
|
+
makeContribution('c3', 'idea', 'Performance is key for distributed apps', 0.7),
|
|
153
|
+
]);
|
|
154
|
+
|
|
155
|
+
const result = synthesizer.synthesize(session);
|
|
156
|
+
|
|
157
|
+
expect(result.metadata.keyThemes.length).toBeGreaterThan(0);
|
|
158
|
+
expect(result.metadata.keyThemes).toContain('distributed');
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('should respect maxSourcesPerSynthesis', () => {
|
|
162
|
+
synthesizer = new ContributionSynthesizer({ maxSourcesPerSynthesis: 2 });
|
|
163
|
+
const session = makeSession([
|
|
164
|
+
makeContribution('c1', 'idea', 'Idea 1', 0.9),
|
|
165
|
+
makeContribution('c2', 'idea', 'Idea 2', 0.8),
|
|
166
|
+
makeContribution('c3', 'idea', 'Idea 3', 0.7),
|
|
167
|
+
makeContribution('c4', 'idea', 'Idea 4', 0.6),
|
|
168
|
+
]);
|
|
169
|
+
|
|
170
|
+
const result = synthesizer.synthesize(session);
|
|
171
|
+
|
|
172
|
+
expect(result.sourceContributions.length).toBeLessThanOrEqual(2);
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
describe('synthesizeSubset', () => {
|
|
177
|
+
it('should synthesize a subset of contributions', () => {
|
|
178
|
+
const contributions = [
|
|
179
|
+
makeContribution('c1', 'idea', 'Idea one', 0.9),
|
|
180
|
+
makeContribution('c2', 'idea', 'Idea two', 0.8),
|
|
181
|
+
];
|
|
182
|
+
|
|
183
|
+
const result = synthesizer.synthesizeSubset(contributions);
|
|
184
|
+
|
|
185
|
+
expect(result.sourceContributions).toHaveLength(2);
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
describe('merge', () => {
|
|
190
|
+
it('should merge two contributions', () => {
|
|
191
|
+
const a = makeContribution('c1', 'idea', 'First idea', 0.8);
|
|
192
|
+
const b = makeContribution('c2', 'idea', 'Second idea', 0.9);
|
|
193
|
+
|
|
194
|
+
const merged = synthesizer.merge(a, b);
|
|
195
|
+
|
|
196
|
+
expect(merged.id).toContain('merged');
|
|
197
|
+
expect(merged.content).toContain('First idea');
|
|
198
|
+
expect(merged.content).toContain('Second idea');
|
|
199
|
+
expect(merged.agentId).toBe('synthesizer');
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it('should take higher type in hierarchy', () => {
|
|
203
|
+
const idea = makeContribution('c1', 'idea', 'An idea', 0.8);
|
|
204
|
+
const solution = makeContribution('c2', 'solution', 'A solution', 0.9);
|
|
205
|
+
|
|
206
|
+
const merged = synthesizer.merge(idea, solution);
|
|
207
|
+
expect(merged.type).toBe('solution');
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it('should average and boost confidence', () => {
|
|
211
|
+
const a = makeContribution('c1', 'idea', 'First', 0.6);
|
|
212
|
+
const b = makeContribution('c2', 'idea', 'Second', 0.8);
|
|
213
|
+
|
|
214
|
+
const merged = synthesizer.merge(a, b);
|
|
215
|
+
|
|
216
|
+
// (0.6 + 0.8) / 2 + 0.05 = 0.75
|
|
217
|
+
expect(merged.confidence).toBeCloseTo(0.75, 2);
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
describe('findSimilar', () => {
|
|
222
|
+
it('should find similar contributions', () => {
|
|
223
|
+
const target = makeContribution('t', 'idea', 'Implement distributed caching layer');
|
|
224
|
+
const candidates = [
|
|
225
|
+
makeContribution('c1', 'idea', 'Use distributed cache for performance'),
|
|
226
|
+
makeContribution('c2', 'idea', 'Something completely different about databases'),
|
|
227
|
+
makeContribution('c3', 'idea', 'Caching distributed systems architecture'),
|
|
228
|
+
];
|
|
229
|
+
|
|
230
|
+
const similar = synthesizer.findSimilar(target, candidates, 0.3);
|
|
231
|
+
|
|
232
|
+
expect(similar.length).toBeGreaterThanOrEqual(1);
|
|
233
|
+
expect(similar.some((c) => c.id === 'c1' || c.id === 'c3')).toBe(true);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it('should not include target in results', () => {
|
|
237
|
+
const target = makeContribution('t', 'idea', 'Test content');
|
|
238
|
+
const candidates = [target, makeContribution('c1', 'idea', 'Test content identical')];
|
|
239
|
+
|
|
240
|
+
const similar = synthesizer.findSimilar(target, candidates);
|
|
241
|
+
|
|
242
|
+
expect(similar.find((c) => c.id === 't')).toBeUndefined();
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it('should return empty array for no matches', () => {
|
|
246
|
+
const target = makeContribution('t', 'idea', 'Alpha beta gamma');
|
|
247
|
+
const candidates = [makeContribution('c1', 'idea', 'Omega theta zeta')];
|
|
248
|
+
|
|
249
|
+
const similar = synthesizer.findSimilar(target, candidates, 0.9);
|
|
250
|
+
|
|
251
|
+
expect(similar).toHaveLength(0);
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
});
|