@axlsdk/axl 0.10.4 → 0.11.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/dist/index.cjs CHANGED
@@ -2281,6 +2281,14 @@ var WorkflowContext = class _WorkflowContext {
2281
2281
  input;
2282
2282
  executionId;
2283
2283
  metadata;
2284
+ /**
2285
+ * Total cost accumulated by agent calls in this context.
2286
+ * Inside a `ctx.budget()` block, returns only that block's cost.
2287
+ * After the block completes, the nested cost is rolled up into the parent total.
2288
+ */
2289
+ get totalCost() {
2290
+ return this.budgetContext?.totalCost ?? 0;
2291
+ }
2284
2292
  config;
2285
2293
  providerRegistry;
2286
2294
  sessionHistory;
@@ -3780,6 +3788,11 @@ ${summaryResponse.content}`
3780
3788
  });
3781
3789
  return decision2;
3782
3790
  }
3791
+ if (!this.pendingDecisions) {
3792
+ throw new Error(
3793
+ "Tool requires approval but no approval handler is configured. Provide awaitHumanHandler to createContext() or use runtime.execute() with workflow infrastructure."
3794
+ );
3795
+ }
3783
3796
  if (this.stateStore) {
3784
3797
  await this.stateStore.savePendingDecision(this.executionId, {
3785
3798
  executionId: this.executionId,
@@ -3805,7 +3818,7 @@ ${summaryResponse.content}`
3805
3818
  data: { event: "await_human", channel: options.channel, prompt: options.prompt }
3806
3819
  });
3807
3820
  const decision = await new Promise((resolve) => {
3808
- this.pendingDecisions?.set(this.executionId, resolve);
3821
+ this.pendingDecisions.set(this.executionId, resolve);
3809
3822
  });
3810
3823
  if (this.stateStore) {
3811
3824
  await this.stateStore.saveExecutionState(this.executionId, {
@@ -4023,6 +4036,7 @@ function workflow(config) {
4023
4036
  // src/runtime.ts
4024
4037
  var import_node_events2 = require("events");
4025
4038
  var import_node_crypto2 = require("crypto");
4039
+ var import_node_async_hooks2 = require("async_hooks");
4026
4040
  var import_node_fs2 = require("fs");
4027
4041
  var import_node_path2 = require("path");
4028
4042
 
@@ -5242,6 +5256,7 @@ function hashInput(input) {
5242
5256
  }
5243
5257
  return (hash >>> 0).toString(16);
5244
5258
  }
5259
+ var costScopeStorage = new import_node_async_hooks2.AsyncLocalStorage();
5245
5260
  var AxlRuntime = class extends import_node_events2.EventEmitter {
5246
5261
  config;
5247
5262
  workflows = /* @__PURE__ */ new Map();
@@ -5387,8 +5402,14 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
5387
5402
  "axl-eval is required for AxlRuntime.runRegisteredEval(). Install it with: npm install @axlsdk/eval"
5388
5403
  );
5389
5404
  }
5390
- const executeFn = entry.executeWorkflow;
5391
- return runEvalFn(entry.config, executeFn, void 0, this);
5405
+ const originalExecuteFn = entry.executeWorkflow;
5406
+ const wrappedExecuteFn = async (input, runtime) => {
5407
+ const { result, cost: trackedCost } = await this.trackCost(async () => {
5408
+ return originalExecuteFn(input, runtime);
5409
+ });
5410
+ return { output: result.output, cost: result.cost ?? trackedCost };
5411
+ };
5412
+ return runEvalFn(entry.config, wrappedExecuteFn, void 0, this);
5392
5413
  }
5393
5414
  return this.eval(
5394
5415
  entry.config
@@ -5399,21 +5420,38 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
5399
5420
  return [...this.executions.values()];
5400
5421
  }
5401
5422
  /**
5402
- * Create a lightweight WorkflowContext for ad-hoc use (tool testing, prototyping).
5403
- * The context has access to the runtime's providers, state store, and MCP manager
5404
- * but no session history, streaming callbacks, or budget tracking.
5423
+ * Create a WorkflowContext for ad-hoc use (evals, tool testing, prototyping).
5424
+ * The context has access to the runtime's providers, state store, MCP manager,
5425
+ * and automatically emits trace events and tracks cost.
5405
5426
  */
5406
5427
  createContext(options) {
5428
+ const executionId = (0, import_node_crypto2.randomUUID)();
5429
+ const budgetLimit = options?.budget ? parseCost(options.budget) : Infinity;
5430
+ this.registerWithCostScope(executionId);
5407
5431
  return new WorkflowContext({
5408
5432
  input: void 0,
5409
- executionId: (0, import_node_crypto2.randomUUID)(),
5433
+ executionId,
5410
5434
  metadata: options?.metadata,
5411
5435
  config: this.config,
5412
5436
  providerRegistry: this.providerRegistry,
5413
5437
  stateStore: this.stateStore,
5414
5438
  mcpManager: this.mcpManager,
5415
5439
  spanManager: this.spanManager,
5416
- memoryManager: this.memoryManager
5440
+ memoryManager: this.memoryManager,
5441
+ sessionHistory: options?.sessionHistory,
5442
+ signal: options?.signal,
5443
+ onToken: options?.onToken,
5444
+ awaitHumanHandler: options?.awaitHumanHandler,
5445
+ onTrace: (event) => {
5446
+ this.emit("trace", event);
5447
+ this.outputTraceEvent(event);
5448
+ },
5449
+ budgetContext: {
5450
+ totalCost: 0,
5451
+ limit: budgetLimit,
5452
+ exceeded: false,
5453
+ policy: "finish_and_stop"
5454
+ }
5417
5455
  });
5418
5456
  }
5419
5457
  /** Register a custom provider instance. */
@@ -5432,6 +5470,7 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
5432
5470
  const executionId = (0, import_node_crypto2.randomUUID)();
5433
5471
  const controller = new AbortController();
5434
5472
  this.abortControllers.set(executionId, controller);
5473
+ this.registerWithCostScope(executionId);
5435
5474
  const execInfo = {
5436
5475
  executionId,
5437
5476
  workflow: name,
@@ -5476,7 +5515,13 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
5476
5515
  mcpManager: this.mcpManager,
5477
5516
  memoryManager: this.memoryManager,
5478
5517
  resumeMode: !!options?.metadata?.resumeMode,
5479
- spanManager: this.spanManager
5518
+ spanManager: this.spanManager,
5519
+ budgetContext: {
5520
+ totalCost: 0,
5521
+ limit: Infinity,
5522
+ exceeded: false,
5523
+ policy: "finish_and_stop"
5524
+ }
5480
5525
  });
5481
5526
  ctx.log("workflow_start", { workflow: name, executionId });
5482
5527
  return this.spanManager.withSpanAsync(
@@ -5531,6 +5576,7 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
5531
5576
  const executionId = (0, import_node_crypto2.randomUUID)();
5532
5577
  this.abortControllers.set(executionId, controller);
5533
5578
  const sessionHistory = options?.metadata?.sessionHistory ?? void 0;
5579
+ this.registerWithCostScope(executionId);
5534
5580
  execInfo = {
5535
5581
  executionId,
5536
5582
  workflow: name,
@@ -5621,7 +5667,13 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
5621
5667
  mcpManager: this.mcpManager,
5622
5668
  memoryManager: this.memoryManager,
5623
5669
  resumeMode: !!options?.metadata?.resumeMode,
5624
- spanManager: this.spanManager
5670
+ spanManager: this.spanManager,
5671
+ budgetContext: {
5672
+ totalCost: 0,
5673
+ limit: Infinity,
5674
+ exceeded: false,
5675
+ policy: "finish_and_stop"
5676
+ }
5625
5677
  });
5626
5678
  return this.spanManager.withSpanAsync(
5627
5679
  "axl.workflow.execute",
@@ -5793,28 +5845,21 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
5793
5845
  * @see Spec Section 13.5
5794
5846
  */
5795
5847
  async eval(config) {
5796
- let runEval;
5848
+ let runEvalFn;
5797
5849
  try {
5798
- ({ runEval } = await import("@axlsdk/eval"));
5850
+ ({ runEval: runEvalFn } = await import("@axlsdk/eval"));
5799
5851
  } catch {
5800
5852
  throw new Error(
5801
5853
  "axl-eval is required for AxlRuntime.eval(). Install it with: npm install @axlsdk/eval"
5802
5854
  );
5803
5855
  }
5804
5856
  const executeWorkflow = async (input) => {
5805
- let cost = 0;
5806
- const costListener = (event) => {
5807
- if (event.cost) cost += event.cost;
5808
- };
5809
- this.on("trace", costListener);
5810
- try {
5811
- const output = await this.execute(config.workflow, input);
5812
- return { output, cost };
5813
- } finally {
5814
- this.off("trace", costListener);
5815
- }
5857
+ const { result, cost } = await this.trackCost(async () => {
5858
+ return this.execute(config.workflow, input);
5859
+ });
5860
+ return { output: result, cost };
5816
5861
  };
5817
- return runEval(config, executeWorkflow, void 0, this);
5862
+ return runEvalFn(config, executeWorkflow, void 0, this);
5818
5863
  }
5819
5864
  /**
5820
5865
  * Compare two evaluation results to detect regressions and improvements.
@@ -5833,6 +5878,46 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
5833
5878
  }
5834
5879
  return evalCompareFn(baseline, candidate);
5835
5880
  }
5881
+ /**
5882
+ * Track cost across any runtime operations within the given function.
5883
+ * Uses AsyncLocalStorage to scope cost attribution to specific execution IDs,
5884
+ * making it correct with concurrent calls.
5885
+ *
5886
+ * Works with both `createContext()` and `execute()` calls inside `fn`.
5887
+ */
5888
+ async trackCost(fn) {
5889
+ const parentScope = costScopeStorage.getStore();
5890
+ const scope = {
5891
+ totalCost: 0,
5892
+ trackedIds: /* @__PURE__ */ new Set(),
5893
+ parent: parentScope
5894
+ };
5895
+ const costListener = (event) => {
5896
+ if (event.cost && scope.trackedIds.has(event.executionId)) {
5897
+ scope.totalCost += event.cost;
5898
+ }
5899
+ };
5900
+ this.setMaxListeners(this.getMaxListeners() + 1);
5901
+ this.on("trace", costListener);
5902
+ try {
5903
+ const result = await costScopeStorage.run(scope, fn);
5904
+ return { result, cost: scope.totalCost };
5905
+ } finally {
5906
+ this.off("trace", costListener);
5907
+ this.setMaxListeners(this.getMaxListeners() - 1);
5908
+ }
5909
+ }
5910
+ /** Register an execution ID with the active cost scope for trackCost() attribution. */
5911
+ registerWithCostScope(executionId) {
5912
+ const costScope = costScopeStorage.getStore();
5913
+ if (costScope) {
5914
+ let scope = costScope;
5915
+ while (scope) {
5916
+ scope.trackedIds.add(executionId);
5917
+ scope = scope.parent;
5918
+ }
5919
+ }
5920
+ }
5836
5921
  /**
5837
5922
  * Handle trace event output based on configuration.
5838
5923
  *