@starlink-awaken/agentmesh 1.0.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.
@@ -0,0 +1,76 @@
1
+ import { EventEmitter } from 'events';
2
+ import type { AgentMessage, EventType } from '../types/index.js';
3
+
4
+ interface EventBusOptions {
5
+ logger?: Console;
6
+ }
7
+
8
+ export class EventBus extends EventEmitter {
9
+ private logger: Console;
10
+
11
+ constructor(options: EventBusOptions = {}) {
12
+ super();
13
+ this.logger = options.logger || console;
14
+ this.setMaxListeners(100);
15
+ }
16
+
17
+ /**
18
+ * 发布事件
19
+ */
20
+ publish(eventType: EventType, data: AgentMessage): void {
21
+ const event = {
22
+ type: eventType,
23
+ data,
24
+ timestamp: Date.now()
25
+ };
26
+ this.emit(eventType, event);
27
+ this.logger.info(`[EventBus] Published: ${eventType}`, { id: data.id });
28
+ }
29
+
30
+ /**
31
+ * 订阅事件
32
+ */
33
+ subscribe(eventType: EventType, handler: (event: { type: EventType; data: AgentMessage; timestamp: number }) => void): () => void {
34
+ this.on(eventType, handler);
35
+ this.logger.info(`[EventBus] Subscribed to: ${eventType}`);
36
+
37
+ // 返回取消订阅函数
38
+ return () => {
39
+ this.off(eventType, handler);
40
+ this.logger.info(`[EventBus] Unsubscribed from: ${eventType}`);
41
+ };
42
+ }
43
+
44
+ /**
45
+ * 发布任务事件
46
+ */
47
+ publishTaskEvent(eventType: Exclude<EventType, 'agent.registered' | 'agent.unregistered'>, message: AgentMessage): void {
48
+ this.publish(eventType, message);
49
+ }
50
+
51
+ /**
52
+ * 发布 Agent 事件
53
+ */
54
+ publishAgentEvent(eventType: 'agent.registered' | 'agent.unregistered', message: AgentMessage): void {
55
+ this.publish(eventType, message);
56
+ }
57
+
58
+ /**
59
+ * 获取所有事件类型
60
+ */
61
+ getEventTypes(): EventType[] {
62
+ return [
63
+ 'agent.registered',
64
+ 'agent.unregistered',
65
+ 'task.submitted',
66
+ 'task.assigned',
67
+ 'task.started',
68
+ 'task.progress',
69
+ 'task.completed',
70
+ 'task.failed',
71
+ 'context.updated'
72
+ ];
73
+ }
74
+ }
75
+
76
+ export const eventBus = new EventBus();
@@ -0,0 +1,216 @@
1
+ import { writeFileSync, existsSync, mkdirSync, appendFileSync } from 'fs';
2
+ import { join } from 'path';
3
+
4
+ interface Metric {
5
+ name: string;
6
+ value: number;
7
+ timestamp: number;
8
+ tags?: Record<string, string>;
9
+ }
10
+
11
+ interface LogEntry {
12
+ level: 'debug' | 'info' | 'warn' | 'error';
13
+ message: string;
14
+ timestamp: number;
15
+ data?: any;
16
+ }
17
+
18
+ export class Metrics {
19
+ private metrics: Map<string, Metric[]> = new Map();
20
+ private logs: LogEntry[] = [];
21
+ private logDir: string;
22
+ private maxMetricsHistory = 1000;
23
+ private maxLogsHistory = 1000;
24
+
25
+ constructor(logDir: string = './logs') {
26
+ this.logDir = logDir;
27
+ this.ensureLogDir();
28
+ }
29
+
30
+ private ensureLogDir(): void {
31
+ if (!existsSync(this.logDir)) {
32
+ mkdirSync(this.logDir, { recursive: true });
33
+ }
34
+ }
35
+
36
+ /**
37
+ * 记录指标
38
+ */
39
+ record(name: string, value: number, tags?: Record<string, string>): void {
40
+ const metric: Metric = {
41
+ name,
42
+ value,
43
+ timestamp: Date.now(),
44
+ tags
45
+ };
46
+
47
+ if (!this.metrics.has(name)) {
48
+ this.metrics.set(name, []);
49
+ }
50
+
51
+ const history = this.metrics.get(name)!;
52
+ history.push(metric);
53
+
54
+ // 保持历史记录数量
55
+ if (history.length > this.maxMetricsHistory) {
56
+ history.shift();
57
+ }
58
+ }
59
+
60
+ /**
61
+ * 递增计数器
62
+ */
63
+ increment(name: string, tags?: Record<string, string>): void {
64
+ const current = this.get(name);
65
+ this.record(name, current + 1, tags);
66
+ }
67
+
68
+ /**
69
+ * 记录执行时间
70
+ */
71
+ timing(name: string, duration: number, tags?: Record<string, string>): void {
72
+ this.record(`${name}.duration`, duration, tags);
73
+ }
74
+
75
+ /**
76
+ * 获取指标值
77
+ */
78
+ get(name: string): number {
79
+ const history = this.metrics.get(name);
80
+ if (!history || history.length === 0) return 0;
81
+ const last = history[history.length - 1];
82
+ return last?.value ?? 0;
83
+ }
84
+
85
+ /**
86
+ * 获取指标历史
87
+ */
88
+ getHistory(name: string, limit?: number): Metric[] {
89
+ const history = this.metrics.get(name);
90
+ if (!history) return [];
91
+ return limit ? history.slice(-limit) : history;
92
+ }
93
+
94
+ /**
95
+ * 获取所有指标摘要
96
+ */
97
+ getSummary(): Record<string, { count: number; last: number; avg: number }> {
98
+ const summary: Record<string, { count: number; last: number; avg: number }> = {};
99
+
100
+ for (const [name, history] of this.metrics) {
101
+ if (history.length === 0) continue;
102
+
103
+ const values = history.map(m => m.value);
104
+ const sum = values.reduce((a, b) => a + b, 0);
105
+
106
+ summary[name] = {
107
+ count: history.length,
108
+ last: values[values.length - 1] ?? 0,
109
+ avg: sum / values.length
110
+ };
111
+ }
112
+
113
+ return summary;
114
+ }
115
+
116
+ /**
117
+ * 记录日志
118
+ */
119
+ log(level: LogEntry['level'], message: string, data?: any): void {
120
+ const entry: LogEntry = {
121
+ level,
122
+ message,
123
+ timestamp: Date.now(),
124
+ data
125
+ };
126
+
127
+ this.logs.push(entry);
128
+
129
+ // 保持日志数量
130
+ if (this.logs.length > this.maxLogsHistory) {
131
+ this.logs.shift();
132
+ }
133
+
134
+ // 写入文件
135
+ this.writeLogToFile(entry);
136
+ }
137
+
138
+ private writeLogToFile(entry: LogEntry): void {
139
+ try {
140
+ const date = new Date(entry.timestamp).toISOString().split('T')[0];
141
+ const logFile = join(this.logDir, `gateway-${date}.log`);
142
+ const line = `[${new Date(entry.timestamp).toISOString()}] [${entry.level.toUpperCase()}] ${entry.message}${entry.data ? ' ' + JSON.stringify(entry.data) : ''}\n`;
143
+ appendFileSync(logFile, line);
144
+ } catch (error) {
145
+ console.error('Failed to write log to file:', error);
146
+ }
147
+ }
148
+
149
+ /**
150
+ * 获取日志
151
+ */
152
+ getLogs(level?: LogEntry['level'], limit?: number): LogEntry[] {
153
+ let filtered = this.logs;
154
+
155
+ if (level) {
156
+ filtered = filtered.filter(l => l.level === level);
157
+ }
158
+
159
+ return limit ? filtered.slice(-limit) : filtered;
160
+ }
161
+
162
+ /**
163
+ * 获取统计信息
164
+ */
165
+ getStats(): {
166
+ uptime: number;
167
+ totalTasks: number;
168
+ completedTasks: number;
169
+ failedTasks: number;
170
+ activeAgents: number;
171
+ metricsCount: number;
172
+ logsCount: number;
173
+ } {
174
+ const taskHistory = this.metrics.get('tasks.submitted') || [];
175
+ const completedHistory = this.metrics.get('tasks.completed') || [];
176
+ const failedHistory = this.metrics.get('tasks.failed') || [];
177
+
178
+ return {
179
+ uptime: this.startTime ? Date.now() - this.startTime : 0,
180
+ totalTasks: taskHistory.length,
181
+ completedTasks: completedHistory.length,
182
+ failedTasks: failedHistory.length,
183
+ activeAgents: this.metrics.get('agents.active')?.[0]?.value || 0,
184
+ metricsCount: this.metrics.size,
185
+ logsCount: this.logs.length
186
+ };
187
+ }
188
+
189
+ private startTime = Date.now();
190
+
191
+ /**
192
+ * 启动计时
193
+ */
194
+ start(): void {
195
+ this.startTime = Date.now();
196
+ }
197
+
198
+ /**
199
+ * 重置指标
200
+ */
201
+ reset(): void {
202
+ this.metrics.clear();
203
+ this.logs = [];
204
+ this.startTime = Date.now();
205
+ }
206
+ }
207
+
208
+ export const metrics = new Metrics();
209
+
210
+ // 便捷方法
211
+ export const logger = {
212
+ debug: (message: string, data?: any) => metrics.log('debug', message, data),
213
+ info: (message: string, data?: any) => metrics.log('info', message, data),
214
+ warn: (message: string, data?: any) => metrics.log('warn', message, data),
215
+ error: (message: string, data?: any) => metrics.log('error', message, data)
216
+ };
@@ -0,0 +1,105 @@
1
+ import type { Agent, RoutingRule, AgentMessage } from '../types/index.js';
2
+
3
+ export class Router {
4
+ private rules: RoutingRule[] = [];
5
+ private agents: Map<string, Agent> = new Map();
6
+ private defaultAgent?: string;
7
+
8
+ /**
9
+ * 配置路由规则
10
+ */
11
+ configure(rules: RoutingRule[], defaultAgent?: string): void {
12
+ // 按优先级排序
13
+ this.rules = [...rules].sort((a, b) => b.priority - a.priority);
14
+ this.defaultAgent = defaultAgent;
15
+ }
16
+
17
+ /**
18
+ * 注册 Agent
19
+ */
20
+ registerAgent(agent: Agent): void {
21
+ this.agents.set(agent.id, agent);
22
+ }
23
+
24
+ /**
25
+ * 注销 Agent
26
+ */
27
+ unregisterAgent(agentId: string): void {
28
+ this.agents.delete(agentId);
29
+ }
30
+
31
+ /**
32
+ * 获取所有在线 Agent
33
+ */
34
+ getOnlineAgents(): Agent[] {
35
+ return Array.from(this.agents.values()).filter(a => a.status === 'online');
36
+ }
37
+
38
+ /**
39
+ * 路由任务到合适的 Agent
40
+ */
41
+ route(message: AgentMessage): { agentIds: string[]; strategy: 'direct' | 'broadcast' } {
42
+ const task = message.payload?.task || '';
43
+
44
+ // 匹配路由规则
45
+ for (const rule of this.rules) {
46
+ if (this.matchesRule(task, rule.keywords)) {
47
+ // 检查 Agent 是否在线
48
+ if (rule.strategy === 'broadcast' && rule.agents) {
49
+ const availableAgents = rule.agents.filter(id => this.isAgentOnline(id));
50
+ if (availableAgents.length > 0) {
51
+ return { agentIds: availableAgents, strategy: 'broadcast' };
52
+ }
53
+ } else if (rule.agent && this.isAgentOnline(rule.agent)) {
54
+ return { agentIds: [rule.agent], strategy: 'direct' };
55
+ }
56
+ }
57
+ }
58
+
59
+ // 使用默认 Agent
60
+ if (this.defaultAgent && this.isAgentOnline(this.defaultAgent)) {
61
+ return { agentIds: [this.defaultAgent], strategy: 'direct' };
62
+ }
63
+
64
+ // 返回所有在线 Agent 作为兜底
65
+ const onlineAgents = this.getOnlineAgents();
66
+ if (onlineAgents.length > 0) {
67
+ return { agentIds: onlineAgents.map(a => a.id), strategy: 'broadcast' };
68
+ }
69
+
70
+ // 没有可用的 Agent
71
+ return { agentIds: [], strategy: 'direct' };
72
+ }
73
+
74
+ /**
75
+ * 检查任务是否匹配路由关键词
76
+ */
77
+ private matchesRule(task: string, keywords: string[]): boolean {
78
+ const lowerTask = task.toLowerCase();
79
+ return keywords.some(keyword => lowerTask.includes(keyword.toLowerCase()));
80
+ }
81
+
82
+ /**
83
+ * 检查 Agent 是否在线
84
+ */
85
+ private isAgentOnline(agentId: string): boolean {
86
+ const agent = this.agents.get(agentId);
87
+ return agent?.status === 'online';
88
+ }
89
+
90
+ /**
91
+ * 获取 Agent 信息
92
+ */
93
+ getAgent(agentId: string): Agent | undefined {
94
+ return this.agents.get(agentId);
95
+ }
96
+
97
+ /**
98
+ * 获取所有 Agent
99
+ */
100
+ getAllAgents(): Agent[] {
101
+ return Array.from(this.agents.values());
102
+ }
103
+ }
104
+
105
+ export const router = new Router();
@@ -0,0 +1,248 @@
1
+ import { v4 as uuidv4 } from 'uuid';
2
+ import type { Task, AgentMessage, Agent, Error } from '../types/index.js';
3
+ import { eventBus } from './event-bus.js';
4
+ import { router } from './router.js';
5
+ import { contextManager } from './context-manager.js';
6
+ import { agentRegistry } from './agent-registry.js';
7
+
8
+ export class TaskManager {
9
+ private tasks: Map<string, Task> = new Map();
10
+
11
+ /**
12
+ * 创建新任务
13
+ */
14
+ async createTask(request: AgentMessage): Promise<Task> {
15
+ const taskId = uuidv4();
16
+ const task: Task = {
17
+ id: taskId,
18
+ status: 'pending',
19
+ request,
20
+ assignedAgents: [],
21
+ createdAt: Date.now(),
22
+ updatedAt: Date.now()
23
+ };
24
+
25
+ this.tasks.set(taskId, task);
26
+
27
+ // 发布任务提交事件
28
+ eventBus.publishTaskEvent('task.submitted', {
29
+ ...request,
30
+ id: taskId
31
+ });
32
+
33
+ return task;
34
+ }
35
+
36
+ /**
37
+ * 分配任务到 Agent
38
+ */
39
+ assignTask(taskId: string, agentIds: string[]): Task | null {
40
+ const task = this.tasks.get(taskId);
41
+ if (!task) {
42
+ return null;
43
+ }
44
+
45
+ task.assignedAgents = agentIds;
46
+ task.status = 'assigned';
47
+ task.updatedAt = Date.now();
48
+
49
+ // 发布任务分配事件
50
+ eventBus.publishTaskEvent('task.assigned', {
51
+ ...task.request,
52
+ id: taskId
53
+ });
54
+
55
+ return task;
56
+ }
57
+
58
+ /**
59
+ * 开始执行任务
60
+ */
61
+ startTask(taskId: string): Task | null {
62
+ const task = this.tasks.get(taskId);
63
+ if (!task) {
64
+ return null;
65
+ }
66
+
67
+ task.status = 'running';
68
+ task.updatedAt = Date.now();
69
+
70
+ eventBus.publishTaskEvent('task.started', {
71
+ ...task.request,
72
+ id: taskId
73
+ });
74
+
75
+ return task;
76
+ }
77
+
78
+ /**
79
+ * 完成任务
80
+ */
81
+ completeTask(taskId: string, result: unknown): Task | null {
82
+ const task = this.tasks.get(taskId);
83
+ if (!task) {
84
+ return null;
85
+ }
86
+
87
+ task.status = 'completed';
88
+ task.result = result;
89
+ task.updatedAt = Date.now();
90
+
91
+ eventBus.publishTaskEvent('task.completed', {
92
+ ...task.request,
93
+ id: taskId,
94
+ result
95
+ });
96
+
97
+ return task;
98
+ }
99
+
100
+ /**
101
+ * 任务失败
102
+ */
103
+ failTask(taskId: string, error: Error): Task | null {
104
+ const task = this.tasks.get(taskId);
105
+ if (!task) {
106
+ return null;
107
+ }
108
+
109
+ task.status = 'failed';
110
+ task.error = error;
111
+ task.updatedAt = Date.now();
112
+
113
+ eventBus.publishTaskEvent('task.failed', {
114
+ ...task.request,
115
+ id: taskId,
116
+ error
117
+ });
118
+
119
+ return task;
120
+ }
121
+
122
+ /**
123
+ * 获取任务
124
+ */
125
+ getTask(taskId: string): Task | undefined {
126
+ return this.tasks.get(taskId);
127
+ }
128
+
129
+ /**
130
+ * 获取所有任务
131
+ */
132
+ getAllTasks(): Task[] {
133
+ return Array.from(this.tasks.values());
134
+ }
135
+
136
+ /**
137
+ * 处理任务
138
+ */
139
+ async processTask(message: AgentMessage): Promise<Task> {
140
+ // 1. 创建任务
141
+ const task = await this.createTask(message);
142
+
143
+ // 2. 如果有共享空间,添加消息到上下文
144
+ if (message.payload?.context?.shared_space_id) {
145
+ await contextManager.addMessage(
146
+ message.payload.context.shared_space_id,
147
+ message
148
+ );
149
+ }
150
+
151
+ // 3. 路由任务到 Agent
152
+ const { agentIds, strategy } = router.route(message);
153
+
154
+ if (agentIds.length === 0) {
155
+ this.failTask(task.id, {
156
+ code: 'NO_AGENT_AVAILABLE',
157
+ message: 'No available agents to handle this task'
158
+ });
159
+ throw new Error('No available agents');
160
+ }
161
+
162
+ // 4. 分配任务
163
+ this.assignTask(task.id, agentIds);
164
+ this.startTask(task.id);
165
+
166
+ // 5. 执行任务
167
+ await this.executeTask(task, agentIds, strategy);
168
+
169
+ return task;
170
+ }
171
+
172
+ /**
173
+ * 执行任务
174
+ */
175
+ private async executeTask(
176
+ task: Task,
177
+ agentIds: string[],
178
+ strategy: 'direct' | 'broadcast'
179
+ ): Promise<void> {
180
+ const results: Record<string, unknown> = {};
181
+
182
+ if (strategy === 'direct' && agentIds[0]) {
183
+ // 单 Agent 执行
184
+ const agentId = agentIds[0]!;
185
+ const adapter = agentRegistry.get(agentId);
186
+
187
+ if (!adapter) {
188
+ this.failTask(task.id, {
189
+ code: 'AGENT_NOT_FOUND',
190
+ message: `Agent ${agentId} not found`
191
+ });
192
+ return;
193
+ }
194
+
195
+ try {
196
+ console.log(`[TaskManager] Executing task ${task.id} with agent ${agentId}`);
197
+ const response = await adapter.invoke(task.request);
198
+ results[agentId] = response.result;
199
+
200
+ // 如果有共享空间,添加响应到上下文
201
+ if (task.request.payload?.context?.shared_space_id) {
202
+ await contextManager.addMessage(
203
+ task.request.payload.context.shared_space_id,
204
+ response
205
+ );
206
+ }
207
+
208
+ this.completeTask(task.id, response.result);
209
+ } catch (error: unknown) {
210
+ const errorMessage = error instanceof Error ? error.message : String(error);
211
+ this.failTask(task.id, {
212
+ code: 'EXECUTION_ERROR',
213
+ message: errorMessage
214
+ });
215
+ }
216
+ } else {
217
+ // 广播模式:多个 Agent 同时执行
218
+ const promises = agentIds.map(async (agentId) => {
219
+ const adapter = agentRegistry.get(agentId);
220
+ if (!adapter) {
221
+ results[agentId] = { error: `Agent ${agentId} not found` };
222
+ return;
223
+ }
224
+
225
+ try {
226
+ const response = await adapter.invoke(task.request);
227
+ results[agentId] = response.result;
228
+
229
+ // 添加响应到上下文
230
+ if (task.request.payload?.context?.shared_space_id) {
231
+ await contextManager.addMessage(
232
+ task.request.payload.context.shared_space_id,
233
+ response
234
+ );
235
+ }
236
+ } catch (error: unknown) {
237
+ const errorMessage = error instanceof Error ? error.message : String(error);
238
+ results[agentId] = { error: errorMessage };
239
+ }
240
+ });
241
+
242
+ await Promise.all(promises);
243
+ this.completeTask(task.id, results);
244
+ }
245
+ }
246
+ }
247
+
248
+ export const taskManager = new TaskManager();