@axlsdk/axl 0.11.6 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -2520,6 +2520,11 @@ ${JSON.stringify(jsonSchema, null, 2)}`;
2520
2520
  model: modelUri,
2521
2521
  promptVersion: agent2._config.version,
2522
2522
  cost: response.cost,
2523
+ tokens: response.usage ? {
2524
+ input: response.usage.prompt_tokens,
2525
+ output: response.usage.completion_tokens,
2526
+ reasoning: response.usage.reasoning_tokens
2527
+ } : void 0,
2523
2528
  duration: Date.now() - startTime,
2524
2529
  data: { prompt, response: response.content }
2525
2530
  });
@@ -3907,6 +3912,8 @@ var MemoryStore = class {
3907
3912
  decisions = /* @__PURE__ */ new Map();
3908
3913
  executionStates = /* @__PURE__ */ new Map();
3909
3914
  memories = /* @__PURE__ */ new Map();
3915
+ executionHistory = /* @__PURE__ */ new Map();
3916
+ evalHistory = /* @__PURE__ */ new Map();
3910
3917
  constructor() {
3911
3918
  this.loadPersistedState();
3912
3919
  }
@@ -4003,11 +4010,36 @@ var MemoryStore = class {
4003
4010
  async getAllMemory(scope) {
4004
4011
  const scopeMap = this.memories.get(scope);
4005
4012
  if (!scopeMap) return [];
4006
- return Array.from(scopeMap.entries()).map(([key, value]) => ({ key, value }));
4013
+ return Array.from(scopeMap.entries()).map(([key, value]) => ({
4014
+ key,
4015
+ value: structuredClone(value)
4016
+ }));
4007
4017
  }
4008
4018
  async deleteMemory(scope, key) {
4009
4019
  this.memories.get(scope)?.delete(key);
4010
4020
  }
4021
+ // ── Execution History ──────────────────────────────────────────────
4022
+ async saveExecution(execution) {
4023
+ this.executionHistory.set(execution.executionId, structuredClone(execution));
4024
+ }
4025
+ async getExecution(executionId) {
4026
+ const exec = this.executionHistory.get(executionId);
4027
+ return exec ? structuredClone(exec) : null;
4028
+ }
4029
+ async listExecutions(limit) {
4030
+ const sorted = [...this.executionHistory.values()].sort((a, b) => b.startedAt - a.startedAt);
4031
+ const result = limit ? sorted.slice(0, limit) : sorted;
4032
+ return result.map((e) => structuredClone(e));
4033
+ }
4034
+ // ── Eval History ──────────────────────────────────────────────────
4035
+ async saveEvalResult(entry) {
4036
+ this.evalHistory.set(entry.id, structuredClone(entry));
4037
+ }
4038
+ async listEvalResults(limit) {
4039
+ const sorted = [...this.evalHistory.values()].sort((a, b) => b.timestamp - a.timestamp);
4040
+ const result = limit ? sorted.slice(0, limit) : sorted;
4041
+ return result.map((e) => structuredClone(e));
4042
+ }
4011
4043
  // ── Sessions (Studio introspection) ─────────────────────────────────
4012
4044
  async listSessions() {
4013
4045
  return [...this.sessions.keys()];
@@ -4067,6 +4099,19 @@ function safeJsonParse(data) {
4067
4099
  return null;
4068
4100
  }
4069
4101
  }
4102
+ function rowToExecutionInfo(row) {
4103
+ return {
4104
+ executionId: row.execution_id,
4105
+ workflow: row.workflow,
4106
+ status: row.status,
4107
+ totalCost: row.total_cost,
4108
+ startedAt: row.started_at,
4109
+ completedAt: row.completed_at ?? void 0,
4110
+ duration: row.duration,
4111
+ error: row.error ?? void 0,
4112
+ steps: safeJsonParse(row.steps) ?? []
4113
+ };
4114
+ }
4070
4115
  var SQLiteStore = class {
4071
4116
  db;
4072
4117
  constructor(filePath) {
@@ -4127,6 +4172,28 @@ var SQLiteStore = class {
4127
4172
  updated_at TEXT NOT NULL DEFAULT (datetime('now')),
4128
4173
  PRIMARY KEY (scope, key)
4129
4174
  );
4175
+
4176
+ CREATE TABLE IF NOT EXISTS execution_history (
4177
+ execution_id TEXT PRIMARY KEY,
4178
+ workflow TEXT NOT NULL,
4179
+ status TEXT NOT NULL,
4180
+ total_cost REAL NOT NULL DEFAULT 0,
4181
+ started_at INTEGER NOT NULL,
4182
+ completed_at INTEGER,
4183
+ duration INTEGER NOT NULL DEFAULT 0,
4184
+ error TEXT,
4185
+ steps TEXT NOT NULL
4186
+ );
4187
+
4188
+ CREATE TABLE IF NOT EXISTS eval_history (
4189
+ id TEXT PRIMARY KEY,
4190
+ eval_name TEXT NOT NULL,
4191
+ timestamp INTEGER NOT NULL,
4192
+ data TEXT NOT NULL
4193
+ );
4194
+
4195
+ CREATE INDEX IF NOT EXISTS idx_exec_history_started ON execution_history (started_at DESC);
4196
+ CREATE INDEX IF NOT EXISTS idx_eval_history_timestamp ON eval_history (timestamp DESC);
4130
4197
  `);
4131
4198
  }
4132
4199
  // ── Checkpoints ────────────────────────────────────────────────────────
@@ -4240,6 +4307,47 @@ var SQLiteStore = class {
4240
4307
  const rows = stmt.all();
4241
4308
  return rows.map((r) => r.execution_id);
4242
4309
  }
4310
+ // ── Execution History ────────────────────────────────────────────────────
4311
+ async saveExecution(execution) {
4312
+ this.db.prepare(
4313
+ "INSERT OR REPLACE INTO execution_history (execution_id, workflow, status, total_cost, started_at, completed_at, duration, error, steps) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"
4314
+ ).run(
4315
+ execution.executionId,
4316
+ execution.workflow,
4317
+ execution.status,
4318
+ execution.totalCost,
4319
+ execution.startedAt,
4320
+ execution.completedAt ?? null,
4321
+ execution.duration,
4322
+ execution.error ?? null,
4323
+ JSON.stringify(execution.steps)
4324
+ );
4325
+ }
4326
+ async getExecution(executionId) {
4327
+ const row = this.db.prepare("SELECT * FROM execution_history WHERE execution_id = ?").get(executionId);
4328
+ return row ? rowToExecutionInfo(row) : null;
4329
+ }
4330
+ async listExecutions(limit) {
4331
+ const sql = limit ? "SELECT * FROM execution_history ORDER BY started_at DESC LIMIT ?" : "SELECT * FROM execution_history ORDER BY started_at DESC";
4332
+ const rows = limit ? this.db.prepare(sql).all(limit) : this.db.prepare(sql).all();
4333
+ return rows.map(rowToExecutionInfo);
4334
+ }
4335
+ // ── Eval History ────────────────────────────────────────────────────────
4336
+ async saveEvalResult(entry) {
4337
+ this.db.prepare(
4338
+ "INSERT OR REPLACE INTO eval_history (id, eval_name, timestamp, data) VALUES (?, ?, ?, ?)"
4339
+ ).run(entry.id, entry.eval, entry.timestamp, JSON.stringify(entry.data));
4340
+ }
4341
+ async listEvalResults(limit) {
4342
+ const sql = limit ? "SELECT * FROM eval_history ORDER BY timestamp DESC LIMIT ?" : "SELECT * FROM eval_history ORDER BY timestamp DESC";
4343
+ const rows = limit ? this.db.prepare(sql).all(limit) : this.db.prepare(sql).all();
4344
+ return rows.map((r) => ({
4345
+ id: r.id,
4346
+ eval: r.eval_name,
4347
+ timestamp: r.timestamp,
4348
+ data: safeJsonParse(r.data)
4349
+ }));
4350
+ }
4243
4351
  // ── Sessions (Studio introspection) ────────────────────────────────────
4244
4352
  async listSessions() {
4245
4353
  const rows = this.db.prepare("SELECT session_id FROM sessions").all();
@@ -5124,6 +5232,10 @@ var AxlRuntime = class extends EventEmitter2 {
5124
5232
  mcpManager;
5125
5233
  memoryManager;
5126
5234
  spanManager = new NoopSpanManager();
5235
+ historicalExecutions = /* @__PURE__ */ new Map();
5236
+ historicalExecutionsLoadPromise = null;
5237
+ evalHistory = [];
5238
+ evalHistoryLoadPromise = null;
5127
5239
  constructor(config) {
5128
5240
  super();
5129
5241
  this.config = resolveConfig(config ?? {});
@@ -5168,6 +5280,20 @@ var AxlRuntime = class extends EventEmitter2 {
5168
5280
  return new MemoryStore();
5169
5281
  }
5170
5282
  }
5283
+ /**
5284
+ * Persist a completed/failed execution to the state store (fire-and-forget)
5285
+ * and move it from the active executions map to the historical cache.
5286
+ */
5287
+ persistExecution(execInfo) {
5288
+ const snapshot = structuredClone(execInfo);
5289
+ if (this.stateStore.saveExecution) {
5290
+ this.stateStore.saveExecution(snapshot).catch(() => {
5291
+ });
5292
+ }
5293
+ const id = execInfo.executionId;
5294
+ this.historicalExecutions.set(id, snapshot);
5295
+ this.executions.delete(id);
5296
+ }
5171
5297
  /** Register a workflow with the runtime. */
5172
5298
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
5173
5299
  register(workflow2) {
@@ -5246,6 +5372,7 @@ var AxlRuntime = class extends EventEmitter2 {
5246
5372
  async runRegisteredEval(name) {
5247
5373
  const entry = this.registeredEvals.get(name);
5248
5374
  if (!entry) throw new Error(`Eval "${name}" is not registered`);
5375
+ let result;
5249
5376
  if (entry.executeWorkflow) {
5250
5377
  let runEvalFn;
5251
5378
  try {
@@ -5257,20 +5384,50 @@ var AxlRuntime = class extends EventEmitter2 {
5257
5384
  }
5258
5385
  const originalExecuteFn = entry.executeWorkflow;
5259
5386
  const wrappedExecuteFn = async (input, runtime) => {
5260
- const { result, cost: trackedCost } = await this.trackCost(async () => {
5387
+ const { result: result2, cost: trackedCost } = await this.trackCost(async () => {
5261
5388
  return originalExecuteFn(input, runtime);
5262
5389
  });
5263
- return { output: result.output, cost: result.cost ?? trackedCost };
5390
+ return { output: result2.output, cost: result2.cost ?? trackedCost };
5264
5391
  };
5265
- return runEvalFn(entry.config, wrappedExecuteFn, void 0, this);
5392
+ result = await runEvalFn(entry.config, wrappedExecuteFn, this);
5393
+ } else {
5394
+ result = await this.eval(
5395
+ entry.config
5396
+ );
5266
5397
  }
5267
- return this.eval(
5268
- entry.config
5269
- );
5398
+ const resultObj = result;
5399
+ try {
5400
+ await this.saveEvalResult({
5401
+ id: resultObj.id ?? randomUUID2(),
5402
+ eval: name,
5403
+ timestamp: Date.now(),
5404
+ data: structuredClone(result)
5405
+ });
5406
+ } catch {
5407
+ }
5408
+ return result;
5270
5409
  }
5271
- /** Get all execution info (running + completed). */
5272
- getExecutions() {
5273
- return [...this.executions.values()];
5410
+ /** Get all execution info (running + completed + historical). */
5411
+ async getExecutions() {
5412
+ if (!this.historicalExecutionsLoadPromise && this.stateStore.listExecutions) {
5413
+ this.historicalExecutionsLoadPromise = this.stateStore.listExecutions().then((stored) => {
5414
+ for (const exec of stored) {
5415
+ if (!this.executions.has(exec.executionId) && !this.historicalExecutions.has(exec.executionId)) {
5416
+ this.historicalExecutions.set(exec.executionId, exec);
5417
+ }
5418
+ }
5419
+ }).catch(() => {
5420
+ this.historicalExecutionsLoadPromise = null;
5421
+ });
5422
+ }
5423
+ if (this.historicalExecutionsLoadPromise) {
5424
+ await this.historicalExecutionsLoadPromise;
5425
+ }
5426
+ const merged = new Map(this.historicalExecutions);
5427
+ for (const [id, exec] of this.executions) {
5428
+ merged.set(id, exec);
5429
+ }
5430
+ return [...merged.values()].sort((a, b) => b.startedAt - a.startedAt);
5274
5431
  }
5275
5432
  /**
5276
5433
  * Create a WorkflowContext for ad-hoc use (evals, tool testing, prototyping).
@@ -5311,6 +5468,10 @@ var AxlRuntime = class extends EventEmitter2 {
5311
5468
  registerProvider(name, provider) {
5312
5469
  this.providerRegistry.registerInstance(name, provider);
5313
5470
  }
5471
+ /** Resolve a provider:model URI to a Provider instance and model name. */
5472
+ resolveProvider(uri) {
5473
+ return this.providerRegistry.resolve(uri, this.config);
5474
+ }
5314
5475
  /** Execute a workflow and return the result. */
5315
5476
  async execute(name, input, options) {
5316
5477
  const workflow2 = this.workflows.get(name);
@@ -5402,6 +5563,7 @@ var AxlRuntime = class extends EventEmitter2 {
5402
5563
  }
5403
5564
  span.setAttribute("axl.workflow.cost", execInfo.totalCost);
5404
5565
  span.setAttribute("axl.workflow.duration", execInfo.duration);
5566
+ this.persistExecution(execInfo);
5405
5567
  return output;
5406
5568
  } catch (err) {
5407
5569
  execInfo.status = "failed";
@@ -5409,6 +5571,7 @@ var AxlRuntime = class extends EventEmitter2 {
5409
5571
  execInfo.duration = execInfo.completedAt - execInfo.startedAt;
5410
5572
  execInfo.error = err instanceof Error ? err.message : String(err);
5411
5573
  ctx.log("workflow_end", { workflow: name, status: "failed", error: execInfo.error });
5574
+ this.persistExecution(execInfo);
5412
5575
  throw err;
5413
5576
  } finally {
5414
5577
  this.abortControllers.delete(executionId);
@@ -5422,6 +5585,7 @@ var AxlRuntime = class extends EventEmitter2 {
5422
5585
  const controller = new AbortController();
5423
5586
  axlStream.on("close", () => controller.abort());
5424
5587
  let execInfo;
5588
+ let ctx;
5425
5589
  const run = async () => {
5426
5590
  const workflow2 = this.workflows.get(name);
5427
5591
  if (!workflow2) throw new Error(`Workflow "${name}" not registered`);
@@ -5440,7 +5604,7 @@ var AxlRuntime = class extends EventEmitter2 {
5440
5604
  duration: 0
5441
5605
  };
5442
5606
  this.executions.set(executionId, execInfo);
5443
- const ctx = new WorkflowContext({
5607
+ const wfCtx = new WorkflowContext({
5444
5608
  input: validated,
5445
5609
  executionId,
5446
5610
  metadata: options?.metadata,
@@ -5528,6 +5692,7 @@ var AxlRuntime = class extends EventEmitter2 {
5528
5692
  policy: "finish_and_stop"
5529
5693
  }
5530
5694
  });
5695
+ ctx = wfCtx;
5531
5696
  return this.spanManager.withSpanAsync(
5532
5697
  "axl.workflow.execute",
5533
5698
  {
@@ -5537,16 +5702,23 @@ var AxlRuntime = class extends EventEmitter2 {
5537
5702
  },
5538
5703
  async (span) => {
5539
5704
  try {
5540
- const rawResult = await workflow2.handler(ctx);
5705
+ const rawResult = await workflow2.handler(wfCtx);
5541
5706
  const result = workflow2.outputSchema ? workflow2.outputSchema.parse(rawResult) : rawResult;
5542
5707
  execInfo.status = "completed";
5543
5708
  execInfo.completedAt = Date.now();
5544
5709
  execInfo.duration = execInfo.completedAt - execInfo.startedAt;
5710
+ wfCtx.log("workflow_end", {
5711
+ workflow: name,
5712
+ status: "completed",
5713
+ duration: execInfo.duration,
5714
+ cost: execInfo.totalCost
5715
+ });
5545
5716
  if (this.stateStore.deleteCheckpoints) {
5546
5717
  await this.stateStore.deleteCheckpoints(executionId);
5547
5718
  }
5548
5719
  span.setAttribute("axl.workflow.cost", execInfo.totalCost);
5549
5720
  span.setAttribute("axl.workflow.duration", execInfo.duration);
5721
+ this.persistExecution(execInfo);
5550
5722
  return result;
5551
5723
  } finally {
5552
5724
  this.abortControllers.delete(executionId);
@@ -5560,6 +5732,12 @@ var AxlRuntime = class extends EventEmitter2 {
5560
5732
  execInfo.completedAt = Date.now();
5561
5733
  execInfo.duration = execInfo.completedAt - execInfo.startedAt;
5562
5734
  execInfo.error = err instanceof Error ? err.message : String(err);
5735
+ ctx?.log("workflow_end", {
5736
+ workflow: name,
5737
+ status: "failed",
5738
+ error: execInfo.error
5739
+ });
5740
+ this.persistExecution(execInfo);
5563
5741
  }
5564
5742
  axlStream._error(err instanceof Error ? err : new Error(String(err)));
5565
5743
  });
@@ -5603,7 +5781,45 @@ var AxlRuntime = class extends EventEmitter2 {
5603
5781
  }
5604
5782
  /** Get execution details by ID. */
5605
5783
  async getExecution(executionId) {
5606
- return this.executions.get(executionId);
5784
+ const inMemory = this.executions.get(executionId);
5785
+ if (inMemory) return inMemory;
5786
+ const cached = this.historicalExecutions.get(executionId);
5787
+ if (cached) return cached;
5788
+ if (this.stateStore.getExecution) {
5789
+ const stored = await this.stateStore.getExecution(executionId);
5790
+ if (stored) {
5791
+ this.historicalExecutions.set(executionId, stored);
5792
+ return stored;
5793
+ }
5794
+ }
5795
+ return void 0;
5796
+ }
5797
+ /** Save an eval result to history. */
5798
+ async saveEvalResult(entry) {
5799
+ this.evalHistory.unshift(entry);
5800
+ if (this.stateStore.saveEvalResult) {
5801
+ await this.stateStore.saveEvalResult(entry);
5802
+ }
5803
+ }
5804
+ /** Get eval result history (most recent first). */
5805
+ async getEvalHistory() {
5806
+ if (!this.evalHistoryLoadPromise && this.stateStore.listEvalResults) {
5807
+ this.evalHistoryLoadPromise = this.stateStore.listEvalResults().then((stored) => {
5808
+ const ids = new Set(this.evalHistory.map((e) => e.id));
5809
+ for (const entry of stored) {
5810
+ if (!ids.has(entry.id)) {
5811
+ this.evalHistory.push(entry);
5812
+ }
5813
+ }
5814
+ this.evalHistory.sort((a, b) => b.timestamp - a.timestamp);
5815
+ }).catch(() => {
5816
+ this.evalHistoryLoadPromise = null;
5817
+ });
5818
+ }
5819
+ if (this.evalHistoryLoadPromise) {
5820
+ await this.evalHistoryLoadPromise;
5821
+ }
5822
+ return [...this.evalHistory];
5607
5823
  }
5608
5824
  /** List pending human decisions. */
5609
5825
  async getPendingDecisions() {
@@ -5712,7 +5928,7 @@ var AxlRuntime = class extends EventEmitter2 {
5712
5928
  });
5713
5929
  return { output: result, cost };
5714
5930
  };
5715
- return runEvalFn(config, executeWorkflow, void 0, this);
5931
+ return runEvalFn(config, executeWorkflow, this);
5716
5932
  }
5717
5933
  /**
5718
5934
  * Compare two evaluation results to detect regressions and improvements.
@@ -5911,6 +6127,18 @@ var RedisStore = class _RedisStore {
5911
6127
  pendingExecSetKey() {
5912
6128
  return "axl:pending-executions";
5913
6129
  }
6130
+ execHistoryKey(executionId) {
6131
+ return `axl:exec-history:${executionId}`;
6132
+ }
6133
+ execHistorySetKey() {
6134
+ return "axl:exec-history-ids";
6135
+ }
6136
+ evalHistoryKey(id) {
6137
+ return `axl:eval-history:${id}`;
6138
+ }
6139
+ evalHistorySetKey() {
6140
+ return "axl:eval-history-ids";
6141
+ }
5914
6142
  // ── Checkpoints ──────────────────────────────────────────────────────
5915
6143
  async saveCheckpoint(executionId, step, data) {
5916
6144
  await this.client.hSet(this.checkpointKey(executionId), String(step), JSON.stringify(data));
@@ -5982,6 +6210,42 @@ var RedisStore = class _RedisStore {
5982
6210
  async listPendingExecutions() {
5983
6211
  return this.client.sMembers(this.pendingExecSetKey());
5984
6212
  }
6213
+ // ── Execution History ────────────────────────────────────────────────
6214
+ async saveExecution(execution) {
6215
+ await this.client.sAdd(this.execHistorySetKey(), execution.executionId);
6216
+ await this.client.set(this.execHistoryKey(execution.executionId), JSON.stringify(execution));
6217
+ }
6218
+ async getExecution(executionId) {
6219
+ const raw = await this.client.get(this.execHistoryKey(executionId));
6220
+ return raw ? JSON.parse(raw) : null;
6221
+ }
6222
+ async listExecutions(limit) {
6223
+ const ids = await this.client.sMembers(this.execHistorySetKey());
6224
+ if (ids.length === 0) return [];
6225
+ const entries = [];
6226
+ for (const id of ids) {
6227
+ const raw = await this.client.get(this.execHistoryKey(id));
6228
+ if (raw) entries.push(JSON.parse(raw));
6229
+ }
6230
+ entries.sort((a, b) => b.startedAt - a.startedAt);
6231
+ return limit ? entries.slice(0, limit) : entries;
6232
+ }
6233
+ // ── Eval History ────────────────────────────────────────────────────
6234
+ async saveEvalResult(entry) {
6235
+ await this.client.sAdd(this.evalHistorySetKey(), entry.id);
6236
+ await this.client.set(this.evalHistoryKey(entry.id), JSON.stringify(entry));
6237
+ }
6238
+ async listEvalResults(limit) {
6239
+ const ids = await this.client.sMembers(this.evalHistorySetKey());
6240
+ if (ids.length === 0) return [];
6241
+ const entries = [];
6242
+ for (const id of ids) {
6243
+ const raw = await this.client.get(this.evalHistoryKey(id));
6244
+ if (raw) entries.push(JSON.parse(raw));
6245
+ }
6246
+ entries.sort((a, b) => b.timestamp - a.timestamp);
6247
+ return limit ? entries.slice(0, limit) : entries;
6248
+ }
5985
6249
  // ── Sessions (Studio introspection) ────────────────────────────────────
5986
6250
  async listSessions() {
5987
6251
  return this.client.sMembers("axl:session-ids");