@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.
@@ -0,0 +1,668 @@
1
+ import { AxAgent, AxAI, AxGen } from "@ax-llm/ax";
2
+
3
+ import type {
4
+ AxSignature,
5
+ AxAgentic,
6
+ AxFunction,
7
+ AxProgramForwardOptions,
8
+ AxProgramStreamingForwardOptions,
9
+ AxGenStreamingOut,
10
+ AxStepHooks,
11
+ } from "@ax-llm/ax";
12
+
13
+ import type {
14
+ StateInstance,
15
+ FunctionRegistryType,
16
+ UsageCost,
17
+ MCPTransportConfig,
18
+ ACEConfig,
19
+ AgentExecutionMode,
20
+ AxCrewAxAgentOptions,
21
+ } from "../types.js";
22
+
23
+ import type { AxCrew } from "./crew.js";
24
+ import { DeferredToolManager } from "./deferredTools.js";
25
+ import { MetricsRegistry } from "../metrics/index.js";
26
+
27
+ // Define the interface for the agent configuration
28
+ export interface ParsedAgentConfig {
29
+ ai: AxAI;
30
+ name: string;
31
+ executionMode: AgentExecutionMode;
32
+ axAgentOptions?: AxCrewAxAgentOptions;
33
+ description: string;
34
+ definition?: string;
35
+ signature: string | AxSignature;
36
+ functions: (
37
+ | AxFunction
38
+ | (new (state: Record<string, any>) => { toFunction: () => AxFunction })
39
+ | undefined
40
+ )[];
41
+ mcpServers?: Record<string, MCPTransportConfig>;
42
+ subAgentNames: string[];
43
+ examples?: Array<Record<string, any>>;
44
+ tracker?: any;
45
+ /** Agent-level forward options (maxSteps, showThoughts, thinkingTokenBudget, etc.) used as defaults when the caller doesn't supply them. */
46
+ forwardOptions?: Record<string, any>;
47
+ }
48
+
49
+ // Extend the AxAgent class from ax-llm
50
+ class StatefulAxAgent extends AxAgent<any, any> {
51
+ crewState: 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 deferredToolManager?: DeferredToolManager;
60
+ private agentForwardOptions?: Record<string, any>;
61
+ private static readonly modernAxAgentRuntime =
62
+ typeof (AxAgent as any)?.prototype?.getFunction === "function" &&
63
+ typeof (AxAgent as any)?.prototype?.setExamples !== "function";
64
+ // ACE-related optional state
65
+ private aceConfig?: ACEConfig;
66
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
67
+ private aceOptimizer?: any;
68
+ private acePlaybook?: any;
69
+ private aceBaseInstruction?: string; // Original description before playbook injection
70
+ private isAxAIService(obj: any): obj is AxAI {
71
+ return !!obj && typeof obj.getName === 'function' && typeof obj.chat === 'function';
72
+ }
73
+
74
+ constructor(
75
+ ai: AxAI,
76
+ options: Readonly<{
77
+ name: string;
78
+ description: string;
79
+ executionMode?: AgentExecutionMode;
80
+ axAgentOptions?: AxCrewAxAgentOptions;
81
+ definition?: string;
82
+ signature: string | AxSignature;
83
+ agents?: AxAgentic<any, any>[] | undefined;
84
+ functions?: (AxFunction | (() => AxFunction))[] | undefined;
85
+ examples?: Array<Record<string, any>> | undefined;
86
+ mcpServers?: Record<string, MCPTransportConfig> | undefined;
87
+ debug?: boolean;
88
+ forwardOptions?: Record<string, any>;
89
+ }>,
90
+ state: StateInstance
91
+ ) {
92
+ const { examples, debug } = options;
93
+ const resolvedFunctions = (options.functions ?? []).map((fn) =>
94
+ typeof fn === "function" ? fn() : fn
95
+ ) as AxFunction[];
96
+ const resolvedAgents = (options.agents ?? []) as AxAgentic<any, any>[];
97
+ const effectiveDefinition = (options.definition ?? options.description).trim();
98
+
99
+ if (StatefulAxAgent.modernAxAgentRuntime) {
100
+ const configuredAgentOptions = (options.axAgentOptions ?? {}) as Record<string, any>;
101
+ const configuredAgentGraph = (configuredAgentOptions.agents ?? {}) as Record<string, any>;
102
+ const configuredFunctionGraph = (configuredAgentOptions.functions ?? {}) as Record<string, any>;
103
+ const configuredActorOptions = (configuredAgentOptions.actorOptions ?? {}) as Record<string, any>;
104
+ const configuredResponderOptions = (configuredAgentOptions.responderOptions ?? {}) as Record<string, any>;
105
+
106
+ const modernOptions: Record<string, unknown> = {
107
+ ...configuredAgentOptions,
108
+ debug: debug ?? configuredAgentOptions.debug ?? false,
109
+ contextFields: Array.isArray(configuredAgentOptions.contextFields)
110
+ ? configuredAgentOptions.contextFields
111
+ : [],
112
+ };
113
+
114
+ modernOptions.agents = {
115
+ ...configuredAgentGraph,
116
+ local: resolvedAgents,
117
+ };
118
+ modernOptions.functions = {
119
+ ...configuredFunctionGraph,
120
+ local: resolvedFunctions,
121
+ };
122
+
123
+ if (effectiveDefinition.length > 0) {
124
+ modernOptions.actorOptions = {
125
+ ...configuredActorOptions,
126
+ description: configuredActorOptions.description ?? effectiveDefinition,
127
+ };
128
+ modernOptions.responderOptions = {
129
+ ...configuredResponderOptions,
130
+ description: configuredResponderOptions.description ?? effectiveDefinition,
131
+ };
132
+ } else {
133
+ modernOptions.actorOptions = configuredActorOptions;
134
+ modernOptions.responderOptions = configuredResponderOptions;
135
+ }
136
+
137
+ super(
138
+ {
139
+ ai,
140
+ agentIdentity: {
141
+ name: options.name,
142
+ description: options.description,
143
+ },
144
+ signature: options.signature as any,
145
+ } as any,
146
+ modernOptions as any
147
+ );
148
+ } else {
149
+ super(
150
+ {
151
+ name: options.name,
152
+ description: options.description,
153
+ definition: options.definition,
154
+ signature: options.signature,
155
+ agents: resolvedAgents,
156
+ functions: resolvedFunctions,
157
+ debug: debug ?? false,
158
+ } as any,
159
+ {} as any
160
+ );
161
+ }
162
+
163
+ this.crewState = state;
164
+ this.axai = ai;
165
+ this.agentName = options.name;
166
+ this.agentDefinition = effectiveDefinition;
167
+ this.executionMode = options.executionMode ?? "axgen";
168
+ this.debugEnabled = debug ?? false;
169
+ this.agentForwardOptions = options.forwardOptions;
170
+ // Convert sub-agents to callable functions so AxGen can invoke them as tools
171
+ const subAgentFunctions: AxFunction[] = resolvedAgents
172
+ .map(agent => {
173
+ try { return agent.getFunction() as AxFunction; }
174
+ catch { return undefined; }
175
+ })
176
+ .filter((fn): fn is AxFunction => fn !== undefined);
177
+
178
+ this.axGenProgram = new AxGen(options.signature as any, {
179
+ description: effectiveDefinition,
180
+ functions: [...resolvedFunctions, ...subAgentFunctions],
181
+ } as any);
182
+
183
+ for (const agent of resolvedAgents) {
184
+ try {
185
+ const childName = agent.getFunction().name;
186
+ this.axGenProgram.register(agent as any, childName);
187
+ } catch {
188
+ // Best-effort registration for optimizer/introspection support.
189
+ }
190
+ }
191
+
192
+ // Apply examples to compatibility layer if provided
193
+ if (examples && examples.length > 0) {
194
+ this.setExamplesCompat(examples);
195
+ }
196
+ }
197
+
198
+ /**
199
+ * @deprecated Use setExamplesCompat() to avoid Ax runtime version coupling.
200
+ */
201
+ setExamples(examples: Readonly<Array<Record<string, any>>>): void {
202
+ this.setExamplesCompat(examples);
203
+ }
204
+
205
+ setExamplesCompat(examples: Readonly<Array<Record<string, any>>>): void {
206
+ this.axGenProgram.setExamples(examples as any);
207
+
208
+ const baseSetExamples = (AxAgent.prototype as any).setExamples;
209
+ if (typeof baseSetExamples === "function") {
210
+ baseSetExamples.call(this, examples);
211
+ return;
212
+ }
213
+
214
+ const internalProgram = (this as any).program;
215
+ if (typeof internalProgram?.setExamples === "function") {
216
+ internalProgram.setExamples(examples as any);
217
+ }
218
+ }
219
+
220
+ /**
221
+ * @deprecated Use setDescriptionCompat() to avoid Ax runtime version coupling.
222
+ */
223
+ setDescription(description: string): void {
224
+ this.setDescriptionCompat(description);
225
+ }
226
+
227
+ setDescriptionCompat(description: string): void {
228
+ this.agentDefinition = description;
229
+ this.axGenProgram.setDescription(description);
230
+
231
+ const baseSetDescription = (AxAgent.prototype as any).setDescription;
232
+ if (typeof baseSetDescription === "function") {
233
+ baseSetDescription.call(this, description);
234
+ return;
235
+ }
236
+
237
+ const agentRuntime = this as any;
238
+ if (typeof agentRuntime.program?.setDescription === "function") {
239
+ agentRuntime.program.setDescription(description);
240
+ }
241
+ agentRuntime.actorDescription = description;
242
+ agentRuntime.responderDescription = description;
243
+ if (typeof agentRuntime._buildSplitPrograms === "function") {
244
+ agentRuntime._buildSplitPrograms();
245
+ }
246
+ }
247
+
248
+ override getUsage() {
249
+ if (this.executionMode === "axgen") {
250
+ return this.axGenProgram.getUsage();
251
+ }
252
+ return super.getUsage();
253
+ }
254
+
255
+ override resetUsage() {
256
+ this.axGenProgram.resetUsage();
257
+ super.resetUsage();
258
+ }
259
+
260
+ private resolveInvocationArgs<TOptions>(
261
+ first: Record<string, any> | AxAI,
262
+ second?: Record<string, any> | Readonly<TOptions>,
263
+ third?: Readonly<TOptions>
264
+ ): {
265
+ ai: AxAI;
266
+ values: Record<string, any>;
267
+ options?: Readonly<TOptions>;
268
+ calledWithAI: boolean;
269
+ } {
270
+ const calledWithAI = this.isAxAIService(first);
271
+ const ai = (calledWithAI ? first : this.axai) as AxAI;
272
+ if (!ai) {
273
+ throw new Error(`No AI instance is configured for agent "${this.agentName}"`);
274
+ }
275
+
276
+ const values = (calledWithAI ? second : first) as Record<string, any>;
277
+ const callerOptions = (calledWithAI ? third : second) as Readonly<TOptions> | undefined;
278
+
279
+ // Merge agent-level forward options as defaults under caller-supplied options
280
+ const options = this.agentForwardOptions
281
+ ? ({ ...this.agentForwardOptions, ...callerOptions } as Readonly<TOptions>)
282
+ : callerOptions;
283
+
284
+ return { ai, values, options, calledWithAI };
285
+ }
286
+
287
+ /** Merge deferred tool step hooks into forward options */
288
+ private mergeStepHooks(options?: Readonly<AxProgramForwardOptions<any>>): Readonly<AxProgramForwardOptions<any>> | undefined {
289
+ if (!this.deferredToolManager?.isActive) return options;
290
+
291
+ const deferredHooks = this.deferredToolManager.getStepHooks();
292
+ const existingHooks = (options as any)?.stepHooks as AxStepHooks | undefined;
293
+
294
+ const mergedHooks: AxStepHooks = {
295
+ beforeStep: async (ctx) => {
296
+ await existingHooks?.beforeStep?.(ctx);
297
+ await deferredHooks.beforeStep?.(ctx);
298
+ },
299
+ afterStep: existingHooks?.afterStep,
300
+ afterFunctionExecution: async (ctx) => {
301
+ await existingHooks?.afterFunctionExecution?.(ctx);
302
+ await deferredHooks.afterFunctionExecution?.(ctx);
303
+ },
304
+ };
305
+
306
+ return { ...options, stepHooks: mergedHooks } as any;
307
+ }
308
+
309
+ private async executeForwardByMode(
310
+ mode: AgentExecutionMode,
311
+ ai: AxAI,
312
+ values: Record<string, any>,
313
+ options?: Readonly<AxProgramForwardOptions<any>>
314
+ ): Promise<Record<string, any>> {
315
+ const opts = this.mergeStepHooks(options);
316
+ if (mode === "axgen") {
317
+ return this.axGenProgram.forward(ai, values, opts as any);
318
+ }
319
+ return super.forward(ai, values, opts as any);
320
+ }
321
+
322
+ private recordUsageMetrics(
323
+ labels: { crewId: string; agent: string },
324
+ mode: AgentExecutionMode
325
+ ): void {
326
+ const builtIn =
327
+ mode === "axgen" ? this.axGenProgram.getUsage?.() : super.getUsage?.();
328
+ if (!Array.isArray(builtIn)) return;
329
+
330
+ const totals = builtIn.reduce(
331
+ (acc: any, u: any) => {
332
+ const pt = u.tokens?.promptTokens ?? u.promptTokens ?? 0;
333
+ const ct = u.tokens?.completionTokens ?? u.completionTokens ?? 0;
334
+ acc.promptTokens += typeof pt === "number" ? pt : 0;
335
+ acc.completionTokens += typeof ct === "number" ? ct : 0;
336
+ const model =
337
+ u.model ||
338
+ (this.axai as any)?.getLastUsedChatModel?.() ||
339
+ (this.axai as any)?.defaults?.model;
340
+ if (model) {
341
+ acc.byModel[model] = (acc.byModel[model] || 0) + (pt + ct);
342
+ }
343
+ return acc;
344
+ },
345
+ { promptTokens: 0, completionTokens: 0, byModel: {} as Record<string, number> }
346
+ );
347
+
348
+ MetricsRegistry.recordTokens(labels, {
349
+ promptTokens: totals.promptTokens,
350
+ completionTokens: totals.completionTokens,
351
+ totalTokens: totals.promptTokens + totals.completionTokens,
352
+ });
353
+
354
+ const costTracker = (this as any).costTracker;
355
+ try {
356
+ for (const [m, count] of Object.entries(totals.byModel)) {
357
+ costTracker?.trackTokens?.(count, m);
358
+ }
359
+ const totalUSD = Number(costTracker?.getCurrentCost?.() ?? 0);
360
+ if (!Number.isNaN(totalUSD) && totalUSD > 0) {
361
+ MetricsRegistry.recordEstimatedCost(labels, totalUSD);
362
+ }
363
+ } catch {}
364
+ }
365
+
366
+ private async runForwardInvocation(
367
+ mode: AgentExecutionMode,
368
+ first: Record<string, any> | AxAI,
369
+ second?: Record<string, any> | Readonly<AxProgramForwardOptions<any>>,
370
+ third?: Readonly<AxProgramForwardOptions<any>>
371
+ ): Promise<Record<string, any>> {
372
+ const { ai, values, options, calledWithAI } = this.resolveInvocationArgs(
373
+ first,
374
+ second,
375
+ third
376
+ );
377
+
378
+ const start = performance.now();
379
+ const crewId = (this.crewState as any)?.crewId || this.crewState.get?.("crewId") || "default";
380
+ const labels = { crewId, agent: this.agentName };
381
+ const taskId = `task_${crewId}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
382
+
383
+ // Track execution context in crew for ACE feedback routing
384
+ const crewInstance = (this.crewState as any)?.crew as AxCrew;
385
+ if (crewInstance) {
386
+ if (calledWithAI) {
387
+ const parentTaskId = (this.crewState as any)?.currentTaskId;
388
+ if (parentTaskId) {
389
+ crewInstance.trackAgentExecution(parentTaskId, this.agentName, values);
390
+ }
391
+ } else {
392
+ crewInstance.trackAgentExecution(taskId, this.agentName, values);
393
+ (this.crewState as any).currentTaskId = taskId;
394
+ }
395
+ }
396
+
397
+ if (this.debugEnabled) {
398
+ console.log(`[ACE Debug] forward() called, mode=${mode}, aceConfig=${!!this.aceConfig}`);
399
+ }
400
+ if (this.aceConfig) {
401
+ await this.composeInstructionWithPlaybook();
402
+ }
403
+
404
+ const result = await this.executeForwardByMode(mode, ai, values, options);
405
+
406
+ const durationMs = performance.now() - start;
407
+ MetricsRegistry.recordRequest(labels, false, durationMs);
408
+ this.recordUsageMetrics(labels, mode);
409
+
410
+ if (crewInstance) {
411
+ if (calledWithAI) {
412
+ const parentTaskId = (this.crewState as any)?.currentTaskId;
413
+ if (parentTaskId) {
414
+ crewInstance.recordAgentResult(parentTaskId, this.agentName, result);
415
+ }
416
+ } else {
417
+ crewInstance.recordAgentResult(taskId, this.agentName, result);
418
+ delete (this.crewState as any).currentTaskId;
419
+ (result as any)._taskId = taskId;
420
+ }
421
+ }
422
+
423
+ return result;
424
+ }
425
+
426
+ // Function overloads for forward method
427
+ async forward(values: Record<string, any>, options?: Readonly<AxProgramForwardOptions<any>>): Promise<Record<string, any>>;
428
+ async forward(ai: AxAI, values: Record<string, any>, options?: Readonly<AxProgramForwardOptions<any>>): Promise<Record<string, any>>;
429
+
430
+ // Implementation
431
+ async forward(
432
+ first: Record<string, any> | AxAI,
433
+ second?: Record<string, any> | Readonly<AxProgramForwardOptions<any>>,
434
+ third?: Readonly<AxProgramForwardOptions<any>>
435
+ ): Promise<Record<string, any>> {
436
+ return this.runForwardInvocation(this.executionMode, first, second, third);
437
+ }
438
+
439
+ private runStreamingInvocation(
440
+ mode: AgentExecutionMode,
441
+ first: Record<string, any> | AxAI,
442
+ second?: Record<string, any> | Readonly<AxProgramStreamingForwardOptions<any>>,
443
+ third?: Readonly<AxProgramStreamingForwardOptions<any>>
444
+ ): AxGenStreamingOut<any> {
445
+ const { ai, values, options } = this.resolveInvocationArgs(
446
+ first,
447
+ second,
448
+ third
449
+ );
450
+ const start = performance.now();
451
+ const crewId = (this.crewState as any)?.crewId || this.crewState.get?.("crewId") || "default";
452
+ const labels = { crewId, agent: this.agentName };
453
+
454
+ const opts = this.mergeStepHooks(options as any);
455
+ const createStream = () =>
456
+ mode === "axgen"
457
+ ? this.axGenProgram.streamingForward(ai, values, opts as any)
458
+ : super.streamingForward(ai, values, opts as any);
459
+
460
+ const wrappedGenerator = (async function* (this: StatefulAxAgent) {
461
+ if (this.aceConfig) {
462
+ await this.composeInstructionWithPlaybook();
463
+ }
464
+
465
+ const streamingResult = createStream();
466
+ try {
467
+ for await (const chunk of streamingResult) {
468
+ yield chunk;
469
+ }
470
+ } finally {
471
+ const durationMs = performance.now() - start;
472
+ MetricsRegistry.recordRequest(labels, true, durationMs);
473
+ this.recordUsageMetrics(labels, mode);
474
+ }
475
+ }).bind(this)();
476
+
477
+ return wrappedGenerator as AxGenStreamingOut<any>;
478
+ }
479
+
480
+ // Add streaming forward method overloads
481
+ streamingForward(values: Record<string, any>, options?: Readonly<AxProgramStreamingForwardOptions<any>>): AxGenStreamingOut<any>;
482
+ streamingForward(ai: AxAI, values: Record<string, any>, options?: Readonly<AxProgramStreamingForwardOptions<any>>): AxGenStreamingOut<any>;
483
+
484
+ // Implementation
485
+ streamingForward(
486
+ first: Record<string, any> | AxAI,
487
+ second?: Record<string, any> | Readonly<AxProgramStreamingForwardOptions<any>>,
488
+ third?: Readonly<AxProgramStreamingForwardOptions<any>>
489
+ ): AxGenStreamingOut<any> {
490
+ return this.runStreamingInvocation(this.executionMode, first, second, third);
491
+ }
492
+
493
+ // Legacy cost API removed: rely on Ax trackers for cost reporting
494
+ getLastUsageCost(): UsageCost | null { return null; }
495
+
496
+ // Get the accumulated costs for all runs of this agent
497
+ getAccumulatedCosts(): UsageCost | null { return null; }
498
+
499
+ // Metrics API for this agent
500
+ getMetrics() {
501
+ const crewId = (this.crewState as any)?.crewId || (this.crewState.get?.('crewId')) || 'default';
502
+ return MetricsRegistry.snapshot({ crewId, agent: this.agentName } as any);
503
+ }
504
+
505
+ resetMetrics(): void {
506
+ const crewId = (this.crewState as any)?.crewId || (this.crewState.get?.('crewId')) || 'default';
507
+ MetricsRegistry.reset({ crewId, agent: this.agentName } as any);
508
+ }
509
+
510
+ // =============
511
+ // ACE API
512
+ // =============
513
+
514
+ async initACE(ace?: ACEConfig): Promise<void> {
515
+ this.aceConfig = ace;
516
+ if (!ace) return;
517
+ try {
518
+ this.aceBaseInstruction =
519
+ this.agentDefinition || this.getSignature().getDescription() || '';
520
+
521
+ const { buildACEOptimizer, loadInitialPlaybook, createEmptyPlaybook } = await import('./ace.js');
522
+ this.aceOptimizer = buildACEOptimizer(this.axai, ace);
523
+
524
+ if (!ace.compileOnStart) {
525
+ (this.aceOptimizer as any).program = this;
526
+ }
527
+
528
+ const initial = await loadInitialPlaybook(ace.persistence);
529
+ this.applyPlaybook(initial ?? createEmptyPlaybook());
530
+
531
+ if (this.debugEnabled) {
532
+ console.log(`[ACE Debug] Initialized for ${this.agentName}, base instruction: ${this.aceBaseInstruction?.slice(0, 50)}...`);
533
+ }
534
+ } catch (error) {
535
+ console.warn(`Failed to initialize ACE for agent ${this.agentName}:`, error);
536
+ }
537
+ }
538
+
539
+ async optimizeOffline(params?: { metric?: any; examples?: any[] }): Promise<void> {
540
+ if (!this.aceConfig || !this.aceOptimizer) return;
541
+ try {
542
+ const { runOfflineCompile, resolveMetric } = await import('./ace.js');
543
+ const registry = (this as any).__functionsRegistry as FunctionRegistryType | undefined;
544
+ const metric = params?.metric || resolveMetric(this.aceConfig.metric, registry || {} as any);
545
+ const examples = params?.examples || [];
546
+
547
+ if (!metric || examples.length === 0) {
548
+ console.warn(`ACE offline compile skipped for ${this.agentName}: missing metric or examples`);
549
+ return;
550
+ }
551
+
552
+ const result = await runOfflineCompile({
553
+ program: this,
554
+ optimizer: this.aceOptimizer,
555
+ metric,
556
+ examples,
557
+ persistence: this.aceConfig.persistence
558
+ });
559
+
560
+ if (result?.artifact?.playbook) {
561
+ await this.applyPlaybook(result.artifact.playbook);
562
+ }
563
+ } catch (error) {
564
+ console.warn(`ACE offline compile failed for ${this.agentName}:`, error);
565
+ }
566
+ }
567
+
568
+ async applyOnlineUpdate(params: { example: any; prediction: any; feedback?: string }): Promise<void> {
569
+ if (!this.aceConfig) return;
570
+ if (!params.feedback?.trim()) return;
571
+
572
+ try {
573
+ const { persistPlaybook, addFeedbackToPlaybook, createEmptyPlaybook } = await import('./ace.js');
574
+
575
+ let playbook = this.acePlaybook ?? (this.aceOptimizer as any)?.playbook;
576
+ if (!playbook) {
577
+ playbook = createEmptyPlaybook();
578
+ }
579
+
580
+ if (this.debugEnabled) {
581
+ console.log(`[ACE Debug] Adding feedback to playbook: "${params.feedback}"`);
582
+ }
583
+
584
+ const teacherAI = (this.aceOptimizer as any)?.teacherAI;
585
+ const aiForAnalysis = teacherAI ?? this.axai;
586
+
587
+ await addFeedbackToPlaybook(playbook, params.feedback, aiForAnalysis, this.debugEnabled);
588
+
589
+ this.applyPlaybook(playbook);
590
+
591
+ if (this.aceOptimizer) {
592
+ (this.aceOptimizer as any).playbook = playbook;
593
+ }
594
+
595
+ if (this.aceConfig.persistence?.autoPersist) {
596
+ await persistPlaybook(playbook, this.aceConfig.persistence);
597
+ }
598
+
599
+ if (this.debugEnabled) {
600
+ console.log(`[ACE Debug] Playbook updated, sections: ${Object.keys(playbook.sections || {}).join(', ')}`);
601
+ }
602
+ } catch (error) {
603
+ console.warn(`ACE online update failed for ${this.agentName}:`, error);
604
+ }
605
+ }
606
+
607
+ getPlaybook(): any | undefined {
608
+ return this.acePlaybook;
609
+ }
610
+
611
+ applyPlaybook(pb: any): void {
612
+ this.acePlaybook = pb;
613
+ try {
614
+ (this.aceOptimizer as any).playbook = pb;
615
+ } catch {
616
+ // Ignore - optimizer may not be initialized yet
617
+ }
618
+ }
619
+
620
+ private async composeInstructionWithPlaybook(): Promise<void> {
621
+ const playbook = this.acePlaybook ?? (this.aceOptimizer as any)?.playbook;
622
+
623
+ if (this.debugEnabled) {
624
+ console.log(`[ACE Debug] composeInstructionWithPlaybook called`);
625
+ console.log(`[ACE Debug] playbook exists: ${!!playbook}, sections: ${playbook ? Object.keys(playbook.sections || {}).length : 0}`);
626
+ console.log(`[ACE Debug] baseInstruction: "${this.aceBaseInstruction?.slice(0, 50)}..."`);
627
+ }
628
+
629
+ if (!playbook) return;
630
+
631
+ try {
632
+ const { renderPlaybook } = await import('./ace.js');
633
+ const rendered = renderPlaybook(playbook);
634
+
635
+ if (this.debugEnabled) {
636
+ console.log(`[ACE Debug] rendered playbook (${rendered.length} chars): ${rendered.slice(0, 100)}...`);
637
+ }
638
+
639
+ if (!rendered) return;
640
+
641
+ const baseInstruction = this.aceBaseInstruction || '';
642
+ const combinedInstruction = [baseInstruction.trim(), '', rendered]
643
+ .filter((part) => part.trim().length > 0)
644
+ .join('\n\n');
645
+
646
+ if (this.debugEnabled) {
647
+ console.log(`[ACE Debug] combinedInstruction (${combinedInstruction.length} chars)`);
648
+ }
649
+
650
+ if (combinedInstruction.length >= 20) {
651
+ const program = (this as any).program;
652
+ if (program?.setDescription) {
653
+ program.setDescription(combinedInstruction);
654
+ }
655
+ this.setDescription(combinedInstruction);
656
+
657
+ if (this.debugEnabled) {
658
+ console.log(`[ACE Debug] setDescription called successfully`);
659
+ console.log(`[ACE Debug] Verifying - signature desc length: ${this.getSignature().getDescription()?.length}`);
660
+ }
661
+ }
662
+ } catch (error) {
663
+ console.warn('[ACE Debug] Failed to compose instruction:', error);
664
+ }
665
+ }
666
+ }
667
+
668
+ export { StatefulAxAgent };
package/src/index.ts CHANGED
@@ -2,12 +2,13 @@ import { AxCrew } from './agents/index.js';
2
2
  import { AxCrewFunctions } from './functions/index.js';
3
3
  import type { AxCrewConfig, AxCrewOptions, AgentConfig, AgentExecutionMode, AxCrewAxAgentOptions } from './types.js';
4
4
 
5
- import type {
6
- UsageCost,
7
- AggregatedMetrics,
5
+ import type {
6
+ UsageCost,
7
+ AggregatedMetrics,
8
8
  AggregatedCosts,
9
- StateInstance,
9
+ StateInstance,
10
10
  FunctionRegistryType,
11
+ DeferredToolsConfig,
11
12
  ACEConfig,
12
13
  ACETeacherConfig,
13
14
  ACEPersistenceConfig,
@@ -56,6 +57,8 @@ export {
56
57
  type AxCrewOptions,
57
58
  type StateInstance,
58
59
  type UsageCost,
60
+ // Deferred tools
61
+ type DeferredToolsConfig,
59
62
  // ACE type exports
60
63
  type ACEConfig,
61
64
  type ACETeacherConfig,