@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,551 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @holoscript/core - Task Delegation Service
|
|
3
|
+
*
|
|
4
|
+
* Cross-agent task forwarding with support for local (in-process) and remote
|
|
5
|
+
* (A2A JSON-RPC) delegation. Includes auto-delegation via capability matching,
|
|
6
|
+
* retry with exponential backoff, and delegation history tracking.
|
|
7
|
+
*
|
|
8
|
+
* Part of HoloScript v5.5 "Agents as Universal Orchestrators".
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { randomUUID } from 'crypto';
|
|
12
|
+
import type { AgentManifest } from './AgentManifest';
|
|
13
|
+
import type { AgentRegistry } from './AgentRegistry';
|
|
14
|
+
import type { CapabilityQuery } from './CapabilityMatcher';
|
|
15
|
+
import type { FederatedRegistryAdapter } from './FederatedRegistryAdapter';
|
|
16
|
+
|
|
17
|
+
// =============================================================================
|
|
18
|
+
// TYPES
|
|
19
|
+
// =============================================================================
|
|
20
|
+
|
|
21
|
+
export interface DelegationRequest {
|
|
22
|
+
/** Target agent ID (from AgentManifest.id) */
|
|
23
|
+
targetAgentId: string;
|
|
24
|
+
/** Skill/tool name to invoke on the target agent */
|
|
25
|
+
skillId: string;
|
|
26
|
+
/** Arguments to pass to the skill */
|
|
27
|
+
arguments: Record<string, unknown>;
|
|
28
|
+
/** Timeout in ms (default 30_000) */
|
|
29
|
+
timeout?: number;
|
|
30
|
+
/** Number of retries on failure (default 0) */
|
|
31
|
+
retries?: number;
|
|
32
|
+
/** Callback URL for async results (reserved for future use) */
|
|
33
|
+
callbackUrl?: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface DelegationResult {
|
|
37
|
+
/** Unique task ID for tracking */
|
|
38
|
+
taskId: string;
|
|
39
|
+
/** Final delegation status */
|
|
40
|
+
status: 'completed' | 'failed' | 'timeout' | 'rejected';
|
|
41
|
+
/** Result data on success */
|
|
42
|
+
result?: unknown;
|
|
43
|
+
/** Error message on failure */
|
|
44
|
+
error?: string;
|
|
45
|
+
/** Total execution time in ms */
|
|
46
|
+
durationMs: number;
|
|
47
|
+
/** Info about the delegated agent */
|
|
48
|
+
delegatedTo: { agentId: string; endpoint: string };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface TaskDelegationConfig {
|
|
52
|
+
/** Default timeout for delegations in ms (default 30_000) */
|
|
53
|
+
defaultTimeout: number;
|
|
54
|
+
/** Maximum delegation history entries (default 1000, LRU eviction) */
|
|
55
|
+
maxHistory: number;
|
|
56
|
+
/** Custom fetch function (for testing) */
|
|
57
|
+
fetchFn?: (url: string, init?: RequestInit) => Promise<Response>;
|
|
58
|
+
/** Local tool executor for in-process agents */
|
|
59
|
+
localExecutor?: (skillId: string, args: Record<string, unknown>) => Promise<unknown>;
|
|
60
|
+
/** Optional observability hook for per-task delegation trace events */
|
|
61
|
+
traceHook?: (event: DelegationTraceEvent) => void;
|
|
62
|
+
/** Optional transport adapter for remote A2A calls */
|
|
63
|
+
transportAdapter?: A2ATransportAdapter;
|
|
64
|
+
/** Optional idempotency key factory for remote A2A calls */
|
|
65
|
+
idempotencyKeyFactory?: (ctx: {
|
|
66
|
+
taskId: string;
|
|
67
|
+
attempt: number;
|
|
68
|
+
endpointUrl: string;
|
|
69
|
+
skillId: string;
|
|
70
|
+
}) => string;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface A2ATransportAdapter {
|
|
74
|
+
send(input: {
|
|
75
|
+
endpointUrl: string;
|
|
76
|
+
requestBody: Record<string, unknown>;
|
|
77
|
+
idempotencyKey: string;
|
|
78
|
+
fetchFn: (url: string, init?: RequestInit) => Promise<Response>;
|
|
79
|
+
}): Promise<unknown>;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export type DelegationTracePhase =
|
|
83
|
+
| 'start'
|
|
84
|
+
| 'attempt'
|
|
85
|
+
| 'retry'
|
|
86
|
+
| 'success'
|
|
87
|
+
| 'failure'
|
|
88
|
+
| 'timeout'
|
|
89
|
+
| 'rejected'
|
|
90
|
+
| 'replay_requested'
|
|
91
|
+
| 'replay_completed'
|
|
92
|
+
| 'replay_failed';
|
|
93
|
+
|
|
94
|
+
export interface DelegationTraceEvent {
|
|
95
|
+
taskId: string;
|
|
96
|
+
phase: DelegationTracePhase;
|
|
97
|
+
timestamp: string;
|
|
98
|
+
metadata?: Record<string, unknown>;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const CANONICAL_TASK_BRIDGE_SCHEMA = 'holoscript.task-bridge.v1' as const;
|
|
102
|
+
|
|
103
|
+
interface DelegationTaskEnvelope {
|
|
104
|
+
id: string;
|
|
105
|
+
intent: string;
|
|
106
|
+
skillId: string;
|
|
107
|
+
input: Record<string, unknown>;
|
|
108
|
+
idempotency_key: string;
|
|
109
|
+
timeout: number;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
interface CanonicalTaskEnvelope {
|
|
113
|
+
schema: typeof CANONICAL_TASK_BRIDGE_SCHEMA;
|
|
114
|
+
task: DelegationTaskEnvelope;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function createCanonicalTaskEnvelope(task: DelegationTaskEnvelope): CanonicalTaskEnvelope {
|
|
118
|
+
return {
|
|
119
|
+
schema: CANONICAL_TASK_BRIDGE_SCHEMA,
|
|
120
|
+
task,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function canonicalTaskToA2ASendMessage(
|
|
125
|
+
envelope: CanonicalTaskEnvelope,
|
|
126
|
+
requestId: string,
|
|
127
|
+
timestamp = new Date().toISOString()
|
|
128
|
+
) {
|
|
129
|
+
return {
|
|
130
|
+
jsonrpc: '2.0' as const,
|
|
131
|
+
id: requestId,
|
|
132
|
+
method: 'a2a.sendMessage' as const,
|
|
133
|
+
params: {
|
|
134
|
+
message: {
|
|
135
|
+
role: 'user' as const,
|
|
136
|
+
parts: [
|
|
137
|
+
{
|
|
138
|
+
type: 'data' as const,
|
|
139
|
+
mimeType: 'application/json',
|
|
140
|
+
data: {
|
|
141
|
+
schema: envelope.schema,
|
|
142
|
+
task: envelope.task,
|
|
143
|
+
skillId: envelope.task.skillId,
|
|
144
|
+
arguments: envelope.task.input,
|
|
145
|
+
idempotencyKey: envelope.task.idempotency_key,
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
],
|
|
149
|
+
timestamp,
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const DEFAULT_CONFIG: TaskDelegationConfig = {
|
|
156
|
+
defaultTimeout: 30_000,
|
|
157
|
+
maxHistory: 1000,
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
// =============================================================================
|
|
161
|
+
// TASK DELEGATION SERVICE
|
|
162
|
+
// =============================================================================
|
|
163
|
+
|
|
164
|
+
export class TaskDelegationService {
|
|
165
|
+
private registry: AgentRegistry;
|
|
166
|
+
private adapter?: FederatedRegistryAdapter;
|
|
167
|
+
private config: TaskDelegationConfig;
|
|
168
|
+
private history: DelegationResult[] = [];
|
|
169
|
+
private traceHistory: DelegationTraceEvent[] = [];
|
|
170
|
+
private requestHistory = new Map<string, DelegationRequest>();
|
|
171
|
+
|
|
172
|
+
constructor(
|
|
173
|
+
registry: AgentRegistry,
|
|
174
|
+
adapter?: FederatedRegistryAdapter,
|
|
175
|
+
config: Partial<TaskDelegationConfig> = {}
|
|
176
|
+
) {
|
|
177
|
+
this.registry = registry;
|
|
178
|
+
this.adapter = adapter;
|
|
179
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// ===========================================================================
|
|
183
|
+
// DELEGATION
|
|
184
|
+
// ===========================================================================
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Delegate a task to a specific agent by ID.
|
|
188
|
+
*/
|
|
189
|
+
async delegateTo(request: DelegationRequest): Promise<DelegationResult> {
|
|
190
|
+
const startTime = Date.now();
|
|
191
|
+
const taskId = randomUUID();
|
|
192
|
+
const timeout = request.timeout ?? this.config.defaultTimeout;
|
|
193
|
+
const maxRetries = request.retries ?? 0;
|
|
194
|
+
|
|
195
|
+
this.requestHistory.set(taskId, { ...request, arguments: { ...request.arguments } });
|
|
196
|
+
this.emitTrace(taskId, 'start', {
|
|
197
|
+
targetAgentId: request.targetAgentId,
|
|
198
|
+
skillId: request.skillId,
|
|
199
|
+
timeout,
|
|
200
|
+
maxRetries,
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
// Resolve target agent
|
|
204
|
+
const manifest = this.registry.get(request.targetAgentId);
|
|
205
|
+
if (!manifest) {
|
|
206
|
+
const result: DelegationResult = {
|
|
207
|
+
taskId,
|
|
208
|
+
status: 'rejected',
|
|
209
|
+
error: `Agent not found: ${request.targetAgentId}`,
|
|
210
|
+
durationMs: Date.now() - startTime,
|
|
211
|
+
delegatedTo: { agentId: request.targetAgentId, endpoint: 'unknown' },
|
|
212
|
+
};
|
|
213
|
+
this.addToHistory(result);
|
|
214
|
+
this.emitTrace(taskId, 'rejected', { reason: result.error });
|
|
215
|
+
return result;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const endpoint = this.getPrimaryEndpoint(manifest);
|
|
219
|
+
const delegatedTo = { agentId: manifest.id, endpoint: endpoint?.address || 'local' };
|
|
220
|
+
|
|
221
|
+
// Retry loop
|
|
222
|
+
let lastError = '';
|
|
223
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
224
|
+
this.emitTrace(taskId, 'attempt', { attempt, maxRetries });
|
|
225
|
+
if (attempt > 0) {
|
|
226
|
+
// Exponential backoff with jitter
|
|
227
|
+
const delay = Math.min(1000 * Math.pow(2, attempt - 1) + Math.random() * 200, 10_000);
|
|
228
|
+
this.emitTrace(taskId, 'retry', { attempt, delayMs: delay });
|
|
229
|
+
await this.sleep(delay);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
try {
|
|
233
|
+
const result = await this.executeWithTimeout(
|
|
234
|
+
() =>
|
|
235
|
+
this.executeOnAgent(
|
|
236
|
+
manifest,
|
|
237
|
+
request.skillId,
|
|
238
|
+
request.arguments,
|
|
239
|
+
taskId,
|
|
240
|
+
attempt
|
|
241
|
+
),
|
|
242
|
+
timeout
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
const delegationResult: DelegationResult = {
|
|
246
|
+
taskId,
|
|
247
|
+
status: 'completed',
|
|
248
|
+
result,
|
|
249
|
+
durationMs: Date.now() - startTime,
|
|
250
|
+
delegatedTo,
|
|
251
|
+
};
|
|
252
|
+
this.addToHistory(delegationResult);
|
|
253
|
+
this.emitTrace(taskId, 'success', { attempt });
|
|
254
|
+
return delegationResult;
|
|
255
|
+
} catch (err) {
|
|
256
|
+
lastError = err instanceof Error ? err.message : String(err);
|
|
257
|
+
if (lastError === 'TIMEOUT') {
|
|
258
|
+
const result: DelegationResult = {
|
|
259
|
+
taskId,
|
|
260
|
+
status: 'timeout',
|
|
261
|
+
error: `Delegation timed out after ${timeout}ms`,
|
|
262
|
+
durationMs: Date.now() - startTime,
|
|
263
|
+
delegatedTo,
|
|
264
|
+
};
|
|
265
|
+
this.addToHistory(result);
|
|
266
|
+
this.emitTrace(taskId, 'timeout', { attempt, timeoutMs: timeout });
|
|
267
|
+
return result;
|
|
268
|
+
}
|
|
269
|
+
this.emitTrace(taskId, 'failure', { attempt, error: lastError });
|
|
270
|
+
// Continue retrying on non-timeout errors
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// All retries exhausted
|
|
275
|
+
const result: DelegationResult = {
|
|
276
|
+
taskId,
|
|
277
|
+
status: 'failed',
|
|
278
|
+
error: lastError || 'Unknown error',
|
|
279
|
+
durationMs: Date.now() - startTime,
|
|
280
|
+
delegatedTo,
|
|
281
|
+
};
|
|
282
|
+
this.addToHistory(result);
|
|
283
|
+
this.emitTrace(taskId, 'failure', { retriesExhausted: true, error: result.error });
|
|
284
|
+
return result;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Auto-delegate: find the best agent for a capability then delegate.
|
|
289
|
+
*/
|
|
290
|
+
async autoDelegate(
|
|
291
|
+
query: CapabilityQuery,
|
|
292
|
+
skillId: string,
|
|
293
|
+
args: Record<string, unknown>,
|
|
294
|
+
options?: { timeout?: number; retries?: number }
|
|
295
|
+
): Promise<DelegationResult> {
|
|
296
|
+
// Find the best agent
|
|
297
|
+
const bestAgent = await this.registry.findBest({
|
|
298
|
+
...query,
|
|
299
|
+
includeOffline: false,
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
if (!bestAgent) {
|
|
303
|
+
const startTime = Date.now();
|
|
304
|
+
const result: DelegationResult = {
|
|
305
|
+
taskId: randomUUID(),
|
|
306
|
+
status: 'rejected',
|
|
307
|
+
error: `No agent found matching query: ${JSON.stringify(query)}`,
|
|
308
|
+
durationMs: Date.now() - startTime,
|
|
309
|
+
delegatedTo: { agentId: 'none', endpoint: 'none' },
|
|
310
|
+
};
|
|
311
|
+
this.addToHistory(result);
|
|
312
|
+
return result;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return this.delegateTo({
|
|
316
|
+
targetAgentId: bestAgent.id,
|
|
317
|
+
skillId,
|
|
318
|
+
arguments: args,
|
|
319
|
+
timeout: options?.timeout,
|
|
320
|
+
retries: options?.retries,
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Get the status of a previously delegated task from history.
|
|
326
|
+
*/
|
|
327
|
+
getStatus(taskId: string): DelegationResult | undefined {
|
|
328
|
+
return this.history.find((r) => r.taskId === taskId);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Get the full delegation history.
|
|
333
|
+
*/
|
|
334
|
+
getDelegationHistory(): DelegationResult[] {
|
|
335
|
+
return [...this.history];
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Get observability trace events for delegations (optionally filtered by taskId).
|
|
340
|
+
*/
|
|
341
|
+
getTraceHistory(taskId?: string): DelegationTraceEvent[] {
|
|
342
|
+
if (!taskId) return [...this.traceHistory];
|
|
343
|
+
return this.traceHistory.filter((event) => event.taskId === taskId);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Replay a previously delegated task using its original request payload.
|
|
348
|
+
*/
|
|
349
|
+
async replay(taskId: string, overrides: Partial<DelegationRequest> = {}): Promise<DelegationResult> {
|
|
350
|
+
const original = this.requestHistory.get(taskId);
|
|
351
|
+
if (!original) {
|
|
352
|
+
throw new Error(`Replay unavailable for taskId: ${taskId}`);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const replayRequest: DelegationRequest = {
|
|
356
|
+
...original,
|
|
357
|
+
...overrides,
|
|
358
|
+
arguments: {
|
|
359
|
+
...original.arguments,
|
|
360
|
+
...(overrides.arguments ?? {}),
|
|
361
|
+
},
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
this.emitTrace(taskId, 'replay_requested', {
|
|
365
|
+
targetAgentId: replayRequest.targetAgentId,
|
|
366
|
+
skillId: replayRequest.skillId,
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
try {
|
|
370
|
+
const replayResult = await this.delegateTo(replayRequest);
|
|
371
|
+
this.emitTrace(taskId, 'replay_completed', { replayTaskId: replayResult.taskId });
|
|
372
|
+
return replayResult;
|
|
373
|
+
} catch (error) {
|
|
374
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
375
|
+
this.emitTrace(taskId, 'replay_failed', { error: message });
|
|
376
|
+
throw error;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Get delegation stats.
|
|
382
|
+
*/
|
|
383
|
+
getStats(): {
|
|
384
|
+
total: number;
|
|
385
|
+
completed: number;
|
|
386
|
+
failed: number;
|
|
387
|
+
timeout: number;
|
|
388
|
+
rejected: number;
|
|
389
|
+
} {
|
|
390
|
+
return {
|
|
391
|
+
total: this.history.length,
|
|
392
|
+
completed: this.history.filter((r) => r.status === 'completed').length,
|
|
393
|
+
failed: this.history.filter((r) => r.status === 'failed').length,
|
|
394
|
+
timeout: this.history.filter((r) => r.status === 'timeout').length,
|
|
395
|
+
rejected: this.history.filter((r) => r.status === 'rejected').length,
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// ===========================================================================
|
|
400
|
+
// PRIVATE HELPERS
|
|
401
|
+
// ===========================================================================
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Execute a skill on an agent (local or remote).
|
|
405
|
+
*/
|
|
406
|
+
private async executeOnAgent(
|
|
407
|
+
manifest: AgentManifest,
|
|
408
|
+
skillId: string,
|
|
409
|
+
args: Record<string, unknown>,
|
|
410
|
+
taskId: string,
|
|
411
|
+
attempt: number
|
|
412
|
+
): Promise<unknown> {
|
|
413
|
+
const endpoint = this.getPrimaryEndpoint(manifest);
|
|
414
|
+
|
|
415
|
+
// Local agent: use local executor
|
|
416
|
+
if (!endpoint || endpoint.protocol === 'local') {
|
|
417
|
+
if (this.config.localExecutor) {
|
|
418
|
+
return this.config.localExecutor(skillId, args);
|
|
419
|
+
}
|
|
420
|
+
throw new Error(`No local executor configured for agent ${manifest.id}`);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// Remote agent: A2A JSON-RPC sendMessage
|
|
424
|
+
if (endpoint.protocol === 'http' || endpoint.protocol === 'https') {
|
|
425
|
+
return this.executeRemote(endpoint.address, skillId, args, taskId, attempt);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
throw new Error(`Unsupported endpoint protocol: ${endpoint.protocol}`);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Execute via remote A2A JSON-RPC.
|
|
433
|
+
*/
|
|
434
|
+
private async executeRemote(
|
|
435
|
+
endpointUrl: string,
|
|
436
|
+
skillId: string,
|
|
437
|
+
args: Record<string, unknown>,
|
|
438
|
+
taskId: string,
|
|
439
|
+
attempt: number
|
|
440
|
+
): Promise<unknown> {
|
|
441
|
+
const fetchFn = this.config.fetchFn || globalThis.fetch;
|
|
442
|
+
const idempotencyKey =
|
|
443
|
+
this.config.idempotencyKeyFactory?.({
|
|
444
|
+
taskId,
|
|
445
|
+
attempt,
|
|
446
|
+
endpointUrl,
|
|
447
|
+
skillId,
|
|
448
|
+
}) ?? `hs-delegation-${taskId}-attempt-${attempt}`;
|
|
449
|
+
|
|
450
|
+
const envelope = createCanonicalTaskEnvelope({
|
|
451
|
+
id: taskId,
|
|
452
|
+
intent: skillId,
|
|
453
|
+
skillId,
|
|
454
|
+
input: args,
|
|
455
|
+
idempotency_key: idempotencyKey,
|
|
456
|
+
timeout: this.config.defaultTimeout,
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
const jsonRpcRequest = canonicalTaskToA2ASendMessage(envelope, randomUUID());
|
|
460
|
+
|
|
461
|
+
if (this.config.transportAdapter) {
|
|
462
|
+
return this.config.transportAdapter.send({
|
|
463
|
+
endpointUrl,
|
|
464
|
+
requestBody: jsonRpcRequest as Record<string, unknown>,
|
|
465
|
+
idempotencyKey,
|
|
466
|
+
fetchFn,
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
const response = await fetchFn(endpointUrl, {
|
|
471
|
+
method: 'POST',
|
|
472
|
+
headers: {
|
|
473
|
+
'Content-Type': 'application/json',
|
|
474
|
+
'Idempotency-Key': idempotencyKey,
|
|
475
|
+
},
|
|
476
|
+
body: JSON.stringify(jsonRpcRequest),
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
if (!response.ok) {
|
|
480
|
+
throw new Error(`Remote agent returned HTTP ${response.status}`);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
const data = (await response.json()) as {
|
|
484
|
+
result?: { status?: string; artifacts?: unknown[] };
|
|
485
|
+
error?: { message: string };
|
|
486
|
+
};
|
|
487
|
+
|
|
488
|
+
if (data.error) {
|
|
489
|
+
throw new Error(data.error.message);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
return data.result;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* Execute a function with a timeout.
|
|
497
|
+
*/
|
|
498
|
+
private async executeWithTimeout<T>(fn: () => Promise<T>, timeoutMs: number): Promise<T> {
|
|
499
|
+
return Promise.race([
|
|
500
|
+
fn(),
|
|
501
|
+
new Promise<never>((_, reject) => {
|
|
502
|
+
setTimeout(() => reject(new Error('TIMEOUT')), timeoutMs);
|
|
503
|
+
}),
|
|
504
|
+
]);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* Get the primary endpoint from a manifest.
|
|
509
|
+
*/
|
|
510
|
+
private getPrimaryEndpoint(manifest: AgentManifest) {
|
|
511
|
+
return manifest.endpoints.find((e) => e.primary) || manifest.endpoints[0];
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* Add a result to history with LRU eviction.
|
|
516
|
+
*/
|
|
517
|
+
private addToHistory(result: DelegationResult): void {
|
|
518
|
+
this.history.push(result);
|
|
519
|
+
while (this.history.length > this.config.maxHistory) {
|
|
520
|
+
const evicted = this.history.shift();
|
|
521
|
+
if (evicted) {
|
|
522
|
+
this.requestHistory.delete(evicted.taskId);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Emit and store delegation observability traces.
|
|
529
|
+
*/
|
|
530
|
+
private emitTrace(
|
|
531
|
+
taskId: string,
|
|
532
|
+
phase: DelegationTracePhase,
|
|
533
|
+
metadata?: Record<string, unknown>
|
|
534
|
+
): void {
|
|
535
|
+
const event: DelegationTraceEvent = {
|
|
536
|
+
taskId,
|
|
537
|
+
phase,
|
|
538
|
+
timestamp: new Date().toISOString(),
|
|
539
|
+
...(metadata ? { metadata } : {}),
|
|
540
|
+
};
|
|
541
|
+
this.traceHistory.push(event);
|
|
542
|
+
this.config.traceHook?.(event);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* Sleep utility.
|
|
547
|
+
*/
|
|
548
|
+
private sleep(ms: number): Promise<void> {
|
|
549
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
550
|
+
}
|
|
551
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AgentManifest — Production Test Suite
|
|
3
|
+
*
|
|
4
|
+
* Covers: AgentManifestBuilder (fluent API), createManifest, validateManifest.
|
|
5
|
+
*/
|
|
6
|
+
import { describe, it, expect } from 'vitest';
|
|
7
|
+
import { AgentManifestBuilder, createManifest, validateManifest } from '../AgentManifest';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Helper — build() requires at least id, name, version, one capability, one endpoint.
|
|
11
|
+
*/
|
|
12
|
+
function minBuilder() {
|
|
13
|
+
return new AgentManifestBuilder()
|
|
14
|
+
.identity('a1', 'TestAgent', '1.0.0')
|
|
15
|
+
.addCapability({ type: 'compute', domain: 'physics' })
|
|
16
|
+
.addEndpoint({ protocol: 'http', address: 'localhost' });
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
describe('AgentManifestBuilder — Production', () => {
|
|
20
|
+
it('builds a valid manifest with required fields', () => {
|
|
21
|
+
const m = minBuilder().build();
|
|
22
|
+
expect(m.id).toBe('a1');
|
|
23
|
+
expect(m.name).toBe('TestAgent');
|
|
24
|
+
expect(m.version).toBe('1.0.0');
|
|
25
|
+
expect(m.capabilities!.length).toBe(1);
|
|
26
|
+
expect(m.endpoints!.length).toBe(1);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('fluent chaining sets all optional fields', () => {
|
|
30
|
+
const m = minBuilder()
|
|
31
|
+
.description('A test agent')
|
|
32
|
+
.trust('verified')
|
|
33
|
+
.healthCheck(5000)
|
|
34
|
+
.tags('physics', 'ai')
|
|
35
|
+
.metadata({ custom: true })
|
|
36
|
+
.build();
|
|
37
|
+
expect(m.description).toBe('A test agent');
|
|
38
|
+
expect(m.trustLevel).toBe('verified');
|
|
39
|
+
expect(m.healthCheckInterval).toBe(5000);
|
|
40
|
+
expect(m.tags).toContain('physics');
|
|
41
|
+
expect(m.metadata?.custom).toBe(true);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('addCapability adds multiple capabilities', () => {
|
|
45
|
+
const m = minBuilder().addCapability({ type: 'render', domain: 'graphics' }).build();
|
|
46
|
+
expect(m.capabilities!.length).toBe(2);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('addEndpoint adds endpoint with port', () => {
|
|
50
|
+
const m = minBuilder()
|
|
51
|
+
.addEndpoint({ protocol: 'ws', address: 'localhost', port: 8080 })
|
|
52
|
+
.build();
|
|
53
|
+
expect(m.endpoints!.length).toBe(2);
|
|
54
|
+
expect(m.endpoints!.some((e) => e.port === 8080)).toBe(true);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('spatial sets spatial scope', () => {
|
|
58
|
+
const m = minBuilder().spatial({ global: true }).build();
|
|
59
|
+
expect(m.spatialScope?.global).toBe(true);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('build throws without identity', () => {
|
|
63
|
+
expect(() =>
|
|
64
|
+
new AgentManifestBuilder()
|
|
65
|
+
.addCapability({ type: 'compute', domain: 'physics' })
|
|
66
|
+
.addEndpoint({ protocol: 'http', address: 'localhost' })
|
|
67
|
+
.build()
|
|
68
|
+
).toThrow();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('build throws without capability', () => {
|
|
72
|
+
expect(() =>
|
|
73
|
+
new AgentManifestBuilder()
|
|
74
|
+
.identity('a1', 'Agent', '1.0.0')
|
|
75
|
+
.addEndpoint({ protocol: 'http', address: 'localhost' })
|
|
76
|
+
.build()
|
|
77
|
+
).toThrow();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('build throws without endpoint', () => {
|
|
81
|
+
expect(() =>
|
|
82
|
+
new AgentManifestBuilder()
|
|
83
|
+
.identity('a1', 'Agent', '1.0.0')
|
|
84
|
+
.addCapability({ type: 'compute', domain: 'physics' })
|
|
85
|
+
.build()
|
|
86
|
+
).toThrow();
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
describe('createManifest — Production', () => {
|
|
91
|
+
it('returns a builder instance', () => {
|
|
92
|
+
const builder = createManifest();
|
|
93
|
+
expect(builder).toBeInstanceOf(AgentManifestBuilder);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('can chain and build', () => {
|
|
97
|
+
const m = createManifest()
|
|
98
|
+
.identity('b1', 'Built', '1.0.0')
|
|
99
|
+
.addCapability({ type: 'compute', domain: 'physics' })
|
|
100
|
+
.addEndpoint({ protocol: 'http', address: 'localhost' })
|
|
101
|
+
.build();
|
|
102
|
+
expect(m.id).toBe('b1');
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
describe('validateManifest — Production', () => {
|
|
107
|
+
it('valid manifest passes', () => {
|
|
108
|
+
const result = validateManifest({
|
|
109
|
+
id: 'v1',
|
|
110
|
+
name: 'Valid',
|
|
111
|
+
version: '1.0.0',
|
|
112
|
+
capabilities: [{ type: 'compute', domain: 'physics' }],
|
|
113
|
+
endpoints: [{ protocol: 'http', address: 'localhost' }],
|
|
114
|
+
});
|
|
115
|
+
expect(result.valid).toBe(true);
|
|
116
|
+
expect(result.errors.length).toBe(0);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('missing id fails validation', () => {
|
|
120
|
+
const result = validateManifest({ name: 'NoId', version: '1.0.0' });
|
|
121
|
+
expect(result.valid).toBe(false);
|
|
122
|
+
expect(result.errors.length).toBeGreaterThan(0);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('missing name fails validation', () => {
|
|
126
|
+
const result = validateManifest({ id: 'x', version: '1.0.0' });
|
|
127
|
+
expect(result.valid).toBe(false);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('missing version fails validation', () => {
|
|
131
|
+
const result = validateManifest({ id: 'x', name: 'X' });
|
|
132
|
+
expect(result.valid).toBe(false);
|
|
133
|
+
});
|
|
134
|
+
});
|