@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,694 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @holoscript/core - Negotiation Protocol
|
|
3
|
+
*
|
|
4
|
+
* Core protocol for multi-agent negotiation and consensus building.
|
|
5
|
+
* Manages negotiation sessions, proposals, voting, and resolution.
|
|
6
|
+
*
|
|
7
|
+
* Part of HoloScript v3.1 Agentic Choreography.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* const protocol = new NegotiationProtocol();
|
|
12
|
+
*
|
|
13
|
+
* // Start a negotiation
|
|
14
|
+
* const session = await protocol.initiate({
|
|
15
|
+
* topic: 'task-assignment',
|
|
16
|
+
* participants: ['agent-1', 'agent-2', 'agent-3'],
|
|
17
|
+
* votingMechanism: 'majority',
|
|
18
|
+
* timeout: 60000,
|
|
19
|
+
* });
|
|
20
|
+
*
|
|
21
|
+
* // Submit proposals
|
|
22
|
+
* await protocol.propose(session.id, {
|
|
23
|
+
* proposerId: 'agent-1',
|
|
24
|
+
* title: 'Process sequentially',
|
|
25
|
+
* description: 'Handle tasks one at a time',
|
|
26
|
+
* content: { strategy: 'sequential' },
|
|
27
|
+
* });
|
|
28
|
+
*
|
|
29
|
+
* // Vote on proposals
|
|
30
|
+
* await protocol.vote(session.id, {
|
|
31
|
+
* agentId: 'agent-2',
|
|
32
|
+
* ranking: ['proposal-1'],
|
|
33
|
+
* });
|
|
34
|
+
*
|
|
35
|
+
* // Resolve when ready
|
|
36
|
+
* const resolution = await protocol.resolve(session.id);
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* @version 3.1.0
|
|
40
|
+
* @Sprint v3.1 (March 2026)
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
import type {
|
|
44
|
+
NegotiationSession,
|
|
45
|
+
NegotiationConfig,
|
|
46
|
+
Proposal,
|
|
47
|
+
Vote,
|
|
48
|
+
Resolution,
|
|
49
|
+
ResolutionOutcome,
|
|
50
|
+
NegotiationEvents,
|
|
51
|
+
ProposalInput,
|
|
52
|
+
VoteInput,
|
|
53
|
+
InitiateOptions,
|
|
54
|
+
} from './NegotiationTypes';
|
|
55
|
+
|
|
56
|
+
import {
|
|
57
|
+
getVotingHandler,
|
|
58
|
+
checkQuorum,
|
|
59
|
+
getTrustWeight,
|
|
60
|
+
type VotingResult,
|
|
61
|
+
} from './VotingMechanisms';
|
|
62
|
+
|
|
63
|
+
// =============================================================================
|
|
64
|
+
// TYPES
|
|
65
|
+
// =============================================================================
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Audit log entry
|
|
69
|
+
*/
|
|
70
|
+
export interface AuditEntry {
|
|
71
|
+
timestamp: number;
|
|
72
|
+
sessionId: string;
|
|
73
|
+
action: 'initiated' | 'proposal_submitted' | 'vote_cast' | 'resolved' | 'timeout' | 'escalated';
|
|
74
|
+
agentId?: string;
|
|
75
|
+
details: Record<string, unknown>;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Session deadline info
|
|
80
|
+
*/
|
|
81
|
+
interface DeadlineInfo {
|
|
82
|
+
sessionId: string;
|
|
83
|
+
timer: ReturnType<typeof setTimeout>;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Event handler type
|
|
88
|
+
*/
|
|
89
|
+
type EventHandler<T> = (data: T) => void | Promise<void>;
|
|
90
|
+
|
|
91
|
+
// =============================================================================
|
|
92
|
+
// NEGOTIATION PROTOCOL
|
|
93
|
+
// =============================================================================
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* NegotiationProtocol manages multi-agent negotiation sessions.
|
|
97
|
+
*
|
|
98
|
+
* Features:
|
|
99
|
+
* - Multiple voting mechanisms (majority, consensus, ranked, etc.)
|
|
100
|
+
* - Proposal lifecycle management
|
|
101
|
+
* - Quorum and deadline enforcement
|
|
102
|
+
* - Deadlock detection and escalation
|
|
103
|
+
* - Full audit logging
|
|
104
|
+
*/
|
|
105
|
+
export class NegotiationProtocol {
|
|
106
|
+
private sessions: Map<string, NegotiationSession> = new Map();
|
|
107
|
+
private auditLog: AuditEntry[] = [];
|
|
108
|
+
private deadlines: Map<string, DeadlineInfo> = new Map();
|
|
109
|
+
private eventHandlers: Map<keyof NegotiationEvents, Set<EventHandler<any>>> = new Map();
|
|
110
|
+
private sessionCounter = 0;
|
|
111
|
+
private proposalCounter = 0;
|
|
112
|
+
private voteCounter = 0;
|
|
113
|
+
|
|
114
|
+
// ===========================================================================
|
|
115
|
+
// PUBLIC API
|
|
116
|
+
// ===========================================================================
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Initiate a new negotiation session
|
|
120
|
+
*/
|
|
121
|
+
async initiate(options: InitiateOptions): Promise<NegotiationSession> {
|
|
122
|
+
const sessionId = this.generateSessionId();
|
|
123
|
+
const timeout = options.timeout ?? 60000;
|
|
124
|
+
|
|
125
|
+
const config: NegotiationConfig = {
|
|
126
|
+
mechanism: options.votingMechanism || 'majority',
|
|
127
|
+
votingMechanism: options.votingMechanism || 'majority',
|
|
128
|
+
quorum: options.quorum ?? 0.5,
|
|
129
|
+
timeout: timeout,
|
|
130
|
+
votingDeadline: timeout,
|
|
131
|
+
proposalDeadline: timeout,
|
|
132
|
+
maxRounds: options.maxRounds ?? 3,
|
|
133
|
+
allowAbstain: options.allowAbstain ?? true,
|
|
134
|
+
requireJustification: options.requireJustification ?? false,
|
|
135
|
+
tieBreaker: options.tieBreaker ?? 'random',
|
|
136
|
+
escalationPath: options.escalationPath,
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const now = Date.now();
|
|
140
|
+
const session: NegotiationSession = {
|
|
141
|
+
id: sessionId,
|
|
142
|
+
topic: options.topic,
|
|
143
|
+
description: options.description,
|
|
144
|
+
participants: [...options.participants],
|
|
145
|
+
status: 'open',
|
|
146
|
+
proposals: [],
|
|
147
|
+
votes: [],
|
|
148
|
+
config,
|
|
149
|
+
round: 1,
|
|
150
|
+
currentRound: 1,
|
|
151
|
+
createdAt: now,
|
|
152
|
+
startedAt: now,
|
|
153
|
+
lastActivityAt: now,
|
|
154
|
+
deadline: now + timeout,
|
|
155
|
+
history: [],
|
|
156
|
+
metadata: options.metadata || {},
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
this.sessions.set(sessionId, session);
|
|
160
|
+
this.startDeadlineTimer(session);
|
|
161
|
+
this.audit('initiated', sessionId, undefined, {
|
|
162
|
+
topic: options.topic,
|
|
163
|
+
participants: options.participants,
|
|
164
|
+
config,
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
await this.emit('sessionStarted', { session });
|
|
168
|
+
|
|
169
|
+
return session;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Submit a proposal to a session
|
|
174
|
+
*/
|
|
175
|
+
async propose(sessionId: string, input: ProposalInput): Promise<Proposal> {
|
|
176
|
+
const session = this.getSession(sessionId);
|
|
177
|
+
this.validateSessionOpen(session);
|
|
178
|
+
this.validateParticipant(session, input.proposerId);
|
|
179
|
+
|
|
180
|
+
const proposalId = this.generateProposalId();
|
|
181
|
+
|
|
182
|
+
const proposal: Proposal = {
|
|
183
|
+
id: proposalId,
|
|
184
|
+
sessionId,
|
|
185
|
+
proposerId: input.proposerId,
|
|
186
|
+
agentId: input.proposerId,
|
|
187
|
+
title: input.title,
|
|
188
|
+
description: input.description,
|
|
189
|
+
content: input.content,
|
|
190
|
+
priority: input.priority ?? 0,
|
|
191
|
+
status: 'submitted',
|
|
192
|
+
submittedAt: Date.now(),
|
|
193
|
+
metadata: input.metadata || {},
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
session.proposals.push(proposal);
|
|
197
|
+
session.lastActivityAt = Date.now();
|
|
198
|
+
this.audit('proposal_submitted', sessionId, input.proposerId, {
|
|
199
|
+
proposalId,
|
|
200
|
+
title: input.title,
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
await this.emit('proposalSubmitted', { session, proposal });
|
|
204
|
+
|
|
205
|
+
return proposal;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Cast a vote in a session
|
|
210
|
+
*/
|
|
211
|
+
async vote(sessionId: string, input: VoteInput): Promise<Vote> {
|
|
212
|
+
const session = this.getSession(sessionId);
|
|
213
|
+
this.validateSessionOpen(session);
|
|
214
|
+
this.validateParticipant(session, input.agentId);
|
|
215
|
+
this.validateNotDuplicate(session, input.agentId);
|
|
216
|
+
|
|
217
|
+
const mechanism = session.config.mechanism || session.config.votingMechanism || 'majority';
|
|
218
|
+
const handler = getVotingHandler(mechanism);
|
|
219
|
+
|
|
220
|
+
// Create vote structure
|
|
221
|
+
const vote: Vote = {
|
|
222
|
+
id: this.generateVoteId(),
|
|
223
|
+
sessionId,
|
|
224
|
+
agentId: input.agentId,
|
|
225
|
+
ranking: input.ranking || [],
|
|
226
|
+
approvals: input.approvals,
|
|
227
|
+
weight: input.weight ?? getTrustWeight('local'),
|
|
228
|
+
justification: input.justification,
|
|
229
|
+
abstain: input.abstain ?? false,
|
|
230
|
+
timestamp: Date.now(),
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
// Validate vote if not abstaining
|
|
234
|
+
if (!vote.abstain && !handler.validateVote(vote, session.proposals)) {
|
|
235
|
+
throw new Error(
|
|
236
|
+
`Invalid vote: ranking must reference valid proposal IDs for mechanism '${mechanism}'`
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
session.votes.push(vote);
|
|
241
|
+
this.audit('vote_cast', sessionId, input.agentId, {
|
|
242
|
+
voteId: vote.id,
|
|
243
|
+
abstain: vote.abstain,
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
await this.emit('voteReceived', { session, vote });
|
|
247
|
+
|
|
248
|
+
// Check if all participants have voted
|
|
249
|
+
if (this.allVotesCast(session)) {
|
|
250
|
+
await this.tryAutoResolve(session);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return vote;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Resolve a negotiation session
|
|
258
|
+
*/
|
|
259
|
+
async resolve(sessionId: string, force = false): Promise<Resolution> {
|
|
260
|
+
const session = this.getSession(sessionId);
|
|
261
|
+
|
|
262
|
+
if (session.status !== 'open' && session.status !== 'voting') {
|
|
263
|
+
if (session.resolution) {
|
|
264
|
+
return session.resolution;
|
|
265
|
+
}
|
|
266
|
+
throw new Error(`Session ${sessionId} is ${session.status}, cannot resolve`);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Check quorum unless forced
|
|
270
|
+
const mechanism = session.config.mechanism || session.config.votingMechanism || 'majority';
|
|
271
|
+
if (!force) {
|
|
272
|
+
const quorumMet = checkQuorum(
|
|
273
|
+
session.votes,
|
|
274
|
+
session.participants.length,
|
|
275
|
+
session.config,
|
|
276
|
+
mechanism
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
if (!quorumMet) {
|
|
280
|
+
return this.createFailedResolution(session, 'quorum_not_met');
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Count votes
|
|
285
|
+
const handler = getVotingHandler(mechanism);
|
|
286
|
+
const result = handler.count(
|
|
287
|
+
session.votes.filter((v) => !v.abstain),
|
|
288
|
+
session.proposals,
|
|
289
|
+
session.config,
|
|
290
|
+
session.round || session.currentRound || 1
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
const resolution = this.buildResolution(session, result);
|
|
294
|
+
|
|
295
|
+
// Update session
|
|
296
|
+
session.status = resolution.outcome === 'deadlock' ? 'deadlock' : 'resolved';
|
|
297
|
+
session.resolution = resolution;
|
|
298
|
+
session.resolvedAt = Date.now();
|
|
299
|
+
|
|
300
|
+
this.clearDeadline(sessionId);
|
|
301
|
+
this.audit('resolved', sessionId, undefined, {
|
|
302
|
+
outcome: resolution.outcome,
|
|
303
|
+
winnerId: resolution.winnerId,
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
await this.emit('sessionResolved', { session, resolution });
|
|
307
|
+
|
|
308
|
+
return resolution;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Get session by ID
|
|
313
|
+
*/
|
|
314
|
+
getSession(sessionId: string): NegotiationSession {
|
|
315
|
+
const session = this.sessions.get(sessionId);
|
|
316
|
+
if (!session) {
|
|
317
|
+
throw new Error(`Negotiation session not found: ${sessionId}`);
|
|
318
|
+
}
|
|
319
|
+
return session;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Get all active sessions
|
|
324
|
+
*/
|
|
325
|
+
getActiveSessions(): NegotiationSession[] {
|
|
326
|
+
return Array.from(this.sessions.values()).filter(
|
|
327
|
+
(s) => s.status === 'open' || s.status === 'voting'
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Get sessions for a specific agent
|
|
333
|
+
*/
|
|
334
|
+
getAgentSessions(agentId: string): NegotiationSession[] {
|
|
335
|
+
return Array.from(this.sessions.values()).filter((s) => s.participants.includes(agentId));
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Cancel a session
|
|
340
|
+
*/
|
|
341
|
+
async cancel(sessionId: string, _reason?: string): Promise<void> {
|
|
342
|
+
const session = this.getSession(sessionId);
|
|
343
|
+
|
|
344
|
+
if (session.status === 'resolved' || session.status === 'cancelled') {
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
session.status = 'cancelled';
|
|
349
|
+
const mechanism = session.config.mechanism || session.config.votingMechanism || 'majority';
|
|
350
|
+
session.resolution = {
|
|
351
|
+
sessionId,
|
|
352
|
+
outcome: 'cancelled',
|
|
353
|
+
tallies: [],
|
|
354
|
+
finalTallies: [],
|
|
355
|
+
mechanism,
|
|
356
|
+
rounds: session.round || 1,
|
|
357
|
+
resolvedAt: Date.now(),
|
|
358
|
+
participationRate: session.votes.length / session.participants.length,
|
|
359
|
+
timestamp: Date.now(),
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
this.clearDeadline(sessionId);
|
|
363
|
+
await this.emit('sessionResolved', {
|
|
364
|
+
session,
|
|
365
|
+
resolution: session.resolution,
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Escalate a deadlocked session
|
|
371
|
+
*/
|
|
372
|
+
async escalate(sessionId: string): Promise<Resolution> {
|
|
373
|
+
const session = this.getSession(sessionId);
|
|
374
|
+
|
|
375
|
+
if (!session.config.escalationPath) {
|
|
376
|
+
throw new Error(`No escalation path configured for session ${sessionId}`);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
session.status = 'escalated';
|
|
380
|
+
this.audit('escalated', sessionId, undefined, {
|
|
381
|
+
escalationPath: session.config.escalationPath,
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
const mechanism = session.config.mechanism || session.config.votingMechanism || 'majority';
|
|
385
|
+
const resolution: Resolution = {
|
|
386
|
+
sessionId,
|
|
387
|
+
outcome: 'escalated',
|
|
388
|
+
tallies: session.resolution?.tallies || [],
|
|
389
|
+
finalTallies: session.resolution?.finalTallies || [],
|
|
390
|
+
mechanism,
|
|
391
|
+
rounds: session.round || 1,
|
|
392
|
+
resolvedAt: Date.now(),
|
|
393
|
+
participationRate: session.votes.length / session.participants.length,
|
|
394
|
+
timestamp: Date.now(),
|
|
395
|
+
escalatedTo: session.config.escalationPath,
|
|
396
|
+
};
|
|
397
|
+
|
|
398
|
+
session.resolution = resolution;
|
|
399
|
+
await this.emit('sessionResolved', { session, resolution });
|
|
400
|
+
|
|
401
|
+
return resolution;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Add a participant to an open session
|
|
406
|
+
*/
|
|
407
|
+
addParticipant(sessionId: string, agentId: string): void {
|
|
408
|
+
const session = this.getSession(sessionId);
|
|
409
|
+
if (session.status !== 'open') {
|
|
410
|
+
throw new Error('Cannot add participant to non-open session');
|
|
411
|
+
}
|
|
412
|
+
if (!session.participants.includes(agentId)) {
|
|
413
|
+
session.participants.push(agentId);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Remove a participant from a session
|
|
419
|
+
*/
|
|
420
|
+
removeParticipant(sessionId: string, agentId: string): void {
|
|
421
|
+
const session = this.getSession(sessionId);
|
|
422
|
+
if (session.status !== 'open') {
|
|
423
|
+
throw new Error('Cannot remove participant from non-open session');
|
|
424
|
+
}
|
|
425
|
+
session.participants = session.participants.filter((p) => p !== agentId);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Subscribe to negotiation events
|
|
430
|
+
*/
|
|
431
|
+
on<K extends keyof NegotiationEvents>(
|
|
432
|
+
event: K,
|
|
433
|
+
handler: EventHandler<NegotiationEvents[K]>
|
|
434
|
+
): () => void {
|
|
435
|
+
if (!this.eventHandlers.has(event)) {
|
|
436
|
+
this.eventHandlers.set(event, new Set());
|
|
437
|
+
}
|
|
438
|
+
this.eventHandlers.get(event)!.add(handler);
|
|
439
|
+
|
|
440
|
+
return () => {
|
|
441
|
+
this.eventHandlers.get(event)?.delete(handler);
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Get audit log
|
|
447
|
+
*/
|
|
448
|
+
getAuditLog(sessionId?: string): AuditEntry[] {
|
|
449
|
+
if (sessionId) {
|
|
450
|
+
return this.auditLog.filter((e) => e.sessionId === sessionId);
|
|
451
|
+
}
|
|
452
|
+
return [...this.auditLog];
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Clear completed sessions older than maxAge
|
|
457
|
+
*/
|
|
458
|
+
pruneOldSessions(maxAgeMs: number): number {
|
|
459
|
+
const cutoff = Date.now() - maxAgeMs;
|
|
460
|
+
let pruned = 0;
|
|
461
|
+
|
|
462
|
+
for (const [id, session] of this.sessions) {
|
|
463
|
+
const sessionTime = session.resolvedAt || session.createdAt || session.startedAt || 0;
|
|
464
|
+
if (
|
|
465
|
+
(session.status === 'resolved' || session.status === 'cancelled') &&
|
|
466
|
+
sessionTime < cutoff
|
|
467
|
+
) {
|
|
468
|
+
this.sessions.delete(id);
|
|
469
|
+
pruned++;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
return pruned;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* Reset protocol state (for testing)
|
|
478
|
+
*/
|
|
479
|
+
reset(): void {
|
|
480
|
+
for (const deadline of this.deadlines.values()) {
|
|
481
|
+
clearTimeout(deadline.timer);
|
|
482
|
+
}
|
|
483
|
+
this.sessions.clear();
|
|
484
|
+
this.auditLog = [];
|
|
485
|
+
this.deadlines.clear();
|
|
486
|
+
this.eventHandlers.clear();
|
|
487
|
+
this.sessionCounter = 0;
|
|
488
|
+
this.proposalCounter = 0;
|
|
489
|
+
this.voteCounter = 0;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// ===========================================================================
|
|
493
|
+
// PRIVATE METHODS
|
|
494
|
+
// ===========================================================================
|
|
495
|
+
|
|
496
|
+
private generateSessionId(): string {
|
|
497
|
+
return `neg-${Date.now()}-${++this.sessionCounter}`;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
private generateProposalId(): string {
|
|
501
|
+
return `prop-${Date.now()}-${++this.proposalCounter}`;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
private generateVoteId(): string {
|
|
505
|
+
return `vote-${Date.now()}-${++this.voteCounter}`;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
private validateSessionOpen(session: NegotiationSession): void {
|
|
509
|
+
if (session.status !== 'open' && session.status !== 'voting') {
|
|
510
|
+
throw new Error(`Session ${session.id} is ${session.status}, not accepting input`);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
private validateParticipant(session: NegotiationSession, agentId: string): void {
|
|
515
|
+
if (!session.participants.includes(agentId)) {
|
|
516
|
+
throw new Error(`Agent ${agentId} is not a participant in session ${session.id}`);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
private validateNotDuplicate(session: NegotiationSession, agentId: string): void {
|
|
521
|
+
const roundVotes = session.votes.filter((v) => v.agentId === agentId && !v.supersededBy);
|
|
522
|
+
if (roundVotes.length > 0) {
|
|
523
|
+
throw new Error(`Agent ${agentId} has already voted in this round of session ${session.id}`);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
private allVotesCast(session: NegotiationSession): boolean {
|
|
528
|
+
const voted = new Set(session.votes.map((v) => v.agentId));
|
|
529
|
+
return session.participants.every((p) => {
|
|
530
|
+
const participantId = typeof p === 'string' ? p : p.id;
|
|
531
|
+
return voted.has(participantId);
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
private async tryAutoResolve(session: NegotiationSession): Promise<void> {
|
|
536
|
+
// Transition to voting if proposals exist
|
|
537
|
+
if (session.status === 'open' && session.proposals.length > 0) {
|
|
538
|
+
session.status = 'voting';
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// Auto-resolve if all have voted
|
|
542
|
+
if (this.allVotesCast(session)) {
|
|
543
|
+
await this.resolve(session.id);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
private buildResolution(session: NegotiationSession, result: VotingResult): Resolution {
|
|
548
|
+
const winningProposal = result.winnerId
|
|
549
|
+
? session.proposals.find((p) => p.id === result.winnerId)
|
|
550
|
+
: undefined;
|
|
551
|
+
|
|
552
|
+
const mechanism = session.config.mechanism || session.config.votingMechanism || 'majority';
|
|
553
|
+
const resolvedAt = Date.now();
|
|
554
|
+
|
|
555
|
+
return {
|
|
556
|
+
sessionId: session.id,
|
|
557
|
+
outcome: result.outcome,
|
|
558
|
+
winnerId: result.winnerId,
|
|
559
|
+
winner: winningProposal,
|
|
560
|
+
winningProposal,
|
|
561
|
+
tallies: result.tallies,
|
|
562
|
+
finalTallies: result.tallies,
|
|
563
|
+
mechanism,
|
|
564
|
+
rounds: session.round || 1,
|
|
565
|
+
round: session.round || session.currentRound,
|
|
566
|
+
resolvedAt,
|
|
567
|
+
consensusLevel: result.consensusLevel,
|
|
568
|
+
dissenters: result.dissenters,
|
|
569
|
+
participationRate: session.votes.length / session.participants.length,
|
|
570
|
+
timestamp: resolvedAt,
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
private createFailedResolution(
|
|
575
|
+
session: NegotiationSession,
|
|
576
|
+
outcome: ResolutionOutcome
|
|
577
|
+
): Resolution {
|
|
578
|
+
session.status = outcome === 'timeout' ? 'timeout' : 'deadlock';
|
|
579
|
+
|
|
580
|
+
const mechanism = session.config.mechanism || session.config.votingMechanism || 'majority';
|
|
581
|
+
const resolvedAt = Date.now();
|
|
582
|
+
|
|
583
|
+
const resolution: Resolution = {
|
|
584
|
+
sessionId: session.id,
|
|
585
|
+
outcome,
|
|
586
|
+
tallies: [],
|
|
587
|
+
finalTallies: [],
|
|
588
|
+
mechanism,
|
|
589
|
+
rounds: session.round || 1,
|
|
590
|
+
resolvedAt,
|
|
591
|
+
participationRate: session.votes.length / session.participants.length,
|
|
592
|
+
timestamp: resolvedAt,
|
|
593
|
+
};
|
|
594
|
+
|
|
595
|
+
session.resolution = resolution;
|
|
596
|
+
this.clearDeadline(session.id);
|
|
597
|
+
|
|
598
|
+
return resolution;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
private startDeadlineTimer(session: NegotiationSession): void {
|
|
602
|
+
const timeout = session.config.timeout || session.config.votingDeadline || 60000;
|
|
603
|
+
const timer = setTimeout(async () => {
|
|
604
|
+
if (session.status === 'open' || session.status === 'voting') {
|
|
605
|
+
this.audit('timeout', session.id, undefined, {
|
|
606
|
+
deadline: session.deadline,
|
|
607
|
+
});
|
|
608
|
+
await this.handleTimeout(session);
|
|
609
|
+
}
|
|
610
|
+
}, timeout);
|
|
611
|
+
|
|
612
|
+
this.deadlines.set(session.id, { sessionId: session.id, timer });
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
private async handleTimeout(session: NegotiationSession): Promise<void> {
|
|
616
|
+
// Try to resolve with current votes
|
|
617
|
+
if (session.votes.length > 0) {
|
|
618
|
+
try {
|
|
619
|
+
await this.resolve(session.id, true);
|
|
620
|
+
return;
|
|
621
|
+
} catch {
|
|
622
|
+
// Fall through to timeout
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
const resolution = this.createFailedResolution(session, 'timeout');
|
|
627
|
+
await this.emit('sessionResolved', { session, resolution });
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
private clearDeadline(sessionId: string): void {
|
|
631
|
+
const deadline = this.deadlines.get(sessionId);
|
|
632
|
+
if (deadline) {
|
|
633
|
+
clearTimeout(deadline.timer);
|
|
634
|
+
this.deadlines.delete(sessionId);
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
private audit(
|
|
639
|
+
action: AuditEntry['action'],
|
|
640
|
+
sessionId: string,
|
|
641
|
+
agentId: string | undefined,
|
|
642
|
+
details: Record<string, unknown>
|
|
643
|
+
): void {
|
|
644
|
+
this.auditLog.push({
|
|
645
|
+
timestamp: Date.now(),
|
|
646
|
+
sessionId,
|
|
647
|
+
action,
|
|
648
|
+
agentId,
|
|
649
|
+
details,
|
|
650
|
+
});
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
private async emit<K extends keyof NegotiationEvents>(
|
|
654
|
+
event: K,
|
|
655
|
+
data: NegotiationEvents[K]
|
|
656
|
+
): Promise<void> {
|
|
657
|
+
const handlers = this.eventHandlers.get(event);
|
|
658
|
+
if (handlers) {
|
|
659
|
+
for (const handler of handlers) {
|
|
660
|
+
try {
|
|
661
|
+
await handler(data);
|
|
662
|
+
} catch (err) {
|
|
663
|
+
console.error(`Error in negotiation event handler for ${event}:`, err);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
// =============================================================================
|
|
671
|
+
// SINGLETON INSTANCE
|
|
672
|
+
// =============================================================================
|
|
673
|
+
|
|
674
|
+
let defaultProtocol: NegotiationProtocol | null = null;
|
|
675
|
+
|
|
676
|
+
/**
|
|
677
|
+
* Get the default NegotiationProtocol instance
|
|
678
|
+
*/
|
|
679
|
+
export function getNegotiationProtocol(): NegotiationProtocol {
|
|
680
|
+
if (!defaultProtocol) {
|
|
681
|
+
defaultProtocol = new NegotiationProtocol();
|
|
682
|
+
}
|
|
683
|
+
return defaultProtocol;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
/**
|
|
687
|
+
* Reset the default protocol instance (for testing)
|
|
688
|
+
*/
|
|
689
|
+
export function resetNegotiationProtocol(): void {
|
|
690
|
+
if (defaultProtocol) {
|
|
691
|
+
defaultProtocol.reset();
|
|
692
|
+
defaultProtocol = null;
|
|
693
|
+
}
|
|
694
|
+
}
|