astrabot 0.1.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 (47) hide show
  1. package/README.md +411 -0
  2. package/ai/ai.config.ts +27 -0
  3. package/ai/auto-retry.ts +117 -0
  4. package/ai/config-loader.ts +132 -0
  5. package/ai/index.ts +4 -0
  6. package/ai/retry-prompt.ts +30 -0
  7. package/bin/astra +2 -0
  8. package/core/retry/error-classifier.ts +208 -0
  9. package/core/retry/index.ts +29 -0
  10. package/core/retry/retry-config.ts +142 -0
  11. package/core/retry/retry-engine.ts +215 -0
  12. package/game/index.html +573 -0
  13. package/game/neon-breaker.html +1037 -0
  14. package/index.ts +140 -0
  15. package/modes/agent/action-tracker.ts +47 -0
  16. package/modes/agent/agent-tools.ts +338 -0
  17. package/modes/agent/approval.ts +184 -0
  18. package/modes/agent/diff-view.ts +34 -0
  19. package/modes/agent/orchestrator.ts +234 -0
  20. package/modes/agent/tool-executor.ts +993 -0
  21. package/modes/agent/types.ts +68 -0
  22. package/modes/ask/orchestrator.ts +230 -0
  23. package/modes/auto.ts +88 -0
  24. package/modes/cli.ts +43 -0
  25. package/modes/multi/agent-pool-manager.ts +337 -0
  26. package/modes/multi/examples.ts +441 -0
  27. package/modes/multi/message-broker.ts +179 -0
  28. package/modes/multi/multi-agent-orchestrator.ts +891 -0
  29. package/modes/multi/orchestrator.ts +414 -0
  30. package/modes/multi/types.ts +245 -0
  31. package/modes/multi/workflow-builder.ts +569 -0
  32. package/modes/plan/orchestrator.ts +198 -0
  33. package/modes/plan/planner.ts +121 -0
  34. package/modes/plan/selection.ts +43 -0
  35. package/modes/plan/types.ts +13 -0
  36. package/modes/plan/web-tools.ts +132 -0
  37. package/modes/setup.ts +210 -0
  38. package/package.json +62 -0
  39. package/session/index.ts +45 -0
  40. package/session/session-context.ts +188 -0
  41. package/session/session-manager.ts +374 -0
  42. package/session/session-tools.ts +109 -0
  43. package/session/store.ts +278 -0
  44. package/tsconfig.json +30 -0
  45. package/tui/spinner.ts +182 -0
  46. package/tui/terminal-md.ts +17 -0
  47. package/tui/wakeup.ts +231 -0
@@ -0,0 +1,441 @@
1
+ import chalk from "chalk";
2
+ import { MultiAgentOrchestrator } from "./multi-agent-orchestrator";
3
+ import { WorkflowBuilder, WorkflowTemplates } from "./workflow-builder";
4
+ import { withSpinner } from "../../tui/spinner";
5
+
6
+ /**
7
+ * Example: Code review with inline failure handling
8
+ */
9
+ export async function exampleCodeReviewWorkflow(): Promise<void> {
10
+ console.log(chalk.bold("\nšŸ¤ Code Review Workflow\n"));
11
+
12
+ const workflow = new WorkflowBuilder(
13
+ "review_001",
14
+ "Review the authentication module for security vulnerabilities",
15
+ )
16
+ .addResearcher(
17
+ "security_analyzer",
18
+ "Security Analyzer",
19
+ "Analyzes code for security vulnerabilities and best practices",
20
+ )
21
+ .addImplementer(
22
+ "code_improver",
23
+ "Code Improver",
24
+ "Fixes identified security issues and improves code",
25
+ )
26
+ .addReviewer(
27
+ "qa_validator",
28
+ "QA Validator",
29
+ "Validates that all security fixes are properly implemented",
30
+ )
31
+ .withSequentialStrategy()
32
+ .withRetryOnFailure(1)
33
+ .withExpectedOutput("Security audit report with fixes applied")
34
+ .build();
35
+
36
+ const validation = new WorkflowBuilder(workflow.id, workflow.goal).validate();
37
+ if (!validation.isValid) {
38
+ console.log(chalk.red("āŒ Validation failed:"));
39
+ validation.errors.forEach((e) => console.log(chalk.red(` • ${e}`)));
40
+ return;
41
+ }
42
+
43
+ const orchestrator = new MultiAgentOrchestrator(workflow);
44
+
45
+ // Listen to events
46
+ const unsubscribe = orchestrator.onEvent((event) => {
47
+ if (event.type === "agent:complete") {
48
+ console.log(chalk.dim(` āœ“ ${event.agentId} completed`));
49
+ } else if (event.type === "agent:failed") {
50
+ console.log(chalk.red(` āœ— ${event.agentId} failed`));
51
+ }
52
+ });
53
+
54
+ await withSpinner(
55
+ {
56
+ message: "Orchestrating review workflow...",
57
+ doneMessage: "workflow completed",
58
+ failMessage: "workflow failed",
59
+ },
60
+ () => orchestrator.execute(),
61
+ );
62
+
63
+ unsubscribe();
64
+
65
+ const summary = orchestrator.getSummary();
66
+ console.log(chalk.bold("\nšŸ“Š Results\n"));
67
+ console.log(`Status: ${chalk.green(summary.status)}`);
68
+ console.log(`Duration: ${summary.duration}ms`);
69
+ console.log(`Completed: ${summary.completedTasks}`);
70
+ console.log(`Failed: ${summary.failedTasks}`);
71
+ }
72
+
73
+ /**
74
+ * Example: Parallel feature development with multiple models
75
+ */
76
+ export async function exampleParallelDevelopment(): Promise<void> {
77
+ console.log(chalk.bold("\n⚔ Parallel Feature Development\n"));
78
+
79
+ const workflow = WorkflowTemplates.featureDevelopmentWorkflow(
80
+ "feature_dev_001",
81
+ "Implement user authentication with OAuth2 support",
82
+ );
83
+
84
+ const orchestrator = new MultiAgentOrchestrator(workflow);
85
+
86
+ await withSpinner(
87
+ {
88
+ message: "Executing parallel feature development...",
89
+ doneMessage: "feature development completed",
90
+ failMessage: "feature development failed",
91
+ },
92
+ () => orchestrator.execute(),
93
+ );
94
+
95
+ console.log(chalk.bold("\nāœ… Feature Development Complete\n"));
96
+ const timeline = orchestrator.getTimeline();
97
+
98
+ for (const result of timeline) {
99
+ if (result.success) {
100
+ console.log(chalk.green(`āœ“ ${result.agentId} (${result.durationMs}ms)`));
101
+ if (result.output) {
102
+ console.log(chalk.dim(` Output: ${result.output.slice(0, 80)}...`));
103
+ }
104
+ } else {
105
+ console.log(chalk.red(`āœ— ${result.agentId}`));
106
+ if (result.error) {
107
+ console.log(chalk.dim(` Error: ${result.error.message}`));
108
+ }
109
+ }
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Example: Collaborative bug investigation
115
+ */
116
+ export async function exampleCollaborativeBugFix(): Promise<void> {
117
+ console.log(chalk.bold("\nšŸ› Collaborative Bug Investigation\n"));
118
+
119
+ const workflow = new WorkflowBuilder(
120
+ "bugfix_001",
121
+ "The login endpoint returns 500 errors intermittently",
122
+ )
123
+ .addResearcher(
124
+ "bug_analyzer",
125
+ "Bug Analyzer",
126
+ "Analyzes error logs and traces the source of the issue",
127
+ )
128
+ .addResearcher(
129
+ "test_investigator",
130
+ "Test Investigator",
131
+ "Writes tests to reproduce the bug",
132
+ )
133
+ .addImplementer(
134
+ "bug_fixer",
135
+ "Bug Fixer",
136
+ "Implements the fix based on findings",
137
+ )
138
+ .withCollaborativeStrategy(60_000)
139
+ .withExpectedOutput("Fixed login endpoint with regression tests")
140
+ .build();
141
+
142
+ const orchestrator = new MultiAgentOrchestrator(workflow);
143
+
144
+ await withSpinner(
145
+ {
146
+ message: "Investigating bug with collaborative agents...",
147
+ doneMessage: "investigation complete",
148
+ failMessage: "investigation failed",
149
+ },
150
+ () => orchestrator.execute(),
151
+ );
152
+
153
+ const messages = orchestrator.getMessageHistory();
154
+ if (messages.length > 0) {
155
+ console.log(chalk.bold("\nšŸ’¬ Agent Communication\n"));
156
+ for (const msg of messages.slice(-5)) {
157
+ console.log(`${chalk.cyan(msg.fromAgentId)} → ${msg.toAgentId || "all"}: ${msg.type}`);
158
+ console.log(chalk.dim(` ${msg.content.slice(0, 80)}...`));
159
+ }
160
+ }
161
+ }
162
+
163
+ /**
164
+ * Example: Advanced DAG workflow with full-stack development
165
+ */
166
+ export async function exampleAdvancedDAGWorkflow(): Promise<void> {
167
+ console.log(chalk.bold("\nšŸš€ Full-Stack Development (DAG)\n"));
168
+
169
+ const workflow = WorkflowTemplates.fullStackFeatureWorkflow(
170
+ "fullstack_001",
171
+ "Build a new user profile management feature with database, API, and UI",
172
+ );
173
+
174
+ console.log(chalk.dim("Dependencies:"));
175
+ for (const agent of workflow.agents) {
176
+ const deps = agent.dependsOn?.join(", ") || "(root)";
177
+ console.log(chalk.dim(` ${agent.name} ← ${deps}`));
178
+ }
179
+ console.log();
180
+
181
+ const orchestrator = new MultiAgentOrchestrator(workflow);
182
+
183
+ // Log events in real-time
184
+ orchestrator.onEvent((event) => {
185
+ if (event.type === "agent:start") {
186
+ console.log(chalk.cyan(`→ ${event.agentId} starting`));
187
+ } else if (event.type === "agent:complete") {
188
+ const duration = event.payload?.duration;
189
+ console.log(chalk.green(`āœ“ ${event.agentId} done${duration ? ` (${duration}ms)` : ""}`));
190
+ }
191
+ });
192
+
193
+ await withSpinner(
194
+ {
195
+ message: "Executing DAG workflow...",
196
+ doneMessage: "all stages completed",
197
+ failMessage: "workflow encountered errors",
198
+ },
199
+ () => orchestrator.execute(),
200
+ );
201
+
202
+ const summary = orchestrator.getSummary();
203
+ console.log(chalk.bold("\nšŸ“ˆ Detailed Report\n"));
204
+ console.log(`Total Duration: ${summary.duration}ms`);
205
+ console.log(`Agents: ${summary.totalAgents}`);
206
+ console.log(`Completed: ${chalk.green(String(summary.completedTasks))}`);
207
+ console.log(`Failed: ${chalk.red(String(summary.failedTasks))}`);
208
+ console.log();
209
+
210
+ for (const result of summary.executionResults) {
211
+ const icon = result.success ? chalk.green("āœ“") : chalk.red("āœ—");
212
+ console.log(`${icon} ${chalk.bold(result.agentId)} (${result.role})`);
213
+ console.log(` Duration: ${result.durationMs}ms, Steps: ${result.steps}`);
214
+ }
215
+ }
216
+
217
+ /**
218
+ * Example: Security audit with parallel reviewers
219
+ */
220
+ export async function exampleSecurityAudit(): Promise<void> {
221
+ console.log(chalk.bold("\nšŸ”’ Security Audit (Parallel)\n"));
222
+
223
+ const workflow = WorkflowTemplates.securityAuditWorkflow(
224
+ "audit_001",
225
+ "Full security audit of the payment processing module",
226
+ );
227
+
228
+ const orchestrator = new MultiAgentOrchestrator(workflow);
229
+
230
+ // Track which agents are running
231
+ const running = new Set<string>();
232
+ orchestrator.onEvent((event) => {
233
+ if (event.type === "agent:start" && event.agentId) {
234
+ running.add(event.agentId);
235
+ console.log(chalk.cyan(`→ [${running.size}] ${event.agentId}`));
236
+ } else if (event.type === "agent:complete" && event.agentId) {
237
+ running.delete(event.agentId);
238
+ console.log(chalk.green(`āœ“ [${running.size}] ${event.agentId}`));
239
+ }
240
+ });
241
+
242
+ await withSpinner(
243
+ {
244
+ message: "Running security audit...",
245
+ doneMessage: "audit completed",
246
+ failMessage: "audit failed",
247
+ },
248
+ () => orchestrator.execute(),
249
+ );
250
+
251
+ const summary = orchestrator.getSummary();
252
+ console.log(chalk.bold("\nšŸ” Audit Summary\n"));
253
+ console.log(`Status: ${summary.status}`);
254
+ console.log(`Duration: ${summary.duration}ms`);
255
+
256
+ console.log(chalk.bold("\nšŸ“‹ Agent Reports\n"));
257
+ const timeline = orchestrator.getTimeline();
258
+ for (const result of timeline) {
259
+ if (result.success) {
260
+ console.log(chalk.green(`āœ“ ${result.agentId}`));
261
+ console.log(chalk.dim(` ${result.output.slice(0, 150)}...`));
262
+ }
263
+ }
264
+ }
265
+
266
+ /**
267
+ * Example: Multi-model orchestration — different models for different agents
268
+ */
269
+ export async function exampleMultiModelOrchestration(): Promise<void> {
270
+ console.log(chalk.bold("\n🧠 Multi-Model Orchestration\n"));
271
+
272
+ const workflow = new WorkflowBuilder(
273
+ "multimodel_001",
274
+ "Refactor API layer to clean architecture",
275
+ )
276
+ .addCoordinator(
277
+ "architect",
278
+ "Software Architect",
279
+ "Designs clean architecture and creates refactoring plan",
280
+ { model: "anthropic/claude-opus-4" }, // Strongest model
281
+ )
282
+ .addImplementer(
283
+ "refactor_impl",
284
+ "Refactoring Implementer",
285
+ "Executes the refactoring plan",
286
+ { model: "anthropic/claude-3.5-sonnet" }, // Fast & capable
287
+ )
288
+ .addReviewer(
289
+ "test_validator",
290
+ "Test Validator",
291
+ "Writes and runs tests to verify refactoring",
292
+ ) // Uses default
293
+ .withHierarchicalStrategy()
294
+ .withRetryOnFailure(1)
295
+ .build();
296
+
297
+ console.log(chalk.bold("Model Assignments:\n"));
298
+ for (const agent of workflow.agents) {
299
+ const model = agent.model ? chalk.cyan(agent.model) : chalk.dim("(default)");
300
+ console.log(` ${chalk.bold(agent.name)}: ${model}`);
301
+ }
302
+ console.log();
303
+
304
+ const orchestrator = new MultiAgentOrchestrator(workflow);
305
+
306
+ await withSpinner(
307
+ {
308
+ message: "Running multi-model orchestration...",
309
+ doneMessage: "orchestration complete",
310
+ failMessage: "orchestration failed",
311
+ },
312
+ () => orchestrator.execute(),
313
+ );
314
+
315
+ const summary = orchestrator.getSummary();
316
+ console.log(chalk.bold("\nšŸ“Š Results\n"));
317
+ for (const result of summary.executionResults) {
318
+ const icon = result.success ? chalk.green("āœ“") : chalk.red("āœ—");
319
+ const agent = workflow.agents.find((a) => a.id === result.agentId);
320
+ const model = agent?.model ? chalk.cyan(`[${agent.model}]`) : chalk.dim("[default]");
321
+ console.log(`${icon} ${chalk.bold(result.agentId)} ${model}`);
322
+ console.log(` ${result.durationMs}ms, ${result.steps} steps`);
323
+ }
324
+ }
325
+
326
+ /**
327
+ * Example: Error handling and retry logic
328
+ */
329
+ export async function exampleErrorHandling(): Promise<void> {
330
+ console.log(chalk.bold("\nāš ļø Error Handling & Retry\n"));
331
+
332
+ const workflow = new WorkflowBuilder(
333
+ "error_handling_001",
334
+ "Test error recovery with retries",
335
+ )
336
+ .addImplementer(
337
+ "risky_impl",
338
+ "Risky Implementer",
339
+ "An agent that might fail and will retry",
340
+ )
341
+ .addReviewer(
342
+ "fallback_reviewer",
343
+ "Fallback Reviewer",
344
+ "Reviews if main impl fails",
345
+ )
346
+ .withParallelStrategy(2, 20_000)
347
+ .withRetryOnFailure(3)
348
+ .withFailureMode("continue")
349
+ .build();
350
+
351
+ console.log(chalk.dim("Config:"));
352
+ console.log(chalk.dim(` - Max retries: 3`));
353
+ console.log(chalk.dim(` - Failure mode: continue`));
354
+ console.log(chalk.dim(` - Timeout: 20s per agent\n`));
355
+
356
+ const orchestrator = new MultiAgentOrchestrator(workflow);
357
+
358
+ orchestrator.onEvent((event) => {
359
+ if (event.type === "agent:retry") {
360
+ const attempt = event.payload?.attempt || 1;
361
+ console.log(chalk.yellow(`↻ ${event.agentId} retry attempt ${attempt}`));
362
+ }
363
+ });
364
+
365
+ await withSpinner(
366
+ {
367
+ message: "Running with error handling...",
368
+ doneMessage: "completed",
369
+ failMessage: "failed",
370
+ },
371
+ () => orchestrator.execute(),
372
+ );
373
+
374
+ const summary = orchestrator.getSummary();
375
+ console.log(chalk.bold("\nšŸ“Š Retry Statistics\n"));
376
+ for (const result of summary.executionResults) {
377
+ console.log(
378
+ `${result.agentId}: attempt ${result.attemptNumber} - ${result.success ? "āœ“" : "āœ—"}`,
379
+ );
380
+ }
381
+ }
382
+
383
+ /**
384
+ * Example: Event-driven monitoring
385
+ */
386
+ export async function exampleEventDrivenMonitoring(): Promise<void> {
387
+ console.log(chalk.bold("\nšŸ“” Event-Driven Monitoring\n"));
388
+
389
+ const workflow = new WorkflowBuilder("events_001", "Demo event monitoring")
390
+ .addResearcher("researcher", "Researcher", "Gathers info")
391
+ .addImplementer("impl", "Implementer", "Implements", { dependsOn: ["researcher"] })
392
+ .addReviewer("reviewer", "Reviewer", "Reviews", { dependsOn: ["impl"] })
393
+ .withDagStrategy(2, 30_000)
394
+ .build();
395
+
396
+ const orchestrator = new MultiAgentOrchestrator(workflow);
397
+
398
+ const eventCounts = {
399
+ "agent:start": 0,
400
+ "agent:complete": 0,
401
+ "agent:failed": 0,
402
+ "workflow:start": 0,
403
+ "workflow:complete": 0,
404
+ };
405
+
406
+ orchestrator.onEvent((event) => {
407
+ const key = event.type as keyof typeof eventCounts;
408
+ if (key in eventCounts) eventCounts[key]++;
409
+
410
+ const icon =
411
+ event.type.includes("complete") || event.type === "workflow:start"
412
+ ? chalk.green("āœ“")
413
+ : chalk.red("āœ—");
414
+ console.log(`${icon} ${event.type}`);
415
+ });
416
+
417
+ await withSpinner(
418
+ {
419
+ message: "Monitoring events...",
420
+ doneMessage: "completed",
421
+ failMessage: "failed",
422
+ },
423
+ () => orchestrator.execute(),
424
+ );
425
+
426
+ console.log(chalk.bold("\nšŸ“Š Event Summary\n"));
427
+ for (const [type, count] of Object.entries(eventCounts)) {
428
+ console.log(`${type}: ${count}`);
429
+ }
430
+ }
431
+
432
+ export default {
433
+ exampleCodeReviewWorkflow,
434
+ exampleParallelDevelopment,
435
+ exampleCollaborativeBugFix,
436
+ exampleAdvancedDAGWorkflow,
437
+ exampleSecurityAudit,
438
+ exampleMultiModelOrchestration,
439
+ exampleErrorHandling,
440
+ exampleEventDrivenMonitoring,
441
+ };
@@ -0,0 +1,179 @@
1
+ import type { AgentMessage, CommunicationChannel } from "./types";
2
+
3
+ /**
4
+ * Implements a publish-subscribe communication channel for agents.
5
+ * Supports directed messages, broadcasts, priority ordering, and replay.
6
+ */
7
+ export class MessageBroker implements CommunicationChannel {
8
+ messageBuffer: AgentMessage[] = [];
9
+ subscribers: Map<string, (msg: AgentMessage) => void> = new Map();
10
+ private messageId = 0;
11
+
12
+ // ─── Sending ───────────────────────────────────────────────────────────────
13
+
14
+ /**
15
+ * Broadcast or direct-send a message.
16
+ * High-priority messages are delivered first within the same tick.
17
+ */
18
+ broadcast(message: Omit<AgentMessage, "id" | "timestamp">): AgentMessage {
19
+ const fullMessage: AgentMessage = {
20
+ id: `msg_${String(++this.messageId).padStart(6, "0")}`,
21
+ timestamp: new Date(),
22
+ priority: "normal",
23
+ ...message,
24
+ };
25
+
26
+ this.messageBuffer.push(fullMessage);
27
+
28
+ if (message.toAgentId) {
29
+ // Directed message
30
+ const callback = this.subscribers.get(message.toAgentId);
31
+ if (callback) callback(fullMessage);
32
+ } else {
33
+ // Broadcast — exclude sender, deliver high-priority first
34
+ const recipients = [...this.subscribers.entries()]
35
+ .filter(([agentId]) => agentId !== message.fromAgentId);
36
+
37
+ // Sort so high-priority callbacks fire first
38
+ const ordered =
39
+ fullMessage.priority === "high"
40
+ ? recipients
41
+ : recipients; // stable order is fine for normal/low
42
+
43
+ for (const [, callback] of ordered) {
44
+ callback(fullMessage);
45
+ }
46
+ }
47
+
48
+ return fullMessage;
49
+ }
50
+
51
+ /**
52
+ * Convenience wrapper: send a directed message from one agent to another.
53
+ */
54
+ send(
55
+ fromAgentId: string,
56
+ toAgentId: string,
57
+ content: string,
58
+ type: AgentMessage["type"] = "request",
59
+ extra?: Partial<Omit<AgentMessage, "id" | "timestamp" | "fromAgentId" | "toAgentId" | "content" | "type">>,
60
+ ): AgentMessage {
61
+ return this.broadcast({
62
+ fromAgentId,
63
+ toAgentId,
64
+ content,
65
+ type,
66
+ requiresResponse: type === "request",
67
+ ...extra,
68
+ });
69
+ }
70
+
71
+ // ─── Subscribing ───────────────────────────────────────────────────────────
72
+
73
+ /**
74
+ * Subscribe an agent to receive messages.
75
+ * Returns an unsubscribe function.
76
+ */
77
+ subscribe(
78
+ agentId: string,
79
+ callback: (msg: AgentMessage) => void,
80
+ ): () => void {
81
+ this.subscribers.set(agentId, callback);
82
+ return () => {
83
+ this.subscribers.delete(agentId);
84
+ };
85
+ }
86
+
87
+ // ─── Querying ─────────────────────────────────────────────────────────────
88
+
89
+ /** All messages directed to (or broadcast to) a specific agent */
90
+ getMessagesFor(agentId: string): AgentMessage[] {
91
+ return this.messageBuffer.filter(
92
+ (msg) => !msg.toAgentId || msg.toAgentId === agentId,
93
+ );
94
+ }
95
+
96
+ /** All messages sent FROM a specific agent */
97
+ getMessagesFrom(agentId: string): AgentMessage[] {
98
+ return this.messageBuffer.filter((msg) => msg.fromAgentId === agentId);
99
+ }
100
+
101
+ /** Bidirectional conversation between two agents */
102
+ getConversation(agentId1: string, agentId2: string): AgentMessage[] {
103
+ return this.messageBuffer.filter(
104
+ (msg) =>
105
+ (msg.fromAgentId === agentId1 && msg.toAgentId === agentId2) ||
106
+ (msg.fromAgentId === agentId2 && msg.toAgentId === agentId1),
107
+ );
108
+ }
109
+
110
+ /** Messages of a specific type */
111
+ getMessagesByType(type: AgentMessage["type"]): AgentMessage[] {
112
+ return this.messageBuffer.filter((msg) => msg.type === type);
113
+ }
114
+
115
+ /** Get the reply chain for a given message ID */
116
+ getReplyChain(messageId: string): AgentMessage[] {
117
+ const chain: AgentMessage[] = [];
118
+ let currentId: string | undefined = messageId;
119
+
120
+ while (currentId) {
121
+ const msg = this.messageBuffer.find((m) => m.id === currentId);
122
+ if (!msg) break;
123
+ chain.unshift(msg);
124
+ currentId = msg.replyToId;
125
+ }
126
+
127
+ return chain;
128
+ }
129
+
130
+ /** Total number of messages in the buffer */
131
+ get messageCount(): number {
132
+ return this.messageBuffer.length;
133
+ }
134
+
135
+ // ─── Buffer Management ─────────────────────────────────────────────────────
136
+
137
+ clearBuffer(): void {
138
+ this.messageBuffer = [];
139
+ }
140
+
141
+ /**
142
+ * Trim the buffer to keep only the last `n` messages.
143
+ * Useful for long-running collaborative workflows.
144
+ */
145
+ trimBuffer(keepLast: number): void {
146
+ if (this.messageBuffer.length > keepLast) {
147
+ this.messageBuffer = this.messageBuffer.slice(-keepLast);
148
+ }
149
+ }
150
+
151
+ // ─── Replay ───────────────────────────────────────────────────────────────
152
+
153
+ /**
154
+ * Replay messages for an agent in chronological order.
155
+ */
156
+ async replayMessages(
157
+ agentId: string,
158
+ callback: (msg: AgentMessage) => Promise<void>,
159
+ ): Promise<void> {
160
+ const messages = this.getMessagesFor(agentId);
161
+ for (const msg of messages) {
162
+ await callback(msg);
163
+ }
164
+ }
165
+
166
+ // ─── Diagnostics ──────────────────────────────────────────────────────────
167
+
168
+ stats() {
169
+ const byType = new Map<string, number>();
170
+ for (const msg of this.messageBuffer) {
171
+ byType.set(msg.type, (byType.get(msg.type) ?? 0) + 1);
172
+ }
173
+ return {
174
+ total: this.messageBuffer.length,
175
+ subscribers: this.subscribers.size,
176
+ byType: Object.fromEntries(byType),
177
+ };
178
+ }
179
+ }