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.
- package/README.md +411 -0
- package/ai/ai.config.ts +27 -0
- package/ai/auto-retry.ts +117 -0
- package/ai/config-loader.ts +132 -0
- package/ai/index.ts +4 -0
- package/ai/retry-prompt.ts +30 -0
- package/bin/astra +2 -0
- package/core/retry/error-classifier.ts +208 -0
- package/core/retry/index.ts +29 -0
- package/core/retry/retry-config.ts +142 -0
- package/core/retry/retry-engine.ts +215 -0
- package/game/index.html +573 -0
- package/game/neon-breaker.html +1037 -0
- package/index.ts +140 -0
- package/modes/agent/action-tracker.ts +47 -0
- package/modes/agent/agent-tools.ts +338 -0
- package/modes/agent/approval.ts +184 -0
- package/modes/agent/diff-view.ts +34 -0
- package/modes/agent/orchestrator.ts +234 -0
- package/modes/agent/tool-executor.ts +993 -0
- package/modes/agent/types.ts +68 -0
- package/modes/ask/orchestrator.ts +230 -0
- package/modes/auto.ts +88 -0
- package/modes/cli.ts +43 -0
- package/modes/multi/agent-pool-manager.ts +337 -0
- package/modes/multi/examples.ts +441 -0
- package/modes/multi/message-broker.ts +179 -0
- package/modes/multi/multi-agent-orchestrator.ts +891 -0
- package/modes/multi/orchestrator.ts +414 -0
- package/modes/multi/types.ts +245 -0
- package/modes/multi/workflow-builder.ts +569 -0
- package/modes/plan/orchestrator.ts +198 -0
- package/modes/plan/planner.ts +121 -0
- package/modes/plan/selection.ts +43 -0
- package/modes/plan/types.ts +13 -0
- package/modes/plan/web-tools.ts +132 -0
- package/modes/setup.ts +210 -0
- package/package.json +62 -0
- package/session/index.ts +45 -0
- package/session/session-context.ts +188 -0
- package/session/session-manager.ts +374 -0
- package/session/session-tools.ts +109 -0
- package/session/store.ts +278 -0
- package/tsconfig.json +30 -0
- package/tui/spinner.ts +182 -0
- package/tui/terminal-md.ts +17 -0
- package/tui/wakeup.ts +231 -0
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AgentConfig,
|
|
3
|
+
AgentInstance,
|
|
4
|
+
AgentPool,
|
|
5
|
+
AgentContext,
|
|
6
|
+
AgentMessage,
|
|
7
|
+
AgentStatus,
|
|
8
|
+
} from "./types";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Manages a pool of agent instances, tracking their status,
|
|
12
|
+
* availability, and execution state.
|
|
13
|
+
*/
|
|
14
|
+
export class AgentPoolManager {
|
|
15
|
+
private pool: AgentPool;
|
|
16
|
+
|
|
17
|
+
constructor() {
|
|
18
|
+
this.pool = {
|
|
19
|
+
agents: new Map(),
|
|
20
|
+
activeAgents: new Set(),
|
|
21
|
+
waitingAgents: new Set(),
|
|
22
|
+
failedAgents: new Set(),
|
|
23
|
+
completedAgents: new Set(),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// ─── Registration ──────────────────────────────────────────────────────────
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Register a new agent in the pool.
|
|
31
|
+
* Idempotent: re-registering the same ID replaces the existing instance.
|
|
32
|
+
*/
|
|
33
|
+
registerAgent(config: AgentConfig): AgentInstance {
|
|
34
|
+
const instance: AgentInstance = {
|
|
35
|
+
config,
|
|
36
|
+
context: {
|
|
37
|
+
goal: "",
|
|
38
|
+
conversationHistory: [],
|
|
39
|
+
sharedState: new Map(),
|
|
40
|
+
metadata: {
|
|
41
|
+
startTime: new Date(),
|
|
42
|
+
currentStep: 0,
|
|
43
|
+
completedTasks: [],
|
|
44
|
+
failedTasks: [],
|
|
45
|
+
retryCount: 0,
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
status: "pending",
|
|
49
|
+
get isActive() {
|
|
50
|
+
return this.status === "running";
|
|
51
|
+
},
|
|
52
|
+
lastMessageTime: new Date(),
|
|
53
|
+
messageQueue: [],
|
|
54
|
+
completionPercentage: 0,
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
this.pool.agents.set(config.id, instance);
|
|
58
|
+
this.pool.waitingAgents.add(config.id);
|
|
59
|
+
|
|
60
|
+
return instance;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ─── Lookups ───────────────────────────────────────────────────────────────
|
|
64
|
+
|
|
65
|
+
getAgent(agentId: string): AgentInstance | undefined {
|
|
66
|
+
return this.pool.agents.get(agentId);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
getAgentsByRole(role: string): AgentInstance[] {
|
|
70
|
+
return Array.from(this.pool.agents.values()).filter(
|
|
71
|
+
(agent) => agent.config.role === role,
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
getAgentsByTag(tag: string): AgentInstance[] {
|
|
76
|
+
return Array.from(this.pool.agents.values()).filter(
|
|
77
|
+
(agent) => agent.config.tags?.includes(tag),
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
getAgentsByStatus(status: AgentStatus): AgentInstance[] {
|
|
82
|
+
return Array.from(this.pool.agents.values()).filter(
|
|
83
|
+
(agent) => agent.status === status,
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
getActiveAgents(): AgentInstance[] {
|
|
88
|
+
return this._resolveIds(this.pool.activeAgents);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
getWaitingAgents(): AgentInstance[] {
|
|
92
|
+
return this._resolveIds(this.pool.waitingAgents);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
getFailedAgents(): AgentInstance[] {
|
|
96
|
+
return this._resolveIds(this.pool.failedAgents);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
getCompletedAgents(): AgentInstance[] {
|
|
100
|
+
return this._resolveIds(this.pool.completedAgents);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
getAllAgents(): AgentInstance[] {
|
|
104
|
+
return Array.from(this.pool.agents.values());
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
private _resolveIds(ids: Set<string>): AgentInstance[] {
|
|
108
|
+
const result: AgentInstance[] = [];
|
|
109
|
+
for (const id of ids) {
|
|
110
|
+
const agent = this.pool.agents.get(id);
|
|
111
|
+
if (agent) result.push(agent);
|
|
112
|
+
}
|
|
113
|
+
return result;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// ─── Status Transitions ────────────────────────────────────────────────────
|
|
117
|
+
|
|
118
|
+
activateAgent(agentId: string): boolean {
|
|
119
|
+
const agent = this.pool.agents.get(agentId);
|
|
120
|
+
if (!agent) return false;
|
|
121
|
+
|
|
122
|
+
agent.status = "running";
|
|
123
|
+
agent.startedAt = new Date();
|
|
124
|
+
agent.lastMessageTime = new Date();
|
|
125
|
+
|
|
126
|
+
this.pool.waitingAgents.delete(agentId);
|
|
127
|
+
this.pool.failedAgents.delete(agentId);
|
|
128
|
+
this.pool.completedAgents.delete(agentId);
|
|
129
|
+
this.pool.activeAgents.add(agentId);
|
|
130
|
+
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
deactivateAgent(agentId: string): boolean {
|
|
135
|
+
const agent = this.pool.agents.get(agentId);
|
|
136
|
+
if (!agent) return false;
|
|
137
|
+
|
|
138
|
+
agent.status = "completed";
|
|
139
|
+
agent.completedAt = new Date();
|
|
140
|
+
agent.completionPercentage = 100;
|
|
141
|
+
|
|
142
|
+
this.pool.activeAgents.delete(agentId);
|
|
143
|
+
this.pool.waitingAgents.delete(agentId);
|
|
144
|
+
this.pool.completedAgents.add(agentId);
|
|
145
|
+
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
markAgentFailed(agentId: string): boolean {
|
|
150
|
+
const agent = this.pool.agents.get(agentId);
|
|
151
|
+
if (!agent) return false;
|
|
152
|
+
|
|
153
|
+
agent.status = "failed";
|
|
154
|
+
agent.completedAt = new Date();
|
|
155
|
+
|
|
156
|
+
this.pool.activeAgents.delete(agentId);
|
|
157
|
+
this.pool.waitingAgents.delete(agentId);
|
|
158
|
+
this.pool.failedAgents.add(agentId);
|
|
159
|
+
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
markAgentRetrying(agentId: string): boolean {
|
|
164
|
+
const agent = this.pool.agents.get(agentId);
|
|
165
|
+
if (!agent) return false;
|
|
166
|
+
|
|
167
|
+
agent.status = "retrying";
|
|
168
|
+
agent.context.metadata.retryCount++;
|
|
169
|
+
|
|
170
|
+
this.pool.failedAgents.delete(agentId);
|
|
171
|
+
this.pool.activeAgents.delete(agentId);
|
|
172
|
+
this.pool.waitingAgents.add(agentId);
|
|
173
|
+
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
markAgentSkipped(agentId: string): boolean {
|
|
178
|
+
const agent = this.pool.agents.get(agentId);
|
|
179
|
+
if (!agent) return false;
|
|
180
|
+
|
|
181
|
+
agent.status = "skipped";
|
|
182
|
+
agent.completedAt = new Date();
|
|
183
|
+
|
|
184
|
+
this.pool.waitingAgents.delete(agentId);
|
|
185
|
+
this.pool.activeAgents.delete(agentId);
|
|
186
|
+
this.pool.completedAgents.add(agentId);
|
|
187
|
+
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// ─── Messaging ─────────────────────────────────────────────────────────────
|
|
192
|
+
|
|
193
|
+
queueMessageFor(agentId: string, message: AgentMessage): boolean {
|
|
194
|
+
const agent = this.pool.agents.get(agentId);
|
|
195
|
+
if (!agent) return false;
|
|
196
|
+
|
|
197
|
+
agent.messageQueue.push(message);
|
|
198
|
+
agent.context.conversationHistory.push(message);
|
|
199
|
+
return true;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/** Drain and return the message queue for an agent */
|
|
203
|
+
flushMessageQueue(agentId: string): AgentMessage[] {
|
|
204
|
+
const agent = this.pool.agents.get(agentId);
|
|
205
|
+
if (!agent) return [];
|
|
206
|
+
|
|
207
|
+
const messages = [...agent.messageQueue];
|
|
208
|
+
agent.messageQueue = [];
|
|
209
|
+
return messages;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// ─── Context & Progress ────────────────────────────────────────────────────
|
|
213
|
+
|
|
214
|
+
updateCompletion(agentId: string, percentage: number): boolean {
|
|
215
|
+
const agent = this.pool.agents.get(agentId);
|
|
216
|
+
if (!agent) return false;
|
|
217
|
+
|
|
218
|
+
agent.completionPercentage = Math.min(100, Math.max(0, percentage));
|
|
219
|
+
return true;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
updateContext(agentId: string, context: Partial<AgentContext>): boolean {
|
|
223
|
+
const agent = this.pool.agents.get(agentId);
|
|
224
|
+
if (!agent) return false;
|
|
225
|
+
|
|
226
|
+
Object.assign(agent.context, context);
|
|
227
|
+
return true;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
setFinding(agentId: string, key: string, value: unknown): boolean {
|
|
231
|
+
const agent = this.pool.agents.get(agentId);
|
|
232
|
+
if (!agent) return false;
|
|
233
|
+
|
|
234
|
+
if (!agent.context.metadata.findings) {
|
|
235
|
+
agent.context.metadata.findings = {};
|
|
236
|
+
}
|
|
237
|
+
agent.context.metadata.findings[key] = value;
|
|
238
|
+
return true;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// ─── DAG Dependency Checking ───────────────────────────────────────────────
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Returns true if all agents listed in `dependsOn` for the given agent
|
|
245
|
+
* have completed successfully.
|
|
246
|
+
*/
|
|
247
|
+
areDependenciesMet(agentId: string): boolean {
|
|
248
|
+
const agent = this.pool.agents.get(agentId);
|
|
249
|
+
if (!agent) return false;
|
|
250
|
+
|
|
251
|
+
const deps = agent.config.dependsOn ?? [];
|
|
252
|
+
for (const depId of deps) {
|
|
253
|
+
const dep = this.pool.agents.get(depId);
|
|
254
|
+
if (!dep || dep.status !== "completed") return false;
|
|
255
|
+
}
|
|
256
|
+
return true;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Returns agent IDs whose dependencies are all completed and which are
|
|
261
|
+
* currently waiting (ready to run).
|
|
262
|
+
*/
|
|
263
|
+
getReadyAgents(): AgentInstance[] {
|
|
264
|
+
return Array.from(this.pool.waitingAgents)
|
|
265
|
+
.map((id) => this.pool.agents.get(id))
|
|
266
|
+
.filter(
|
|
267
|
+
(agent): agent is AgentInstance =>
|
|
268
|
+
agent !== undefined && this.areDependenciesMet(agent.config.id),
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// ─── Pool Inspection ──────────────────────────────────────────────────────
|
|
273
|
+
|
|
274
|
+
getPool(): AgentPool {
|
|
275
|
+
return this.pool;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
areAllAgentsSettled(): boolean {
|
|
279
|
+
return (
|
|
280
|
+
this.pool.activeAgents.size === 0 &&
|
|
281
|
+
this.pool.waitingAgents.size === 0
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
areAllAgentsComplete(): boolean {
|
|
286
|
+
return (
|
|
287
|
+
this.pool.activeAgents.size === 0 &&
|
|
288
|
+
this.pool.waitingAgents.size === 0 &&
|
|
289
|
+
this.pool.failedAgents.size === 0
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
hasFailedAgents(): boolean {
|
|
294
|
+
return this.pool.failedAgents.size > 0;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
reset(): void {
|
|
298
|
+
this.pool = {
|
|
299
|
+
agents: new Map(),
|
|
300
|
+
activeAgents: new Set(),
|
|
301
|
+
waitingAgents: new Set(),
|
|
302
|
+
failedAgents: new Set(),
|
|
303
|
+
completedAgents: new Set(),
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
getStats() {
|
|
308
|
+
const agents = Array.from(this.pool.agents.values());
|
|
309
|
+
const completionPercentage =
|
|
310
|
+
agents.length > 0
|
|
311
|
+
? Math.round(
|
|
312
|
+
agents.reduce((sum, a) => sum + a.completionPercentage, 0) /
|
|
313
|
+
agents.length,
|
|
314
|
+
)
|
|
315
|
+
: 0;
|
|
316
|
+
|
|
317
|
+
return {
|
|
318
|
+
totalAgents: this.pool.agents.size,
|
|
319
|
+
activeAgents: this.pool.activeAgents.size,
|
|
320
|
+
waitingAgents: this.pool.waitingAgents.size,
|
|
321
|
+
failedAgents: this.pool.failedAgents.size,
|
|
322
|
+
completedAgents: this.pool.completedAgents.size,
|
|
323
|
+
completionPercentage,
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/** Human-readable status snapshot for debugging */
|
|
328
|
+
debugSnapshot(): string {
|
|
329
|
+
const lines: string[] = [`Pool snapshot (${this.pool.agents.size} agents):`];
|
|
330
|
+
for (const [id, agent] of this.pool.agents) {
|
|
331
|
+
lines.push(
|
|
332
|
+
` ${id.padEnd(24)} ${agent.status.padEnd(12)} ${agent.completionPercentage}% retry=${agent.context.metadata.retryCount}`,
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
return lines.join("\n");
|
|
336
|
+
}
|
|
337
|
+
}
|