@amitdeshmukh/ax-crew 8.7.3 → 9.0.1
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/CHANGELOG.md +40 -0
- package/README.md +9 -9
- package/dist/agents/agentConfig.d.ts +3 -0
- package/dist/agents/agentConfig.js +12 -4
- package/dist/agents/crew.d.ts +59 -0
- package/dist/agents/crew.js +356 -0
- package/dist/agents/deferredTools.d.ts +49 -0
- package/dist/agents/deferredTools.js +237 -0
- package/dist/agents/index.d.ts +4 -266
- package/dist/agents/index.js +3 -1014
- package/dist/agents/lazyAgent.d.ts +33 -0
- package/dist/agents/lazyAgent.js +78 -0
- package/dist/agents/statefulAgent.d.ts +97 -0
- package/dist/agents/statefulAgent.js +478 -0
- package/dist/index.d.ts +2 -2
- package/dist/types.d.ts +18 -1
- package/examples/graphjin-database-agent.ts +85 -64
- package/examples/write-post-and-publish-to-wordpress.ts +1 -1
- package/package.json +1 -1
- package/src/agents/agentConfig.ts +15 -8
- package/src/agents/crew.ts +444 -0
- package/src/agents/deferredTools.ts +275 -0
- package/src/agents/index.ts +4 -1281
- package/src/agents/lazyAgent.ts +95 -0
- package/src/agents/statefulAgent.ts +668 -0
- package/src/index.ts +7 -4
- package/src/skills/axcrew-functions.md +2 -2
- package/src/skills/axcrew-mcp.md +41 -1
- package/src/skills/axcrew-patterns.md +48 -4
- package/src/skills/axcrew-state.md +16 -16
- package/src/skills/axcrew.md +14 -1
- package/src/types.ts +19 -0
- package/.claude/settings.local.json +0 -13
- package/.claude/skills/ax-crew/SKILL.md +0 -466
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
import { v4 as uuidv4 } from "uuid";
|
|
2
|
+
import type { AxFunction } from "@ax-llm/ax";
|
|
3
|
+
|
|
4
|
+
import type {
|
|
5
|
+
StateInstance,
|
|
6
|
+
FunctionRegistryType,
|
|
7
|
+
AxCrewConfig,
|
|
8
|
+
AxCrewOptions,
|
|
9
|
+
ACEConfig,
|
|
10
|
+
DeferredToolsConfig,
|
|
11
|
+
} from "../types.js";
|
|
12
|
+
|
|
13
|
+
import { createState } from "../state/index.js";
|
|
14
|
+
import { parseCrewConfig, parseAgentConfig } from "./agentConfig.js";
|
|
15
|
+
import { DeferredToolManager } from "./deferredTools.js";
|
|
16
|
+
import { MetricsRegistry } from "../metrics/index.js";
|
|
17
|
+
import { StatefulAxAgent } from "./statefulAgent.js";
|
|
18
|
+
import type { ParsedAgentConfig } from "./statefulAgent.js";
|
|
19
|
+
import { LazyStatefulAxAgent } from "./lazyAgent.js";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* AxCrew orchestrates a set of Ax agents that share state,
|
|
23
|
+
* tools (functions), optional MCP servers, streaming, and a built-in metrics
|
|
24
|
+
* registry for tokens, requests, and estimated cost.
|
|
25
|
+
*
|
|
26
|
+
* Typical usage:
|
|
27
|
+
* const crew = new AxCrew(config, AxCrewFunctions)
|
|
28
|
+
* await crew.addAllAgents()
|
|
29
|
+
* const planner = crew.agents?.get("Planner")
|
|
30
|
+
* const res = await planner?.forward({ task: "Plan something" })
|
|
31
|
+
*
|
|
32
|
+
* Key behaviors:
|
|
33
|
+
* - Validates and instantiates agents from a config-first model
|
|
34
|
+
* - Shares a mutable state object across all agents in the crew
|
|
35
|
+
* - Supports sub-agents and a function registry per agent
|
|
36
|
+
* - Tracks per-agent and crew-level metrics via MetricsRegistry
|
|
37
|
+
* - Provides helpers to add agents (individually, a subset, or all) and
|
|
38
|
+
* to reset metrics/costs when needed
|
|
39
|
+
*/
|
|
40
|
+
class AxCrew {
|
|
41
|
+
private crewConfig: AxCrewConfig;
|
|
42
|
+
private options?: AxCrewOptions;
|
|
43
|
+
functionsRegistry: FunctionRegistryType = {};
|
|
44
|
+
crewId: string;
|
|
45
|
+
agents: Map<string, StatefulAxAgent> | null;
|
|
46
|
+
crewState: StateInstance;
|
|
47
|
+
// Execution history for ACE feedback routing
|
|
48
|
+
private executionHistory: Map<string, {
|
|
49
|
+
taskId: string;
|
|
50
|
+
rootAgent: string;
|
|
51
|
+
involvedAgents: Set<string>;
|
|
52
|
+
taskInput: any;
|
|
53
|
+
agentResults: Map<string, any>;
|
|
54
|
+
startTime: number;
|
|
55
|
+
endTime?: number;
|
|
56
|
+
}> = new Map();
|
|
57
|
+
|
|
58
|
+
constructor(
|
|
59
|
+
crewConfig: AxCrewConfig,
|
|
60
|
+
functionsRegistry: FunctionRegistryType = {},
|
|
61
|
+
options?: AxCrewOptions,
|
|
62
|
+
crewId: string = uuidv4(),
|
|
63
|
+
) {
|
|
64
|
+
if (!crewConfig || typeof crewConfig !== 'object' || !('crew' in crewConfig)) {
|
|
65
|
+
throw new Error('Invalid crew configuration');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
crewConfig.crew.forEach((agent: any) => {
|
|
69
|
+
if (!agent.name || agent.name.trim() === '') {
|
|
70
|
+
throw new Error('Agent name cannot be empty');
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
this.crewConfig = crewConfig;
|
|
75
|
+
this.functionsRegistry = functionsRegistry;
|
|
76
|
+
this.crewId = crewId;
|
|
77
|
+
this.options = options;
|
|
78
|
+
this.agents = new Map<string, StatefulAxAgent>();
|
|
79
|
+
this.crewState = createState(crewId);
|
|
80
|
+
this.crewState.set('crewId', crewId);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Factory function for creating an agent.
|
|
85
|
+
*/
|
|
86
|
+
createAgent = async (agentName: string): Promise<StatefulAxAgent> => {
|
|
87
|
+
try {
|
|
88
|
+
const agentConfig: ParsedAgentConfig = await parseAgentConfig(
|
|
89
|
+
agentName,
|
|
90
|
+
this.crewConfig,
|
|
91
|
+
this.functionsRegistry,
|
|
92
|
+
this.crewState,
|
|
93
|
+
this.options
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
const { ai, name, executionMode, axAgentOptions, description, signature, functions, subAgentNames, examples, tracker, forwardOptions } = agentConfig;
|
|
97
|
+
|
|
98
|
+
// Get subagents for the AI agent
|
|
99
|
+
const subAgents = subAgentNames.map((subAgentName: string) => {
|
|
100
|
+
if (!this.agents?.get(subAgentName)) {
|
|
101
|
+
throw new Error(
|
|
102
|
+
`Sub-agent '${subAgentName}' does not exist in available agents.`
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
return this.agents?.get(subAgentName);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// Dedupe sub-agents by name
|
|
109
|
+
const subAgentSet = new Map<string, StatefulAxAgent>();
|
|
110
|
+
for (const sa of subAgents.filter((agent): agent is StatefulAxAgent => agent !== undefined)) {
|
|
111
|
+
const n = (sa as any)?.agentName ?? (sa as any)?.name ?? '';
|
|
112
|
+
if (!subAgentSet.has(n)) subAgentSet.set(n, sa);
|
|
113
|
+
}
|
|
114
|
+
const uniqueSubAgents = Array.from(subAgentSet.values());
|
|
115
|
+
|
|
116
|
+
// Dedupe functions by name and avoid collision with sub-agent names
|
|
117
|
+
const subAgentNameSet = new Set(uniqueSubAgents.map((sa: any) => sa?.agentName ?? sa?.name).filter(Boolean));
|
|
118
|
+
const uniqueFunctions: AxFunction[] = [];
|
|
119
|
+
const seenFn = new Set<string>();
|
|
120
|
+
for (const fn of functions.filter((fn): fn is AxFunction => fn !== undefined)) {
|
|
121
|
+
const fnName = fn.name;
|
|
122
|
+
if (subAgentNameSet.has(fnName)) continue;
|
|
123
|
+
if (!seenFn.has(fnName)) {
|
|
124
|
+
seenFn.add(fnName);
|
|
125
|
+
uniqueFunctions.push(fn);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Resolve factory functions into AxFunction objects
|
|
130
|
+
const resolvedFunctions: AxFunction[] = uniqueFunctions.map(fn =>
|
|
131
|
+
typeof fn === 'function' ? (fn as () => AxFunction)() : fn
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
// Wrap each function handler to record call count and latency
|
|
135
|
+
const crewId = this.crewId;
|
|
136
|
+
const agentNameForMetrics = name;
|
|
137
|
+
const instrumentedFunctions: AxFunction[] = resolvedFunctions.map(fn => ({
|
|
138
|
+
...fn,
|
|
139
|
+
func: async (args?: any, extra?: any) => {
|
|
140
|
+
const fnStart = performance.now();
|
|
141
|
+
try {
|
|
142
|
+
return await fn.func(args, extra);
|
|
143
|
+
} finally {
|
|
144
|
+
const latencyMs = performance.now() - fnStart;
|
|
145
|
+
MetricsRegistry.recordFunctionCall(
|
|
146
|
+
{ crewId, agent: agentNameForMetrics },
|
|
147
|
+
latencyMs,
|
|
148
|
+
fn.name
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
}));
|
|
153
|
+
|
|
154
|
+
// Deferred tool loading
|
|
155
|
+
const mcpFnNames: ReadonlySet<string> = (agentConfig as any).mcpFunctionNames ?? new Set();
|
|
156
|
+
const deferredConfig: DeferredToolsConfig | undefined = (agentConfig as any).deferredTools;
|
|
157
|
+
const deferredManager = new DeferredToolManager(instrumentedFunctions, mcpFnNames, deferredConfig);
|
|
158
|
+
const effectiveFunctions = deferredManager.isActive
|
|
159
|
+
? deferredManager.getInitialFunctions()
|
|
160
|
+
: instrumentedFunctions;
|
|
161
|
+
|
|
162
|
+
if (deferredManager.isActive) {
|
|
163
|
+
console.log(
|
|
164
|
+
`[ax-crew] Deferred tool loading active for "${name}": ` +
|
|
165
|
+
`${effectiveFunctions.length} core + search_tools, ` +
|
|
166
|
+
`${instrumentedFunctions.length - effectiveFunctions.length + 1} deferred`
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Create the agent
|
|
171
|
+
const agentState = { ...this.crewState, crew: this };
|
|
172
|
+
const agent = new StatefulAxAgent(
|
|
173
|
+
ai,
|
|
174
|
+
{
|
|
175
|
+
name,
|
|
176
|
+
executionMode,
|
|
177
|
+
axAgentOptions,
|
|
178
|
+
description,
|
|
179
|
+
definition: (agentConfig as any).definition,
|
|
180
|
+
signature,
|
|
181
|
+
functions: effectiveFunctions,
|
|
182
|
+
agents: uniqueSubAgents,
|
|
183
|
+
examples,
|
|
184
|
+
debug: (agentConfig as any).debug,
|
|
185
|
+
forwardOptions,
|
|
186
|
+
},
|
|
187
|
+
agentState as StateInstance
|
|
188
|
+
);
|
|
189
|
+
(agent as any).costTracker = tracker;
|
|
190
|
+
if (deferredManager.isActive) {
|
|
191
|
+
(agent as any).deferredToolManager = deferredManager;
|
|
192
|
+
}
|
|
193
|
+
(agent as any).__functionsRegistry = this.functionsRegistry;
|
|
194
|
+
|
|
195
|
+
// Initialize ACE if configured
|
|
196
|
+
try {
|
|
197
|
+
const crewAgent = parseCrewConfig(this.crewConfig).crew.find(a => a.name === name) as any;
|
|
198
|
+
const ace: ACEConfig | undefined = crewAgent?.ace;
|
|
199
|
+
if (ace) {
|
|
200
|
+
await (agent as any).initACE?.(ace);
|
|
201
|
+
if (ace.compileOnStart) {
|
|
202
|
+
const { resolveMetric } = await import('./ace.js');
|
|
203
|
+
const metric = resolveMetric(ace.metric, this.functionsRegistry);
|
|
204
|
+
await (agent as any).optimizeOffline?.({ metric, examples });
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
} catch {}
|
|
208
|
+
|
|
209
|
+
return agent;
|
|
210
|
+
} catch (error) {
|
|
211
|
+
throw error;
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
async addAgent(agentName: string): Promise<void> {
|
|
216
|
+
try {
|
|
217
|
+
if (!this.agents) {
|
|
218
|
+
this.agents = new Map<string, StatefulAxAgent>();
|
|
219
|
+
}
|
|
220
|
+
if (!this.agents.has(agentName)) {
|
|
221
|
+
this.agents.set(agentName, await this.createAgent(agentName));
|
|
222
|
+
}
|
|
223
|
+
if (this.agents && !this.agents.has(agentName)) {
|
|
224
|
+
this.agents.set(agentName, await this.createAgent(agentName));
|
|
225
|
+
}
|
|
226
|
+
} catch (error) {
|
|
227
|
+
console.error(`Failed to create agent '${agentName}':`);
|
|
228
|
+
throw new Error(`Failed to add agent ${agentName}: ${error instanceof Error ? error.message : String(error)}`);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
addLazyAgent(agentName: string): void {
|
|
233
|
+
if (!this.agents) {
|
|
234
|
+
this.agents = new Map<string, StatefulAxAgent>();
|
|
235
|
+
}
|
|
236
|
+
if (!this.agents.has(agentName)) {
|
|
237
|
+
this.agents.set(
|
|
238
|
+
agentName,
|
|
239
|
+
new LazyStatefulAxAgent(this, agentName, this.crewConfig) as any
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
async addAgentsToCrew(agentNames: string[]): Promise<Map<string, StatefulAxAgent> | null> {
|
|
245
|
+
try {
|
|
246
|
+
const parsedConfig = parseCrewConfig(this.crewConfig);
|
|
247
|
+
const dependencyMap = new Map<string, string[]>();
|
|
248
|
+
parsedConfig.crew.forEach(agent => {
|
|
249
|
+
dependencyMap.set(agent.name, agent.agents || []);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
const areDependenciesInitialized = (agentName: string): boolean => {
|
|
253
|
+
const dependencies = dependencyMap.get(agentName) || [];
|
|
254
|
+
return dependencies.every(dep => this.agents?.has(dep));
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
const initializedAgents = new Set<string>();
|
|
258
|
+
|
|
259
|
+
while (initializedAgents.size < agentNames.length) {
|
|
260
|
+
let madeProgress = false;
|
|
261
|
+
|
|
262
|
+
for (const agentName of agentNames) {
|
|
263
|
+
if (initializedAgents.has(agentName)) continue;
|
|
264
|
+
if (areDependenciesInitialized(agentName)) {
|
|
265
|
+
await this.addAgent(agentName);
|
|
266
|
+
initializedAgents.add(agentName);
|
|
267
|
+
madeProgress = true;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (!madeProgress) {
|
|
272
|
+
const remaining = agentNames.filter(agent => !initializedAgents.has(agent));
|
|
273
|
+
throw new Error(`Failed to initialize agents due to missing dependencies: ${remaining.join(', ')}`);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return this.agents;
|
|
278
|
+
} catch (error) {
|
|
279
|
+
throw error;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
async addAllAgents(): Promise<Map<string, StatefulAxAgent> | null> {
|
|
284
|
+
try {
|
|
285
|
+
const parsedConfig = parseCrewConfig(this.crewConfig);
|
|
286
|
+
|
|
287
|
+
const dependencyMap = new Map<string, string[]>();
|
|
288
|
+
parsedConfig.crew.forEach(agent => {
|
|
289
|
+
dependencyMap.set(agent.name, agent.agents || []);
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
const areDependenciesInitialized = (agentName: string): boolean => {
|
|
293
|
+
const dependencies = dependencyMap.get(agentName) || [];
|
|
294
|
+
return dependencies.every(dep => this.agents?.has(dep));
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
const allAgents = parsedConfig.crew.map(agent => agent.name);
|
|
298
|
+
const initializedAgents = new Set<string>();
|
|
299
|
+
|
|
300
|
+
while (initializedAgents.size < allAgents.length) {
|
|
301
|
+
let madeProgress = false;
|
|
302
|
+
|
|
303
|
+
for (const agentName of allAgents) {
|
|
304
|
+
if (initializedAgents.has(agentName)) continue;
|
|
305
|
+
if (areDependenciesInitialized(agentName)) {
|
|
306
|
+
await this.addAgent(agentName);
|
|
307
|
+
initializedAgents.add(agentName);
|
|
308
|
+
madeProgress = true;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (!madeProgress) {
|
|
313
|
+
const remaining = allAgents.filter(agent => !initializedAgents.has(agent));
|
|
314
|
+
throw new Error(`Circular dependency detected or missing dependencies for agents: ${remaining.join(', ')}`);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
return this.agents;
|
|
319
|
+
} catch (error) {
|
|
320
|
+
throw error;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// === ACE execution tracking ===
|
|
325
|
+
|
|
326
|
+
trackAgentExecution(taskId: string, agentName: string, input: any): void {
|
|
327
|
+
if (!this.executionHistory.has(taskId)) {
|
|
328
|
+
this.executionHistory.set(taskId, {
|
|
329
|
+
taskId,
|
|
330
|
+
rootAgent: agentName,
|
|
331
|
+
involvedAgents: new Set([agentName]),
|
|
332
|
+
taskInput: input,
|
|
333
|
+
agentResults: new Map(),
|
|
334
|
+
startTime: Date.now()
|
|
335
|
+
});
|
|
336
|
+
} else {
|
|
337
|
+
const context = this.executionHistory.get(taskId)!;
|
|
338
|
+
context.involvedAgents.add(agentName);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
recordAgentResult(taskId: string, agentName: string, result: any): void {
|
|
343
|
+
const context = this.executionHistory.get(taskId);
|
|
344
|
+
if (context) {
|
|
345
|
+
context.agentResults.set(agentName, result);
|
|
346
|
+
context.endTime = Date.now();
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
getTaskAgentInvolvement(taskId: string): {
|
|
351
|
+
rootAgent: string;
|
|
352
|
+
involvedAgents: string[];
|
|
353
|
+
taskInput: any;
|
|
354
|
+
agentResults: Map<string, any>;
|
|
355
|
+
duration?: number;
|
|
356
|
+
} | null {
|
|
357
|
+
const context = this.executionHistory.get(taskId);
|
|
358
|
+
if (!context) return null;
|
|
359
|
+
|
|
360
|
+
return {
|
|
361
|
+
rootAgent: context.rootAgent,
|
|
362
|
+
involvedAgents: Array.from(context.involvedAgents),
|
|
363
|
+
taskInput: context.taskInput,
|
|
364
|
+
agentResults: context.agentResults,
|
|
365
|
+
duration: context.endTime ? context.endTime - context.startTime : undefined
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
async applyTaskFeedback(params: {
|
|
370
|
+
taskId: string;
|
|
371
|
+
feedback: string;
|
|
372
|
+
strategy?: 'all' | 'primary' | 'weighted';
|
|
373
|
+
}): Promise<void> {
|
|
374
|
+
const involvement = this.getTaskAgentInvolvement(params.taskId);
|
|
375
|
+
if (!involvement) {
|
|
376
|
+
console.warn(`No execution history found for task ${params.taskId}`);
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
const { involvedAgents, taskInput, agentResults } = involvement;
|
|
381
|
+
const strategy = params.strategy || 'all';
|
|
382
|
+
|
|
383
|
+
let agentsToUpdate: string[] = [];
|
|
384
|
+
if (strategy === 'primary') {
|
|
385
|
+
agentsToUpdate = [involvement.rootAgent];
|
|
386
|
+
} else if (strategy === 'all' || strategy === 'weighted') {
|
|
387
|
+
agentsToUpdate = involvedAgents;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
for (const agentName of agentsToUpdate) {
|
|
391
|
+
const agent = this.agents?.get(agentName);
|
|
392
|
+
if (agent && typeof (agent as any).applyOnlineUpdate === 'function') {
|
|
393
|
+
try {
|
|
394
|
+
await (agent as any).applyOnlineUpdate({
|
|
395
|
+
example: taskInput,
|
|
396
|
+
prediction: agentResults.get(agentName),
|
|
397
|
+
feedback: params.feedback
|
|
398
|
+
});
|
|
399
|
+
} catch (error) {
|
|
400
|
+
console.warn(`Failed to apply ACE feedback to agent ${agentName}:`, error);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
cleanupOldExecutions(maxAgeMs: number = 3600000): void {
|
|
407
|
+
const cutoffTime = Date.now() - maxAgeMs;
|
|
408
|
+
for (const [taskId, context] of this.executionHistory) {
|
|
409
|
+
if (context.startTime < cutoffTime) {
|
|
410
|
+
this.executionHistory.delete(taskId);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// === Lifecycle ===
|
|
416
|
+
|
|
417
|
+
destroy() {
|
|
418
|
+
this.agents = null;
|
|
419
|
+
this.executionHistory.clear();
|
|
420
|
+
this.crewState.reset();
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// === Metrics ===
|
|
424
|
+
|
|
425
|
+
resetCosts(): void {
|
|
426
|
+
if (this.agents) {
|
|
427
|
+
for (const [, agent] of this.agents) {
|
|
428
|
+
try { (agent as any).resetUsage?.(); } catch {}
|
|
429
|
+
try { (agent as any).resetMetrics?.(); } catch {}
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
MetricsRegistry.reset({ crewId: this.crewId });
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
getCrewMetrics() {
|
|
436
|
+
return MetricsRegistry.snapshotCrew(this.crewId);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
resetCrewMetrics(): void {
|
|
440
|
+
MetricsRegistry.reset({ crewId: this.crewId });
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
export { AxCrew };
|