Add files via upload
This commit is contained in:
52 files changed
+33300
No files matched your search
@@ -0,0 +1,305 @@
|
||||
/**
|
||||
* T3MP3ST Communications Channel
|
||||
*
|
||||
* Inter-agent communication and message routing.
|
||||
*/
|
||||
|
||||
import { EventEmitter } from 'eventemitter3';
|
||||
import { randomUUID } from 'crypto';
|
||||
import type { Message } from '../types/index.js';
|
||||
|
||||
// =============================================================================
|
||||
// EVENTS
|
||||
// =============================================================================
|
||||
|
||||
export interface CommsEvents {
|
||||
'message:sent': Message;
|
||||
'message:received': Message;
|
||||
'channel:created': Channel;
|
||||
'channel:closed': Channel;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// CHANNEL
|
||||
// =============================================================================
|
||||
|
||||
export interface Channel {
|
||||
id: string;
|
||||
name: string;
|
||||
type: 'broadcast' | 'direct' | 'team';
|
||||
members: string[];
|
||||
createdAt: number;
|
||||
isOpen: boolean;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// COMMS CHANNEL
|
||||
// =============================================================================
|
||||
|
||||
export class CommsChannel extends EventEmitter<CommsEvents> {
|
||||
private channels: Map<string, Channel> = new Map();
|
||||
private messages: Message[] = [];
|
||||
private subscriptions: Map<string, Set<string>> = new Map(); // channelId -> Set<operatorId>
|
||||
|
||||
/**
|
||||
* Create a new channel
|
||||
*/
|
||||
createChannel(params: {
|
||||
name: string;
|
||||
type: Channel['type'];
|
||||
members?: string[];
|
||||
}): Channel {
|
||||
const channel: Channel = {
|
||||
id: randomUUID(),
|
||||
name: params.name,
|
||||
type: params.type,
|
||||
members: params.members || [],
|
||||
createdAt: Date.now(),
|
||||
isOpen: true,
|
||||
};
|
||||
|
||||
this.channels.set(channel.id, channel);
|
||||
this.subscriptions.set(channel.id, new Set(channel.members));
|
||||
this.emit('channel:created', channel);
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a channel by ID
|
||||
*/
|
||||
getChannel(channelId: string): Channel | undefined {
|
||||
return this.channels.get(channelId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get channel by name
|
||||
*/
|
||||
getChannelByName(name: string): Channel | undefined {
|
||||
for (const channel of this.channels.values()) {
|
||||
if (channel.name === name) {
|
||||
return channel;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all channels
|
||||
*/
|
||||
getAllChannels(): Channel[] {
|
||||
return Array.from(this.channels.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Close a channel
|
||||
*/
|
||||
closeChannel(channelId: string): Channel | undefined {
|
||||
const channel = this.channels.get(channelId);
|
||||
if (channel) {
|
||||
channel.isOpen = false;
|
||||
this.emit('channel:closed', channel);
|
||||
}
|
||||
return channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe an operator to a channel
|
||||
*/
|
||||
subscribe(channelId: string, operatorId: string): boolean {
|
||||
const subs = this.subscriptions.get(channelId);
|
||||
if (subs) {
|
||||
subs.add(operatorId);
|
||||
const channel = this.channels.get(channelId);
|
||||
if (channel && !channel.members.includes(operatorId)) {
|
||||
channel.members.push(operatorId);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribe an operator from a channel
|
||||
*/
|
||||
unsubscribe(channelId: string, operatorId: string): boolean {
|
||||
const subs = this.subscriptions.get(channelId);
|
||||
if (subs) {
|
||||
subs.delete(operatorId);
|
||||
const channel = this.channels.get(channelId);
|
||||
if (channel) {
|
||||
channel.members = channel.members.filter(m => m !== operatorId);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message
|
||||
*/
|
||||
send(params: {
|
||||
from: string;
|
||||
to: string | string[];
|
||||
channel: string;
|
||||
type: Message['type'];
|
||||
priority?: Message['priority'];
|
||||
content: string;
|
||||
metadata?: Record<string, unknown>;
|
||||
}): Message {
|
||||
const message: Message = {
|
||||
id: randomUUID(),
|
||||
from: params.from,
|
||||
to: params.to,
|
||||
channel: params.channel,
|
||||
type: params.type,
|
||||
priority: params.priority || 'normal',
|
||||
content: params.content,
|
||||
metadata: params.metadata,
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
|
||||
this.messages.push(message);
|
||||
this.emit('message:sent', message);
|
||||
|
||||
// Emit received event for each recipient
|
||||
const recipients = Array.isArray(params.to) ? params.to : [params.to];
|
||||
for (const recipient of recipients) {
|
||||
if (recipient !== params.from) {
|
||||
this.emit('message:received', message);
|
||||
}
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcast a message to a channel
|
||||
*/
|
||||
broadcast(params: {
|
||||
from: string;
|
||||
channelId: string;
|
||||
type: Message['type'];
|
||||
priority?: Message['priority'];
|
||||
content: string;
|
||||
metadata?: Record<string, unknown>;
|
||||
}): Message | undefined {
|
||||
const channel = this.channels.get(params.channelId);
|
||||
if (!channel || !channel.isOpen) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return this.send({
|
||||
from: params.from,
|
||||
to: channel.members.filter(m => m !== params.from),
|
||||
channel: params.channelId,
|
||||
type: params.type,
|
||||
priority: params.priority,
|
||||
content: params.content,
|
||||
metadata: params.metadata,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get messages for an operator
|
||||
*/
|
||||
getMessagesFor(operatorId: string): Message[] {
|
||||
return this.messages.filter(m => {
|
||||
if (typeof m.to === 'string') {
|
||||
return m.to === operatorId;
|
||||
}
|
||||
return m.to.includes(operatorId);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get messages from an operator
|
||||
*/
|
||||
getMessagesFrom(operatorId: string): Message[] {
|
||||
return this.messages.filter(m => m.from === operatorId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get messages in a channel
|
||||
*/
|
||||
getChannelMessages(channelId: string): Message[] {
|
||||
return this.messages.filter(m => m.channel === channelId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get recent messages
|
||||
*/
|
||||
getRecentMessages(limit: number = 50): Message[] {
|
||||
return this.messages.slice(-limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear messages
|
||||
*/
|
||||
clearMessages(): void {
|
||||
this.messages = [];
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// FACTORY FUNCTIONS
|
||||
// =============================================================================
|
||||
|
||||
export function createMissionComms(_missionId: string): CommsChannel {
|
||||
const comms = new CommsChannel();
|
||||
|
||||
comms.createChannel({
|
||||
name: 'mission-control',
|
||||
type: 'broadcast',
|
||||
});
|
||||
|
||||
comms.createChannel({
|
||||
name: 'intel',
|
||||
type: 'broadcast',
|
||||
});
|
||||
|
||||
comms.createChannel({
|
||||
name: 'alerts',
|
||||
type: 'broadcast',
|
||||
});
|
||||
|
||||
return comms;
|
||||
}
|
||||
|
||||
export function initializeTeamChannels(comms: CommsChannel, teamMembers: string[]): void {
|
||||
comms.createChannel({
|
||||
name: 'team',
|
||||
type: 'team',
|
||||
members: teamMembers,
|
||||
});
|
||||
|
||||
comms.createChannel({
|
||||
name: 'findings',
|
||||
type: 'team',
|
||||
members: teamMembers,
|
||||
});
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// MESSAGE FORMATS
|
||||
// =============================================================================
|
||||
|
||||
export const MESSAGE_FORMATS = {
|
||||
intel: (operatorId: string, intel: string) =>
|
||||
`[INTEL] ${operatorId}: ${intel}`,
|
||||
finding: (operatorId: string, finding: string) =>
|
||||
`[FINDING] ${operatorId}: ${finding}`,
|
||||
alert: (source: string, alert: string) =>
|
||||
`[ALERT] ${source}: ${alert}`,
|
||||
status: (operatorId: string, status: string) =>
|
||||
`[STATUS] ${operatorId}: ${status}`,
|
||||
task: (from: string, to: string, task: string) =>
|
||||
`[TASK] ${from} -> ${to}: ${task}`,
|
||||
};
|
||||
|
||||
export const PRIORITY_INDICATORS = {
|
||||
low: '',
|
||||
normal: '',
|
||||
high: '[HIGH]',
|
||||
critical: '[CRITICAL]',
|
||||
};
|
||||
Reference in new issue
Block a user