@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,864 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Registry Module Tests
|
|
3
|
+
* Sprint 4 Priority 1 - Agent Registry & Discovery
|
|
4
|
+
*
|
|
5
|
+
* Tests for AgentRegistry, AgentManifest, and CapabilityMatcher.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
9
|
+
import {
|
|
10
|
+
AgentRegistry,
|
|
11
|
+
DEFAULT_REGISTRY_CONFIG,
|
|
12
|
+
getDefaultRegistry,
|
|
13
|
+
resetDefaultRegistry,
|
|
14
|
+
} from '../AgentRegistry';
|
|
15
|
+
import {
|
|
16
|
+
createManifest,
|
|
17
|
+
validateManifest,
|
|
18
|
+
AgentManifest,
|
|
19
|
+
AgentManifestBuilder,
|
|
20
|
+
AgentCapability,
|
|
21
|
+
AgentEndpoint,
|
|
22
|
+
} from '../AgentManifest';
|
|
23
|
+
import { CapabilityMatcher, CapabilityQuery, AgentMatch } from '../CapabilityMatcher';
|
|
24
|
+
import { PHASE_ORDER, DEFAULT_PHASE_TIMINGS, AgentPhase } from '../AgentTypes';
|
|
25
|
+
|
|
26
|
+
// Helper to create test manifests quickly
|
|
27
|
+
function createTestManifest(
|
|
28
|
+
id: string,
|
|
29
|
+
capabilities: Array<{ type: string; domain: string }> = [{ type: 'analyze', domain: 'general' }],
|
|
30
|
+
options: Partial<AgentManifest> = {}
|
|
31
|
+
): AgentManifest {
|
|
32
|
+
return {
|
|
33
|
+
id,
|
|
34
|
+
name: options.name || `Test Agent ${id}`,
|
|
35
|
+
version: options.version || '1.0.0',
|
|
36
|
+
capabilities: capabilities.map((c, i) => ({
|
|
37
|
+
type: c.type,
|
|
38
|
+
domain: c.domain,
|
|
39
|
+
id: `${id}-cap-${i}`,
|
|
40
|
+
name: `Capability ${i}`,
|
|
41
|
+
})) as AgentCapability[],
|
|
42
|
+
endpoints: options.endpoints || [{ protocol: 'local' as const, address: 'internal' }],
|
|
43
|
+
trustLevel: options.trustLevel || 'local',
|
|
44
|
+
status: options.status || 'online',
|
|
45
|
+
...options,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// =============================================================================
|
|
50
|
+
// AGENT TYPES TESTS
|
|
51
|
+
// =============================================================================
|
|
52
|
+
|
|
53
|
+
describe('AgentTypes', () => {
|
|
54
|
+
describe('PHASE_ORDER', () => {
|
|
55
|
+
it('should have 7 phases', () => {
|
|
56
|
+
expect(PHASE_ORDER).toHaveLength(7);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('should start with INTAKE', () => {
|
|
60
|
+
expect(PHASE_ORDER[0]).toBe('INTAKE');
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('should end with EVOLVE', () => {
|
|
64
|
+
expect(PHASE_ORDER[6]).toBe('EVOLVE');
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('should have correct phase sequence', () => {
|
|
68
|
+
expect(PHASE_ORDER).toEqual([
|
|
69
|
+
'INTAKE',
|
|
70
|
+
'REFLECT',
|
|
71
|
+
'EXECUTE',
|
|
72
|
+
'COMPRESS',
|
|
73
|
+
'REINTAKE',
|
|
74
|
+
'GROW',
|
|
75
|
+
'EVOLVE',
|
|
76
|
+
]);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
describe('DEFAULT_PHASE_TIMINGS', () => {
|
|
81
|
+
it('should have timings for all phases', () => {
|
|
82
|
+
for (const phase of PHASE_ORDER) {
|
|
83
|
+
expect(DEFAULT_PHASE_TIMINGS[phase]).toBeGreaterThan(0);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('should have reasonable timing values', () => {
|
|
88
|
+
expect(DEFAULT_PHASE_TIMINGS.INTAKE).toBeLessThanOrEqual(5000);
|
|
89
|
+
expect(DEFAULT_PHASE_TIMINGS.EXECUTE).toBeGreaterThanOrEqual(1000);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// =============================================================================
|
|
95
|
+
// AGENT MANIFEST TESTS
|
|
96
|
+
// =============================================================================
|
|
97
|
+
|
|
98
|
+
describe('AgentManifest', () => {
|
|
99
|
+
describe('AgentManifestBuilder', () => {
|
|
100
|
+
it('should create a builder', () => {
|
|
101
|
+
const builder = createManifest();
|
|
102
|
+
expect(builder).toBeInstanceOf(AgentManifestBuilder);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('should build a valid manifest with fluent API', () => {
|
|
106
|
+
const manifest = createManifest()
|
|
107
|
+
.identity('test-agent', 'Test Agent', '1.0.0')
|
|
108
|
+
.description('A test agent')
|
|
109
|
+
.addCapability({
|
|
110
|
+
type: 'analyze',
|
|
111
|
+
domain: 'general',
|
|
112
|
+
id: 'cap-1',
|
|
113
|
+
name: 'Test Capability',
|
|
114
|
+
})
|
|
115
|
+
.addEndpoint({
|
|
116
|
+
protocol: 'local',
|
|
117
|
+
address: 'internal',
|
|
118
|
+
})
|
|
119
|
+
.trust('local')
|
|
120
|
+
.build();
|
|
121
|
+
|
|
122
|
+
expect(manifest.id).toBe('test-agent');
|
|
123
|
+
expect(manifest.name).toBe('Test Agent');
|
|
124
|
+
expect(manifest.version).toBe('1.0.0');
|
|
125
|
+
expect(manifest.capabilities).toHaveLength(1);
|
|
126
|
+
expect(manifest.endpoints).toHaveLength(1);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('should throw when building without id', () => {
|
|
130
|
+
expect(() => {
|
|
131
|
+
createManifest()
|
|
132
|
+
.addCapability({ type: 'analyze', domain: 'general' })
|
|
133
|
+
.addEndpoint({ protocol: 'local', address: 'local' })
|
|
134
|
+
.build();
|
|
135
|
+
}).toThrow();
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('should throw when building without capabilities', () => {
|
|
139
|
+
expect(() => {
|
|
140
|
+
createManifest()
|
|
141
|
+
.identity('agent', 'Agent', '1.0.0')
|
|
142
|
+
.addEndpoint({ protocol: 'local', address: 'local' })
|
|
143
|
+
.build();
|
|
144
|
+
}).toThrow();
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('should throw when building without endpoints', () => {
|
|
148
|
+
expect(() => {
|
|
149
|
+
createManifest()
|
|
150
|
+
.identity('agent', 'Agent', '1.0.0')
|
|
151
|
+
.addCapability({ type: 'analyze', domain: 'general' })
|
|
152
|
+
.build();
|
|
153
|
+
}).toThrow();
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('should add multiple capabilities', () => {
|
|
157
|
+
const manifest = createManifest()
|
|
158
|
+
.identity('multi', 'Multi', '1.0.0')
|
|
159
|
+
.addCapabilities([
|
|
160
|
+
{ type: 'analyze', domain: 'general' },
|
|
161
|
+
{ type: 'generate', domain: 'nlp' },
|
|
162
|
+
])
|
|
163
|
+
.addEndpoint({ protocol: 'local', address: 'local' })
|
|
164
|
+
.build();
|
|
165
|
+
|
|
166
|
+
expect(manifest.capabilities).toHaveLength(2);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('should add tags', () => {
|
|
170
|
+
const manifest = createManifest()
|
|
171
|
+
.identity('tagged', 'Tagged', '1.0.0')
|
|
172
|
+
.addCapability({ type: 'analyze', domain: 'general' })
|
|
173
|
+
.addEndpoint({ protocol: 'local', address: 'local' })
|
|
174
|
+
.tags('ml', 'python')
|
|
175
|
+
.build();
|
|
176
|
+
|
|
177
|
+
expect(manifest.tags).toContain('ml');
|
|
178
|
+
expect(manifest.tags).toContain('python');
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
describe('validateManifest', () => {
|
|
183
|
+
it('should validate valid manifest', () => {
|
|
184
|
+
const manifest = createTestManifest('valid-agent');
|
|
185
|
+
const result = validateManifest(manifest);
|
|
186
|
+
|
|
187
|
+
expect(result.valid).toBe(true);
|
|
188
|
+
expect(result.errors).toHaveLength(0);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it('should reject manifest without id', () => {
|
|
192
|
+
const manifest = {
|
|
193
|
+
name: 'No ID Agent',
|
|
194
|
+
version: '1.0.0',
|
|
195
|
+
capabilities: [{ type: 'analyze', domain: 'general' }],
|
|
196
|
+
endpoints: [{ protocol: 'local', address: 'local' }],
|
|
197
|
+
} as unknown as AgentManifest;
|
|
198
|
+
|
|
199
|
+
const result = validateManifest(manifest);
|
|
200
|
+
|
|
201
|
+
expect(result.valid).toBe(false);
|
|
202
|
+
expect(result.errors.some((e) => e.toLowerCase().includes('id'))).toBe(true);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it('should reject manifest without capabilities', () => {
|
|
206
|
+
const manifest = {
|
|
207
|
+
id: 'agent',
|
|
208
|
+
name: 'Agent',
|
|
209
|
+
version: '1.0.0',
|
|
210
|
+
capabilities: [],
|
|
211
|
+
endpoints: [{ protocol: 'local', address: 'local' }],
|
|
212
|
+
} as unknown as AgentManifest;
|
|
213
|
+
|
|
214
|
+
const result = validateManifest(manifest);
|
|
215
|
+
|
|
216
|
+
expect(result.valid).toBe(false);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it('should reject manifest without endpoints', () => {
|
|
220
|
+
const manifest = {
|
|
221
|
+
id: 'agent',
|
|
222
|
+
name: 'Agent',
|
|
223
|
+
version: '1.0.0',
|
|
224
|
+
capabilities: [{ type: 'analyze', domain: 'general' }],
|
|
225
|
+
endpoints: [],
|
|
226
|
+
} as unknown as AgentManifest;
|
|
227
|
+
|
|
228
|
+
const result = validateManifest(manifest);
|
|
229
|
+
|
|
230
|
+
expect(result.valid).toBe(false);
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it('should validate capabilities have type and domain', () => {
|
|
234
|
+
const manifest = {
|
|
235
|
+
id: 'agent',
|
|
236
|
+
name: 'Agent',
|
|
237
|
+
version: '1.0.0',
|
|
238
|
+
capabilities: [{ id: 'test' }], // Missing type and domain
|
|
239
|
+
endpoints: [{ protocol: 'local', address: 'local' }],
|
|
240
|
+
} as unknown as AgentManifest;
|
|
241
|
+
|
|
242
|
+
const result = validateManifest(manifest);
|
|
243
|
+
|
|
244
|
+
expect(result.valid).toBe(false);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it('should validate endpoints have protocol and address', () => {
|
|
248
|
+
const manifest = {
|
|
249
|
+
id: 'agent',
|
|
250
|
+
name: 'Agent',
|
|
251
|
+
version: '1.0.0',
|
|
252
|
+
capabilities: [{ type: 'analyze', domain: 'general' }],
|
|
253
|
+
endpoints: [{ port: 8080 }], // Missing protocol and address
|
|
254
|
+
} as unknown as AgentManifest;
|
|
255
|
+
|
|
256
|
+
const result = validateManifest(manifest);
|
|
257
|
+
|
|
258
|
+
expect(result.valid).toBe(false);
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
it('should generate warnings for missing description', () => {
|
|
262
|
+
const manifest = createTestManifest('no-desc');
|
|
263
|
+
delete manifest.description;
|
|
264
|
+
|
|
265
|
+
const result = validateManifest(manifest);
|
|
266
|
+
|
|
267
|
+
expect(result.warnings.length).toBeGreaterThan(0);
|
|
268
|
+
expect(result.warnings.some((w) => w.toLowerCase().includes('description'))).toBe(true);
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
// =============================================================================
|
|
274
|
+
// CAPABILITY MATCHER TESTS
|
|
275
|
+
// =============================================================================
|
|
276
|
+
|
|
277
|
+
describe('CapabilityMatcher', () => {
|
|
278
|
+
let matcher: CapabilityMatcher;
|
|
279
|
+
let manifests: AgentManifest[];
|
|
280
|
+
|
|
281
|
+
beforeEach(() => {
|
|
282
|
+
matcher = new CapabilityMatcher();
|
|
283
|
+
manifests = [];
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
describe('Capability Matching', () => {
|
|
287
|
+
it('should match capability by type', () => {
|
|
288
|
+
const capability: AgentCapability = {
|
|
289
|
+
type: 'analyze',
|
|
290
|
+
domain: 'general',
|
|
291
|
+
id: 'cap-1',
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
const query: CapabilityQuery = { type: 'analyze' };
|
|
295
|
+
const match = matcher.matchCapability(capability, query);
|
|
296
|
+
|
|
297
|
+
expect(match).not.toBeNull();
|
|
298
|
+
expect(match?.matchedCriteria).toContain('type');
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
it('should match capability by domain', () => {
|
|
302
|
+
const capability: AgentCapability = {
|
|
303
|
+
type: 'generate',
|
|
304
|
+
domain: 'nlp',
|
|
305
|
+
id: 'cap-1',
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
const query: CapabilityQuery = { domain: 'nlp' };
|
|
309
|
+
const match = matcher.matchCapability(capability, query);
|
|
310
|
+
|
|
311
|
+
expect(match).not.toBeNull();
|
|
312
|
+
expect(match?.matchedCriteria).toContain('domain');
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
it('should return null for non-matching type', () => {
|
|
316
|
+
const capability: AgentCapability = {
|
|
317
|
+
type: 'analyze',
|
|
318
|
+
domain: 'general',
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
const query: CapabilityQuery = { type: 'generate' };
|
|
322
|
+
const match = matcher.matchCapability(capability, query);
|
|
323
|
+
|
|
324
|
+
expect(match).toBeNull();
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
it('should return null for unavailable capability', () => {
|
|
328
|
+
const capability: AgentCapability = {
|
|
329
|
+
type: 'analyze',
|
|
330
|
+
domain: 'general',
|
|
331
|
+
available: false,
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
const query: CapabilityQuery = { type: 'analyze' };
|
|
335
|
+
const match = matcher.matchCapability(capability, query);
|
|
336
|
+
|
|
337
|
+
expect(match).toBeNull();
|
|
338
|
+
});
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
describe('Agent Matching', () => {
|
|
342
|
+
beforeEach(() => {
|
|
343
|
+
manifests = [
|
|
344
|
+
createTestManifest('coder', [
|
|
345
|
+
{ type: 'generate', domain: 'general' },
|
|
346
|
+
{ type: 'analyze', domain: 'general' },
|
|
347
|
+
]),
|
|
348
|
+
createTestManifest('analyst', [
|
|
349
|
+
{ type: 'analyze', domain: 'general' },
|
|
350
|
+
{ type: 'render', domain: 'general' },
|
|
351
|
+
]),
|
|
352
|
+
createTestManifest('ml-agent', [
|
|
353
|
+
{ type: 'analyze', domain: 'vision' },
|
|
354
|
+
{ type: 'detect', domain: 'vision' },
|
|
355
|
+
]),
|
|
356
|
+
];
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
it('should match agent by capability type', () => {
|
|
360
|
+
const query: CapabilityQuery = { type: 'generate' };
|
|
361
|
+
const match = matcher.matchAgent(manifests[0], query);
|
|
362
|
+
|
|
363
|
+
expect(match).not.toBeNull();
|
|
364
|
+
expect(match?.manifest.id).toBe('coder');
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
it('should return null for offline agents', () => {
|
|
368
|
+
const offlineManifest = createTestManifest('offline', [], { status: 'offline' });
|
|
369
|
+
const query: CapabilityQuery = { type: 'analyze' };
|
|
370
|
+
const match = matcher.matchAgent(offlineManifest, query);
|
|
371
|
+
|
|
372
|
+
expect(match).toBeNull();
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
it('should include offline agents with includeOffline', () => {
|
|
376
|
+
const offlineManifest = createTestManifest(
|
|
377
|
+
'offline',
|
|
378
|
+
[{ type: 'analyze', domain: 'general' }],
|
|
379
|
+
{ status: 'offline' }
|
|
380
|
+
);
|
|
381
|
+
const query: CapabilityQuery = { type: 'analyze', includeOffline: true };
|
|
382
|
+
const match = matcher.matchAgent(offlineManifest, query);
|
|
383
|
+
|
|
384
|
+
expect(match).not.toBeNull();
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
it('should filter by min trust level', () => {
|
|
388
|
+
const externalManifest = createTestManifest(
|
|
389
|
+
'external',
|
|
390
|
+
[{ type: 'analyze', domain: 'general' }],
|
|
391
|
+
{ trustLevel: 'external' }
|
|
392
|
+
);
|
|
393
|
+
const query: CapabilityQuery = { minTrust: 'verified' };
|
|
394
|
+
const match = matcher.matchAgent(externalManifest, query);
|
|
395
|
+
|
|
396
|
+
expect(match).toBeNull();
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
it('should match with tags filter', () => {
|
|
400
|
+
const taggedManifest = createTestManifest(
|
|
401
|
+
'tagged',
|
|
402
|
+
[{ type: 'analyze', domain: 'general' }],
|
|
403
|
+
{ tags: ['ml', 'python'] }
|
|
404
|
+
);
|
|
405
|
+
const query: CapabilityQuery = { tags: ['ml'] };
|
|
406
|
+
const match = matcher.matchAgent(taggedManifest, query);
|
|
407
|
+
|
|
408
|
+
expect(match).not.toBeNull();
|
|
409
|
+
});
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
describe('findMatches', () => {
|
|
413
|
+
beforeEach(() => {
|
|
414
|
+
manifests = [
|
|
415
|
+
createTestManifest('agent-1', [{ type: 'analyze', domain: 'general' }]),
|
|
416
|
+
createTestManifest('agent-2', [{ type: 'generate', domain: 'nlp' }]),
|
|
417
|
+
createTestManifest('agent-3', [{ type: 'analyze', domain: 'vision' }]),
|
|
418
|
+
];
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
it('should find all matching agents', () => {
|
|
422
|
+
const query: CapabilityQuery = { type: 'analyze' };
|
|
423
|
+
const matches = matcher.findMatches(manifests, query);
|
|
424
|
+
|
|
425
|
+
expect(matches).toHaveLength(2);
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
it('should return empty array when no matches', () => {
|
|
429
|
+
const query: CapabilityQuery = { type: 'orchestrate' };
|
|
430
|
+
const matches = matcher.findMatches(manifests, query);
|
|
431
|
+
|
|
432
|
+
expect(matches).toHaveLength(0);
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
it('should return matches with scores', () => {
|
|
436
|
+
const query: CapabilityQuery = { type: 'analyze' };
|
|
437
|
+
const matches = matcher.findMatches(manifests, query);
|
|
438
|
+
|
|
439
|
+
for (const match of matches) {
|
|
440
|
+
expect(match.score).toBeGreaterThanOrEqual(0);
|
|
441
|
+
expect(match.score).toBeLessThanOrEqual(1);
|
|
442
|
+
}
|
|
443
|
+
});
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
describe('findBest', () => {
|
|
447
|
+
beforeEach(() => {
|
|
448
|
+
manifests = [
|
|
449
|
+
createTestManifest('agent-a', [{ type: 'analyze', domain: 'general' }], {}),
|
|
450
|
+
createTestManifest('agent-b', [{ type: 'analyze', domain: 'vision' }], {}),
|
|
451
|
+
];
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
it('should return a matching agent', () => {
|
|
455
|
+
const query: CapabilityQuery = { type: 'analyze' };
|
|
456
|
+
const best = matcher.findBest(manifests, query);
|
|
457
|
+
|
|
458
|
+
expect(best).not.toBeNull();
|
|
459
|
+
expect(best?.manifest.id).toMatch(/agent-[ab]/);
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
it('should respect limit of 1', () => {
|
|
463
|
+
const query: CapabilityQuery = { type: 'analyze' };
|
|
464
|
+
const best = matcher.findBest(manifests, query);
|
|
465
|
+
|
|
466
|
+
// Should only return one match
|
|
467
|
+
expect(best).not.toBeNull();
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
it('should return null when no matches', () => {
|
|
471
|
+
const query: CapabilityQuery = { type: 'quantum' };
|
|
472
|
+
const best = matcher.findBest(manifests, query);
|
|
473
|
+
|
|
474
|
+
expect(best).toBeNull();
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
it('should sort by name when specified', () => {
|
|
478
|
+
const query: CapabilityQuery = { type: 'analyze', sortBy: 'name', sortOrder: 'asc' };
|
|
479
|
+
const best = matcher.findBest(manifests, query);
|
|
480
|
+
|
|
481
|
+
expect(best).not.toBeNull();
|
|
482
|
+
// agent-a comes before agent-b alphabetically
|
|
483
|
+
expect(best?.manifest.id).toBe('agent-a');
|
|
484
|
+
});
|
|
485
|
+
});
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
// =============================================================================
|
|
489
|
+
// AGENT REGISTRY TESTS
|
|
490
|
+
// =============================================================================
|
|
491
|
+
|
|
492
|
+
describe('AgentRegistry', () => {
|
|
493
|
+
let registry: AgentRegistry;
|
|
494
|
+
|
|
495
|
+
beforeEach(() => {
|
|
496
|
+
registry = new AgentRegistry({
|
|
497
|
+
autoCleanup: false, // Disable timers for testing
|
|
498
|
+
});
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
afterEach(() => {
|
|
502
|
+
registry.stop();
|
|
503
|
+
registry.clear();
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
describe('Configuration', () => {
|
|
507
|
+
it('should use default config', () => {
|
|
508
|
+
const reg = new AgentRegistry();
|
|
509
|
+
expect(reg).toBeDefined();
|
|
510
|
+
reg.stop();
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
it('should accept custom config', () => {
|
|
514
|
+
const reg = new AgentRegistry({
|
|
515
|
+
maxAgents: 50,
|
|
516
|
+
defaultTTL: 10000,
|
|
517
|
+
});
|
|
518
|
+
expect(reg).toBeDefined();
|
|
519
|
+
reg.stop();
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
it('should have default registry config values', () => {
|
|
523
|
+
expect(DEFAULT_REGISTRY_CONFIG).toBeDefined();
|
|
524
|
+
expect(DEFAULT_REGISTRY_CONFIG.maxAgents).toBeGreaterThan(0);
|
|
525
|
+
});
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
describe('Registration', () => {
|
|
529
|
+
it('should register an agent', async () => {
|
|
530
|
+
const manifest = createTestManifest('test-agent', [{ type: 'analyze', domain: 'general' }]);
|
|
531
|
+
|
|
532
|
+
await registry.register(manifest);
|
|
533
|
+
|
|
534
|
+
expect(registry.has('test-agent')).toBe(true);
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
it('should emit agent:registered event', async () => {
|
|
538
|
+
const listener = vi.fn();
|
|
539
|
+
registry.on('agent:registered', listener);
|
|
540
|
+
|
|
541
|
+
const manifest = createTestManifest('event-agent', [{ type: 'analyze', domain: 'general' }]);
|
|
542
|
+
|
|
543
|
+
await registry.register(manifest);
|
|
544
|
+
|
|
545
|
+
expect(listener).toHaveBeenCalled();
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
it('should throw for invalid manifest', async () => {
|
|
549
|
+
const invalid = {
|
|
550
|
+
name: 'No ID',
|
|
551
|
+
version: '1.0.0',
|
|
552
|
+
} as unknown as AgentManifest;
|
|
553
|
+
|
|
554
|
+
await expect(registry.register(invalid)).rejects.toThrow();
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
it('should update existing agent on re-register', async () => {
|
|
558
|
+
const manifest = createTestManifest('update-agent', [{ type: 'analyze', domain: 'general' }]);
|
|
559
|
+
|
|
560
|
+
await registry.register(manifest);
|
|
561
|
+
|
|
562
|
+
// Update with new capability
|
|
563
|
+
const updated = createTestManifest('update-agent', [
|
|
564
|
+
{ type: 'analyze', domain: 'general' },
|
|
565
|
+
{ type: 'generate', domain: 'nlp' },
|
|
566
|
+
]);
|
|
567
|
+
|
|
568
|
+
const listener = vi.fn();
|
|
569
|
+
registry.on('agent:updated', listener);
|
|
570
|
+
|
|
571
|
+
await registry.register(updated);
|
|
572
|
+
|
|
573
|
+
expect(listener).toHaveBeenCalled();
|
|
574
|
+
});
|
|
575
|
+
|
|
576
|
+
it('should enforce max agents limit', async () => {
|
|
577
|
+
const smallRegistry = new AgentRegistry({
|
|
578
|
+
maxAgents: 2,
|
|
579
|
+
autoCleanup: false,
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
await smallRegistry.register(
|
|
583
|
+
createTestManifest('agent-1', [{ type: 'analyze', domain: 'general' }])
|
|
584
|
+
);
|
|
585
|
+
|
|
586
|
+
await smallRegistry.register(
|
|
587
|
+
createTestManifest('agent-2', [{ type: 'analyze', domain: 'general' }])
|
|
588
|
+
);
|
|
589
|
+
|
|
590
|
+
await expect(
|
|
591
|
+
smallRegistry.register(
|
|
592
|
+
createTestManifest('agent-3', [{ type: 'analyze', domain: 'general' }])
|
|
593
|
+
)
|
|
594
|
+
).rejects.toThrow(/full|maximum/i);
|
|
595
|
+
|
|
596
|
+
smallRegistry.stop();
|
|
597
|
+
});
|
|
598
|
+
});
|
|
599
|
+
|
|
600
|
+
describe('Deregistration', () => {
|
|
601
|
+
it('should deregister an agent', async () => {
|
|
602
|
+
const manifest = createTestManifest('to-remove', [{ type: 'analyze', domain: 'general' }]);
|
|
603
|
+
|
|
604
|
+
await registry.register(manifest);
|
|
605
|
+
await registry.deregister('to-remove');
|
|
606
|
+
|
|
607
|
+
expect(registry.has('to-remove')).toBe(false);
|
|
608
|
+
});
|
|
609
|
+
|
|
610
|
+
it('should emit agent:deregistered event', async () => {
|
|
611
|
+
const listener = vi.fn();
|
|
612
|
+
registry.on('agent:deregistered', listener);
|
|
613
|
+
|
|
614
|
+
const manifest = createTestManifest('deregister-event', [
|
|
615
|
+
{ type: 'analyze', domain: 'general' },
|
|
616
|
+
]);
|
|
617
|
+
|
|
618
|
+
await registry.register(manifest);
|
|
619
|
+
await registry.deregister('deregister-event');
|
|
620
|
+
|
|
621
|
+
expect(listener).toHaveBeenCalled();
|
|
622
|
+
});
|
|
623
|
+
});
|
|
624
|
+
|
|
625
|
+
describe('Discovery', () => {
|
|
626
|
+
beforeEach(async () => {
|
|
627
|
+
await registry.register(
|
|
628
|
+
createTestManifest('dev-agent', [
|
|
629
|
+
{ type: 'generate', domain: 'general' },
|
|
630
|
+
{ type: 'analyze', domain: 'general' },
|
|
631
|
+
])
|
|
632
|
+
);
|
|
633
|
+
|
|
634
|
+
await registry.register(
|
|
635
|
+
createTestManifest('ops-agent', [
|
|
636
|
+
{ type: 'orchestrate', domain: 'general' },
|
|
637
|
+
{ type: 'validate', domain: 'general' },
|
|
638
|
+
])
|
|
639
|
+
);
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
it('should get agent by id', () => {
|
|
643
|
+
const agent = registry.get('dev-agent');
|
|
644
|
+
|
|
645
|
+
expect(agent).toBeDefined();
|
|
646
|
+
expect(agent?.name).toContain('dev-agent');
|
|
647
|
+
});
|
|
648
|
+
|
|
649
|
+
it('should return undefined for non-existent agent', () => {
|
|
650
|
+
const agent = registry.get('ghost');
|
|
651
|
+
expect(agent).toBeUndefined();
|
|
652
|
+
});
|
|
653
|
+
|
|
654
|
+
it('should list all agents', () => {
|
|
655
|
+
const agents = registry.getAllManifests();
|
|
656
|
+
|
|
657
|
+
expect(agents).toHaveLength(2);
|
|
658
|
+
});
|
|
659
|
+
|
|
660
|
+
it('should discover agents by capability type', async () => {
|
|
661
|
+
const matches = await registry.discover({
|
|
662
|
+
type: 'generate',
|
|
663
|
+
});
|
|
664
|
+
|
|
665
|
+
expect(matches.length).toBeGreaterThanOrEqual(1);
|
|
666
|
+
expect(matches[0].id).toBe('dev-agent');
|
|
667
|
+
});
|
|
668
|
+
|
|
669
|
+
it('should find best agent for query', async () => {
|
|
670
|
+
const best = await registry.findBest({
|
|
671
|
+
type: 'orchestrate',
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
expect(best).not.toBeNull();
|
|
675
|
+
expect(best?.id).toBe('ops-agent');
|
|
676
|
+
});
|
|
677
|
+
});
|
|
678
|
+
|
|
679
|
+
describe('Heartbeat', () => {
|
|
680
|
+
it('should accept heartbeat from registered agent', async () => {
|
|
681
|
+
await registry.register(
|
|
682
|
+
createTestManifest('heartbeat-agent', [{ type: 'analyze', domain: 'general' }])
|
|
683
|
+
);
|
|
684
|
+
|
|
685
|
+
await expect(registry.heartbeat('heartbeat-agent')).resolves.not.toThrow();
|
|
686
|
+
});
|
|
687
|
+
|
|
688
|
+
it('should throw for heartbeat from unknown agent', async () => {
|
|
689
|
+
await expect(registry.heartbeat('unknown')).rejects.toThrow();
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
it('should update agent status to online', async () => {
|
|
693
|
+
await registry.register(
|
|
694
|
+
createTestManifest('status-agent', [{ type: 'analyze', domain: 'general' }], {
|
|
695
|
+
status: 'offline',
|
|
696
|
+
})
|
|
697
|
+
);
|
|
698
|
+
|
|
699
|
+
await registry.heartbeat('status-agent');
|
|
700
|
+
|
|
701
|
+
const agent = registry.get('status-agent');
|
|
702
|
+
expect(agent?.status).toBe('online');
|
|
703
|
+
});
|
|
704
|
+
});
|
|
705
|
+
|
|
706
|
+
describe('Stats & Status', () => {
|
|
707
|
+
it('should get agent count', async () => {
|
|
708
|
+
await registry.register(
|
|
709
|
+
createTestManifest('stat-agent', [{ type: 'analyze', domain: 'general' }])
|
|
710
|
+
);
|
|
711
|
+
|
|
712
|
+
expect(registry.size).toBe(1);
|
|
713
|
+
});
|
|
714
|
+
|
|
715
|
+
it('should get status counts', async () => {
|
|
716
|
+
await registry.register(
|
|
717
|
+
createTestManifest('online-agent', [{ type: 'analyze', domain: 'general' }])
|
|
718
|
+
);
|
|
719
|
+
|
|
720
|
+
const counts = registry.getStatusCounts();
|
|
721
|
+
|
|
722
|
+
expect(counts.online).toBeGreaterThanOrEqual(1);
|
|
723
|
+
});
|
|
724
|
+
});
|
|
725
|
+
|
|
726
|
+
describe('Lifecycle', () => {
|
|
727
|
+
it('should start and stop', () => {
|
|
728
|
+
const reg = new AgentRegistry();
|
|
729
|
+
reg.start();
|
|
730
|
+
expect(reg).toBeDefined();
|
|
731
|
+
reg.stop();
|
|
732
|
+
});
|
|
733
|
+
|
|
734
|
+
it('should clear all agents', async () => {
|
|
735
|
+
await registry.register(
|
|
736
|
+
createTestManifest('to-clear', [{ type: 'analyze', domain: 'general' }])
|
|
737
|
+
);
|
|
738
|
+
|
|
739
|
+
registry.clear();
|
|
740
|
+
|
|
741
|
+
expect(registry.size).toBe(0);
|
|
742
|
+
});
|
|
743
|
+
});
|
|
744
|
+
});
|
|
745
|
+
|
|
746
|
+
// =============================================================================
|
|
747
|
+
// INTEGRATION TESTS
|
|
748
|
+
// =============================================================================
|
|
749
|
+
|
|
750
|
+
describe('Agent Module Integration', () => {
|
|
751
|
+
it('should support full agent lifecycle', async () => {
|
|
752
|
+
const registry = new AgentRegistry({ autoCleanup: false });
|
|
753
|
+
|
|
754
|
+
// 1. Create and register agent
|
|
755
|
+
const manifest = createTestManifest('lifecycle-agent', [
|
|
756
|
+
{ type: 'generate', domain: 'general' },
|
|
757
|
+
{ type: 'analyze', domain: 'general' },
|
|
758
|
+
]);
|
|
759
|
+
|
|
760
|
+
await registry.register(manifest);
|
|
761
|
+
expect(registry.has('lifecycle-agent')).toBe(true);
|
|
762
|
+
|
|
763
|
+
// 2. Discover agent
|
|
764
|
+
const found = registry.get('lifecycle-agent');
|
|
765
|
+
expect(found).toBeDefined();
|
|
766
|
+
|
|
767
|
+
// 3. Query by capability
|
|
768
|
+
const matches = await registry.discover({ type: 'generate' });
|
|
769
|
+
expect(matches.some((m) => m.id === 'lifecycle-agent')).toBe(true);
|
|
770
|
+
|
|
771
|
+
// 4. Send heartbeat
|
|
772
|
+
await expect(registry.heartbeat('lifecycle-agent')).resolves.not.toThrow();
|
|
773
|
+
|
|
774
|
+
// 5. Deregister
|
|
775
|
+
await registry.deregister('lifecycle-agent');
|
|
776
|
+
expect(registry.has('lifecycle-agent')).toBe(false);
|
|
777
|
+
|
|
778
|
+
registry.stop();
|
|
779
|
+
});
|
|
780
|
+
|
|
781
|
+
it('should match agents for complex queries', async () => {
|
|
782
|
+
const registry = new AgentRegistry({ autoCleanup: false });
|
|
783
|
+
|
|
784
|
+
// Register diverse agents
|
|
785
|
+
await registry.register(
|
|
786
|
+
createTestManifest('specialist', [
|
|
787
|
+
{ type: 'analyze', domain: 'vision' },
|
|
788
|
+
{ type: 'detect', domain: 'vision' },
|
|
789
|
+
])
|
|
790
|
+
);
|
|
791
|
+
|
|
792
|
+
await registry.register(
|
|
793
|
+
createTestManifest('generalist', [
|
|
794
|
+
{ type: 'analyze', domain: 'general' },
|
|
795
|
+
{ type: 'generate', domain: 'nlp' },
|
|
796
|
+
])
|
|
797
|
+
);
|
|
798
|
+
|
|
799
|
+
await registry.register(
|
|
800
|
+
createTestManifest('devops', [
|
|
801
|
+
{ type: 'orchestrate', domain: 'general' },
|
|
802
|
+
{ type: 'validate', domain: 'general' },
|
|
803
|
+
])
|
|
804
|
+
);
|
|
805
|
+
|
|
806
|
+
// Query by domain
|
|
807
|
+
const visionAgents = await registry.discover({ domain: 'vision' });
|
|
808
|
+
expect(visionAgents.length).toBe(1);
|
|
809
|
+
expect(visionAgents[0].id).toBe('specialist');
|
|
810
|
+
|
|
811
|
+
// Query by type across domains
|
|
812
|
+
const analyzeAgents = await registry.discover({ type: 'analyze' });
|
|
813
|
+
expect(analyzeAgents.length).toBe(2);
|
|
814
|
+
|
|
815
|
+
registry.stop();
|
|
816
|
+
});
|
|
817
|
+
|
|
818
|
+
it('should work with manifest builder', async () => {
|
|
819
|
+
const registry = new AgentRegistry({ autoCleanup: false });
|
|
820
|
+
|
|
821
|
+
const manifest = createManifest()
|
|
822
|
+
.identity('built-agent', 'Built Agent', '1.0.0')
|
|
823
|
+
.description('An agent built with the builder')
|
|
824
|
+
.addCapability({
|
|
825
|
+
type: 'generate',
|
|
826
|
+
domain: 'nlp',
|
|
827
|
+
id: 'gen-nlp',
|
|
828
|
+
name: 'NLP Generation',
|
|
829
|
+
})
|
|
830
|
+
.addEndpoint({
|
|
831
|
+
protocol: 'local',
|
|
832
|
+
address: 'internal',
|
|
833
|
+
primary: true,
|
|
834
|
+
})
|
|
835
|
+
.trust('local')
|
|
836
|
+
.tags('test', 'builder')
|
|
837
|
+
.build();
|
|
838
|
+
|
|
839
|
+
await registry.register(manifest);
|
|
840
|
+
|
|
841
|
+
const found = registry.get('built-agent');
|
|
842
|
+
expect(found).toBeDefined();
|
|
843
|
+
expect(found?.tags).toContain('builder');
|
|
844
|
+
|
|
845
|
+
registry.stop();
|
|
846
|
+
});
|
|
847
|
+
|
|
848
|
+
it('should validate and reject invalid manifests', async () => {
|
|
849
|
+
const registry = new AgentRegistry({ autoCleanup: false });
|
|
850
|
+
|
|
851
|
+
// Missing required fields
|
|
852
|
+
const invalid = {
|
|
853
|
+
name: 'Invalid Agent',
|
|
854
|
+
version: '1.0.0',
|
|
855
|
+
} as unknown as AgentManifest;
|
|
856
|
+
|
|
857
|
+
const validation = validateManifest(invalid);
|
|
858
|
+
expect(validation.valid).toBe(false);
|
|
859
|
+
|
|
860
|
+
await expect(registry.register(invalid)).rejects.toThrow();
|
|
861
|
+
|
|
862
|
+
registry.stop();
|
|
863
|
+
});
|
|
864
|
+
});
|