@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,212 @@
|
|
|
1
|
+
// STATUS: Scaffold — requires absorb service connection and LLM provider for production use
|
|
2
|
+
/**
|
|
3
|
+
* PromptOptimizer — A/B test prompts to find the most effective phrasing.
|
|
4
|
+
*
|
|
5
|
+
* @experimental
|
|
6
|
+
*
|
|
7
|
+
* Part of FW-1.0 self-evolution: the framework optimizes its own prompts
|
|
8
|
+
* by running controlled experiments and measuring quality metrics.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { callLLM } from '../llm/llm-adapter';
|
|
12
|
+
import type { ModelConfig } from '../types';
|
|
13
|
+
|
|
14
|
+
export interface ABTestConfig {
|
|
15
|
+
/** LLM model configuration for running the test */
|
|
16
|
+
model: ModelConfig;
|
|
17
|
+
/** Number of runs per prompt variant (default: 3) */
|
|
18
|
+
runs?: number;
|
|
19
|
+
/** Evaluation criteria (default: quality + relevance) */
|
|
20
|
+
criteria?: EvaluationCriteria[];
|
|
21
|
+
/** Maximum tokens per response */
|
|
22
|
+
maxTokens?: number;
|
|
23
|
+
/** Temperature for test runs */
|
|
24
|
+
temperature?: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface EvaluationCriteria {
|
|
28
|
+
name: string;
|
|
29
|
+
description: string;
|
|
30
|
+
weight: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface PromptVariantResult {
|
|
34
|
+
prompt: string;
|
|
35
|
+
responses: string[];
|
|
36
|
+
avgScore: number;
|
|
37
|
+
scores: number[];
|
|
38
|
+
avgTokens: number;
|
|
39
|
+
avgLatencyMs: number;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface ABTestResult {
|
|
43
|
+
/** The task/question both prompts were tested against */
|
|
44
|
+
task: string;
|
|
45
|
+
/** Results for prompt A */
|
|
46
|
+
variantA: PromptVariantResult;
|
|
47
|
+
/** Results for prompt B */
|
|
48
|
+
variantB: PromptVariantResult;
|
|
49
|
+
/** Which variant won ('A' | 'B' | 'tie') */
|
|
50
|
+
winner: 'A' | 'B' | 'tie';
|
|
51
|
+
/** Confidence in the result (0-1, based on score delta and consistency) */
|
|
52
|
+
confidence: number;
|
|
53
|
+
/** Total tokens consumed across all runs */
|
|
54
|
+
totalTokens: number;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const DEFAULT_CRITERIA: EvaluationCriteria[] = [
|
|
58
|
+
{ name: 'relevance', description: 'How relevant is the response to the task?', weight: 0.4 },
|
|
59
|
+
{ name: 'quality', description: 'How well-structured and clear is the response?', weight: 0.3 },
|
|
60
|
+
{ name: 'completeness', description: 'Does the response fully address the task?', weight: 0.3 },
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
const JUDGE_SYSTEM = `You are a prompt quality evaluator. Score the following response on a scale of 1-10 for each criterion. Return ONLY a JSON object with scores, e.g.: {"relevance": 8, "quality": 7, "completeness": 9}`;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* PromptOptimizer — A/B tests prompt variants to find the best one.
|
|
67
|
+
*
|
|
68
|
+
* @experimental
|
|
69
|
+
*
|
|
70
|
+
* Usage:
|
|
71
|
+
* ```ts
|
|
72
|
+
* const optimizer = new PromptOptimizer({
|
|
73
|
+
* model: { provider: 'anthropic', model: 'claude-sonnet-4-20250514' }
|
|
74
|
+
* });
|
|
75
|
+
* const result = await optimizer.abTest(
|
|
76
|
+
* 'Explain HoloScript in one paragraph.',
|
|
77
|
+
* 'You are a HoloScript expert. Explain HoloScript concisely.',
|
|
78
|
+
* 'What is HoloScript?'
|
|
79
|
+
* );
|
|
80
|
+
* console.log(`Winner: ${result.winner} (confidence: ${result.confidence})`);
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
export class PromptOptimizer {
|
|
84
|
+
private readonly config: ABTestConfig;
|
|
85
|
+
|
|
86
|
+
constructor(config: ABTestConfig) {
|
|
87
|
+
this.config = {
|
|
88
|
+
runs: 3,
|
|
89
|
+
criteria: DEFAULT_CRITERIA,
|
|
90
|
+
maxTokens: 1024,
|
|
91
|
+
temperature: 0.7,
|
|
92
|
+
...config,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Run an A/B test comparing two prompt variants on a given task.
|
|
98
|
+
*/
|
|
99
|
+
async abTest(promptA: string, promptB: string, task: string): Promise<ABTestResult> {
|
|
100
|
+
const runs = this.config.runs!;
|
|
101
|
+
|
|
102
|
+
const [variantA, variantB] = await Promise.all([
|
|
103
|
+
this.runVariant(promptA, task, runs),
|
|
104
|
+
this.runVariant(promptB, task, runs),
|
|
105
|
+
]);
|
|
106
|
+
|
|
107
|
+
const delta = variantA.avgScore - variantB.avgScore;
|
|
108
|
+
const winner: 'A' | 'B' | 'tie' = Math.abs(delta) < 0.5 ? 'tie' : delta > 0 ? 'A' : 'B';
|
|
109
|
+
|
|
110
|
+
// Confidence: based on score delta magnitude and low variance
|
|
111
|
+
const varianceA = this.variance(variantA.scores);
|
|
112
|
+
const varianceB = this.variance(variantB.scores);
|
|
113
|
+
const avgVariance = (varianceA + varianceB) / 2;
|
|
114
|
+
const confidence = Math.min(1, Math.abs(delta) / 3) * Math.max(0.3, 1 - avgVariance / 10);
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
task,
|
|
118
|
+
variantA,
|
|
119
|
+
variantB,
|
|
120
|
+
winner,
|
|
121
|
+
confidence: Math.round(confidence * 100) / 100,
|
|
122
|
+
totalTokens: variantA.avgTokens * runs + variantB.avgTokens * runs,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Run a single variant N times and evaluate.
|
|
128
|
+
*/
|
|
129
|
+
private async runVariant(prompt: string, task: string, runs: number): Promise<PromptVariantResult> {
|
|
130
|
+
const responses: string[] = [];
|
|
131
|
+
const scores: number[] = [];
|
|
132
|
+
let totalTokens = 0;
|
|
133
|
+
let totalLatency = 0;
|
|
134
|
+
|
|
135
|
+
for (let i = 0; i < runs; i++) {
|
|
136
|
+
const start = Date.now();
|
|
137
|
+
const response = await callLLM(this.config.model, [
|
|
138
|
+
{ role: 'system', content: prompt },
|
|
139
|
+
{ role: 'user', content: task },
|
|
140
|
+
], {
|
|
141
|
+
maxTokens: this.config.maxTokens,
|
|
142
|
+
temperature: this.config.temperature,
|
|
143
|
+
});
|
|
144
|
+
const latency = Date.now() - start;
|
|
145
|
+
|
|
146
|
+
responses.push(response.content);
|
|
147
|
+
totalTokens += response.tokensUsed ?? 0;
|
|
148
|
+
totalLatency += latency;
|
|
149
|
+
|
|
150
|
+
// Judge the response
|
|
151
|
+
const score = await this.judge(response.content, task);
|
|
152
|
+
scores.push(score);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
prompt,
|
|
157
|
+
responses,
|
|
158
|
+
avgScore: scores.reduce((a, b) => a + b, 0) / scores.length,
|
|
159
|
+
scores,
|
|
160
|
+
avgTokens: totalTokens / runs,
|
|
161
|
+
avgLatencyMs: totalLatency / runs,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Judge a response using the LLM as evaluator.
|
|
167
|
+
* Returns a weighted score (0-10).
|
|
168
|
+
*/
|
|
169
|
+
private async judge(response: string, task: string): Promise<number> {
|
|
170
|
+
const criteria = this.config.criteria!;
|
|
171
|
+
|
|
172
|
+
try {
|
|
173
|
+
const judgeResponse = await callLLM(this.config.model, [
|
|
174
|
+
{ role: 'system', content: JUDGE_SYSTEM },
|
|
175
|
+
{
|
|
176
|
+
role: 'user',
|
|
177
|
+
content: [
|
|
178
|
+
`Task: ${task}`,
|
|
179
|
+
`Response: ${response}`,
|
|
180
|
+
`Criteria: ${criteria.map(c => `${c.name} (${c.description})`).join(', ')}`,
|
|
181
|
+
].join('\n'),
|
|
182
|
+
},
|
|
183
|
+
], { maxTokens: 200, temperature: 0 });
|
|
184
|
+
|
|
185
|
+
const parsed = JSON.parse(judgeResponse.content) as Record<string, number>;
|
|
186
|
+
let weightedSum = 0;
|
|
187
|
+
let totalWeight = 0;
|
|
188
|
+
|
|
189
|
+
for (const c of criteria) {
|
|
190
|
+
const score = parsed[c.name];
|
|
191
|
+
if (typeof score === 'number' && score >= 1 && score <= 10) {
|
|
192
|
+
weightedSum += score * c.weight;
|
|
193
|
+
totalWeight += c.weight;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return totalWeight > 0 ? weightedSum / totalWeight : 5;
|
|
198
|
+
} catch {
|
|
199
|
+
// If judge fails, return neutral score
|
|
200
|
+
return 5;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Compute variance of a number array.
|
|
206
|
+
*/
|
|
207
|
+
private variance(values: number[]): number {
|
|
208
|
+
if (values.length < 2) return 0;
|
|
209
|
+
const mean = values.reduce((a, b) => a + b, 0) / values.length;
|
|
210
|
+
return values.reduce((sum, v) => sum + (v - mean) ** 2, 0) / (values.length - 1);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
// STATUS: Scaffold — requires absorb service connection and LLM provider for production use
|
|
2
|
+
/**
|
|
3
|
+
* TestGenerator — Auto-generate tests for framework source files.
|
|
4
|
+
*
|
|
5
|
+
* @experimental
|
|
6
|
+
*
|
|
7
|
+
* Uses the LLM adapter to generate vitest test files from source code.
|
|
8
|
+
* Part of FW-1.0 self-evolution: the framework writes its own tests.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { callLLM } from '../llm/llm-adapter';
|
|
12
|
+
import type { ModelConfig } from '../types';
|
|
13
|
+
import { readFileSync, existsSync } from 'fs';
|
|
14
|
+
import { basename, dirname, resolve } from 'path';
|
|
15
|
+
|
|
16
|
+
export interface TestGeneratorConfig {
|
|
17
|
+
/** LLM model configuration */
|
|
18
|
+
model: ModelConfig;
|
|
19
|
+
/** Maximum tokens for test generation */
|
|
20
|
+
maxTokens?: number;
|
|
21
|
+
/** Temperature (lower = more deterministic tests) */
|
|
22
|
+
temperature?: number;
|
|
23
|
+
/** Test framework (default: vitest) */
|
|
24
|
+
testFramework?: 'vitest' | 'jest';
|
|
25
|
+
/** Additional context to include in the prompt */
|
|
26
|
+
additionalContext?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface GeneratedTest {
|
|
30
|
+
/** Source file path */
|
|
31
|
+
sourceFile: string;
|
|
32
|
+
/** Generated test file content */
|
|
33
|
+
testContent: string;
|
|
34
|
+
/** Suggested test file path */
|
|
35
|
+
testFilePath: string;
|
|
36
|
+
/** Number of test cases generated */
|
|
37
|
+
testCount: number;
|
|
38
|
+
/** LLM tokens used */
|
|
39
|
+
tokensUsed: number;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const SYSTEM_PROMPT = `You are a test generation expert for TypeScript projects using vitest.
|
|
43
|
+
Given a source file, generate comprehensive test cases that:
|
|
44
|
+
1. Test all exported functions and classes
|
|
45
|
+
2. Cover happy paths and edge cases
|
|
46
|
+
3. Use vi.mock() for external dependencies
|
|
47
|
+
4. Follow the AAA pattern (Arrange, Act, Assert)
|
|
48
|
+
5. Use descriptive test names
|
|
49
|
+
|
|
50
|
+
Output ONLY the test file content — no explanations, no markdown fences.
|
|
51
|
+
Use import { describe, it, expect, vi } from 'vitest';`;
|
|
52
|
+
|
|
53
|
+
const JEST_SYSTEM_PROMPT = SYSTEM_PROMPT.replace(
|
|
54
|
+
"import { describe, it, expect, vi } from 'vitest';",
|
|
55
|
+
"Use jest globals (describe, it, expect, jest)."
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* TestGenerator — generates test files from source code via LLM.
|
|
60
|
+
*
|
|
61
|
+
* @experimental
|
|
62
|
+
*
|
|
63
|
+
* Usage:
|
|
64
|
+
* ```ts
|
|
65
|
+
* const gen = new TestGenerator({ model: { provider: 'anthropic', model: 'claude-sonnet-4-20250514' } });
|
|
66
|
+
* const result = await gen.generateTests('src/self-improve/absorb-scanner.ts');
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
export class TestGenerator {
|
|
70
|
+
private readonly config: TestGeneratorConfig;
|
|
71
|
+
|
|
72
|
+
constructor(config: TestGeneratorConfig) {
|
|
73
|
+
this.config = {
|
|
74
|
+
maxTokens: 4096,
|
|
75
|
+
temperature: 0.3,
|
|
76
|
+
testFramework: 'vitest',
|
|
77
|
+
...config,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Generate a test file for the given source file.
|
|
83
|
+
*
|
|
84
|
+
* @param filePath - Path to the TypeScript source file
|
|
85
|
+
* @returns Generated test content and metadata
|
|
86
|
+
*/
|
|
87
|
+
async generateTests(filePath: string): Promise<GeneratedTest> {
|
|
88
|
+
const absPath = resolve(filePath);
|
|
89
|
+
|
|
90
|
+
if (!existsSync(absPath)) {
|
|
91
|
+
throw new Error(`Source file not found: ${absPath}`);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const source = readFileSync(absPath, 'utf-8');
|
|
95
|
+
const fileName = basename(absPath, '.ts');
|
|
96
|
+
const dir = dirname(absPath);
|
|
97
|
+
|
|
98
|
+
const systemPrompt = this.config.testFramework === 'jest'
|
|
99
|
+
? JEST_SYSTEM_PROMPT
|
|
100
|
+
: SYSTEM_PROMPT;
|
|
101
|
+
|
|
102
|
+
const userPrompt = [
|
|
103
|
+
`Generate tests for this TypeScript file: ${basename(absPath)}`,
|
|
104
|
+
'',
|
|
105
|
+
'```typescript',
|
|
106
|
+
source,
|
|
107
|
+
'```',
|
|
108
|
+
this.config.additionalContext ? `\nAdditional context:\n${this.config.additionalContext}` : '',
|
|
109
|
+
].join('\n');
|
|
110
|
+
|
|
111
|
+
const response = await callLLM(this.config.model, [
|
|
112
|
+
{ role: 'system', content: systemPrompt },
|
|
113
|
+
{ role: 'user', content: userPrompt },
|
|
114
|
+
], {
|
|
115
|
+
maxTokens: this.config.maxTokens,
|
|
116
|
+
temperature: this.config.temperature,
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
const testContent = cleanTestOutput(response.content);
|
|
120
|
+
const testCount = countTestCases(testContent);
|
|
121
|
+
|
|
122
|
+
// Determine test file path
|
|
123
|
+
const testFilePath = resolve(dir, '__tests__', `${fileName}.test.ts`);
|
|
124
|
+
|
|
125
|
+
return {
|
|
126
|
+
sourceFile: absPath,
|
|
127
|
+
testContent,
|
|
128
|
+
testFilePath,
|
|
129
|
+
testCount,
|
|
130
|
+
tokensUsed: response.tokensUsed ?? 0,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Generate tests for multiple files.
|
|
136
|
+
*
|
|
137
|
+
* @param filePaths - Array of source file paths
|
|
138
|
+
* @returns Array of generated tests
|
|
139
|
+
*/
|
|
140
|
+
async generateBatch(filePaths: string[]): Promise<GeneratedTest[]> {
|
|
141
|
+
const results: GeneratedTest[] = [];
|
|
142
|
+
for (const filePath of filePaths) {
|
|
143
|
+
try {
|
|
144
|
+
const result = await this.generateTests(filePath);
|
|
145
|
+
results.push(result);
|
|
146
|
+
} catch {
|
|
147
|
+
// Skip files that fail — don't block the batch
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return results;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** Strip markdown fences if the LLM included them. */
|
|
155
|
+
function cleanTestOutput(content: string): string {
|
|
156
|
+
let cleaned = content.trim();
|
|
157
|
+
// Remove leading ```typescript or ```ts
|
|
158
|
+
if (cleaned.startsWith('```')) {
|
|
159
|
+
const firstNewline = cleaned.indexOf('\n');
|
|
160
|
+
if (firstNewline !== -1) {
|
|
161
|
+
cleaned = cleaned.slice(firstNewline + 1);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
// Remove trailing ```
|
|
165
|
+
if (cleaned.endsWith('```')) {
|
|
166
|
+
cleaned = cleaned.slice(0, -3).trimEnd();
|
|
167
|
+
}
|
|
168
|
+
return cleaned;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** Count the number of it() or test() calls in the content. */
|
|
172
|
+
function countTestCases(content: string): number {
|
|
173
|
+
const matches = content.match(/\b(?:it|test)\s*\(/g);
|
|
174
|
+
return matches ? matches.length : 0;
|
|
175
|
+
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SkillRouter — Skill-based task routing via agent capabilities.
|
|
3
|
+
*
|
|
4
|
+
* Matches task requirements to agent capabilities, scores by overlap,
|
|
5
|
+
* and returns the best-fit agent. Supports required vs preferred skills.
|
|
6
|
+
*
|
|
7
|
+
* FW-0.6 — Skill-based routing via agent capabilities.
|
|
8
|
+
*
|
|
9
|
+
* @module skill-router
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { AgentConfig, TaskDef, SlotRole } from './types';
|
|
13
|
+
|
|
14
|
+
// =============================================================================
|
|
15
|
+
// TYPES
|
|
16
|
+
// =============================================================================
|
|
17
|
+
|
|
18
|
+
export interface RoutingResult {
|
|
19
|
+
/** The selected agent, or null if no match */
|
|
20
|
+
agent: AgentConfig | null;
|
|
21
|
+
/** Score of the selected agent (0-100) */
|
|
22
|
+
score: number;
|
|
23
|
+
/** All candidates with their scores, sorted descending */
|
|
24
|
+
candidates: ScoredCandidate[];
|
|
25
|
+
/** Reason for selection or rejection */
|
|
26
|
+
reason: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface ScoredCandidate {
|
|
30
|
+
agent: AgentConfig;
|
|
31
|
+
score: number;
|
|
32
|
+
matchedCapabilities: string[];
|
|
33
|
+
roleMatch: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface RoutingPolicy {
|
|
37
|
+
/** Minimum score threshold (0-100) to be eligible (default 10) */
|
|
38
|
+
minScore?: number;
|
|
39
|
+
/** Weight for capability matches (default 15) */
|
|
40
|
+
capabilityWeight?: number;
|
|
41
|
+
/** Weight for exact role match (default 25) */
|
|
42
|
+
roleWeight?: number;
|
|
43
|
+
/** Weight for priority alignment (default 10) */
|
|
44
|
+
priorityWeight?: number;
|
|
45
|
+
/** Required capabilities — agent MUST have all of these */
|
|
46
|
+
requiredCapabilities?: string[];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// =============================================================================
|
|
50
|
+
// SKILL ROUTER
|
|
51
|
+
// =============================================================================
|
|
52
|
+
|
|
53
|
+
export class SkillRouter {
|
|
54
|
+
private defaultPolicy: Required<RoutingPolicy>;
|
|
55
|
+
|
|
56
|
+
constructor(policy: RoutingPolicy = {}) {
|
|
57
|
+
this.defaultPolicy = {
|
|
58
|
+
minScore: policy.minScore ?? 10,
|
|
59
|
+
capabilityWeight: policy.capabilityWeight ?? 15,
|
|
60
|
+
roleWeight: policy.roleWeight ?? 25,
|
|
61
|
+
priorityWeight: policy.priorityWeight ?? 10,
|
|
62
|
+
requiredCapabilities: policy.requiredCapabilities ?? [],
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Route a task to the best-fit agent.
|
|
68
|
+
*
|
|
69
|
+
* Scoring:
|
|
70
|
+
* - Each capability keyword match in task title/description: +capabilityWeight
|
|
71
|
+
* - Exact role match (task.role === agent role mapping): +roleWeight
|
|
72
|
+
* - Priority alignment (agent.claimFilter.maxPriority >= task.priority): +priorityWeight
|
|
73
|
+
* - Required capabilities must ALL be present or agent is filtered out
|
|
74
|
+
*/
|
|
75
|
+
route(task: TaskDef, agents: AgentConfig[], policy?: RoutingPolicy): RoutingResult {
|
|
76
|
+
const p = { ...this.defaultPolicy, ...policy };
|
|
77
|
+
|
|
78
|
+
if (agents.length === 0) {
|
|
79
|
+
return { agent: null, score: 0, candidates: [], reason: 'No agents available' };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const taskText = `${task.title} ${task.description}`.toLowerCase();
|
|
83
|
+
const candidates: ScoredCandidate[] = [];
|
|
84
|
+
|
|
85
|
+
for (const agent of agents) {
|
|
86
|
+
// Check required capabilities
|
|
87
|
+
const agentCaps = new Set(agent.capabilities.map(c => c.toLowerCase()));
|
|
88
|
+
if (p.requiredCapabilities.length > 0) {
|
|
89
|
+
const hasAll = p.requiredCapabilities.every(rc => agentCaps.has(rc.toLowerCase()));
|
|
90
|
+
if (!hasAll) continue;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Check priority alignment
|
|
94
|
+
if (task.priority > agent.claimFilter.maxPriority) continue;
|
|
95
|
+
|
|
96
|
+
let score = 0;
|
|
97
|
+
const matchedCapabilities: string[] = [];
|
|
98
|
+
|
|
99
|
+
// Capability matching — check if agent capabilities appear in task text
|
|
100
|
+
for (const cap of agent.capabilities) {
|
|
101
|
+
const capLower = cap.toLowerCase();
|
|
102
|
+
if (taskText.includes(capLower)) {
|
|
103
|
+
score += p.capabilityWeight;
|
|
104
|
+
matchedCapabilities.push(cap);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Role matching
|
|
109
|
+
const roleMatch = task.role ? this.agentMatchesRole(agent, task.role) : false;
|
|
110
|
+
if (roleMatch) {
|
|
111
|
+
score += p.roleWeight;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Priority alignment bonus (closer to task priority = better fit)
|
|
115
|
+
const priorityDelta = agent.claimFilter.maxPriority - task.priority;
|
|
116
|
+
if (priorityDelta >= 0) {
|
|
117
|
+
// Agents whose maxPriority closely matches get a bonus
|
|
118
|
+
score += Math.max(0, p.priorityWeight - priorityDelta);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Base score for being eligible at all
|
|
122
|
+
score += 5;
|
|
123
|
+
|
|
124
|
+
candidates.push({ agent, score, matchedCapabilities, roleMatch });
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Sort by score descending
|
|
128
|
+
candidates.sort((a, b) => b.score - a.score);
|
|
129
|
+
|
|
130
|
+
// Apply minimum threshold
|
|
131
|
+
const eligible = candidates.filter(c => c.score >= p.minScore);
|
|
132
|
+
|
|
133
|
+
if (eligible.length === 0) {
|
|
134
|
+
return {
|
|
135
|
+
agent: null,
|
|
136
|
+
score: 0,
|
|
137
|
+
candidates,
|
|
138
|
+
reason: candidates.length > 0
|
|
139
|
+
? `${candidates.length} candidates scored below minimum threshold (${p.minScore})`
|
|
140
|
+
: 'No candidates matched task requirements',
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const winner = eligible[0];
|
|
145
|
+
return {
|
|
146
|
+
agent: winner.agent,
|
|
147
|
+
score: winner.score,
|
|
148
|
+
candidates,
|
|
149
|
+
reason: `Best fit: ${winner.agent.name} (score ${winner.score}, ${winner.matchedCapabilities.length} capability matches${winner.roleMatch ? ', role match' : ''})`,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Route a task to multiple agents (for parallel execution or fallback chains).
|
|
155
|
+
*/
|
|
156
|
+
routeMultiple(
|
|
157
|
+
task: TaskDef,
|
|
158
|
+
agents: AgentConfig[],
|
|
159
|
+
count: number,
|
|
160
|
+
policy?: RoutingPolicy
|
|
161
|
+
): RoutingResult {
|
|
162
|
+
const result = this.route(task, agents, policy);
|
|
163
|
+
// Already sorted — just return top N candidates
|
|
164
|
+
const topN = result.candidates.slice(0, count);
|
|
165
|
+
return {
|
|
166
|
+
...result,
|
|
167
|
+
agent: topN[0]?.agent ?? null,
|
|
168
|
+
candidates: topN,
|
|
169
|
+
reason: `Top ${Math.min(count, topN.length)} candidates selected`,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// ── Private ──
|
|
174
|
+
|
|
175
|
+
private agentMatchesRole(agent: AgentConfig, taskRole: SlotRole): boolean {
|
|
176
|
+
// Map agent roles to slot roles
|
|
177
|
+
const roleMapping: Record<string, SlotRole[]> = {
|
|
178
|
+
architect: ['researcher', 'reviewer'],
|
|
179
|
+
coder: ['coder', 'flex'],
|
|
180
|
+
researcher: ['researcher'],
|
|
181
|
+
reviewer: ['reviewer', 'tester'],
|
|
182
|
+
};
|
|
183
|
+
const mappedRoles = roleMapping[agent.role] ?? [];
|
|
184
|
+
return mappedRoles.includes(taskRole) || agent.claimFilter.roles.includes(taskRole);
|
|
185
|
+
}
|
|
186
|
+
}
|