skyloom 1.14.6 → 1.15.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.
- package/.github/workflows/ci.yml +2 -2
- package/.github/workflows/publish.yml +74 -0
- package/CONVERSION_PLAN.md +191 -191
- package/README.md +523 -220
- package/config/default.yaml +46 -43
- package/config/models.yaml +928 -155
- package/config/providers.yaml +109 -6
- package/dist/agents/snow.d.ts +2 -0
- package/dist/agents/snow.d.ts.map +1 -1
- package/dist/agents/snow.js +36 -5
- package/dist/agents/snow.js.map +1 -1
- package/dist/cli/loom_chat.d.ts.map +1 -1
- package/dist/cli/loom_chat.js +207 -1
- package/dist/cli/loom_chat.js.map +1 -1
- package/dist/cli/main.js +190 -40
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/tui.d.ts.map +1 -1
- package/dist/cli/tui.js +6 -31
- package/dist/cli/tui.js.map +1 -1
- package/dist/core/agent.d.ts +6 -4
- package/dist/core/agent.d.ts.map +1 -1
- package/dist/core/agent.js +61 -20
- package/dist/core/agent.js.map +1 -1
- package/dist/core/catalog.d.ts.map +1 -1
- package/dist/core/catalog.js +30 -9
- package/dist/core/catalog.js.map +1 -1
- package/dist/core/commands.d.ts +110 -0
- package/dist/core/commands.d.ts.map +1 -0
- package/dist/core/commands.js +633 -0
- package/dist/core/commands.js.map +1 -0
- package/dist/core/concurrency.d.ts +38 -0
- package/dist/core/concurrency.d.ts.map +1 -0
- package/dist/core/concurrency.js +65 -0
- package/dist/core/concurrency.js.map +1 -0
- package/dist/core/factory.js +16 -16
- package/dist/core/file_checkpoint.d.ts +9 -0
- package/dist/core/file_checkpoint.d.ts.map +1 -1
- package/dist/core/file_checkpoint.js +33 -1
- package/dist/core/file_checkpoint.js.map +1 -1
- package/dist/core/llm.d.ts.map +1 -1
- package/dist/core/llm.js +66 -13
- package/dist/core/llm.js.map +1 -1
- package/dist/core/memory.js +51 -51
- package/dist/core/schemas.d.ts +16 -0
- package/dist/core/schemas.d.ts.map +1 -1
- package/dist/core/schemas.js +32 -0
- package/dist/core/schemas.js.map +1 -1
- package/dist/core/security.d.ts.map +1 -1
- package/dist/core/security.js +27 -0
- package/dist/core/security.js.map +1 -1
- package/dist/core/skymd.js +14 -14
- package/dist/core/trace.d.ts +105 -0
- package/dist/core/trace.d.ts.map +1 -0
- package/dist/core/trace.js +213 -0
- package/dist/core/trace.js.map +1 -0
- package/dist/tools/builtin.d.ts +2 -6
- package/dist/tools/builtin.d.ts.map +1 -1
- package/dist/tools/builtin.js +180 -125
- package/dist/tools/builtin.js.map +1 -1
- package/dist/tools/extra.d.ts +13 -0
- package/dist/tools/extra.d.ts.map +1 -0
- package/dist/tools/extra.js +827 -0
- package/dist/tools/extra.js.map +1 -0
- package/dist/tools/guards.d.ts +12 -0
- package/dist/tools/guards.d.ts.map +1 -0
- package/dist/tools/guards.js +143 -0
- package/dist/tools/guards.js.map +1 -0
- package/dist/tools/model_tool.d.ts.map +1 -1
- package/dist/tools/model_tool.js +24 -4
- package/dist/tools/model_tool.js.map +1 -1
- package/dist/web/markdown.d.ts +32 -0
- package/dist/web/markdown.d.ts.map +1 -0
- package/dist/web/markdown.js +202 -0
- package/dist/web/markdown.js.map +1 -0
- package/dist/web/server.d.ts +4 -0
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/server.js +14 -582
- package/dist/web/server.js.map +1 -1
- package/dist/web/ui.d.ts +31 -0
- package/dist/web/ui.d.ts.map +1 -0
- package/dist/web/ui.js +1009 -0
- package/dist/web/ui.js.map +1 -0
- package/docs/AESTHETIC_DESIGN.md +152 -152
- package/docs/OPTIMIZATION_PLAN.md +178 -178
- package/package.json +68 -68
- package/src/agents/snow.ts +38 -5
- package/src/cli/commands_md.ts +112 -112
- package/src/cli/input_macros.ts +83 -83
- package/src/cli/loom.ts +1041 -1041
- package/src/cli/loom_chat.ts +772 -603
- package/src/cli/main.ts +853 -723
- package/src/cli/tui.ts +264 -289
- package/src/core/agent/guard.ts +133 -133
- package/src/core/agent/task.ts +100 -100
- package/src/core/agent.ts +1630 -1590
- package/src/core/agent_helpers.ts +500 -500
- package/src/core/bus.ts +221 -221
- package/src/core/cache.ts +153 -153
- package/src/core/catalog.ts +199 -178
- package/src/core/circuit_breaker.ts +119 -119
- package/src/core/commands.ts +704 -0
- package/src/core/concurrency.ts +73 -0
- package/src/core/config.ts +365 -365
- package/src/core/constants.ts +95 -95
- package/src/core/factory.ts +656 -656
- package/src/core/file_checkpoint.ts +163 -136
- package/src/core/hooks.ts +126 -126
- package/src/core/llm.ts +972 -915
- package/src/core/logger.ts +143 -143
- package/src/core/mcp.ts +1001 -1001
- package/src/core/memory.ts +1201 -1201
- package/src/core/middleware.ts +350 -350
- package/src/core/model_config.ts +159 -159
- package/src/core/pipelines.ts +424 -424
- package/src/core/schemas.ts +319 -282
- package/src/core/security.ts +27 -0
- package/src/core/semantic.ts +211 -211
- package/src/core/skill.ts +384 -384
- package/src/core/skymd.ts +143 -143
- package/src/core/theme.ts +65 -65
- package/src/core/tool.ts +457 -457
- package/src/core/trace.ts +236 -0
- package/src/core/verify.ts +71 -71
- package/src/plugins/loader.ts +91 -91
- package/src/skills/loader.ts +75 -75
- package/src/tools/builtin.ts +571 -493
- package/src/tools/computer.ts +279 -279
- package/src/tools/extra.ts +662 -0
- package/src/tools/guards.ts +82 -0
- package/src/tools/model_tool.ts +93 -74
- package/src/tools/todo.ts +76 -76
- package/src/web/markdown.ts +193 -0
- package/src/web/server.ts +117 -693
- package/src/web/ui.ts +949 -0
- package/tests/agent.test.ts +211 -159
- package/tests/agent_helpers.test.ts +48 -48
- package/tests/catalog.test.ts +86 -86
- package/tests/checkpoint_commands.test.ts +124 -124
- package/tests/claude_compat.test.ts +110 -110
- package/tests/commands.test.ts +103 -0
- package/tests/concurrency.test.ts +102 -0
- package/tests/config.test.ts +41 -41
- package/tests/extra_tools.test.ts +212 -0
- package/tests/fence_plugin.test.ts +52 -52
- package/tests/guard.test.ts +75 -75
- package/tests/loom.test.ts +337 -337
- package/tests/memory.test.ts +170 -170
- package/tests/model_config.test.ts +109 -109
- package/tests/skymd.test.ts +146 -146
- package/tests/ssrf.test.ts +38 -38
- package/tests/structured_retry.test.ts +87 -0
- package/tests/task.test.ts +60 -60
- package/tests/todo_toolstats.test.ts +94 -94
- package/tests/trace.test.ts +128 -0
- package/tests/tui.test.ts +67 -67
- package/tests/web.test.ts +169 -0
- package/tsconfig.json +38 -38
package/src/core/bus.ts
CHANGED
|
@@ -1,221 +1,221 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Async event-driven message bus for inter-agent communication.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { EventEmitter } from 'events';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Event types for the message bus.
|
|
9
|
-
*/
|
|
10
|
-
export enum EventType {
|
|
11
|
-
TASK_ASSIGNED = 'task_assigned',
|
|
12
|
-
TASK_COMPLETED = 'task_completed',
|
|
13
|
-
TASK_FEEDBACK = 'task_feedback',
|
|
14
|
-
AGENT_REQUEST = 'agent_request',
|
|
15
|
-
AGENT_RESPONSE = 'agent_response',
|
|
16
|
-
SYSTEM_EVENT = 'system_event',
|
|
17
|
-
STATE_CHANGE = 'state_change', // agent state changes
|
|
18
|
-
LLM_CALL = 'llm_call', // LLM request made
|
|
19
|
-
TOOL_CALL = 'tool_call', // tool was called
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Event object for message bus.
|
|
24
|
-
*/
|
|
25
|
-
export class Event {
|
|
26
|
-
type: EventType;
|
|
27
|
-
source: string; // agent name or "system"
|
|
28
|
-
target: string | null; // null = broadcast
|
|
29
|
-
data: Record<string, any>;
|
|
30
|
-
timestamp: Date;
|
|
31
|
-
|
|
32
|
-
constructor(
|
|
33
|
-
type: EventType,
|
|
34
|
-
source: string,
|
|
35
|
-
target?: string | null,
|
|
36
|
-
data?: Record<string, any>
|
|
37
|
-
) {
|
|
38
|
-
this.type = type;
|
|
39
|
-
this.source = source;
|
|
40
|
-
this.target = target || null;
|
|
41
|
-
this.data = data || {};
|
|
42
|
-
this.timestamp = new Date();
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Convert event to JSON-serializable format.
|
|
47
|
-
*/
|
|
48
|
-
toJSON(): Record<string, any> {
|
|
49
|
-
return {
|
|
50
|
-
type: this.type,
|
|
51
|
-
source: this.source,
|
|
52
|
-
target: this.target,
|
|
53
|
-
data: this.data,
|
|
54
|
-
timestamp: this.timestamp.toISOString(),
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Handler function for events.
|
|
61
|
-
*/
|
|
62
|
-
export type Handler = (event: Event) => Promise<void>;
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Pub/sub message bus for agent communication.
|
|
66
|
-
*/
|
|
67
|
-
export class MessageBus {
|
|
68
|
-
private subscribers: Map<string, Handler[]> = new Map();
|
|
69
|
-
private stateListeners: Handler[] = [];
|
|
70
|
-
private history: Event[] = [];
|
|
71
|
-
private maxHistory: number = 2000;
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Subscribe an agent to events.
|
|
75
|
-
*/
|
|
76
|
-
subscribe(agentName: string, handler: Handler): void {
|
|
77
|
-
if (!this.subscribers.has(agentName)) {
|
|
78
|
-
this.subscribers.set(agentName, []);
|
|
79
|
-
}
|
|
80
|
-
this.subscribers.get(agentName)!.push(handler);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Unsubscribe an agent.
|
|
85
|
-
*/
|
|
86
|
-
unsubscribe(agentName: string): void {
|
|
87
|
-
this.subscribers.delete(agentName);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Register a global handler for state change events.
|
|
92
|
-
*/
|
|
93
|
-
onStateChange(handler: Handler): void {
|
|
94
|
-
this.stateListeners.push(handler);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Remove a state change listener.
|
|
99
|
-
*/
|
|
100
|
-
removeStateListener(handler: Handler): void {
|
|
101
|
-
const idx = this.stateListeners.indexOf(handler);
|
|
102
|
-
if (idx >= 0) {
|
|
103
|
-
this.stateListeners.splice(idx, 1);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Trim history to max size.
|
|
109
|
-
*/
|
|
110
|
-
private trimHistory(): void {
|
|
111
|
-
if (this.history.length > this.maxHistory) {
|
|
112
|
-
this.history = this.history.slice(this.history.length - this.maxHistory);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Record event without routing (for state changes / local observation).
|
|
118
|
-
*/
|
|
119
|
-
addEvent(event: Event): void {
|
|
120
|
-
this.history.push(event);
|
|
121
|
-
this.trimHistory();
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Notify state change listeners.
|
|
126
|
-
*/
|
|
127
|
-
async notifyStateChange(event: Event): Promise<void> {
|
|
128
|
-
if (event.type !== EventType.STATE_CHANGE) {
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
for (const handler of this.stateListeners) {
|
|
133
|
-
try {
|
|
134
|
-
await handler(event);
|
|
135
|
-
} catch (err) {
|
|
136
|
-
console.error('state_change handler failed:', err);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Publish an event to subscribers.
|
|
143
|
-
*/
|
|
144
|
-
async publish(event: Event): Promise<void> {
|
|
145
|
-
this.history.push(event);
|
|
146
|
-
this.trimHistory();
|
|
147
|
-
|
|
148
|
-
if (event.target) {
|
|
149
|
-
// Direct message
|
|
150
|
-
const handlers = this.subscribers.get(event.target) || [];
|
|
151
|
-
for (const handler of handlers) {
|
|
152
|
-
try {
|
|
153
|
-
await handler(event);
|
|
154
|
-
} catch (err) {
|
|
155
|
-
console.error(`bus handler failed for target=${event.target}:`, err);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
} else {
|
|
159
|
-
// Broadcast to all except source
|
|
160
|
-
for (const [name, handlers] of this.subscribers.entries()) {
|
|
161
|
-
if (name === event.source) {
|
|
162
|
-
continue;
|
|
163
|
-
}
|
|
164
|
-
for (const handler of handlers) {
|
|
165
|
-
try {
|
|
166
|
-
await handler(event);
|
|
167
|
-
} catch (err) {
|
|
168
|
-
console.error(`bus handler failed for subscriber=${name}:`, err);
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* Get event history.
|
|
177
|
-
*/
|
|
178
|
-
getHistory(
|
|
179
|
-
agentName?: string | null,
|
|
180
|
-
eventType?: EventType | null,
|
|
181
|
-
limit: number = 50
|
|
182
|
-
): Event[] {
|
|
183
|
-
let events = this.history;
|
|
184
|
-
|
|
185
|
-
if (agentName) {
|
|
186
|
-
events = events.filter(e => e.source === agentName || e.target === agentName);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
if (eventType) {
|
|
190
|
-
events = events.filter(e => e.type === eventType);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
return events.slice(Math.max(0, events.length - limit));
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* Clear all history.
|
|
198
|
-
*/
|
|
199
|
-
clearHistory(): void {
|
|
200
|
-
this.history = [];
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* Get event count.
|
|
205
|
-
*/
|
|
206
|
-
getEventCount(): number {
|
|
207
|
-
return this.history.length;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* Get active subscriber count.
|
|
212
|
-
*/
|
|
213
|
-
getSubscriberCount(): number {
|
|
214
|
-
return this.subscribers.size;
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
/**
|
|
219
|
-
* Global event emitter (optional, for direct event emission).
|
|
220
|
-
*/
|
|
221
|
-
export const eventEmitter = new EventEmitter();
|
|
1
|
+
/**
|
|
2
|
+
* Async event-driven message bus for inter-agent communication.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { EventEmitter } from 'events';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Event types for the message bus.
|
|
9
|
+
*/
|
|
10
|
+
export enum EventType {
|
|
11
|
+
TASK_ASSIGNED = 'task_assigned',
|
|
12
|
+
TASK_COMPLETED = 'task_completed',
|
|
13
|
+
TASK_FEEDBACK = 'task_feedback',
|
|
14
|
+
AGENT_REQUEST = 'agent_request',
|
|
15
|
+
AGENT_RESPONSE = 'agent_response',
|
|
16
|
+
SYSTEM_EVENT = 'system_event',
|
|
17
|
+
STATE_CHANGE = 'state_change', // agent state changes
|
|
18
|
+
LLM_CALL = 'llm_call', // LLM request made
|
|
19
|
+
TOOL_CALL = 'tool_call', // tool was called
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Event object for message bus.
|
|
24
|
+
*/
|
|
25
|
+
export class Event {
|
|
26
|
+
type: EventType;
|
|
27
|
+
source: string; // agent name or "system"
|
|
28
|
+
target: string | null; // null = broadcast
|
|
29
|
+
data: Record<string, any>;
|
|
30
|
+
timestamp: Date;
|
|
31
|
+
|
|
32
|
+
constructor(
|
|
33
|
+
type: EventType,
|
|
34
|
+
source: string,
|
|
35
|
+
target?: string | null,
|
|
36
|
+
data?: Record<string, any>
|
|
37
|
+
) {
|
|
38
|
+
this.type = type;
|
|
39
|
+
this.source = source;
|
|
40
|
+
this.target = target || null;
|
|
41
|
+
this.data = data || {};
|
|
42
|
+
this.timestamp = new Date();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Convert event to JSON-serializable format.
|
|
47
|
+
*/
|
|
48
|
+
toJSON(): Record<string, any> {
|
|
49
|
+
return {
|
|
50
|
+
type: this.type,
|
|
51
|
+
source: this.source,
|
|
52
|
+
target: this.target,
|
|
53
|
+
data: this.data,
|
|
54
|
+
timestamp: this.timestamp.toISOString(),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Handler function for events.
|
|
61
|
+
*/
|
|
62
|
+
export type Handler = (event: Event) => Promise<void>;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Pub/sub message bus for agent communication.
|
|
66
|
+
*/
|
|
67
|
+
export class MessageBus {
|
|
68
|
+
private subscribers: Map<string, Handler[]> = new Map();
|
|
69
|
+
private stateListeners: Handler[] = [];
|
|
70
|
+
private history: Event[] = [];
|
|
71
|
+
private maxHistory: number = 2000;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Subscribe an agent to events.
|
|
75
|
+
*/
|
|
76
|
+
subscribe(agentName: string, handler: Handler): void {
|
|
77
|
+
if (!this.subscribers.has(agentName)) {
|
|
78
|
+
this.subscribers.set(agentName, []);
|
|
79
|
+
}
|
|
80
|
+
this.subscribers.get(agentName)!.push(handler);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Unsubscribe an agent.
|
|
85
|
+
*/
|
|
86
|
+
unsubscribe(agentName: string): void {
|
|
87
|
+
this.subscribers.delete(agentName);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Register a global handler for state change events.
|
|
92
|
+
*/
|
|
93
|
+
onStateChange(handler: Handler): void {
|
|
94
|
+
this.stateListeners.push(handler);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Remove a state change listener.
|
|
99
|
+
*/
|
|
100
|
+
removeStateListener(handler: Handler): void {
|
|
101
|
+
const idx = this.stateListeners.indexOf(handler);
|
|
102
|
+
if (idx >= 0) {
|
|
103
|
+
this.stateListeners.splice(idx, 1);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Trim history to max size.
|
|
109
|
+
*/
|
|
110
|
+
private trimHistory(): void {
|
|
111
|
+
if (this.history.length > this.maxHistory) {
|
|
112
|
+
this.history = this.history.slice(this.history.length - this.maxHistory);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Record event without routing (for state changes / local observation).
|
|
118
|
+
*/
|
|
119
|
+
addEvent(event: Event): void {
|
|
120
|
+
this.history.push(event);
|
|
121
|
+
this.trimHistory();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Notify state change listeners.
|
|
126
|
+
*/
|
|
127
|
+
async notifyStateChange(event: Event): Promise<void> {
|
|
128
|
+
if (event.type !== EventType.STATE_CHANGE) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
for (const handler of this.stateListeners) {
|
|
133
|
+
try {
|
|
134
|
+
await handler(event);
|
|
135
|
+
} catch (err) {
|
|
136
|
+
console.error('state_change handler failed:', err);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Publish an event to subscribers.
|
|
143
|
+
*/
|
|
144
|
+
async publish(event: Event): Promise<void> {
|
|
145
|
+
this.history.push(event);
|
|
146
|
+
this.trimHistory();
|
|
147
|
+
|
|
148
|
+
if (event.target) {
|
|
149
|
+
// Direct message
|
|
150
|
+
const handlers = this.subscribers.get(event.target) || [];
|
|
151
|
+
for (const handler of handlers) {
|
|
152
|
+
try {
|
|
153
|
+
await handler(event);
|
|
154
|
+
} catch (err) {
|
|
155
|
+
console.error(`bus handler failed for target=${event.target}:`, err);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
} else {
|
|
159
|
+
// Broadcast to all except source
|
|
160
|
+
for (const [name, handlers] of this.subscribers.entries()) {
|
|
161
|
+
if (name === event.source) {
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
for (const handler of handlers) {
|
|
165
|
+
try {
|
|
166
|
+
await handler(event);
|
|
167
|
+
} catch (err) {
|
|
168
|
+
console.error(`bus handler failed for subscriber=${name}:`, err);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Get event history.
|
|
177
|
+
*/
|
|
178
|
+
getHistory(
|
|
179
|
+
agentName?: string | null,
|
|
180
|
+
eventType?: EventType | null,
|
|
181
|
+
limit: number = 50
|
|
182
|
+
): Event[] {
|
|
183
|
+
let events = this.history;
|
|
184
|
+
|
|
185
|
+
if (agentName) {
|
|
186
|
+
events = events.filter(e => e.source === agentName || e.target === agentName);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (eventType) {
|
|
190
|
+
events = events.filter(e => e.type === eventType);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return events.slice(Math.max(0, events.length - limit));
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Clear all history.
|
|
198
|
+
*/
|
|
199
|
+
clearHistory(): void {
|
|
200
|
+
this.history = [];
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Get event count.
|
|
205
|
+
*/
|
|
206
|
+
getEventCount(): number {
|
|
207
|
+
return this.history.length;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Get active subscriber count.
|
|
212
|
+
*/
|
|
213
|
+
getSubscriberCount(): number {
|
|
214
|
+
return this.subscribers.size;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Global event emitter (optional, for direct event emission).
|
|
220
|
+
*/
|
|
221
|
+
export const eventEmitter = new EventEmitter();
|