@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.
@@ -1,1281 +1,4 @@
1
- import { v4 as uuidv4 } from "uuid";
2
- import { AxAgent, AxAI, AxGen, AxSignature as AxSignatureClass } from "@ax-llm/ax";
3
-
4
- import type {
5
- AxSignature,
6
- AxAgentic,
7
- AxFunction,
8
- AxProgramForwardOptions,
9
- AxProgramStreamingForwardOptions,
10
- AxGenStreamingOut,
11
- } from "@ax-llm/ax";
12
-
13
- import type {
14
- StateInstance,
15
- FunctionRegistryType,
16
- UsageCost,
17
- AxCrewConfig,
18
- AxCrewOptions,
19
- MCPTransportConfig,
20
- ACEConfig,
21
- AgentExecutionMode,
22
- AxCrewAxAgentOptions,
23
- } from "../types.js";
24
-
25
- import { createState } from "../state/index.js";
26
- import { parseCrewConfig, parseAgentConfig } from "./agentConfig.js";
27
- import { MetricsRegistry } from "../metrics/index.js";
28
-
29
- // Define the interface for the agent configuration
30
- interface ParsedAgentConfig {
31
- ai: AxAI;
32
- name: string;
33
- executionMode: AgentExecutionMode;
34
- axAgentOptions?: AxCrewAxAgentOptions;
35
- description: string;
36
- definition?: string;
37
- signature: string | AxSignature;
38
- functions: (
39
- | AxFunction
40
- | (new (state: Record<string, any>) => { toFunction: () => AxFunction })
41
- | undefined
42
- )[];
43
- mcpServers?: Record<string, MCPTransportConfig>;
44
- subAgentNames: string[];
45
- examples?: Array<Record<string, any>>;
46
- tracker?: any;
47
- }
48
-
49
- // Extend the AxAgent class from ax-llm
50
- class StatefulAxAgent extends AxAgent<any, any> {
51
- state: StateInstance;
52
- axai: any;
53
- private agentName: string;
54
- private agentDefinition: string;
55
- private executionMode: AgentExecutionMode;
56
- private axGenProgram: AxGen<any, any>;
57
- private costTracker?: any;
58
- private debugEnabled: boolean = false;
59
- private static readonly modernAxAgentRuntime =
60
- typeof (AxAgent as any)?.prototype?.getFunction === "function" &&
61
- typeof (AxAgent as any)?.prototype?.setExamples !== "function";
62
- // ACE-related optional state
63
- private aceConfig?: ACEConfig;
64
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
65
- private aceOptimizer?: any;
66
- private acePlaybook?: any;
67
- private aceBaseInstruction?: string; // Original description before playbook injection
68
- private isAxAIService(obj: any): obj is AxAI {
69
- return !!obj && typeof obj.getName === 'function' && typeof obj.chat === 'function';
70
- }
71
-
72
- constructor(
73
- ai: AxAI,
74
- options: Readonly<{
75
- name: string;
76
- description: string;
77
- executionMode?: AgentExecutionMode;
78
- axAgentOptions?: AxCrewAxAgentOptions;
79
- definition?: string;
80
- signature: string | AxSignature;
81
- agents?: AxAgentic<any, any>[] | undefined;
82
- functions?: (AxFunction | (() => AxFunction))[] | undefined;
83
- examples?: Array<Record<string, any>> | undefined;
84
- mcpServers?: Record<string, MCPTransportConfig> | undefined;
85
- debug?: boolean;
86
- }>,
87
- state: StateInstance
88
- ) {
89
- const { examples, debug } = options;
90
- const resolvedFunctions = (options.functions ?? []).map((fn) =>
91
- typeof fn === "function" ? fn() : fn
92
- ) as AxFunction[];
93
- const resolvedAgents = (options.agents ?? []) as AxAgentic<any, any>[];
94
- const effectiveDefinition = (options.definition ?? options.description).trim();
95
-
96
- if (StatefulAxAgent.modernAxAgentRuntime) {
97
- const configuredAgentOptions = (options.axAgentOptions ?? {}) as Record<string, any>;
98
- const configuredAgentGraph = (configuredAgentOptions.agents ?? {}) as Record<string, any>;
99
- const configuredFunctionGraph = (configuredAgentOptions.functions ?? {}) as Record<string, any>;
100
- const configuredActorOptions = (configuredAgentOptions.actorOptions ?? {}) as Record<string, any>;
101
- const configuredResponderOptions = (configuredAgentOptions.responderOptions ?? {}) as Record<string, any>;
102
-
103
- const modernOptions: Record<string, unknown> = {
104
- ...configuredAgentOptions,
105
- debug: debug ?? configuredAgentOptions.debug ?? false,
106
- contextFields: Array.isArray(configuredAgentOptions.contextFields)
107
- ? configuredAgentOptions.contextFields
108
- : [],
109
- };
110
-
111
- modernOptions.agents = {
112
- ...configuredAgentGraph,
113
- local: resolvedAgents,
114
- };
115
- modernOptions.functions = {
116
- ...configuredFunctionGraph,
117
- local: resolvedFunctions,
118
- };
119
-
120
- if (effectiveDefinition.length > 0) {
121
- modernOptions.actorOptions = {
122
- ...configuredActorOptions,
123
- description: configuredActorOptions.description ?? effectiveDefinition,
124
- };
125
- modernOptions.responderOptions = {
126
- ...configuredResponderOptions,
127
- description: configuredResponderOptions.description ?? effectiveDefinition,
128
- };
129
- } else {
130
- modernOptions.actorOptions = configuredActorOptions;
131
- modernOptions.responderOptions = configuredResponderOptions;
132
- }
133
-
134
- super(
135
- {
136
- ai,
137
- agentIdentity: {
138
- name: options.name,
139
- description: options.description,
140
- },
141
- signature: options.signature as any,
142
- } as any,
143
- modernOptions as any
144
- );
145
- } else {
146
- super(
147
- {
148
- name: options.name,
149
- description: options.description,
150
- definition: options.definition,
151
- signature: options.signature,
152
- agents: resolvedAgents,
153
- functions: resolvedFunctions,
154
- debug: debug ?? false,
155
- } as any,
156
- {} as any
157
- );
158
- }
159
-
160
- this.state = state;
161
- this.axai = ai;
162
- this.agentName = options.name;
163
- this.agentDefinition = effectiveDefinition;
164
- this.executionMode = options.executionMode ?? "axgen";
165
- this.debugEnabled = debug ?? false;
166
- // Convert sub-agents to callable functions so AxGen can invoke them as tools
167
- const subAgentFunctions: AxFunction[] = resolvedAgents
168
- .map(agent => {
169
- try { return agent.getFunction() as AxFunction; }
170
- catch { return undefined; }
171
- })
172
- .filter((fn): fn is AxFunction => fn !== undefined);
173
-
174
- this.axGenProgram = new AxGen(options.signature as any, {
175
- description: effectiveDefinition,
176
- functions: [...resolvedFunctions, ...subAgentFunctions],
177
- } as any);
178
-
179
- for (const agent of resolvedAgents) {
180
- try {
181
- const childName = agent.getFunction().name;
182
- this.axGenProgram.register(agent as any, childName);
183
- } catch {
184
- // Best-effort registration for optimizer/introspection support.
185
- }
186
- }
187
-
188
- // Apply examples to compatibility layer if provided
189
- if (examples && examples.length > 0) {
190
- this.setExamplesCompat(examples);
191
- }
192
- }
193
-
194
- /**
195
- * @deprecated Use setExamplesCompat() to avoid Ax runtime version coupling.
196
- */
197
- setExamples(examples: Readonly<Array<Record<string, any>>>): void {
198
- this.setExamplesCompat(examples);
199
- }
200
-
201
- setExamplesCompat(examples: Readonly<Array<Record<string, any>>>): void {
202
- this.axGenProgram.setExamples(examples as any);
203
-
204
- const baseSetExamples = (AxAgent.prototype as any).setExamples;
205
- if (typeof baseSetExamples === "function") {
206
- baseSetExamples.call(this, examples);
207
- return;
208
- }
209
-
210
- const internalProgram = (this as any).program;
211
- if (typeof internalProgram?.setExamples === "function") {
212
- internalProgram.setExamples(examples as any);
213
- }
214
- }
215
-
216
- /**
217
- * @deprecated Use setDescriptionCompat() to avoid Ax runtime version coupling.
218
- */
219
- setDescription(description: string): void {
220
- this.setDescriptionCompat(description);
221
- }
222
-
223
- setDescriptionCompat(description: string): void {
224
- this.agentDefinition = description;
225
- this.axGenProgram.setDescription(description);
226
-
227
- const baseSetDescription = (AxAgent.prototype as any).setDescription;
228
- if (typeof baseSetDescription === "function") {
229
- baseSetDescription.call(this, description);
230
- return;
231
- }
232
-
233
- const agentRuntime = this as any;
234
- if (typeof agentRuntime.program?.setDescription === "function") {
235
- agentRuntime.program.setDescription(description);
236
- }
237
- agentRuntime.actorDescription = description;
238
- agentRuntime.responderDescription = description;
239
- if (typeof agentRuntime._buildSplitPrograms === "function") {
240
- agentRuntime._buildSplitPrograms();
241
- }
242
- }
243
-
244
- override getUsage() {
245
- if (this.executionMode === "axgen") {
246
- return this.axGenProgram.getUsage();
247
- }
248
- return super.getUsage();
249
- }
250
-
251
- override resetUsage() {
252
- this.axGenProgram.resetUsage();
253
- super.resetUsage();
254
- }
255
-
256
- private resolveInvocationArgs<TOptions>(
257
- first: Record<string, any> | AxAI,
258
- second?: Record<string, any> | Readonly<TOptions>,
259
- third?: Readonly<TOptions>
260
- ): {
261
- ai: AxAI;
262
- values: Record<string, any>;
263
- options?: Readonly<TOptions>;
264
- calledWithAI: boolean;
265
- } {
266
- const calledWithAI = this.isAxAIService(first);
267
- const ai = (calledWithAI ? first : this.axai) as AxAI;
268
- if (!ai) {
269
- throw new Error(`No AI instance is configured for agent "${this.agentName}"`);
270
- }
271
-
272
- const values = (calledWithAI ? second : first) as Record<string, any>;
273
- const options = (calledWithAI ? third : second) as Readonly<TOptions> | undefined;
274
-
275
- return { ai, values, options, calledWithAI };
276
- }
277
-
278
- private async executeForwardByMode(
279
- mode: AgentExecutionMode,
280
- ai: AxAI,
281
- values: Record<string, any>,
282
- options?: Readonly<AxProgramForwardOptions<any>>
283
- ): Promise<Record<string, any>> {
284
- if (mode === "axgen") {
285
- return this.axGenProgram.forward(ai, values, options as any);
286
- }
287
- return super.forward(ai, values, options as any);
288
- }
289
-
290
- private recordUsageMetrics(
291
- labels: { crewId: string; agent: string },
292
- mode: AgentExecutionMode
293
- ): void {
294
- const builtIn =
295
- mode === "axgen" ? this.axGenProgram.getUsage?.() : super.getUsage?.();
296
- if (!Array.isArray(builtIn)) return;
297
-
298
- const totals = builtIn.reduce(
299
- (acc: any, u: any) => {
300
- const pt = u.tokens?.promptTokens ?? u.promptTokens ?? 0;
301
- const ct = u.tokens?.completionTokens ?? u.completionTokens ?? 0;
302
- acc.promptTokens += typeof pt === "number" ? pt : 0;
303
- acc.completionTokens += typeof ct === "number" ? ct : 0;
304
- const model =
305
- u.model ||
306
- (this.axai as any)?.getLastUsedChatModel?.() ||
307
- (this.axai as any)?.defaults?.model;
308
- if (model) {
309
- acc.byModel[model] = (acc.byModel[model] || 0) + (pt + ct);
310
- }
311
- return acc;
312
- },
313
- { promptTokens: 0, completionTokens: 0, byModel: {} as Record<string, number> }
314
- );
315
-
316
- MetricsRegistry.recordTokens(labels, {
317
- promptTokens: totals.promptTokens,
318
- completionTokens: totals.completionTokens,
319
- totalTokens: totals.promptTokens + totals.completionTokens,
320
- });
321
-
322
- const costTracker = (this as any).costTracker;
323
- try {
324
- for (const [m, count] of Object.entries(totals.byModel)) {
325
- costTracker?.trackTokens?.(count, m);
326
- }
327
- const totalUSD = Number(costTracker?.getCurrentCost?.() ?? 0);
328
- if (!Number.isNaN(totalUSD) && totalUSD > 0) {
329
- MetricsRegistry.recordEstimatedCost(labels, totalUSD);
330
- }
331
- } catch {}
332
- }
333
-
334
- private async runForwardInvocation(
335
- mode: AgentExecutionMode,
336
- first: Record<string, any> | AxAI,
337
- second?: Record<string, any> | Readonly<AxProgramForwardOptions<any>>,
338
- third?: Readonly<AxProgramForwardOptions<any>>
339
- ): Promise<Record<string, any>> {
340
- const { ai, values, options, calledWithAI } = this.resolveInvocationArgs(
341
- first,
342
- second,
343
- third
344
- );
345
-
346
- const start = performance.now();
347
- const crewId = (this.state as any)?.crewId || this.state.get?.("crewId") || "default";
348
- const labels = { crewId, agent: this.agentName };
349
- const taskId = `task_${crewId}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
350
-
351
- // Track execution context in crew for ACE feedback routing
352
- const crewInstance = (this.state as any)?.crew as AxCrew;
353
- if (crewInstance) {
354
- if (calledWithAI) {
355
- const parentTaskId = (this.state as any)?.currentTaskId;
356
- if (parentTaskId) {
357
- crewInstance.trackAgentExecution(parentTaskId, this.agentName, values);
358
- }
359
- } else {
360
- crewInstance.trackAgentExecution(taskId, this.agentName, values);
361
- (this.state as any).currentTaskId = taskId;
362
- }
363
- }
364
-
365
- if (this.debugEnabled) {
366
- console.log(`[ACE Debug] forward() called, mode=${mode}, aceConfig=${!!this.aceConfig}`);
367
- }
368
- if (this.aceConfig) {
369
- await this.composeInstructionWithPlaybook();
370
- }
371
-
372
- const result = await this.executeForwardByMode(mode, ai, values, options);
373
-
374
- const durationMs = performance.now() - start;
375
- MetricsRegistry.recordRequest(labels, false, durationMs);
376
- this.recordUsageMetrics(labels, mode);
377
-
378
- if (crewInstance) {
379
- if (calledWithAI) {
380
- const parentTaskId = (this.state as any)?.currentTaskId;
381
- if (parentTaskId) {
382
- crewInstance.recordAgentResult(parentTaskId, this.agentName, result);
383
- }
384
- } else {
385
- crewInstance.recordAgentResult(taskId, this.agentName, result);
386
- delete (this.state as any).currentTaskId;
387
- (result as any)._taskId = taskId;
388
- }
389
- }
390
-
391
- return result;
392
- }
393
-
394
- // Function overloads for forward method
395
- async forward(values: Record<string, any>, options?: Readonly<AxProgramForwardOptions<any>>): Promise<Record<string, any>>;
396
- async forward(ai: AxAI, values: Record<string, any>, options?: Readonly<AxProgramForwardOptions<any>>): Promise<Record<string, any>>;
397
-
398
- // Implementation
399
- async forward(
400
- first: Record<string, any> | AxAI,
401
- second?: Record<string, any> | Readonly<AxProgramForwardOptions<any>>,
402
- third?: Readonly<AxProgramForwardOptions<any>>
403
- ): Promise<Record<string, any>> {
404
- return this.runForwardInvocation(this.executionMode, first, second, third);
405
- }
406
-
407
- private runStreamingInvocation(
408
- mode: AgentExecutionMode,
409
- first: Record<string, any> | AxAI,
410
- second?: Record<string, any> | Readonly<AxProgramStreamingForwardOptions<any>>,
411
- third?: Readonly<AxProgramStreamingForwardOptions<any>>
412
- ): AxGenStreamingOut<any> {
413
- const { ai, values, options } = this.resolveInvocationArgs(
414
- first,
415
- second,
416
- third
417
- );
418
- const start = performance.now();
419
- const crewId = (this.state as any)?.crewId || this.state.get?.("crewId") || "default";
420
- const labels = { crewId, agent: this.agentName };
421
-
422
- const createStream = () =>
423
- mode === "axgen"
424
- ? this.axGenProgram.streamingForward(ai, values, options as any)
425
- : super.streamingForward(ai, values, options as any);
426
-
427
- const wrappedGenerator = (async function* (this: StatefulAxAgent) {
428
- if (this.aceConfig) {
429
- await this.composeInstructionWithPlaybook();
430
- }
431
-
432
- const streamingResult = createStream();
433
- try {
434
- for await (const chunk of streamingResult) {
435
- yield chunk;
436
- }
437
- } finally {
438
- const durationMs = performance.now() - start;
439
- MetricsRegistry.recordRequest(labels, true, durationMs);
440
- this.recordUsageMetrics(labels, mode);
441
- }
442
- }).bind(this)();
443
-
444
- return wrappedGenerator as AxGenStreamingOut<any>;
445
- }
446
-
447
- // Add streaming forward method overloads
448
- streamingForward(values: Record<string, any>, options?: Readonly<AxProgramStreamingForwardOptions<any>>): AxGenStreamingOut<any>;
449
- streamingForward(ai: AxAI, values: Record<string, any>, options?: Readonly<AxProgramStreamingForwardOptions<any>>): AxGenStreamingOut<any>;
450
-
451
- // Implementation
452
- streamingForward(
453
- first: Record<string, any> | AxAI,
454
- second?: Record<string, any> | Readonly<AxProgramStreamingForwardOptions<any>>,
455
- third?: Readonly<AxProgramStreamingForwardOptions<any>>
456
- ): AxGenStreamingOut<any> {
457
- return this.runStreamingInvocation(this.executionMode, first, second, third);
458
- }
459
-
460
- // Legacy cost API removed: rely on Ax trackers for cost reporting
461
- getLastUsageCost(): UsageCost | null { return null; }
462
-
463
- // Get the accumulated costs for all runs of this agent
464
- getAccumulatedCosts(): UsageCost | null { return null; }
465
-
466
- // Metrics API for this agent
467
- /**
468
- * Get the current metrics snapshot for this agent.
469
- * Includes request counts, error rates, token usage, estimated USD cost, and function call stats.
470
- *
471
- * @returns A metrics snapshot scoped to this agent within its crew.
472
- */
473
- getMetrics() {
474
- const crewId = (this.state as any)?.crewId || (this.state.get?.('crewId')) || 'default';
475
- return MetricsRegistry.snapshot({ crewId, agent: this.agentName } as any);
476
- }
477
- /**
478
- * Reset all tracked metrics for this agent (does not affect other agents).
479
- * Call this to start fresh measurement windows for the agent.
480
- */
481
- resetMetrics(): void {
482
- const crewId = (this.state as any)?.crewId || (this.state.get?.('crewId')) || 'default';
483
- MetricsRegistry.reset({ crewId, agent: this.agentName } as any);
484
- }
485
-
486
- // =============
487
- // ACE API - Agentic Context Engineering for online learning
488
- // Reference: https://axllm.dev/ace/
489
- // =============
490
-
491
- /**
492
- * Initialize ACE (Agentic Context Engineering) for this agent.
493
- * Builds the optimizer and loads any initial playbook from persistence.
494
- * Sets up the optimizer for online-only mode if compileOnStart is false.
495
- */
496
- async initACE(ace?: ACEConfig): Promise<void> {
497
- this.aceConfig = ace;
498
- if (!ace) return;
499
- try {
500
- // Capture base instruction BEFORE any playbook injection (mirrors AxACE.extractProgramInstruction)
501
- this.aceBaseInstruction =
502
- this.agentDefinition || this.getSignature().getDescription() || '';
503
-
504
- const { buildACEOptimizer, loadInitialPlaybook, createEmptyPlaybook } = await import('./ace.js');
505
- // Build optimizer with agent's AI as student
506
- this.aceOptimizer = buildACEOptimizer(this.axai, ace);
507
-
508
- // For online-only mode (no offline compile), we need to set the program
509
- // reference so applyOnlineUpdate can work. AxACE requires compile() to
510
- // set the program, but we can set it directly for online-only use.
511
- if (!ace.compileOnStart) {
512
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
513
- (this.aceOptimizer as any).program = this;
514
- }
515
-
516
- // Load initial playbook or create empty one
517
- const initial = await loadInitialPlaybook(ace.persistence);
518
- this.applyPlaybook(initial ?? createEmptyPlaybook());
519
-
520
- if (this.debugEnabled) {
521
- console.log(`[ACE Debug] Initialized for ${this.agentName}, base instruction: ${this.aceBaseInstruction?.slice(0, 50)}...`);
522
- }
523
- } catch (error) {
524
- console.warn(`Failed to initialize ACE for agent ${this.agentName}:`, error);
525
- }
526
- }
527
-
528
- /**
529
- * Run offline ACE compilation with examples and metric.
530
- * Compiles the playbook based on training examples.
531
- */
532
- async optimizeOffline(params?: { metric?: any; examples?: any[] }): Promise<void> {
533
- if (!this.aceConfig || !this.aceOptimizer) return;
534
- try {
535
- const { runOfflineCompile, resolveMetric } = await import('./ace.js');
536
- const registry = (this as any).__functionsRegistry as FunctionRegistryType | undefined;
537
- const metric = params?.metric || resolveMetric(this.aceConfig.metric, registry || {} as any);
538
- const examples = params?.examples || [];
539
-
540
- if (!metric || examples.length === 0) {
541
- console.warn(`ACE offline compile skipped for ${this.agentName}: missing metric or examples`);
542
- return;
543
- }
544
-
545
- const result = await runOfflineCompile({
546
- program: this,
547
- optimizer: this.aceOptimizer,
548
- metric,
549
- examples,
550
- persistence: this.aceConfig.persistence
551
- });
552
-
553
- // Apply optimized playbook if compilation succeeded
554
- if (result?.artifact?.playbook) {
555
- await this.applyPlaybook(result.artifact.playbook);
556
- }
557
- } catch (error) {
558
- console.warn(`ACE offline compile failed for ${this.agentName}:`, error);
559
- }
560
- }
561
-
562
- /**
563
- * Apply online ACE update based on user feedback.
564
- *
565
- * For preference-based feedback (e.g., "only show flights between 9am-12pm"),
566
- * we use our own feedback analyzer that preserves specificity.
567
- *
568
- * Note: AxACE's built-in curator is designed for error correction (severity mismatches)
569
- * and tends to over-abstract preference feedback into generic guidelines.
570
- * We bypass it and directly use our feedback analyzer for better results.
571
- */
572
- async applyOnlineUpdate(params: { example: any; prediction: any; feedback?: string }): Promise<void> {
573
- if (!this.aceConfig) return;
574
- if (!params.feedback?.trim()) return; // Nothing to do without feedback
575
-
576
- try {
577
- const { persistPlaybook, addFeedbackToPlaybook, createEmptyPlaybook } = await import('./ace.js');
578
-
579
- // Get or create playbook
580
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
581
- let playbook = this.acePlaybook ?? (this.aceOptimizer as any)?.playbook;
582
- if (!playbook) {
583
- playbook = createEmptyPlaybook();
584
- }
585
-
586
- if (this.debugEnabled) {
587
- console.log(`[ACE Debug] Adding feedback to playbook: "${params.feedback}"`);
588
- }
589
-
590
- // Use teacher AI (or student AI as fallback) for smart categorization
591
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
592
- const teacherAI = (this.aceOptimizer as any)?.teacherAI;
593
- const aiForAnalysis = teacherAI ?? this.axai;
594
-
595
- // Directly add feedback to playbook using our analyzer (preserves specificity)
596
- await addFeedbackToPlaybook(playbook, params.feedback, aiForAnalysis, this.debugEnabled);
597
-
598
- // Store updated playbook (injection happens in next forward() call)
599
- this.applyPlaybook(playbook);
600
-
601
- // Sync with optimizer if available
602
- if (this.aceOptimizer) {
603
- (this.aceOptimizer as any).playbook = playbook;
604
- }
605
-
606
- // Persist if auto-persist enabled
607
- if (this.aceConfig.persistence?.autoPersist) {
608
- await persistPlaybook(playbook, this.aceConfig.persistence);
609
- }
610
-
611
- if (this.debugEnabled) {
612
- console.log(`[ACE Debug] Playbook updated, sections: ${Object.keys(playbook.sections || {}).join(', ')}`);
613
- }
614
- } catch (error) {
615
- console.warn(`ACE online update failed for ${this.agentName}:`, error);
616
- }
617
- }
618
-
619
- /**
620
- * Get the current ACE playbook for this agent.
621
- */
622
- getPlaybook(): any | undefined {
623
- return this.acePlaybook;
624
- }
625
-
626
- /**
627
- * Apply an ACE playbook to this agent.
628
- * Stores the playbook for use in next forward() call.
629
- * Note: Playbook is composed into instruction BEFORE each forward(), mirroring AxACE.compile behavior.
630
- */
631
- applyPlaybook(pb: any): void {
632
- this.acePlaybook = pb;
633
-
634
- // Also update optimizer's internal playbook if possible
635
- try {
636
- (this.aceOptimizer as any).playbook = pb;
637
- } catch {
638
- // Ignore - optimizer may not be initialized yet
639
- }
640
- }
641
-
642
- /**
643
- * Compose instruction with current playbook and set on agent.
644
- * This mirrors what AxACE does internally before each forward() during compile().
645
- * Should be called BEFORE forward() to ensure playbook is in the prompt.
646
- */
647
- private async composeInstructionWithPlaybook(): Promise<void> {
648
- const playbook = this.acePlaybook ?? (this.aceOptimizer as any)?.playbook;
649
-
650
- if (this.debugEnabled) {
651
- console.log(`[ACE Debug] composeInstructionWithPlaybook called`);
652
- console.log(`[ACE Debug] playbook exists: ${!!playbook}, sections: ${playbook ? Object.keys(playbook.sections || {}).length : 0}`);
653
- console.log(`[ACE Debug] baseInstruction: "${this.aceBaseInstruction?.slice(0, 50)}..."`);
654
- }
655
-
656
- if (!playbook) return;
657
-
658
- try {
659
- const { renderPlaybook } = await import('./ace.js');
660
- const rendered = renderPlaybook(playbook);
661
-
662
- if (this.debugEnabled) {
663
- console.log(`[ACE Debug] rendered playbook (${rendered.length} chars): ${rendered.slice(0, 100)}...`);
664
- }
665
-
666
- if (!rendered) return;
667
-
668
- // Compose: base instruction + playbook (just like AxACE.composeInstruction)
669
- const baseInstruction = this.aceBaseInstruction || '';
670
- const combinedInstruction = [baseInstruction.trim(), '', rendered]
671
- .filter((part) => part.trim().length > 0)
672
- .join('\n\n');
673
-
674
- if (this.debugEnabled) {
675
- console.log(`[ACE Debug] combinedInstruction (${combinedInstruction.length} chars)`);
676
- }
677
-
678
- if (combinedInstruction.length >= 20) {
679
- // Call setDescription on the internal program (like AxACE does)
680
- // AxAgent.setDescription() only updates the signature, but we need
681
- // to update the program's description which is used for the system prompt
682
- const program = (this as any).program;
683
- if (program?.setDescription) {
684
- program.setDescription(combinedInstruction);
685
- }
686
- // Also update via AxAgent's setDescription for consistency
687
- this.setDescription(combinedInstruction);
688
-
689
- if (this.debugEnabled) {
690
- console.log(`[ACE Debug] setDescription called successfully`);
691
- console.log(`[ACE Debug] Verifying - signature desc length: ${this.getSignature().getDescription()?.length}`);
692
- }
693
- }
694
- } catch (error) {
695
- console.warn('[ACE Debug] Failed to compose instruction:', error);
696
- }
697
- }
698
- }
699
-
700
- /**
701
- * Lightweight proxy that stands in for a real agent in the crew's agent map.
702
- * It exposes the same `getFunction()` interface (built from the crew config)
703
- * but defers the expensive `createAgent()` call — and therefore MCP server
704
- * startup — until the Manager actually delegates to it.
705
- *
706
- * Usage: `crew.addLazyAgent("CreateChart")` instead of `crew.addAgent("CreateChart")`
707
- */
708
- class LazyStatefulAxAgent {
709
- private realAgent: StatefulAxAgent | null = null;
710
- private crewRef: any; // AxCrew — forward-declared to avoid circular ref
711
- private agentName: string;
712
- private description: string;
713
- private signatureStr: string;
714
- private func: AxFunction;
715
- private _id: string = "lazy";
716
-
717
- constructor(crewRef: any, agentName: string, crewConfig: AxCrewConfig) {
718
- this.crewRef = crewRef;
719
- this.agentName = agentName;
720
-
721
- const agentDef = parseCrewConfig(crewConfig).crew.find(
722
- (a) => a.name === agentName
723
- );
724
- if (!agentDef) {
725
- throw new Error(`Agent "${agentName}" not found in crew config`);
726
- }
727
-
728
- this.description = agentDef.description;
729
- this.signatureStr = agentDef.signature as string;
730
-
731
- // Build the tool schema from the signature's input fields
732
- const sig = new AxSignatureClass(this.signatureStr);
733
- const parameters = sig.toInputJSONSchema();
734
-
735
- this.func = {
736
- name: agentName.replace(/[^a-zA-Z0-9_]/g, "_").toLowerCase(),
737
- description: this.description,
738
- parameters,
739
- func: async (args?: any) => {
740
- const agent = await this.resolve();
741
- return agent.forward(args);
742
- },
743
- };
744
- }
745
-
746
- private async resolve(): Promise<StatefulAxAgent> {
747
- if (!this.realAgent) {
748
- const agent = await this.crewRef.createAgent(this.agentName) as StatefulAxAgent;
749
- agent.setId(this._id);
750
- this.realAgent = agent;
751
- }
752
- return this.realAgent!;
753
- }
754
-
755
- // AxAgentic interface
756
- getFunction(): AxFunction {
757
- return this.func;
758
- }
759
-
760
- getSignature() {
761
- return new AxSignatureClass(this.signatureStr);
762
- }
763
-
764
- // AxProgrammable / AxTunable stubs
765
- getId(): string { return this._id; }
766
- setId(id: string): void { this._id = id; }
767
- getTraces(): any[] { return this.realAgent?.getTraces() ?? []; }
768
- setDemos(): void { /* no-op until resolved */ }
769
- getUsage(): any[] { return this.realAgent?.getUsage() ?? []; }
770
- resetUsage(): void { this.realAgent?.resetUsage(); }
771
-
772
- // Forward / streaming — resolve on demand
773
- async forward(...args: any[]): Promise<any> {
774
- const agent = await this.resolve();
775
- return (agent as any).forward(...args);
776
- }
777
- streamingForward(...args: any[]): any {
778
- // Must be sync to match the interface, so we wrap in an async generator
779
- const self = this;
780
- async function* lazyStream() {
781
- const agent = await self.resolve();
782
- yield* (agent as any).streamingForward(...args);
783
- }
784
- return lazyStream();
785
- }
786
- }
787
-
788
- /**
789
- * AxCrew orchestrates a set of Ax agents that share state,
790
- * tools (functions), optional MCP servers, streaming, and a built-in metrics
791
- * registry for tokens, requests, and estimated cost.
792
- *
793
- * Typical usage:
794
- * const crew = new AxCrew(config, AxCrewFunctions)
795
- * await crew.addAllAgents()
796
- * const planner = crew.agents?.get("Planner")
797
- * const res = await planner?.forward({ task: "Plan something" })
798
- *
799
- * Key behaviors:
800
- * - Validates and instantiates agents from a config-first model
801
- * - Shares a mutable state object across all agents in the crew
802
- * - Supports sub-agents and a function registry per agent
803
- * - Tracks per-agent and crew-level metrics via MetricsRegistry
804
- * - Provides helpers to add agents (individually, a subset, or all) and
805
- * to reset metrics/costs when needed
806
- */
807
- class AxCrew {
808
- private crewConfig: AxCrewConfig;
809
- private options?: AxCrewOptions;
810
- functionsRegistry: FunctionRegistryType = {};
811
- crewId: string;
812
- agents: Map<string, StatefulAxAgent> | null;
813
- state: StateInstance;
814
- // Execution history for ACE feedback routing
815
- private executionHistory: Map<string, {
816
- taskId: string;
817
- rootAgent: string;
818
- involvedAgents: Set<string>;
819
- taskInput: any;
820
- agentResults: Map<string, any>;
821
- startTime: number;
822
- endTime?: number;
823
- }> = new Map();
824
-
825
- /**
826
- * Creates an instance of AxCrew.
827
- * @param {AxCrewConfig} crewConfig - JSON object with crew configuration.
828
- * @param {FunctionRegistryType} [functionsRegistry={}] - The registry of functions to use in the crew.
829
- * @param {AxCrewOptions} [options] - Optional settings for the crew (e.g., telemetry).
830
- * @param {string} [crewId=uuidv4()] - The unique identifier for the crew.
831
- */
832
- constructor(
833
- crewConfig: AxCrewConfig,
834
- functionsRegistry: FunctionRegistryType = {},
835
- options?: AxCrewOptions,
836
- crewId: string = uuidv4(),
837
- ) {
838
- // Basic validation of crew configuration
839
- if (!crewConfig || typeof crewConfig !== 'object' || !('crew' in crewConfig)) {
840
- throw new Error('Invalid crew configuration');
841
- }
842
-
843
- // Validate each agent in the crew
844
- crewConfig.crew.forEach((agent: any) => {
845
- if (!agent.name || agent.name.trim() === '') {
846
- throw new Error('Agent name cannot be empty');
847
- }
848
- });
849
-
850
- this.crewConfig = crewConfig;
851
- this.functionsRegistry = functionsRegistry;
852
- this.crewId = crewId;
853
- this.options = options;
854
- this.agents = new Map<string, StatefulAxAgent>();
855
- this.state = createState(crewId);
856
- // Make crewId discoverable to metrics
857
- this.state.set('crewId', crewId);
858
- }
859
-
860
- /**
861
- * Factory function for creating an agent.
862
- * @param {string} agentName - The name of the agent to create.
863
- * @returns {StatefulAxAgent} The created StatefulAxAgent instance.
864
- * @throws Will throw an error if the agent creation fails.
865
- */
866
- createAgent = async (agentName: string): Promise<StatefulAxAgent> => {
867
- try {
868
- const agentConfig: ParsedAgentConfig = await parseAgentConfig(
869
- agentName,
870
- this.crewConfig,
871
- this.functionsRegistry,
872
- this.state,
873
- this.options
874
- );
875
-
876
- // Destructure with type assertion
877
- const { ai, name, executionMode, axAgentOptions, description, signature, functions, subAgentNames, examples, tracker } = agentConfig;
878
-
879
- // Get subagents for the AI agent
880
- const subAgents = subAgentNames.map((subAgentName: string) => {
881
- if (!this.agents?.get(subAgentName)) {
882
- throw new Error(
883
- `Sub-agent '${subAgentName}' does not exist in available agents.`
884
- );
885
- }
886
- return this.agents?.get(subAgentName);
887
- });
888
-
889
- // Dedupe sub-agents by name (defensive)
890
- const subAgentSet = new Map<string, StatefulAxAgent>();
891
- for (const sa of subAgents.filter((agent): agent is StatefulAxAgent => agent !== undefined)) {
892
- const n = (sa as any)?.agentName ?? (sa as any)?.name ?? '';
893
- if (!subAgentSet.has(n)) subAgentSet.set(n, sa);
894
- }
895
- const uniqueSubAgents = Array.from(subAgentSet.values());
896
-
897
- // Dedupe functions by name and avoid collision with sub-agent names
898
- const subAgentNameSet = new Set(uniqueSubAgents.map((sa: any) => sa?.agentName ?? sa?.name).filter(Boolean));
899
- const uniqueFunctions: AxFunction[] = [];
900
- const seenFn = new Set<string>();
901
- for (const fn of functions.filter((fn): fn is AxFunction => fn !== undefined)) {
902
- const fnName = fn.name;
903
- if (subAgentNameSet.has(fnName)) {
904
- // Skip function that collides with a sub-agent name
905
- continue;
906
- }
907
- if (!seenFn.has(fnName)) {
908
- seenFn.add(fnName);
909
- uniqueFunctions.push(fn);
910
- }
911
- }
912
-
913
- // Resolve factory functions (e.g., () => new Foo(state).toFunction()) into
914
- // AxFunction objects before instrumenting, since spreading a JS function
915
- // does not copy non-enumerable properties like `name`.
916
- const resolvedFunctions: AxFunction[] = uniqueFunctions.map(fn =>
917
- typeof fn === 'function' ? (fn as () => AxFunction)() : fn
918
- );
919
-
920
- // Wrap each function handler to record call count and latency in MetricsRegistry
921
- const crewId = this.crewId;
922
- const agentNameForMetrics = name;
923
- const instrumentedFunctions: AxFunction[] = resolvedFunctions.map(fn => ({
924
- ...fn,
925
- func: async (args?: any, extra?: any) => {
926
- const fnStart = performance.now();
927
- try {
928
- return await fn.func(args, extra);
929
- } finally {
930
- const latencyMs = performance.now() - fnStart;
931
- MetricsRegistry.recordFunctionCall(
932
- { crewId, agent: agentNameForMetrics },
933
- latencyMs,
934
- fn.name
935
- );
936
- }
937
- },
938
- }));
939
-
940
- // Create an instance of StatefulAxAgent
941
- // Set crew reference in state for execution tracking (ACE feedback routing)
942
- const agentState = { ...this.state, crew: this };
943
- const agent = new StatefulAxAgent(
944
- ai,
945
- {
946
- name,
947
- executionMode,
948
- axAgentOptions,
949
- description,
950
- definition: (agentConfig as any).definition,
951
- signature,
952
- functions: instrumentedFunctions,
953
- agents: uniqueSubAgents,
954
- examples,
955
- debug: (agentConfig as any).debug,
956
- },
957
- agentState as StateInstance
958
- );
959
- (agent as any).costTracker = tracker;
960
- (agent as any).__functionsRegistry = this.functionsRegistry;
961
-
962
- // Initialize ACE if configured
963
- try {
964
- const crewAgent = parseCrewConfig(this.crewConfig).crew.find(a => a.name === name) as any;
965
- const ace: ACEConfig | undefined = crewAgent?.ace;
966
- if (ace) {
967
- await (agent as any).initACE?.(ace);
968
- if (ace.compileOnStart) {
969
- const { resolveMetric } = await import('./ace.js');
970
- const metric = resolveMetric(ace.metric, this.functionsRegistry);
971
- await (agent as any).optimizeOffline?.({ metric, examples });
972
- }
973
- }
974
- } catch {}
975
-
976
- return agent;
977
- } catch (error) {
978
- throw error;
979
- }
980
- };
981
-
982
- /**
983
- * Adds an agent to the crew by name.
984
- * @param {string} agentName - The name of the agent to add.
985
- */
986
- async addAgent(agentName: string): Promise<void> {
987
- try {
988
- if (!this.agents) {
989
- this.agents = new Map<string, StatefulAxAgent>();
990
- }
991
- if (!this.agents.has(agentName)) {
992
- this.agents.set(agentName, await this.createAgent(agentName));
993
- }
994
- if (this.agents && !this.agents.has(agentName)) {
995
- this.agents.set(agentName, await this.createAgent(agentName));
996
- }
997
- } catch (error) {
998
- console.error(`Failed to create agent '${agentName}':`);
999
- throw new Error(`Failed to add agent ${agentName}: ${error instanceof Error ? error.message : String(error)}`);
1000
- }
1001
- }
1002
-
1003
- /**
1004
- * Adds a lazy agent to the crew by name.
1005
- * The agent's tool schema is built immediately from the crew config,
1006
- * but the expensive initialization (MCP servers, AI client, etc.) is
1007
- * deferred until the Manager actually delegates to this agent.
1008
- *
1009
- * Use this for sub-agents that may not be needed on every request
1010
- * (e.g., agents with stdio MCP servers that spawn a process).
1011
- *
1012
- * @param {string} agentName - The name of the agent to add lazily.
1013
- */
1014
- addLazyAgent(agentName: string): void {
1015
- if (!this.agents) {
1016
- this.agents = new Map<string, StatefulAxAgent>();
1017
- }
1018
- if (!this.agents.has(agentName)) {
1019
- this.agents.set(
1020
- agentName,
1021
- new LazyStatefulAxAgent(this, agentName, this.crewConfig) as any
1022
- );
1023
- }
1024
- }
1025
-
1026
- /**
1027
- * Sets up agents in the crew by name.
1028
- * For an array of Agent names provided, it adds
1029
- * the agent to the crew if not already present.
1030
- * @param {string[]} agentNames - An array of agent names to configure.
1031
- * @returns {Map<string, StatefulAxAgent> | null} A map of agent names to their corresponding instances.
1032
- */
1033
- async addAgentsToCrew(agentNames: string[]): Promise<Map<string, StatefulAxAgent> | null> {
1034
- try {
1035
- // Parse the crew config to get agent dependencies
1036
- const parsedConfig = parseCrewConfig(this.crewConfig);
1037
- const dependencyMap = new Map<string, string[]>();
1038
- parsedConfig.crew.forEach(agent => {
1039
- dependencyMap.set(agent.name, agent.agents || []);
1040
- });
1041
-
1042
- // Function to check if all dependencies are initialized
1043
- const areDependenciesInitialized = (agentName: string): boolean => {
1044
- const dependencies = dependencyMap.get(agentName) || [];
1045
- return dependencies.every(dep => this.agents?.has(dep));
1046
- };
1047
-
1048
- // Initialize agents sequentially based on dependencies
1049
- const initializedAgents = new Set<string>();
1050
-
1051
- while (initializedAgents.size < agentNames.length) {
1052
- let madeProgress = false;
1053
-
1054
- for (const agentName of agentNames) {
1055
- // Skip if already initialized
1056
- if (initializedAgents.has(agentName)) continue;
1057
-
1058
- // Check if all dependencies are initialized
1059
- if (areDependenciesInitialized(agentName)) {
1060
- await this.addAgent(agentName);
1061
- initializedAgents.add(agentName);
1062
- madeProgress = true;
1063
- }
1064
- }
1065
-
1066
- // If we couldn't initialize any agents in this iteration, we have a circular dependency
1067
- if (!madeProgress) {
1068
- const remaining = agentNames.filter(agent => !initializedAgents.has(agent));
1069
- throw new Error(`Failed to initialize agents due to missing dependencies: ${remaining.join(', ')}`);
1070
- }
1071
- }
1072
-
1073
- return this.agents;
1074
- } catch (error) {
1075
- throw error;
1076
- }
1077
- }
1078
-
1079
- async addAllAgents(): Promise<Map<string, StatefulAxAgent> | null> {
1080
- try {
1081
- // Parse the crew config and get all agent configs
1082
- const parsedConfig = parseCrewConfig(this.crewConfig);
1083
-
1084
- // Create a map of agent dependencies
1085
- const dependencyMap = new Map<string, string[]>();
1086
- parsedConfig.crew.forEach(agent => {
1087
- dependencyMap.set(agent.name, agent.agents || []);
1088
- });
1089
-
1090
- // Function to check if all dependencies are initialized
1091
- const areDependenciesInitialized = (agentName: string): boolean => {
1092
- const dependencies = dependencyMap.get(agentName) || [];
1093
- return dependencies.every(dep => this.agents?.has(dep));
1094
- };
1095
-
1096
- // Get all agent names
1097
- const allAgents = parsedConfig.crew.map(agent => agent.name);
1098
- const initializedAgents = new Set<string>();
1099
-
1100
- // Keep trying to initialize agents until all are done or we can't make progress
1101
- while (initializedAgents.size < allAgents.length) {
1102
- let madeProgress = false;
1103
-
1104
- for (const agentName of allAgents) {
1105
- // Skip if already initialized
1106
- if (initializedAgents.has(agentName)) continue;
1107
-
1108
- // Check if all dependencies are initialized
1109
- if (areDependenciesInitialized(agentName)) {
1110
- await this.addAgent(agentName);
1111
- initializedAgents.add(agentName);
1112
- madeProgress = true;
1113
- }
1114
- }
1115
-
1116
- // If we couldn't initialize any agents in this iteration, we have a circular dependency
1117
- if (!madeProgress) {
1118
- const remaining = allAgents.filter(agent => !initializedAgents.has(agent));
1119
- throw new Error(`Circular dependency detected or missing dependencies for agents: ${remaining.join(', ')}`);
1120
- }
1121
- }
1122
-
1123
- return this.agents;
1124
- } catch (error) {
1125
- throw error;
1126
- }
1127
- }
1128
-
1129
- /**
1130
- * Track agent execution for ACE feedback routing
1131
- */
1132
- trackAgentExecution(taskId: string, agentName: string, input: any): void {
1133
- if (!this.executionHistory.has(taskId)) {
1134
- this.executionHistory.set(taskId, {
1135
- taskId,
1136
- rootAgent: agentName,
1137
- involvedAgents: new Set([agentName]),
1138
- taskInput: input,
1139
- agentResults: new Map(),
1140
- startTime: Date.now()
1141
- });
1142
- } else {
1143
- // Add to involved agents if not already present
1144
- const context = this.executionHistory.get(taskId)!;
1145
- context.involvedAgents.add(agentName);
1146
- }
1147
- }
1148
-
1149
- /**
1150
- * Record agent result for ACE feedback routing
1151
- */
1152
- recordAgentResult(taskId: string, agentName: string, result: any): void {
1153
- const context = this.executionHistory.get(taskId);
1154
- if (context) {
1155
- context.agentResults.set(agentName, result);
1156
- context.endTime = Date.now();
1157
- }
1158
- }
1159
-
1160
- /**
1161
- * Get agent involvement for a task (used for ACE feedback routing)
1162
- */
1163
- getTaskAgentInvolvement(taskId: string): {
1164
- rootAgent: string;
1165
- involvedAgents: string[];
1166
- taskInput: any;
1167
- agentResults: Map<string, any>;
1168
- duration?: number;
1169
- } | null {
1170
- const context = this.executionHistory.get(taskId);
1171
- if (!context) return null;
1172
-
1173
- return {
1174
- rootAgent: context.rootAgent,
1175
- involvedAgents: Array.from(context.involvedAgents),
1176
- taskInput: context.taskInput,
1177
- agentResults: context.agentResults,
1178
- duration: context.endTime ? context.endTime - context.startTime : undefined
1179
- };
1180
- }
1181
-
1182
- /**
1183
- * Apply feedback to agents involved in a task for ACE online learning
1184
- */
1185
- async applyTaskFeedback(params: {
1186
- taskId: string;
1187
- feedback: string;
1188
- strategy?: 'all' | 'primary' | 'weighted';
1189
- }): Promise<void> {
1190
- const involvement = this.getTaskAgentInvolvement(params.taskId);
1191
- if (!involvement) {
1192
- console.warn(`No execution history found for task ${params.taskId}`);
1193
- return;
1194
- }
1195
-
1196
- const { involvedAgents, taskInput, agentResults } = involvement;
1197
- const strategy = params.strategy || 'all';
1198
-
1199
- // Determine which agents to update based on strategy
1200
- let agentsToUpdate: string[] = [];
1201
- if (strategy === 'primary') {
1202
- agentsToUpdate = [involvement.rootAgent];
1203
- } else if (strategy === 'all' || strategy === 'weighted') {
1204
- agentsToUpdate = involvedAgents;
1205
- }
1206
-
1207
- // Apply feedback to each involved agent
1208
- for (const agentName of agentsToUpdate) {
1209
- const agent = this.agents?.get(agentName);
1210
- if (agent && typeof (agent as any).applyOnlineUpdate === 'function') {
1211
- try {
1212
- await (agent as any).applyOnlineUpdate({
1213
- example: taskInput,
1214
- prediction: agentResults.get(agentName),
1215
- feedback: params.feedback
1216
- });
1217
- } catch (error) {
1218
- console.warn(`Failed to apply ACE feedback to agent ${agentName}:`, error);
1219
- }
1220
- }
1221
- }
1222
- }
1223
-
1224
- /**
1225
- * Clean up old execution history (call periodically to prevent memory leaks)
1226
- */
1227
- cleanupOldExecutions(maxAgeMs: number = 3600000): void { // Default 1 hour
1228
- const cutoffTime = Date.now() - maxAgeMs;
1229
- for (const [taskId, context] of this.executionHistory) {
1230
- if (context.startTime < cutoffTime) {
1231
- this.executionHistory.delete(taskId);
1232
- }
1233
- }
1234
- }
1235
-
1236
- /**
1237
- * Cleans up the crew by dereferencing agents and resetting the state.
1238
- */
1239
- destroy() {
1240
- this.agents = null;
1241
- this.executionHistory.clear();
1242
- this.state.reset();
1243
- }
1244
-
1245
-
1246
- /**
1247
- * Resets all cost and usage tracking for the entire crew.
1248
- * Also calls each agent's `resetUsage` (if available) and clears crew-level metrics.
1249
- */
1250
- resetCosts(): void {
1251
- // Reset AxAgent built-in usage and our metrics registry
1252
- if (this.agents) {
1253
- for (const [, agent] of this.agents) {
1254
- try { (agent as any).resetUsage?.(); } catch {}
1255
- try { (agent as any).resetMetrics?.(); } catch {}
1256
- }
1257
- }
1258
- MetricsRegistry.reset({ crewId: this.crewId });
1259
- }
1260
-
1261
- // Metrics API
1262
- /**
1263
- * Get an aggregate metrics snapshot for the entire crew.
1264
- * Sums requests, errors, tokens, and estimated cost across all agents in the crew.
1265
- *
1266
- * @returns Crew-level metrics snapshot.
1267
- */
1268
- getCrewMetrics() {
1269
- return MetricsRegistry.snapshotCrew(this.crewId);
1270
- }
1271
- /**
1272
- * Reset all tracked metrics for the entire crew.
1273
- * Use to clear totals before a new measurement period.
1274
- */
1275
- resetCrewMetrics(): void {
1276
- MetricsRegistry.reset({ crewId: this.crewId });
1277
- }
1278
- }
1279
-
1280
- export { AxCrew, LazyStatefulAxAgent };
1281
- export type { StatefulAxAgent };
1
+ export { StatefulAxAgent } from './statefulAgent.js';
2
+ export type { ParsedAgentConfig } from './statefulAgent.js';
3
+ export { LazyStatefulAxAgent } from './lazyAgent.js';
4
+ export { AxCrew } from './crew.js';