agentic-qe 3.6.0 → 3.6.2
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/.claude/agents/v3/qe-devils-advocate.md +218 -0
- package/.claude/agents/v3/qe-quality-criteria-recommender.md +2 -2
- package/.claude/skills/qe-iterative-loop/SKILL.md +1 -1
- package/.claude/skills/release/SKILL.md +17 -31
- package/.claude/skills/skills-manifest.json +1 -1
- package/README.md +38 -35
- package/package.json +1 -1
- package/scripts/cloud-db-config.json +1 -1
- package/v3/CHANGELOG.md +44 -0
- package/v3/README.md +7 -7
- package/v3/assets/agents/v3/qe-devils-advocate.md +218 -0
- package/v3/assets/agents/v3/qe-quality-criteria-recommender.md +2 -2
- package/v3/assets/skills/qe-iterative-loop/SKILL.md +1 -1
- package/v3/dist/agents/devils-advocate/agent.d.ts +103 -0
- package/v3/dist/agents/devils-advocate/agent.d.ts.map +1 -0
- package/v3/dist/agents/devils-advocate/agent.js +240 -0
- package/v3/dist/agents/devils-advocate/agent.js.map +1 -0
- package/v3/dist/agents/devils-advocate/index.d.ts +60 -0
- package/v3/dist/agents/devils-advocate/index.d.ts.map +1 -0
- package/v3/dist/agents/devils-advocate/index.js +72 -0
- package/v3/dist/agents/devils-advocate/index.js.map +1 -0
- package/v3/dist/agents/devils-advocate/strategies.d.ts +59 -0
- package/v3/dist/agents/devils-advocate/strategies.d.ts.map +1 -0
- package/v3/dist/agents/devils-advocate/strategies.js +438 -0
- package/v3/dist/agents/devils-advocate/strategies.js.map +1 -0
- package/v3/dist/agents/devils-advocate/types.d.ts +182 -0
- package/v3/dist/agents/devils-advocate/types.d.ts.map +1 -0
- package/v3/dist/agents/devils-advocate/types.js +96 -0
- package/v3/dist/agents/devils-advocate/types.js.map +1 -0
- package/v3/dist/agents/index.d.ts +20 -0
- package/v3/dist/agents/index.d.ts.map +1 -0
- package/v3/dist/agents/index.js +20 -0
- package/v3/dist/agents/index.js.map +1 -0
- package/v3/dist/cli/bundle.js +4489 -119
- package/v3/dist/coordination/agent-teams/adapter.d.ts +108 -0
- package/v3/dist/coordination/agent-teams/adapter.d.ts.map +1 -0
- package/v3/dist/coordination/agent-teams/adapter.js +316 -0
- package/v3/dist/coordination/agent-teams/adapter.js.map +1 -0
- package/v3/dist/coordination/agent-teams/domain-team-manager.d.ts +164 -0
- package/v3/dist/coordination/agent-teams/domain-team-manager.d.ts.map +1 -0
- package/v3/dist/coordination/agent-teams/domain-team-manager.js +342 -0
- package/v3/dist/coordination/agent-teams/domain-team-manager.js.map +1 -0
- package/v3/dist/coordination/agent-teams/index.d.ts +53 -0
- package/v3/dist/coordination/agent-teams/index.d.ts.map +1 -0
- package/v3/dist/coordination/agent-teams/index.js +61 -0
- package/v3/dist/coordination/agent-teams/index.js.map +1 -0
- package/v3/dist/coordination/agent-teams/mailbox.d.ts +142 -0
- package/v3/dist/coordination/agent-teams/mailbox.d.ts.map +1 -0
- package/v3/dist/coordination/agent-teams/mailbox.js +395 -0
- package/v3/dist/coordination/agent-teams/mailbox.js.map +1 -0
- package/v3/dist/coordination/agent-teams/tracing.d.ts +199 -0
- package/v3/dist/coordination/agent-teams/tracing.d.ts.map +1 -0
- package/v3/dist/coordination/agent-teams/tracing.js +308 -0
- package/v3/dist/coordination/agent-teams/tracing.js.map +1 -0
- package/v3/dist/coordination/agent-teams/types.d.ts +121 -0
- package/v3/dist/coordination/agent-teams/types.d.ts.map +1 -0
- package/v3/dist/coordination/agent-teams/types.js +17 -0
- package/v3/dist/coordination/agent-teams/types.js.map +1 -0
- package/v3/dist/coordination/circuit-breaker/breaker-registry.d.ts +146 -0
- package/v3/dist/coordination/circuit-breaker/breaker-registry.d.ts.map +1 -0
- package/v3/dist/coordination/circuit-breaker/breaker-registry.js +368 -0
- package/v3/dist/coordination/circuit-breaker/breaker-registry.js.map +1 -0
- package/v3/dist/coordination/circuit-breaker/domain-circuit-breaker.d.ts +134 -0
- package/v3/dist/coordination/circuit-breaker/domain-circuit-breaker.d.ts.map +1 -0
- package/v3/dist/coordination/circuit-breaker/domain-circuit-breaker.js +337 -0
- package/v3/dist/coordination/circuit-breaker/domain-circuit-breaker.js.map +1 -0
- package/v3/dist/coordination/circuit-breaker/index.d.ts +46 -0
- package/v3/dist/coordination/circuit-breaker/index.d.ts.map +1 -0
- package/v3/dist/coordination/circuit-breaker/index.js +51 -0
- package/v3/dist/coordination/circuit-breaker/index.js.map +1 -0
- package/v3/dist/coordination/circuit-breaker/types.d.ts +112 -0
- package/v3/dist/coordination/circuit-breaker/types.d.ts.map +1 -0
- package/v3/dist/coordination/circuit-breaker/types.js +10 -0
- package/v3/dist/coordination/circuit-breaker/types.js.map +1 -0
- package/v3/dist/coordination/competing-hypotheses/hypothesis-manager.d.ts +122 -0
- package/v3/dist/coordination/competing-hypotheses/hypothesis-manager.d.ts.map +1 -0
- package/v3/dist/coordination/competing-hypotheses/hypothesis-manager.js +377 -0
- package/v3/dist/coordination/competing-hypotheses/hypothesis-manager.js.map +1 -0
- package/v3/dist/coordination/competing-hypotheses/index.d.ts +34 -0
- package/v3/dist/coordination/competing-hypotheses/index.d.ts.map +1 -0
- package/v3/dist/coordination/competing-hypotheses/index.js +39 -0
- package/v3/dist/coordination/competing-hypotheses/index.js.map +1 -0
- package/v3/dist/coordination/competing-hypotheses/types.d.ts +134 -0
- package/v3/dist/coordination/competing-hypotheses/types.d.ts.map +1 -0
- package/v3/dist/coordination/competing-hypotheses/types.js +20 -0
- package/v3/dist/coordination/competing-hypotheses/types.js.map +1 -0
- package/v3/dist/coordination/dynamic-scaling/dynamic-scaler.d.ts +173 -0
- package/v3/dist/coordination/dynamic-scaling/dynamic-scaler.d.ts.map +1 -0
- package/v3/dist/coordination/dynamic-scaling/dynamic-scaler.js +368 -0
- package/v3/dist/coordination/dynamic-scaling/dynamic-scaler.js.map +1 -0
- package/v3/dist/coordination/dynamic-scaling/index.d.ts +38 -0
- package/v3/dist/coordination/dynamic-scaling/index.d.ts.map +1 -0
- package/v3/dist/coordination/dynamic-scaling/index.js +39 -0
- package/v3/dist/coordination/dynamic-scaling/index.js.map +1 -0
- package/v3/dist/coordination/dynamic-scaling/types.d.ts +147 -0
- package/v3/dist/coordination/dynamic-scaling/types.d.ts.map +1 -0
- package/v3/dist/coordination/dynamic-scaling/types.js +40 -0
- package/v3/dist/coordination/dynamic-scaling/types.js.map +1 -0
- package/v3/dist/coordination/federation/federation-mailbox.d.ts +215 -0
- package/v3/dist/coordination/federation/federation-mailbox.d.ts.map +1 -0
- package/v3/dist/coordination/federation/federation-mailbox.js +442 -0
- package/v3/dist/coordination/federation/federation-mailbox.js.map +1 -0
- package/v3/dist/coordination/federation/index.d.ts +38 -0
- package/v3/dist/coordination/federation/index.d.ts.map +1 -0
- package/v3/dist/coordination/federation/index.js +39 -0
- package/v3/dist/coordination/federation/index.js.map +1 -0
- package/v3/dist/coordination/federation/types.d.ts +103 -0
- package/v3/dist/coordination/federation/types.d.ts.map +1 -0
- package/v3/dist/coordination/federation/types.js +20 -0
- package/v3/dist/coordination/federation/types.js.map +1 -0
- package/v3/dist/coordination/fleet-tiers/index.d.ts +39 -0
- package/v3/dist/coordination/fleet-tiers/index.d.ts.map +1 -0
- package/v3/dist/coordination/fleet-tiers/index.js +44 -0
- package/v3/dist/coordination/fleet-tiers/index.js.map +1 -0
- package/v3/dist/coordination/fleet-tiers/tier-config.d.ts +60 -0
- package/v3/dist/coordination/fleet-tiers/tier-config.d.ts.map +1 -0
- package/v3/dist/coordination/fleet-tiers/tier-config.js +242 -0
- package/v3/dist/coordination/fleet-tiers/tier-config.js.map +1 -0
- package/v3/dist/coordination/fleet-tiers/tier-selector.d.ts +134 -0
- package/v3/dist/coordination/fleet-tiers/tier-selector.d.ts.map +1 -0
- package/v3/dist/coordination/fleet-tiers/tier-selector.js +373 -0
- package/v3/dist/coordination/fleet-tiers/tier-selector.js.map +1 -0
- package/v3/dist/coordination/fleet-tiers/types.d.ts +137 -0
- package/v3/dist/coordination/fleet-tiers/types.d.ts.map +1 -0
- package/v3/dist/coordination/fleet-tiers/types.js +20 -0
- package/v3/dist/coordination/fleet-tiers/types.js.map +1 -0
- package/v3/dist/coordination/index.d.ts +16 -0
- package/v3/dist/coordination/index.d.ts.map +1 -1
- package/v3/dist/coordination/index.js +29 -0
- package/v3/dist/coordination/index.js.map +1 -1
- package/v3/dist/coordination/queen-coordinator.d.ts +79 -0
- package/v3/dist/coordination/queen-coordinator.d.ts.map +1 -1
- package/v3/dist/coordination/queen-coordinator.js +363 -0
- package/v3/dist/coordination/queen-coordinator.js.map +1 -1
- package/v3/dist/coordination/task-dag/dag.d.ts +93 -0
- package/v3/dist/coordination/task-dag/dag.d.ts.map +1 -0
- package/v3/dist/coordination/task-dag/dag.js +496 -0
- package/v3/dist/coordination/task-dag/dag.js.map +1 -0
- package/v3/dist/coordination/task-dag/index.d.ts +54 -0
- package/v3/dist/coordination/task-dag/index.d.ts.map +1 -0
- package/v3/dist/coordination/task-dag/index.js +62 -0
- package/v3/dist/coordination/task-dag/index.js.map +1 -0
- package/v3/dist/coordination/task-dag/scheduler.d.ts +123 -0
- package/v3/dist/coordination/task-dag/scheduler.d.ts.map +1 -0
- package/v3/dist/coordination/task-dag/scheduler.js +262 -0
- package/v3/dist/coordination/task-dag/scheduler.js.map +1 -0
- package/v3/dist/coordination/task-dag/types.d.ts +103 -0
- package/v3/dist/coordination/task-dag/types.d.ts.map +1 -0
- package/v3/dist/coordination/task-dag/types.js +9 -0
- package/v3/dist/coordination/task-dag/types.js.map +1 -0
- package/v3/dist/domains/enterprise-integration/services/odata-service.js +3 -3
- package/v3/dist/domains/enterprise-integration/services/odata-service.js.map +1 -1
- package/v3/dist/domains/enterprise-integration/services/soap-wsdl-service.d.ts.map +1 -1
- package/v3/dist/domains/enterprise-integration/services/soap-wsdl-service.js +9 -4
- package/v3/dist/domains/enterprise-integration/services/soap-wsdl-service.js.map +1 -1
- package/v3/dist/domains/requirements-validation/services/quality-criteria/quality-criteria-service.js +1 -1
- package/v3/dist/domains/requirements-validation/services/quality-criteria/quality-criteria-service.js.map +1 -1
- package/v3/dist/hooks/index.d.ts +8 -1
- package/v3/dist/hooks/index.d.ts.map +1 -1
- package/v3/dist/hooks/index.js +8 -1
- package/v3/dist/hooks/index.js.map +1 -1
- package/v3/dist/hooks/quality-gate-enforcer.d.ts +134 -0
- package/v3/dist/hooks/quality-gate-enforcer.d.ts.map +1 -0
- package/v3/dist/hooks/quality-gate-enforcer.js +265 -0
- package/v3/dist/hooks/quality-gate-enforcer.js.map +1 -0
- package/v3/dist/hooks/reasoning-bank-pattern-store.d.ts +60 -0
- package/v3/dist/hooks/reasoning-bank-pattern-store.d.ts.map +1 -0
- package/v3/dist/hooks/reasoning-bank-pattern-store.js +179 -0
- package/v3/dist/hooks/reasoning-bank-pattern-store.js.map +1 -0
- package/v3/dist/hooks/task-completed-hook.d.ts +174 -0
- package/v3/dist/hooks/task-completed-hook.d.ts.map +1 -0
- package/v3/dist/hooks/task-completed-hook.js +330 -0
- package/v3/dist/hooks/task-completed-hook.js.map +1 -0
- package/v3/dist/hooks/teammate-idle-hook.d.ts +167 -0
- package/v3/dist/hooks/teammate-idle-hook.d.ts.map +1 -0
- package/v3/dist/hooks/teammate-idle-hook.js +332 -0
- package/v3/dist/hooks/teammate-idle-hook.js.map +1 -0
- package/v3/dist/index.d.ts +3 -0
- package/v3/dist/index.d.ts.map +1 -1
- package/v3/dist/index.js +4 -0
- package/v3/dist/index.js.map +1 -1
- package/v3/dist/init/agents-installer.d.ts +5 -1
- package/v3/dist/init/agents-installer.d.ts.map +1 -1
- package/v3/dist/init/agents-installer.js +13 -5
- package/v3/dist/init/agents-installer.js.map +1 -1
- package/v3/dist/init/phases/12-verification.d.ts.map +1 -1
- package/v3/dist/init/phases/12-verification.js +13 -1
- package/v3/dist/init/phases/12-verification.js.map +1 -1
- package/v3/dist/kernel/unified-memory.d.ts.map +1 -1
- package/v3/dist/kernel/unified-memory.js +303 -18
- package/v3/dist/kernel/unified-memory.js.map +1 -1
- package/v3/dist/learning/pattern-store.js +1 -1
- package/v3/dist/learning/pattern-store.js.map +1 -1
- package/v3/dist/learning/qe-patterns.d.ts +2 -0
- package/v3/dist/learning/qe-patterns.d.ts.map +1 -1
- package/v3/dist/learning/qe-patterns.js.map +1 -1
- package/v3/dist/learning/qe-reasoning-bank.d.ts.map +1 -1
- package/v3/dist/learning/qe-reasoning-bank.js +16 -3
- package/v3/dist/learning/qe-reasoning-bank.js.map +1 -1
- package/v3/dist/mcp/bundle.js +4473 -133
- package/v3/dist/sync/cloud/tunnel-manager.d.ts.map +1 -1
- package/v3/dist/sync/cloud/tunnel-manager.js +11 -0
- package/v3/dist/sync/cloud/tunnel-manager.js.map +1 -1
- package/v3/package.json +1 -1
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agentic QE v3 - Federation Mailbox
|
|
3
|
+
* ADR-064 Phase 4B: Routes messages between fleet instances
|
|
4
|
+
*
|
|
5
|
+
* The FederationMailbox manages cross-fleet communication for the AQE
|
|
6
|
+
* federation layer. It provides:
|
|
7
|
+
* - Service registration and discovery by domain
|
|
8
|
+
* - Heartbeat-based health monitoring with automatic degradation
|
|
9
|
+
* - Priority-based routing with fallback to domain-based discovery
|
|
10
|
+
* - Outbox/inbox queues for asynchronous transport integration
|
|
11
|
+
* - Subscription-based message delivery to local handlers
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* ```typescript
|
|
15
|
+
* import { createFederationMailbox } from './federation-mailbox.js';
|
|
16
|
+
*
|
|
17
|
+
* const mailbox = createFederationMailbox({ localFleetId: 'fleet-a' });
|
|
18
|
+
*
|
|
19
|
+
* // Register a remote fleet
|
|
20
|
+
* mailbox.registerService('fleet-b', 'Coverage Fleet', ['coverage-analysis']);
|
|
21
|
+
*
|
|
22
|
+
* // Add a route
|
|
23
|
+
* mailbox.addRoute('test-generation', 'coverage-analysis', 'fleet-b', 10);
|
|
24
|
+
*
|
|
25
|
+
* // Send a message
|
|
26
|
+
* const msg = mailbox.send('fleet-b', 'test-generation', 'coverage-analysis',
|
|
27
|
+
* 'task-request', { spec: 'auth-module' });
|
|
28
|
+
*
|
|
29
|
+
* // Transport layer drains outbox and delivers
|
|
30
|
+
* const pending = mailbox.drainOutbox();
|
|
31
|
+
*
|
|
32
|
+
* // Subscribe to incoming messages
|
|
33
|
+
* const unsub = mailbox.onMessage((msg) => console.log(msg.type));
|
|
34
|
+
* mailbox.receive(incomingMessage);
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
import type { FleetId, FederatedService, FederatedMessage, FederatedMessageType, FederationRoute, FederationHealth, FederationConfig } from './types.js';
|
|
38
|
+
/** Handler for incoming federated messages */
|
|
39
|
+
export type FederatedMessageHandler = (message: FederatedMessage) => void;
|
|
40
|
+
/**
|
|
41
|
+
* Routes messages between fleet instances in a federated AQE deployment.
|
|
42
|
+
* Manages service registration, routing rules, and message queues.
|
|
43
|
+
*
|
|
44
|
+
* Messages flow through an outbox (for sending) and inbox (for receiving).
|
|
45
|
+
* A transport layer (not included) is responsible for physically moving
|
|
46
|
+
* messages between fleet instances by draining the outbox and calling
|
|
47
|
+
* `receive()` on the target mailbox.
|
|
48
|
+
*/
|
|
49
|
+
export declare class FederationMailbox {
|
|
50
|
+
private readonly config;
|
|
51
|
+
private readonly services;
|
|
52
|
+
private readonly routes;
|
|
53
|
+
private readonly outbox;
|
|
54
|
+
private readonly inbox;
|
|
55
|
+
private readonly handlers;
|
|
56
|
+
private messagesSent;
|
|
57
|
+
private messagesReceived;
|
|
58
|
+
constructor(config?: Partial<FederationConfig>);
|
|
59
|
+
/**
|
|
60
|
+
* Register a remote fleet service in the federation.
|
|
61
|
+
* The service is immediately marked as 'active' with a fresh heartbeat.
|
|
62
|
+
*
|
|
63
|
+
* @param fleetId - Unique fleet identifier
|
|
64
|
+
* @param name - Human-readable service name
|
|
65
|
+
* @param domains - QE domains this service handles
|
|
66
|
+
* @param metadata - Optional key-value metadata
|
|
67
|
+
* @returns Immutable snapshot of the registered service
|
|
68
|
+
* @throws Error if a service with the same fleetId is already registered
|
|
69
|
+
*/
|
|
70
|
+
registerService(fleetId: FleetId, name: string, domains: string[], metadata?: Record<string, string>): FederatedService;
|
|
71
|
+
/**
|
|
72
|
+
* Deregister a service, removing it and all routes targeting it.
|
|
73
|
+
*
|
|
74
|
+
* @param fleetId - Fleet to deregister
|
|
75
|
+
* @returns True if the service existed and was removed
|
|
76
|
+
*/
|
|
77
|
+
deregisterService(fleetId: FleetId): boolean;
|
|
78
|
+
/**
|
|
79
|
+
* Record a heartbeat from a remote service, updating its last-seen
|
|
80
|
+
* timestamp. If the service was degraded or unreachable, it is
|
|
81
|
+
* promoted back to 'active'.
|
|
82
|
+
*
|
|
83
|
+
* @param fleetId - Fleet sending the heartbeat
|
|
84
|
+
* @returns True if the service exists and the heartbeat was recorded
|
|
85
|
+
*/
|
|
86
|
+
heartbeat(fleetId: FleetId): boolean;
|
|
87
|
+
/**
|
|
88
|
+
* Add a routing rule for cross-fleet message delivery.
|
|
89
|
+
* Routes are sorted by priority (descending) so highest-priority routes
|
|
90
|
+
* are evaluated first during resolution.
|
|
91
|
+
*
|
|
92
|
+
* @param sourceDomain - Source domain that sends messages
|
|
93
|
+
* @param targetDomain - Target domain that receives messages
|
|
94
|
+
* @param targetFleetId - Fleet this route delivers to
|
|
95
|
+
* @param priority - Route priority (higher = preferred, default 0)
|
|
96
|
+
* @returns Immutable snapshot of the created route
|
|
97
|
+
* @throws Error if the maximum number of routes has been reached
|
|
98
|
+
*/
|
|
99
|
+
addRoute(sourceDomain: string, targetDomain: string, targetFleetId: FleetId, priority?: number): FederationRoute;
|
|
100
|
+
/**
|
|
101
|
+
* Remove a routing rule matching the source domain and target fleet.
|
|
102
|
+
*
|
|
103
|
+
* @param sourceDomain - Source domain of the route
|
|
104
|
+
* @param targetFleetId - Target fleet of the route
|
|
105
|
+
* @returns True if a matching route was found and removed
|
|
106
|
+
*/
|
|
107
|
+
removeRoute(sourceDomain: string, targetFleetId: FleetId): boolean;
|
|
108
|
+
/**
|
|
109
|
+
* Send a message to a specific fleet or resolve the target via routing.
|
|
110
|
+
* When `targetFleetId` is `'any'`, the mailbox uses priority-based route
|
|
111
|
+
* resolution with fallback to domain-based service discovery.
|
|
112
|
+
*
|
|
113
|
+
* Messages are placed in the outbox for the transport layer to drain
|
|
114
|
+
* and deliver to the target fleet's mailbox.
|
|
115
|
+
*
|
|
116
|
+
* @param targetFleetId - Specific fleet ID or 'any' for route resolution
|
|
117
|
+
* @param sourceDomain - Domain context of the sender
|
|
118
|
+
* @param targetDomain - Domain the message is intended for
|
|
119
|
+
* @param type - Message type classification
|
|
120
|
+
* @param payload - Message payload
|
|
121
|
+
* @param options - Optional correlation ID and TTL
|
|
122
|
+
* @returns The created message
|
|
123
|
+
* @throws Error if no route can be resolved when targetFleetId is 'any'
|
|
124
|
+
*/
|
|
125
|
+
send(targetFleetId: FleetId | 'any', sourceDomain: string, targetDomain: string, type: FederatedMessageType, payload: unknown, options?: {
|
|
126
|
+
correlationId?: string;
|
|
127
|
+
ttl?: number;
|
|
128
|
+
}): FederatedMessage;
|
|
129
|
+
/**
|
|
130
|
+
* Receive a message from a remote fleet. The message is placed in the
|
|
131
|
+
* inbox and all registered handlers are notified synchronously.
|
|
132
|
+
*
|
|
133
|
+
* @param message - The incoming federated message
|
|
134
|
+
*/
|
|
135
|
+
receive(message: FederatedMessage): void;
|
|
136
|
+
/**
|
|
137
|
+
* Subscribe to incoming federated messages.
|
|
138
|
+
*
|
|
139
|
+
* @param handler - Callback invoked on each received message
|
|
140
|
+
* @returns Unsubscribe function
|
|
141
|
+
*/
|
|
142
|
+
onMessage(handler: FederatedMessageHandler): () => void;
|
|
143
|
+
/**
|
|
144
|
+
* Drain the outbox, returning all pending outbound messages.
|
|
145
|
+
* The outbox is cleared after draining. The transport layer should
|
|
146
|
+
* call this periodically to deliver messages to remote fleets.
|
|
147
|
+
*
|
|
148
|
+
* @returns Array of messages that were in the outbox
|
|
149
|
+
*/
|
|
150
|
+
drainOutbox(): FederatedMessage[];
|
|
151
|
+
/**
|
|
152
|
+
* Drain the inbox, returning all pending inbound messages.
|
|
153
|
+
* The inbox is cleared after draining.
|
|
154
|
+
*
|
|
155
|
+
* @returns Array of messages that were in the inbox
|
|
156
|
+
*/
|
|
157
|
+
drainInbox(): FederatedMessage[];
|
|
158
|
+
/**
|
|
159
|
+
* Find active services that handle a specific domain.
|
|
160
|
+
*
|
|
161
|
+
* @param domain - QE domain to search for
|
|
162
|
+
* @returns Array of active services that list the domain
|
|
163
|
+
*/
|
|
164
|
+
findServicesByDomain(domain: string): FederatedService[];
|
|
165
|
+
/**
|
|
166
|
+
* Get a service by its fleet ID.
|
|
167
|
+
*
|
|
168
|
+
* @param fleetId - Fleet identifier to look up
|
|
169
|
+
* @returns Service snapshot or undefined if not found
|
|
170
|
+
*/
|
|
171
|
+
getService(fleetId: FleetId): FederatedService | undefined;
|
|
172
|
+
/**
|
|
173
|
+
* List all registered services (regardless of status).
|
|
174
|
+
*
|
|
175
|
+
* @returns Array of service snapshots
|
|
176
|
+
*/
|
|
177
|
+
listServices(): FederatedService[];
|
|
178
|
+
/**
|
|
179
|
+
* List all routing rules.
|
|
180
|
+
*
|
|
181
|
+
* @returns Array of route snapshots
|
|
182
|
+
*/
|
|
183
|
+
listRoutes(): FederationRoute[];
|
|
184
|
+
/**
|
|
185
|
+
* Check service health and update statuses based on heartbeat age.
|
|
186
|
+
* Services that have not sent a heartbeat within `serviceTimeoutMs`
|
|
187
|
+
* are marked 'unreachable'. Services past 60% of the timeout are
|
|
188
|
+
* marked 'degraded'.
|
|
189
|
+
*/
|
|
190
|
+
checkHealth(): void;
|
|
191
|
+
/**
|
|
192
|
+
* Get a summary of the federation's current health.
|
|
193
|
+
*
|
|
194
|
+
* @returns Health snapshot with counts and counters
|
|
195
|
+
*/
|
|
196
|
+
getHealth(): FederationHealth;
|
|
197
|
+
/**
|
|
198
|
+
* Dispose all state, clearing services, routes, queues, and handlers.
|
|
199
|
+
*/
|
|
200
|
+
dispose(): void;
|
|
201
|
+
/**
|
|
202
|
+
* Resolve a route for the given source -> target domain pair.
|
|
203
|
+
* First checks explicit routes (sorted by priority), then falls back
|
|
204
|
+
* to domain-based service discovery.
|
|
205
|
+
*/
|
|
206
|
+
private resolveRoute;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Create a new FederationMailbox instance.
|
|
210
|
+
*
|
|
211
|
+
* @param config - Optional partial configuration (merged with defaults)
|
|
212
|
+
* @returns A fresh FederationMailbox
|
|
213
|
+
*/
|
|
214
|
+
export declare function createFederationMailbox(config?: Partial<FederationConfig>): FederationMailbox;
|
|
215
|
+
//# sourceMappingURL=federation-mailbox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"federation-mailbox.d.ts","sourceRoot":"","sources":["../../../src/coordination/federation/federation-mailbox.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAGH,OAAO,KAAK,EACV,OAAO,EACP,gBAAgB,EAChB,gBAAgB,EAChB,oBAAoB,EACpB,eAAe,EACf,gBAAgB,EAChB,gBAAgB,EAEjB,MAAM,YAAY,CAAC;AAOpB,8CAA8C;AAC9C,MAAM,MAAM,uBAAuB,GAAG,CAAC,OAAO,EAAE,gBAAgB,KAAK,IAAI,CAAC;AAM1E;;;;;;;;GAQG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAmB;IAC1C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAsC;IAC/D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsB;IAC7C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA0B;IACjD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA0B;IAChD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAiC;IAC1D,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,gBAAgB,CAAK;gBAEjB,MAAM,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC;IAQ9C;;;;;;;;;;OAUG;IACH,eAAe,CACb,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EAAE,EACjB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAChC,gBAAgB;IAkBnB;;;;;OAKG;IACH,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO;IAgB5C;;;;;;;OAOG;IACH,SAAS,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO;IAcpC;;;;;;;;;;;OAWG;IACH,QAAQ,CACN,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,OAAO,EACtB,QAAQ,GAAE,MAAU,GACnB,eAAe;IAgBlB;;;;;;OAMG;IACH,WAAW,CAAC,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,OAAO,GAAG,OAAO;IAalE;;;;;;;;;;;;;;;;OAgBG;IACH,IAAI,CACF,aAAa,EAAE,OAAO,GAAG,KAAK,EAC9B,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,oBAAoB,EAC1B,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE;QAAE,aAAa,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,GACjD,gBAAgB;IAkCnB;;;;;OAKG;IACH,OAAO,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI;IAexC;;;;;OAKG;IACH,SAAS,CAAC,OAAO,EAAE,uBAAuB,GAAG,MAAM,IAAI;IAYvD;;;;;;OAMG;IACH,WAAW,IAAI,gBAAgB,EAAE;IAMjC;;;;;OAKG;IACH,UAAU,IAAI,gBAAgB,EAAE;IAUhC;;;;;OAKG;IACH,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,gBAAgB,EAAE;IAMxD;;;;;OAKG;IACH,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,gBAAgB,GAAG,SAAS;IAK1D;;;;OAIG;IACH,YAAY,IAAI,gBAAgB,EAAE;IAIlC;;;;OAIG;IACH,UAAU,IAAI,eAAe,EAAE;IAQ/B;;;;;OAKG;IACH,WAAW,IAAI,IAAI;IAanB;;;;OAIG;IACH,SAAS,IAAI,gBAAgB;IAgB7B;;OAEG;IACH,OAAO,IAAI,IAAI;IAYf;;;;OAIG;IACH,OAAO,CAAC,YAAY;CAsBrB;AAiED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,GACjC,iBAAiB,CAEnB"}
|
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agentic QE v3 - Federation Mailbox
|
|
3
|
+
* ADR-064 Phase 4B: Routes messages between fleet instances
|
|
4
|
+
*
|
|
5
|
+
* The FederationMailbox manages cross-fleet communication for the AQE
|
|
6
|
+
* federation layer. It provides:
|
|
7
|
+
* - Service registration and discovery by domain
|
|
8
|
+
* - Heartbeat-based health monitoring with automatic degradation
|
|
9
|
+
* - Priority-based routing with fallback to domain-based discovery
|
|
10
|
+
* - Outbox/inbox queues for asynchronous transport integration
|
|
11
|
+
* - Subscription-based message delivery to local handlers
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* ```typescript
|
|
15
|
+
* import { createFederationMailbox } from './federation-mailbox.js';
|
|
16
|
+
*
|
|
17
|
+
* const mailbox = createFederationMailbox({ localFleetId: 'fleet-a' });
|
|
18
|
+
*
|
|
19
|
+
* // Register a remote fleet
|
|
20
|
+
* mailbox.registerService('fleet-b', 'Coverage Fleet', ['coverage-analysis']);
|
|
21
|
+
*
|
|
22
|
+
* // Add a route
|
|
23
|
+
* mailbox.addRoute('test-generation', 'coverage-analysis', 'fleet-b', 10);
|
|
24
|
+
*
|
|
25
|
+
* // Send a message
|
|
26
|
+
* const msg = mailbox.send('fleet-b', 'test-generation', 'coverage-analysis',
|
|
27
|
+
* 'task-request', { spec: 'auth-module' });
|
|
28
|
+
*
|
|
29
|
+
* // Transport layer drains outbox and delivers
|
|
30
|
+
* const pending = mailbox.drainOutbox();
|
|
31
|
+
*
|
|
32
|
+
* // Subscribe to incoming messages
|
|
33
|
+
* const unsub = mailbox.onMessage((msg) => console.log(msg.type));
|
|
34
|
+
* mailbox.receive(incomingMessage);
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
import { randomUUID } from 'node:crypto';
|
|
38
|
+
import { DEFAULT_FEDERATION_CONFIG } from './types.js';
|
|
39
|
+
// ============================================================================
|
|
40
|
+
// Federation Mailbox
|
|
41
|
+
// ============================================================================
|
|
42
|
+
/**
|
|
43
|
+
* Routes messages between fleet instances in a federated AQE deployment.
|
|
44
|
+
* Manages service registration, routing rules, and message queues.
|
|
45
|
+
*
|
|
46
|
+
* Messages flow through an outbox (for sending) and inbox (for receiving).
|
|
47
|
+
* A transport layer (not included) is responsible for physically moving
|
|
48
|
+
* messages between fleet instances by draining the outbox and calling
|
|
49
|
+
* `receive()` on the target mailbox.
|
|
50
|
+
*/
|
|
51
|
+
export class FederationMailbox {
|
|
52
|
+
config;
|
|
53
|
+
services = new Map();
|
|
54
|
+
routes = [];
|
|
55
|
+
outbox = [];
|
|
56
|
+
inbox = [];
|
|
57
|
+
handlers = [];
|
|
58
|
+
messagesSent = 0;
|
|
59
|
+
messagesReceived = 0;
|
|
60
|
+
constructor(config) {
|
|
61
|
+
this.config = { ...DEFAULT_FEDERATION_CONFIG, ...config };
|
|
62
|
+
}
|
|
63
|
+
// ============================================================================
|
|
64
|
+
// Service Registration
|
|
65
|
+
// ============================================================================
|
|
66
|
+
/**
|
|
67
|
+
* Register a remote fleet service in the federation.
|
|
68
|
+
* The service is immediately marked as 'active' with a fresh heartbeat.
|
|
69
|
+
*
|
|
70
|
+
* @param fleetId - Unique fleet identifier
|
|
71
|
+
* @param name - Human-readable service name
|
|
72
|
+
* @param domains - QE domains this service handles
|
|
73
|
+
* @param metadata - Optional key-value metadata
|
|
74
|
+
* @returns Immutable snapshot of the registered service
|
|
75
|
+
* @throws Error if a service with the same fleetId is already registered
|
|
76
|
+
*/
|
|
77
|
+
registerService(fleetId, name, domains, metadata) {
|
|
78
|
+
if (this.services.has(fleetId)) {
|
|
79
|
+
throw new Error(`Service '${fleetId}' already registered`);
|
|
80
|
+
}
|
|
81
|
+
const now = Date.now();
|
|
82
|
+
const service = {
|
|
83
|
+
fleetId,
|
|
84
|
+
name,
|
|
85
|
+
domains: [...domains],
|
|
86
|
+
status: 'active',
|
|
87
|
+
registeredAt: now,
|
|
88
|
+
lastHeartbeat: now,
|
|
89
|
+
metadata,
|
|
90
|
+
};
|
|
91
|
+
this.services.set(fleetId, service);
|
|
92
|
+
return toServiceSnapshot(service);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Deregister a service, removing it and all routes targeting it.
|
|
96
|
+
*
|
|
97
|
+
* @param fleetId - Fleet to deregister
|
|
98
|
+
* @returns True if the service existed and was removed
|
|
99
|
+
*/
|
|
100
|
+
deregisterService(fleetId) {
|
|
101
|
+
const service = this.services.get(fleetId);
|
|
102
|
+
if (!service)
|
|
103
|
+
return false;
|
|
104
|
+
service.status = 'deregistered';
|
|
105
|
+
// Remove routes pointing to this fleet
|
|
106
|
+
for (let i = this.routes.length - 1; i >= 0; i--) {
|
|
107
|
+
if (this.routes[i].targetFleetId === fleetId) {
|
|
108
|
+
this.routes.splice(i, 1);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
this.services.delete(fleetId);
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Record a heartbeat from a remote service, updating its last-seen
|
|
116
|
+
* timestamp. If the service was degraded or unreachable, it is
|
|
117
|
+
* promoted back to 'active'.
|
|
118
|
+
*
|
|
119
|
+
* @param fleetId - Fleet sending the heartbeat
|
|
120
|
+
* @returns True if the service exists and the heartbeat was recorded
|
|
121
|
+
*/
|
|
122
|
+
heartbeat(fleetId) {
|
|
123
|
+
const service = this.services.get(fleetId);
|
|
124
|
+
if (!service)
|
|
125
|
+
return false;
|
|
126
|
+
service.lastHeartbeat = Date.now();
|
|
127
|
+
if (service.status === 'degraded' || service.status === 'unreachable') {
|
|
128
|
+
service.status = 'active';
|
|
129
|
+
}
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
// ============================================================================
|
|
133
|
+
// Routing
|
|
134
|
+
// ============================================================================
|
|
135
|
+
/**
|
|
136
|
+
* Add a routing rule for cross-fleet message delivery.
|
|
137
|
+
* Routes are sorted by priority (descending) so highest-priority routes
|
|
138
|
+
* are evaluated first during resolution.
|
|
139
|
+
*
|
|
140
|
+
* @param sourceDomain - Source domain that sends messages
|
|
141
|
+
* @param targetDomain - Target domain that receives messages
|
|
142
|
+
* @param targetFleetId - Fleet this route delivers to
|
|
143
|
+
* @param priority - Route priority (higher = preferred, default 0)
|
|
144
|
+
* @returns Immutable snapshot of the created route
|
|
145
|
+
* @throws Error if the maximum number of routes has been reached
|
|
146
|
+
*/
|
|
147
|
+
addRoute(sourceDomain, targetDomain, targetFleetId, priority = 0) {
|
|
148
|
+
if (this.routes.length >= this.config.maxRoutes) {
|
|
149
|
+
throw new Error(`Maximum routes (${this.config.maxRoutes}) reached`);
|
|
150
|
+
}
|
|
151
|
+
const route = {
|
|
152
|
+
sourceDomain,
|
|
153
|
+
targetDomain,
|
|
154
|
+
targetFleetId,
|
|
155
|
+
priority,
|
|
156
|
+
active: true,
|
|
157
|
+
};
|
|
158
|
+
this.routes.push(route);
|
|
159
|
+
this.routes.sort((a, b) => b.priority - a.priority);
|
|
160
|
+
return toRouteSnapshot(route);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Remove a routing rule matching the source domain and target fleet.
|
|
164
|
+
*
|
|
165
|
+
* @param sourceDomain - Source domain of the route
|
|
166
|
+
* @param targetFleetId - Target fleet of the route
|
|
167
|
+
* @returns True if a matching route was found and removed
|
|
168
|
+
*/
|
|
169
|
+
removeRoute(sourceDomain, targetFleetId) {
|
|
170
|
+
const idx = this.routes.findIndex(r => r.sourceDomain === sourceDomain && r.targetFleetId === targetFleetId);
|
|
171
|
+
if (idx < 0)
|
|
172
|
+
return false;
|
|
173
|
+
this.routes.splice(idx, 1);
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
// ============================================================================
|
|
177
|
+
// Message Sending
|
|
178
|
+
// ============================================================================
|
|
179
|
+
/**
|
|
180
|
+
* Send a message to a specific fleet or resolve the target via routing.
|
|
181
|
+
* When `targetFleetId` is `'any'`, the mailbox uses priority-based route
|
|
182
|
+
* resolution with fallback to domain-based service discovery.
|
|
183
|
+
*
|
|
184
|
+
* Messages are placed in the outbox for the transport layer to drain
|
|
185
|
+
* and deliver to the target fleet's mailbox.
|
|
186
|
+
*
|
|
187
|
+
* @param targetFleetId - Specific fleet ID or 'any' for route resolution
|
|
188
|
+
* @param sourceDomain - Domain context of the sender
|
|
189
|
+
* @param targetDomain - Domain the message is intended for
|
|
190
|
+
* @param type - Message type classification
|
|
191
|
+
* @param payload - Message payload
|
|
192
|
+
* @param options - Optional correlation ID and TTL
|
|
193
|
+
* @returns The created message
|
|
194
|
+
* @throws Error if no route can be resolved when targetFleetId is 'any'
|
|
195
|
+
*/
|
|
196
|
+
send(targetFleetId, sourceDomain, targetDomain, type, payload, options) {
|
|
197
|
+
const resolvedTarget = targetFleetId === 'any'
|
|
198
|
+
? this.resolveRoute(sourceDomain, targetDomain)
|
|
199
|
+
: targetFleetId;
|
|
200
|
+
if (!resolvedTarget) {
|
|
201
|
+
throw new Error(`No route found for ${sourceDomain} -> ${targetDomain}`);
|
|
202
|
+
}
|
|
203
|
+
const message = {
|
|
204
|
+
id: randomUUID(),
|
|
205
|
+
sourceFleetId: this.config.localFleetId,
|
|
206
|
+
targetFleetId: resolvedTarget,
|
|
207
|
+
sourceDomain,
|
|
208
|
+
targetDomain,
|
|
209
|
+
type,
|
|
210
|
+
payload,
|
|
211
|
+
timestamp: Date.now(),
|
|
212
|
+
ttl: options?.ttl,
|
|
213
|
+
correlationId: options?.correlationId,
|
|
214
|
+
};
|
|
215
|
+
if (this.outbox.length >= this.config.maxPendingMessages) {
|
|
216
|
+
this.outbox.shift(); // Drop oldest to stay within limit
|
|
217
|
+
}
|
|
218
|
+
this.outbox.push(message);
|
|
219
|
+
this.messagesSent++;
|
|
220
|
+
return message;
|
|
221
|
+
}
|
|
222
|
+
// ============================================================================
|
|
223
|
+
// Message Receiving
|
|
224
|
+
// ============================================================================
|
|
225
|
+
/**
|
|
226
|
+
* Receive a message from a remote fleet. The message is placed in the
|
|
227
|
+
* inbox and all registered handlers are notified synchronously.
|
|
228
|
+
*
|
|
229
|
+
* @param message - The incoming federated message
|
|
230
|
+
*/
|
|
231
|
+
receive(message) {
|
|
232
|
+
if (this.inbox.length >= this.config.maxPendingMessages) {
|
|
233
|
+
this.inbox.shift(); // Drop oldest to stay within limit
|
|
234
|
+
}
|
|
235
|
+
this.inbox.push(message);
|
|
236
|
+
this.messagesReceived++;
|
|
237
|
+
for (const handler of this.handlers) {
|
|
238
|
+
try {
|
|
239
|
+
handler(message);
|
|
240
|
+
}
|
|
241
|
+
catch {
|
|
242
|
+
// Swallow handler errors to avoid breaking delivery
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Subscribe to incoming federated messages.
|
|
248
|
+
*
|
|
249
|
+
* @param handler - Callback invoked on each received message
|
|
250
|
+
* @returns Unsubscribe function
|
|
251
|
+
*/
|
|
252
|
+
onMessage(handler) {
|
|
253
|
+
this.handlers.push(handler);
|
|
254
|
+
return () => {
|
|
255
|
+
const idx = this.handlers.indexOf(handler);
|
|
256
|
+
if (idx >= 0)
|
|
257
|
+
this.handlers.splice(idx, 1);
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
// ============================================================================
|
|
261
|
+
// Queue Draining
|
|
262
|
+
// ============================================================================
|
|
263
|
+
/**
|
|
264
|
+
* Drain the outbox, returning all pending outbound messages.
|
|
265
|
+
* The outbox is cleared after draining. The transport layer should
|
|
266
|
+
* call this periodically to deliver messages to remote fleets.
|
|
267
|
+
*
|
|
268
|
+
* @returns Array of messages that were in the outbox
|
|
269
|
+
*/
|
|
270
|
+
drainOutbox() {
|
|
271
|
+
const messages = [...this.outbox];
|
|
272
|
+
this.outbox.length = 0;
|
|
273
|
+
return messages;
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Drain the inbox, returning all pending inbound messages.
|
|
277
|
+
* The inbox is cleared after draining.
|
|
278
|
+
*
|
|
279
|
+
* @returns Array of messages that were in the inbox
|
|
280
|
+
*/
|
|
281
|
+
drainInbox() {
|
|
282
|
+
const messages = [...this.inbox];
|
|
283
|
+
this.inbox.length = 0;
|
|
284
|
+
return messages;
|
|
285
|
+
}
|
|
286
|
+
// ============================================================================
|
|
287
|
+
// Service Discovery
|
|
288
|
+
// ============================================================================
|
|
289
|
+
/**
|
|
290
|
+
* Find active services that handle a specific domain.
|
|
291
|
+
*
|
|
292
|
+
* @param domain - QE domain to search for
|
|
293
|
+
* @returns Array of active services that list the domain
|
|
294
|
+
*/
|
|
295
|
+
findServicesByDomain(domain) {
|
|
296
|
+
return Array.from(this.services.values())
|
|
297
|
+
.filter(s => s.status === 'active' && s.domains.includes(domain))
|
|
298
|
+
.map(toServiceSnapshot);
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Get a service by its fleet ID.
|
|
302
|
+
*
|
|
303
|
+
* @param fleetId - Fleet identifier to look up
|
|
304
|
+
* @returns Service snapshot or undefined if not found
|
|
305
|
+
*/
|
|
306
|
+
getService(fleetId) {
|
|
307
|
+
const s = this.services.get(fleetId);
|
|
308
|
+
return s ? toServiceSnapshot(s) : undefined;
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* List all registered services (regardless of status).
|
|
312
|
+
*
|
|
313
|
+
* @returns Array of service snapshots
|
|
314
|
+
*/
|
|
315
|
+
listServices() {
|
|
316
|
+
return Array.from(this.services.values()).map(toServiceSnapshot);
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* List all routing rules.
|
|
320
|
+
*
|
|
321
|
+
* @returns Array of route snapshots
|
|
322
|
+
*/
|
|
323
|
+
listRoutes() {
|
|
324
|
+
return this.routes.map(toRouteSnapshot);
|
|
325
|
+
}
|
|
326
|
+
// ============================================================================
|
|
327
|
+
// Health Monitoring
|
|
328
|
+
// ============================================================================
|
|
329
|
+
/**
|
|
330
|
+
* Check service health and update statuses based on heartbeat age.
|
|
331
|
+
* Services that have not sent a heartbeat within `serviceTimeoutMs`
|
|
332
|
+
* are marked 'unreachable'. Services past 60% of the timeout are
|
|
333
|
+
* marked 'degraded'.
|
|
334
|
+
*/
|
|
335
|
+
checkHealth() {
|
|
336
|
+
const now = Date.now();
|
|
337
|
+
for (const service of this.services.values()) {
|
|
338
|
+
if (service.status === 'deregistered')
|
|
339
|
+
continue;
|
|
340
|
+
const elapsed = now - service.lastHeartbeat;
|
|
341
|
+
if (elapsed > this.config.serviceTimeoutMs) {
|
|
342
|
+
service.status = 'unreachable';
|
|
343
|
+
}
|
|
344
|
+
else if (elapsed > this.config.serviceTimeoutMs * 0.6) {
|
|
345
|
+
service.status = 'degraded';
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Get a summary of the federation's current health.
|
|
351
|
+
*
|
|
352
|
+
* @returns Health snapshot with counts and counters
|
|
353
|
+
*/
|
|
354
|
+
getHealth() {
|
|
355
|
+
return {
|
|
356
|
+
localFleetId: this.config.localFleetId,
|
|
357
|
+
connectedServices: Array.from(this.services.values())
|
|
358
|
+
.filter(s => s.status === 'active').length,
|
|
359
|
+
activeRoutes: this.routes.filter(r => r.active).length,
|
|
360
|
+
pendingMessages: this.outbox.length + this.inbox.length,
|
|
361
|
+
messagesSent: this.messagesSent,
|
|
362
|
+
messagesReceived: this.messagesReceived,
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
// ============================================================================
|
|
366
|
+
// Lifecycle
|
|
367
|
+
// ============================================================================
|
|
368
|
+
/**
|
|
369
|
+
* Dispose all state, clearing services, routes, queues, and handlers.
|
|
370
|
+
*/
|
|
371
|
+
dispose() {
|
|
372
|
+
this.services.clear();
|
|
373
|
+
this.routes.length = 0;
|
|
374
|
+
this.outbox.length = 0;
|
|
375
|
+
this.inbox.length = 0;
|
|
376
|
+
this.handlers.length = 0;
|
|
377
|
+
}
|
|
378
|
+
// ============================================================================
|
|
379
|
+
// Private Helpers
|
|
380
|
+
// ============================================================================
|
|
381
|
+
/**
|
|
382
|
+
* Resolve a route for the given source -> target domain pair.
|
|
383
|
+
* First checks explicit routes (sorted by priority), then falls back
|
|
384
|
+
* to domain-based service discovery.
|
|
385
|
+
*/
|
|
386
|
+
resolveRoute(sourceDomain, targetDomain) {
|
|
387
|
+
// Check explicit routes in priority order
|
|
388
|
+
for (const route of this.routes) {
|
|
389
|
+
if (!route.active)
|
|
390
|
+
continue;
|
|
391
|
+
if (route.sourceDomain === sourceDomain &&
|
|
392
|
+
route.targetDomain === targetDomain) {
|
|
393
|
+
const service = this.services.get(route.targetFleetId);
|
|
394
|
+
if (service && service.status === 'active') {
|
|
395
|
+
return route.targetFleetId;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
// Fallback: find any active service that handles the target domain
|
|
400
|
+
const candidates = this.findServicesByDomain(targetDomain);
|
|
401
|
+
return candidates.length > 0 ? candidates[0].fleetId : undefined;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
// ============================================================================
|
|
405
|
+
// Snapshot Helpers
|
|
406
|
+
// ============================================================================
|
|
407
|
+
/** Create an immutable snapshot of a mutable service */
|
|
408
|
+
function toServiceSnapshot(service) {
|
|
409
|
+
return {
|
|
410
|
+
fleetId: service.fleetId,
|
|
411
|
+
name: service.name,
|
|
412
|
+
domains: [...service.domains],
|
|
413
|
+
status: service.status,
|
|
414
|
+
registeredAt: service.registeredAt,
|
|
415
|
+
lastHeartbeat: service.lastHeartbeat,
|
|
416
|
+
metadata: service.metadata,
|
|
417
|
+
endpoint: undefined,
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
/** Create an immutable snapshot of a mutable route */
|
|
421
|
+
function toRouteSnapshot(route) {
|
|
422
|
+
return {
|
|
423
|
+
sourceDomain: route.sourceDomain,
|
|
424
|
+
targetDomain: route.targetDomain,
|
|
425
|
+
targetFleetId: route.targetFleetId,
|
|
426
|
+
priority: route.priority,
|
|
427
|
+
active: route.active,
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
// ============================================================================
|
|
431
|
+
// Factory Function
|
|
432
|
+
// ============================================================================
|
|
433
|
+
/**
|
|
434
|
+
* Create a new FederationMailbox instance.
|
|
435
|
+
*
|
|
436
|
+
* @param config - Optional partial configuration (merged with defaults)
|
|
437
|
+
* @returns A fresh FederationMailbox
|
|
438
|
+
*/
|
|
439
|
+
export function createFederationMailbox(config) {
|
|
440
|
+
return new FederationMailbox(config);
|
|
441
|
+
}
|
|
442
|
+
//# sourceMappingURL=federation-mailbox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"federation-mailbox.js","sourceRoot":"","sources":["../../../src/coordination/federation/federation-mailbox.ts"],"names":[],"mappings":"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"}
|