@cogitator-ai/swarms 0.3.20 → 0.4.0

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.
Files changed (65) hide show
  1. package/README.md +192 -0
  2. package/dist/communication/index.d.ts +6 -3
  3. package/dist/communication/index.d.ts.map +1 -1
  4. package/dist/communication/index.js +6 -3
  5. package/dist/communication/index.js.map +1 -1
  6. package/dist/communication/redis-blackboard.d.ts +36 -0
  7. package/dist/communication/redis-blackboard.d.ts.map +1 -0
  8. package/dist/communication/redis-blackboard.js +203 -0
  9. package/dist/communication/redis-blackboard.js.map +1 -0
  10. package/dist/communication/redis-event-emitter.d.ts +36 -0
  11. package/dist/communication/redis-event-emitter.d.ts.map +1 -0
  12. package/dist/communication/redis-event-emitter.js +130 -0
  13. package/dist/communication/redis-event-emitter.js.map +1 -0
  14. package/dist/communication/redis-message-bus.d.ts +34 -0
  15. package/dist/communication/redis-message-bus.d.ts.map +1 -0
  16. package/dist/communication/redis-message-bus.js +151 -0
  17. package/dist/communication/redis-message-bus.js.map +1 -0
  18. package/dist/distributed/distributed-coordinator.d.ts +92 -0
  19. package/dist/distributed/distributed-coordinator.d.ts.map +1 -0
  20. package/dist/distributed/distributed-coordinator.js +329 -0
  21. package/dist/distributed/distributed-coordinator.js.map +1 -0
  22. package/dist/distributed/index.d.ts +2 -0
  23. package/dist/distributed/index.d.ts.map +1 -0
  24. package/dist/distributed/index.js +2 -0
  25. package/dist/distributed/index.js.map +1 -0
  26. package/dist/strategies/auction.d.ts +3 -4
  27. package/dist/strategies/auction.d.ts.map +1 -1
  28. package/dist/strategies/auction.js +1 -1
  29. package/dist/strategies/auction.js.map +1 -1
  30. package/dist/strategies/consensus.d.ts +3 -4
  31. package/dist/strategies/consensus.d.ts.map +1 -1
  32. package/dist/strategies/consensus.js +1 -1
  33. package/dist/strategies/consensus.js.map +1 -1
  34. package/dist/strategies/debate.d.ts +3 -4
  35. package/dist/strategies/debate.d.ts.map +1 -1
  36. package/dist/strategies/debate.js +1 -1
  37. package/dist/strategies/debate.js.map +1 -1
  38. package/dist/strategies/hierarchical.d.ts +3 -4
  39. package/dist/strategies/hierarchical.d.ts.map +1 -1
  40. package/dist/strategies/hierarchical.js +1 -1
  41. package/dist/strategies/hierarchical.js.map +1 -1
  42. package/dist/strategies/index.d.ts +2 -3
  43. package/dist/strategies/index.d.ts.map +1 -1
  44. package/dist/strategies/index.js.map +1 -1
  45. package/dist/strategies/negotiation-strategy.d.ts +3 -4
  46. package/dist/strategies/negotiation-strategy.d.ts.map +1 -1
  47. package/dist/strategies/negotiation-strategy.js +4 -4
  48. package/dist/strategies/negotiation-strategy.js.map +1 -1
  49. package/dist/strategies/pipeline.d.ts +3 -4
  50. package/dist/strategies/pipeline.d.ts.map +1 -1
  51. package/dist/strategies/pipeline.js +1 -1
  52. package/dist/strategies/pipeline.js.map +1 -1
  53. package/dist/strategies/round-robin.d.ts +3 -4
  54. package/dist/strategies/round-robin.d.ts.map +1 -1
  55. package/dist/strategies/round-robin.js +1 -1
  56. package/dist/strategies/round-robin.js.map +1 -1
  57. package/dist/swarm.d.ts +14 -1
  58. package/dist/swarm.d.ts.map +1 -1
  59. package/dist/swarm.js +101 -15
  60. package/dist/swarm.js.map +1 -1
  61. package/package.json +2 -1
  62. package/dist/strategies/negotiation.d.ts +0 -39
  63. package/dist/strategies/negotiation.d.ts.map +0 -1
  64. package/dist/strategies/negotiation.js +0 -607
  65. package/dist/strategies/negotiation.js.map +0 -1
package/README.md CHANGED
@@ -655,6 +655,185 @@ swarm.once('swarm:complete', (event) => {
655
655
 
656
656
  ---
657
657
 
658
+ ## Distributed Execution
659
+
660
+ Run swarm agents across multiple workers using Redis-backed communication and BullMQ job queues. Each agent executes as a separate job, enabling horizontal scaling and parallel processing.
661
+
662
+ ### Basic Distributed Swarm
663
+
664
+ ```typescript
665
+ import { Cogitator, Agent } from '@cogitator-ai/core';
666
+ import { SwarmBuilder } from '@cogitator-ai/swarms';
667
+
668
+ const cogitator = new Cogitator({ defaultModel: 'gpt-4o' });
669
+
670
+ const swarm = new SwarmBuilder('distributed-team')
671
+ .strategy('hierarchical')
672
+ .supervisor(new Agent({ name: 'lead', instructions: 'Coordinate the team' }))
673
+ .workers([
674
+ new Agent({ name: 'analyst-1', instructions: 'Analyze data' }),
675
+ new Agent({ name: 'analyst-2', instructions: 'Analyze data' }),
676
+ new Agent({ name: 'analyst-3', instructions: 'Analyze data' }),
677
+ ])
678
+ .distributed({
679
+ enabled: true,
680
+ queue: 'swarm-agent-jobs',
681
+ timeout: 300000,
682
+ redis: {
683
+ host: 'localhost',
684
+ port: 6379,
685
+ },
686
+ })
687
+ .build(cogitator);
688
+
689
+ const result = await swarm.run({
690
+ input: 'Analyze Q4 sales data across all regions',
691
+ });
692
+
693
+ // Cleanup Redis connections when done
694
+ await swarm.close();
695
+ ```
696
+
697
+ ### Distributed Configuration Options
698
+
699
+ ```typescript
700
+ interface DistributedSwarmConfig {
701
+ enabled: boolean;
702
+ queue?: string; // Job queue name (default: 'swarm-agent-jobs')
703
+ workerConcurrency?: number; // Workers per process (default: 4)
704
+ timeout?: number; // Job timeout in ms (default: 300000)
705
+ redis?: {
706
+ host?: string; // Redis host (default: 'localhost')
707
+ port?: number; // Redis port (default: 6379)
708
+ password?: string; // Redis password
709
+ keyPrefix?: string; // Key prefix (default: 'swarm')
710
+ db?: number; // Redis database (default: 0)
711
+ };
712
+ retry?: {
713
+ maxRetries?: number; // Max retry attempts
714
+ backoff?: 'constant' | 'linear' | 'exponential';
715
+ initialDelay?: number; // Initial delay in ms
716
+ maxDelay?: number; // Max delay in ms
717
+ };
718
+ cleanupAfter?: number; // Cleanup keys after ms
719
+ }
720
+ ```
721
+
722
+ ### Redis-Backed Communication
723
+
724
+ Distributed swarms use Redis for shared state synchronization:
725
+
726
+ ```typescript
727
+ import { RedisMessageBus, RedisBlackboard, RedisSwarmEventEmitter } from '@cogitator-ai/swarms';
728
+ import Redis from 'ioredis';
729
+
730
+ const redis = new Redis({ host: 'localhost', port: 6379 });
731
+
732
+ // Message bus for agent-to-agent communication
733
+ const messageBus = new RedisMessageBus(
734
+ { enabled: true, protocol: 'direct' },
735
+ { redis, swarmId: 'my-swarm', keyPrefix: 'swarm' }
736
+ );
737
+ await messageBus.initialize();
738
+
739
+ // Shared blackboard for state
740
+ const blackboard = new RedisBlackboard(
741
+ { enabled: true, sections: { results: [] }, trackHistory: true },
742
+ { redis, swarmId: 'my-swarm', keyPrefix: 'swarm' }
743
+ );
744
+ await blackboard.initialize();
745
+
746
+ // Event emitter for cross-worker events
747
+ const events = new RedisSwarmEventEmitter({
748
+ redis,
749
+ swarmId: 'my-swarm',
750
+ keyPrefix: 'swarm',
751
+ });
752
+ await events.initialize();
753
+ ```
754
+
755
+ ### Setting Up Workers
756
+
757
+ Workers process distributed swarm jobs. Use with `@cogitator-ai/worker`:
758
+
759
+ ```typescript
760
+ import { WorkerPool } from '@cogitator-ai/worker';
761
+
762
+ const pool = new WorkerPool({
763
+ concurrency: 4,
764
+ redis: {
765
+ host: 'localhost',
766
+ port: 6379,
767
+ },
768
+ queues: ['swarm-agent-jobs'],
769
+ });
770
+
771
+ await pool.start();
772
+
773
+ // Workers automatically process swarm-agent jobs
774
+ // Each job runs a single agent and publishes results back to Redis
775
+ ```
776
+
777
+ ### Architecture
778
+
779
+ ```
780
+ ┌─────────────────────────────────────────────────────────────┐
781
+ │ Swarm.run() │
782
+ │ distributed: true → DistributedSwarmCoordinator │
783
+ │ distributed: false → SwarmCoordinator (in-memory) │
784
+ └─────────────────────────────────────────────────────────────┘
785
+
786
+ ┌───────────────┴───────────────┐
787
+ ▼ ▼
788
+ ┌─────────────────────────┐ ┌─────────────────────────────┐
789
+ │ DistributedCoordinator │ │ Redis (Shared State) │
790
+ │ - dispatches agent jobs│────▶│ - swarm:{id}:blackboard │
791
+ │ - subscribes to results│ │ - swarm:{id}:messages │
792
+ │ - coordinates strategy │ │ - swarm:{id}:results │
793
+ └─────────────────────────┘ └─────────────────────────────┘
794
+ │ ▲
795
+ │ job queue │
796
+ ▼ │
797
+ ┌─────────────────────────┐ │
798
+ │ BullMQ Queue │ │
799
+ │ swarm-agent-jobs │ │
800
+ └─────────────────────────┘ │
801
+ │ │
802
+ ┌─────────┼─────────┐ │
803
+ ▼ ▼ ▼ │
804
+ ┌───────┐ ┌───────┐ ┌───────┐ │
805
+ │Worker1│ │Worker2│ │Worker3│ ───────────────┘
806
+ │ Agent │ │ Agent │ │ Agent │ publish results
807
+ └───────┘ └───────┘ └───────┘
808
+ ```
809
+
810
+ ### Local vs Distributed
811
+
812
+ The same swarm works in both modes with identical API:
813
+
814
+ ```typescript
815
+ // Local execution (in-process)
816
+ const localSwarm = new SwarmBuilder('local-team')
817
+ .strategy('consensus')
818
+ .agents([agent1, agent2, agent3])
819
+ .consensus({ threshold: 0.6, maxRounds: 3, resolution: 'majority', onNoConsensus: 'fail' })
820
+ .build(cogitator);
821
+
822
+ // Distributed execution (across workers)
823
+ const distributedSwarm = new SwarmBuilder('distributed-team')
824
+ .strategy('consensus')
825
+ .agents([agent1, agent2, agent3])
826
+ .consensus({ threshold: 0.6, maxRounds: 3, resolution: 'majority', onNoConsensus: 'fail' })
827
+ .distributed({ enabled: true, redis: { host: 'redis.example.com' } })
828
+ .build(cogitator);
829
+
830
+ // Same API for both
831
+ const localResult = await localSwarm.run({ input: 'Task...' });
832
+ const distributedResult = await distributedSwarm.run({ input: 'Task...' });
833
+ ```
834
+
835
+ ---
836
+
658
837
  ## Swarm Control
659
838
 
660
839
  ### Pause and Resume
@@ -784,6 +963,19 @@ import type {
784
963
  } from '@cogitator-ai/swarms';
785
964
  ```
786
965
 
966
+ ### Distributed Types
967
+
968
+ ```typescript
969
+ import type { DistributedSwarmConfig } from '@cogitator-ai/types';
970
+
971
+ import {
972
+ RedisMessageBus,
973
+ RedisBlackboard,
974
+ RedisSwarmEventEmitter,
975
+ DistributedSwarmCoordinator,
976
+ } from '@cogitator-ai/swarms';
977
+ ```
978
+
787
979
  ---
788
980
 
789
981
  ## Examples
@@ -1,7 +1,10 @@
1
1
  /**
2
2
  * Communication primitives for swarm coordination
3
3
  */
4
- export { SwarmEventEmitterImpl } from './event-emitter';
5
- export { InMemoryMessageBus, createMessageBus } from './message-bus';
6
- export { InMemoryBlackboard, createBlackboard } from './blackboard';
4
+ export { SwarmEventEmitterImpl } from './event-emitter.js';
5
+ export { InMemoryMessageBus, createMessageBus } from './message-bus.js';
6
+ export { InMemoryBlackboard, createBlackboard } from './blackboard.js';
7
+ export { RedisMessageBus, type RedisMessageBusOptions } from './redis-message-bus.js';
8
+ export { RedisBlackboard, type RedisBlackboardOptions } from './redis-blackboard.js';
9
+ export { RedisSwarmEventEmitter, type RedisEventEmitterOptions } from './redis-event-emitter.js';
7
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/communication/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACrE,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/communication/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACxE,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAEvE,OAAO,EAAE,eAAe,EAAE,KAAK,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AACtF,OAAO,EAAE,eAAe,EAAE,KAAK,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AACrF,OAAO,EAAE,sBAAsB,EAAE,KAAK,wBAAwB,EAAE,MAAM,0BAA0B,CAAC"}
@@ -1,7 +1,10 @@
1
1
  /**
2
2
  * Communication primitives for swarm coordination
3
3
  */
4
- export { SwarmEventEmitterImpl } from './event-emitter';
5
- export { InMemoryMessageBus, createMessageBus } from './message-bus';
6
- export { InMemoryBlackboard, createBlackboard } from './blackboard';
4
+ export { SwarmEventEmitterImpl } from './event-emitter.js';
5
+ export { InMemoryMessageBus, createMessageBus } from './message-bus.js';
6
+ export { InMemoryBlackboard, createBlackboard } from './blackboard.js';
7
+ export { RedisMessageBus } from './redis-message-bus.js';
8
+ export { RedisBlackboard } from './redis-blackboard.js';
9
+ export { RedisSwarmEventEmitter } from './redis-event-emitter.js';
7
10
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/communication/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACrE,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/communication/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACxE,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAEvE,OAAO,EAAE,eAAe,EAA+B,MAAM,wBAAwB,CAAC;AACtF,OAAO,EAAE,eAAe,EAA+B,MAAM,uBAAuB,CAAC;AACrF,OAAO,EAAE,sBAAsB,EAAiC,MAAM,0BAA0B,CAAC"}
@@ -0,0 +1,36 @@
1
+ import type { Blackboard, BlackboardConfig, BlackboardSection, BlackboardHistoryEntry } from '@cogitator-ai/types';
2
+ import type { Redis } from 'ioredis';
3
+ export interface RedisBlackboardOptions {
4
+ redis: Redis;
5
+ swarmId: string;
6
+ keyPrefix?: string;
7
+ }
8
+ export declare class RedisBlackboard implements Blackboard {
9
+ private redis;
10
+ private subscriber;
11
+ private swarmId;
12
+ private keyPrefix;
13
+ private config;
14
+ private subscriptions;
15
+ private localCache;
16
+ private historyCache;
17
+ constructor(config: BlackboardConfig, options: RedisBlackboardOptions);
18
+ private sectionKey;
19
+ private historyKey;
20
+ private channelKey;
21
+ initialize(): Promise<void>;
22
+ read<T = unknown>(section: string): T;
23
+ write<T>(section: string, data: T, agentName: string): void;
24
+ append<T>(section: string, item: T, agentName: string): void;
25
+ has(section: string): boolean;
26
+ delete(section: string): void;
27
+ subscribe(section: string, handler: (data: unknown, agentName: string) => void): () => void;
28
+ getSections(): string[];
29
+ getSection<T = unknown>(section: string): BlackboardSection<T> | undefined;
30
+ getHistory(section: string): BlackboardHistoryEntry[];
31
+ clear(): void;
32
+ close(): Promise<void>;
33
+ syncFromRedis(): Promise<void>;
34
+ private notifySubscribers;
35
+ }
36
+ //# sourceMappingURL=redis-blackboard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis-blackboard.d.ts","sourceRoot":"","sources":["../../src/communication/redis-blackboard.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,gBAAgB,EAChB,iBAAiB,EACjB,sBAAsB,EACvB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAErC,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,KAAK,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAUD,qBAAa,eAAgB,YAAW,UAAU;IAChD,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,UAAU,CAAQ;IAC1B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,MAAM,CAAmB;IACjC,OAAO,CAAC,aAAa,CAAsE;IAC3F,OAAO,CAAC,UAAU,CAAoC;IACtD,OAAO,CAAC,YAAY,CAA+C;gBAEvD,MAAM,EAAE,gBAAgB,EAAE,OAAO,EAAE,sBAAsB;IAQrE,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,UAAU;IAIZ,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAyDjC,IAAI,CAAC,CAAC,GAAG,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,CAAC;IAQrC,KAAK,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IA2C3D,MAAM,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAgB5D,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAI7B,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAQ7B,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI;IAiB3F,WAAW,IAAI,MAAM,EAAE;IAIvB,UAAU,CAAC,CAAC,GAAG,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,iBAAiB,CAAC,CAAC,CAAC,GAAG,SAAS;IAY1E,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,sBAAsB,EAAE;IAIrD,KAAK,IAAI,IAAI;IAWP,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAKtB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAepC,OAAO,CAAC,iBAAiB;CAU1B"}
@@ -0,0 +1,203 @@
1
+ export class RedisBlackboard {
2
+ redis;
3
+ subscriber;
4
+ swarmId;
5
+ keyPrefix;
6
+ config;
7
+ subscriptions = new Map();
8
+ localCache = new Map();
9
+ historyCache = new Map();
10
+ constructor(config, options) {
11
+ this.config = config;
12
+ this.redis = options.redis;
13
+ this.subscriber = options.redis.duplicate();
14
+ this.swarmId = options.swarmId;
15
+ this.keyPrefix = options.keyPrefix ?? 'swarm';
16
+ }
17
+ sectionKey(section) {
18
+ return `${this.keyPrefix}:${this.swarmId}:blackboard:${section}`;
19
+ }
20
+ historyKey(section) {
21
+ return `${this.keyPrefix}:${this.swarmId}:blackboard:${section}:history`;
22
+ }
23
+ channelKey() {
24
+ return `${this.keyPrefix}:${this.swarmId}:blackboard:changes`;
25
+ }
26
+ async initialize() {
27
+ for (const [name, initialData] of Object.entries(this.config.sections)) {
28
+ const section = {
29
+ name,
30
+ data: initialData,
31
+ lastModified: Date.now(),
32
+ modifiedBy: 'system',
33
+ version: 1,
34
+ };
35
+ this.localCache.set(name, section);
36
+ const existing = await this.redis.get(this.sectionKey(name));
37
+ if (!existing) {
38
+ await this.redis.set(this.sectionKey(name), JSON.stringify(section));
39
+ if (this.config.trackHistory) {
40
+ const entry = {
41
+ value: initialData,
42
+ writtenBy: 'system',
43
+ timestamp: Date.now(),
44
+ version: 1,
45
+ };
46
+ await this.redis.rpush(this.historyKey(name), JSON.stringify(entry));
47
+ this.historyCache.set(name, [entry]);
48
+ }
49
+ }
50
+ else {
51
+ const stored = JSON.parse(existing);
52
+ this.localCache.set(name, stored);
53
+ }
54
+ }
55
+ await this.subscriber.subscribe(this.channelKey());
56
+ this.subscriber.on('message', (_channel, messageJson) => {
57
+ try {
58
+ const { section, data, agentName, version, timestamp } = JSON.parse(messageJson);
59
+ const stored = {
60
+ name: section,
61
+ data,
62
+ lastModified: timestamp,
63
+ modifiedBy: agentName,
64
+ version,
65
+ };
66
+ this.localCache.set(section, stored);
67
+ this.notifySubscribers(section, data, agentName);
68
+ }
69
+ catch { }
70
+ });
71
+ }
72
+ read(section) {
73
+ const cached = this.localCache.get(section);
74
+ if (!cached) {
75
+ throw new Error(`Blackboard section '${section}' not found`);
76
+ }
77
+ return cached.data;
78
+ }
79
+ write(section, data, agentName) {
80
+ if (!this.config.enabled) {
81
+ throw new Error('Blackboard is not enabled');
82
+ }
83
+ const existing = this.localCache.get(section);
84
+ const version = existing ? existing.version + 1 : 1;
85
+ const timestamp = Date.now();
86
+ const newSection = {
87
+ name: section,
88
+ data,
89
+ lastModified: timestamp,
90
+ modifiedBy: agentName,
91
+ version,
92
+ };
93
+ this.localCache.set(section, newSection);
94
+ void this.redis.set(this.sectionKey(section), JSON.stringify(newSection));
95
+ if (this.config.trackHistory) {
96
+ const entry = {
97
+ value: data,
98
+ writtenBy: agentName,
99
+ timestamp,
100
+ version,
101
+ };
102
+ if (!this.historyCache.has(section)) {
103
+ this.historyCache.set(section, []);
104
+ }
105
+ this.historyCache.get(section).push(entry);
106
+ void this.redis.rpush(this.historyKey(section), JSON.stringify(entry));
107
+ }
108
+ void this.redis.publish(this.channelKey(), JSON.stringify({ section, data, agentName, version, timestamp }));
109
+ this.notifySubscribers(section, data, agentName);
110
+ }
111
+ append(section, item, agentName) {
112
+ const current = this.localCache.get(section);
113
+ if (!current) {
114
+ this.write(section, [item], agentName);
115
+ return;
116
+ }
117
+ if (!Array.isArray(current.data)) {
118
+ throw new Error(`Section '${section}' is not an array, cannot append`);
119
+ }
120
+ const newData = [...current.data, item];
121
+ this.write(section, newData, agentName);
122
+ }
123
+ has(section) {
124
+ return this.localCache.has(section);
125
+ }
126
+ delete(section) {
127
+ this.localCache.delete(section);
128
+ this.historyCache.delete(section);
129
+ this.subscriptions.delete(section);
130
+ void this.redis.del(this.sectionKey(section));
131
+ void this.redis.del(this.historyKey(section));
132
+ }
133
+ subscribe(section, handler) {
134
+ if (!this.subscriptions.has(section)) {
135
+ this.subscriptions.set(section, new Set());
136
+ }
137
+ this.subscriptions.get(section).add(handler);
138
+ return () => {
139
+ const handlers = this.subscriptions.get(section);
140
+ if (handlers) {
141
+ handlers.delete(handler);
142
+ if (handlers.size === 0) {
143
+ this.subscriptions.delete(section);
144
+ }
145
+ }
146
+ };
147
+ }
148
+ getSections() {
149
+ return Array.from(this.localCache.keys());
150
+ }
151
+ getSection(section) {
152
+ const cached = this.localCache.get(section);
153
+ if (!cached)
154
+ return undefined;
155
+ return {
156
+ name: cached.name,
157
+ data: cached.data,
158
+ lastModified: cached.lastModified,
159
+ modifiedBy: cached.modifiedBy,
160
+ version: cached.version,
161
+ };
162
+ }
163
+ getHistory(section) {
164
+ return this.historyCache.get(section) ?? [];
165
+ }
166
+ clear() {
167
+ const sections = Array.from(this.localCache.keys());
168
+ this.localCache.clear();
169
+ this.historyCache.clear();
170
+ for (const section of sections) {
171
+ void this.redis.del(this.sectionKey(section));
172
+ void this.redis.del(this.historyKey(section));
173
+ }
174
+ }
175
+ async close() {
176
+ await this.subscriber.unsubscribe();
177
+ await this.subscriber.quit();
178
+ }
179
+ async syncFromRedis() {
180
+ const pattern = `${this.keyPrefix}:${this.swarmId}:blackboard:*`;
181
+ const keys = await this.redis.keys(pattern);
182
+ for (const key of keys) {
183
+ if (key.endsWith(':history'))
184
+ continue;
185
+ const raw = await this.redis.get(key);
186
+ if (raw) {
187
+ const stored = JSON.parse(raw);
188
+ this.localCache.set(stored.name, stored);
189
+ }
190
+ }
191
+ }
192
+ notifySubscribers(section, data, agentName) {
193
+ const handlers = this.subscriptions.get(section);
194
+ if (handlers) {
195
+ for (const handler of handlers) {
196
+ void Promise.resolve(handler(data, agentName)).catch((error) => {
197
+ console.warn('[RedisBlackboard] Handler error:', error);
198
+ });
199
+ }
200
+ }
201
+ }
202
+ }
203
+ //# sourceMappingURL=redis-blackboard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis-blackboard.js","sourceRoot":"","sources":["../../src/communication/redis-blackboard.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ import type { SwarmEventEmitter, SwarmEventType, SwarmEvent, SwarmEventHandler } from '@cogitator-ai/types';
2
+ import type { Redis } from 'ioredis';
3
+ export interface RedisEventEmitterOptions {
4
+ redis: Redis;
5
+ swarmId: string;
6
+ keyPrefix?: string;
7
+ maxEvents?: number;
8
+ }
9
+ export declare class RedisSwarmEventEmitter implements SwarmEventEmitter {
10
+ private redis;
11
+ private subscriber;
12
+ private swarmId;
13
+ private keyPrefix;
14
+ private maxEvents;
15
+ private handlers;
16
+ private localEvents;
17
+ constructor(options: RedisEventEmitterOptions);
18
+ private eventsKey;
19
+ private channelKey;
20
+ initialize(): Promise<void>;
21
+ on(event: SwarmEventType | '*', handler: SwarmEventHandler): () => void;
22
+ once(event: SwarmEventType, handler: SwarmEventHandler): () => void;
23
+ emit(event: SwarmEventType, data?: unknown, agentName?: string): void;
24
+ emitAsync(event: SwarmEventType, data?: unknown, agentName?: string): Promise<void>;
25
+ off(event: SwarmEventType | '*', handler: SwarmEventHandler): void;
26
+ removeAllListeners(event?: SwarmEventType): void;
27
+ getEvents(): SwarmEvent[];
28
+ getEventsAsync(): Promise<SwarmEvent[]>;
29
+ getEventsByType(type: SwarmEventType): SwarmEvent[];
30
+ getEventsByAgent(agentName: string): SwarmEvent[];
31
+ clearEvents(): void;
32
+ clearEventsAsync(): Promise<void>;
33
+ close(): Promise<void>;
34
+ private notifyHandlers;
35
+ }
36
+ //# sourceMappingURL=redis-event-emitter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis-event-emitter.d.ts","sourceRoot":"","sources":["../../src/communication/redis-event-emitter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,cAAc,EACd,UAAU,EACV,iBAAiB,EAClB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAErC,MAAM,WAAW,wBAAwB;IACvC,KAAK,EAAE,KAAK,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,sBAAuB,YAAW,iBAAiB;IAC9D,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,UAAU,CAAQ;IAC1B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,QAAQ,CAA2D;IAC3E,OAAO,CAAC,WAAW,CAAoB;gBAE3B,OAAO,EAAE,wBAAwB;IAQ7C,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,UAAU;IAIZ,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAejC,EAAE,CAAC,KAAK,EAAE,cAAc,GAAG,GAAG,EAAE,OAAO,EAAE,iBAAiB,GAAG,MAAM,IAAI;IASvE,IAAI,CAAC,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,iBAAiB,GAAG,MAAM,IAAI;IAUnE,IAAI,CAAC,KAAK,EAAE,cAAc,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI;IAI/D,SAAS,CAAC,KAAK,EAAE,cAAc,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAoBzF,GAAG,CAAC,KAAK,EAAE,cAAc,GAAG,GAAG,EAAE,OAAO,EAAE,iBAAiB,GAAG,IAAI;IAUlE,kBAAkB,CAAC,KAAK,CAAC,EAAE,cAAc,GAAG,IAAI;IAQhD,SAAS,IAAI,UAAU,EAAE;IAInB,cAAc,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;IAK7C,eAAe,CAAC,IAAI,EAAE,cAAc,GAAG,UAAU,EAAE;IAInD,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,UAAU,EAAE;IAIjD,WAAW,IAAI,IAAI;IAIb,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;IAKjC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAK5B,OAAO,CAAC,cAAc;CAmBvB"}
@@ -0,0 +1,130 @@
1
+ export class RedisSwarmEventEmitter {
2
+ redis;
3
+ subscriber;
4
+ swarmId;
5
+ keyPrefix;
6
+ maxEvents;
7
+ handlers = new Map();
8
+ localEvents = [];
9
+ constructor(options) {
10
+ this.redis = options.redis;
11
+ this.subscriber = options.redis.duplicate();
12
+ this.swarmId = options.swarmId;
13
+ this.keyPrefix = options.keyPrefix ?? 'swarm';
14
+ this.maxEvents = options.maxEvents ?? 1000;
15
+ }
16
+ eventsKey() {
17
+ return `${this.keyPrefix}:${this.swarmId}:events`;
18
+ }
19
+ channelKey() {
20
+ return `${this.keyPrefix}:${this.swarmId}:events:live`;
21
+ }
22
+ async initialize() {
23
+ await this.subscriber.subscribe(this.channelKey());
24
+ this.subscriber.on('message', (_channel, messageJson) => {
25
+ try {
26
+ const event = JSON.parse(messageJson);
27
+ this.localEvents.push(event);
28
+ if (this.localEvents.length > this.maxEvents) {
29
+ this.localEvents = this.localEvents.slice(-this.maxEvents);
30
+ }
31
+ this.notifyHandlers(event);
32
+ }
33
+ catch { }
34
+ });
35
+ }
36
+ on(event, handler) {
37
+ if (!this.handlers.has(event)) {
38
+ this.handlers.set(event, new Set());
39
+ }
40
+ this.handlers.get(event).add(handler);
41
+ return () => this.off(event, handler);
42
+ }
43
+ once(event, handler) {
44
+ const wrapper = (e) => {
45
+ this.off(event, wrapper);
46
+ void Promise.resolve(handler(e)).catch((error) => {
47
+ console.warn('[RedisSwarmEventEmitter] Once handler error:', error);
48
+ });
49
+ };
50
+ return this.on(event, wrapper);
51
+ }
52
+ emit(event, data, agentName) {
53
+ void this.emitAsync(event, data, agentName);
54
+ }
55
+ async emitAsync(event, data, agentName) {
56
+ const swarmEvent = {
57
+ type: event,
58
+ timestamp: Date.now(),
59
+ agentName,
60
+ data,
61
+ };
62
+ const eventJson = JSON.stringify(swarmEvent);
63
+ await this.redis.rpush(this.eventsKey(), eventJson);
64
+ const len = await this.redis.llen(this.eventsKey());
65
+ if (len > this.maxEvents) {
66
+ await this.redis.ltrim(this.eventsKey(), len - this.maxEvents, -1);
67
+ }
68
+ await this.redis.publish(this.channelKey(), eventJson);
69
+ }
70
+ off(event, handler) {
71
+ const handlers = this.handlers.get(event);
72
+ if (handlers) {
73
+ handlers.delete(handler);
74
+ if (handlers.size === 0) {
75
+ this.handlers.delete(event);
76
+ }
77
+ }
78
+ }
79
+ removeAllListeners(event) {
80
+ if (event) {
81
+ this.handlers.delete(event);
82
+ }
83
+ else {
84
+ this.handlers.clear();
85
+ }
86
+ }
87
+ getEvents() {
88
+ return [...this.localEvents];
89
+ }
90
+ async getEventsAsync() {
91
+ const raw = await this.redis.lrange(this.eventsKey(), 0, -1);
92
+ return raw.map((r) => JSON.parse(r));
93
+ }
94
+ getEventsByType(type) {
95
+ return this.localEvents.filter((e) => e.type === type);
96
+ }
97
+ getEventsByAgent(agentName) {
98
+ return this.localEvents.filter((e) => e.agentName === agentName);
99
+ }
100
+ clearEvents() {
101
+ this.localEvents = [];
102
+ }
103
+ async clearEventsAsync() {
104
+ await this.redis.del(this.eventsKey());
105
+ this.localEvents = [];
106
+ }
107
+ async close() {
108
+ await this.subscriber.unsubscribe();
109
+ await this.subscriber.quit();
110
+ }
111
+ notifyHandlers(event) {
112
+ const handlers = this.handlers.get(event.type);
113
+ if (handlers) {
114
+ for (const handler of handlers) {
115
+ void Promise.resolve(handler(event)).catch((error) => {
116
+ console.warn('[RedisSwarmEventEmitter] Handler error:', error);
117
+ });
118
+ }
119
+ }
120
+ const wildcardHandlers = this.handlers.get('*');
121
+ if (wildcardHandlers) {
122
+ for (const handler of wildcardHandlers) {
123
+ void Promise.resolve(handler(event)).catch((error) => {
124
+ console.warn('[RedisSwarmEventEmitter] Wildcard handler error:', error);
125
+ });
126
+ }
127
+ }
128
+ }
129
+ }
130
+ //# sourceMappingURL=redis-event-emitter.js.map