@axlsdk/axl 0.11.6 → 0.12.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, void 0, 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).
@@ -5402,6 +5559,7 @@ var AxlRuntime = class extends EventEmitter2 {
5402
5559
  }
5403
5560
  span.setAttribute("axl.workflow.cost", execInfo.totalCost);
5404
5561
  span.setAttribute("axl.workflow.duration", execInfo.duration);
5562
+ this.persistExecution(execInfo);
5405
5563
  return output;
5406
5564
  } catch (err) {
5407
5565
  execInfo.status = "failed";
@@ -5409,6 +5567,7 @@ var AxlRuntime = class extends EventEmitter2 {
5409
5567
  execInfo.duration = execInfo.completedAt - execInfo.startedAt;
5410
5568
  execInfo.error = err instanceof Error ? err.message : String(err);
5411
5569
  ctx.log("workflow_end", { workflow: name, status: "failed", error: execInfo.error });
5570
+ this.persistExecution(execInfo);
5412
5571
  throw err;
5413
5572
  } finally {
5414
5573
  this.abortControllers.delete(executionId);
@@ -5422,6 +5581,7 @@ var AxlRuntime = class extends EventEmitter2 {
5422
5581
  const controller = new AbortController();
5423
5582
  axlStream.on("close", () => controller.abort());
5424
5583
  let execInfo;
5584
+ let ctx;
5425
5585
  const run = async () => {
5426
5586
  const workflow2 = this.workflows.get(name);
5427
5587
  if (!workflow2) throw new Error(`Workflow "${name}" not registered`);
@@ -5440,7 +5600,7 @@ var AxlRuntime = class extends EventEmitter2 {
5440
5600
  duration: 0
5441
5601
  };
5442
5602
  this.executions.set(executionId, execInfo);
5443
- const ctx = new WorkflowContext({
5603
+ const wfCtx = new WorkflowContext({
5444
5604
  input: validated,
5445
5605
  executionId,
5446
5606
  metadata: options?.metadata,
@@ -5528,6 +5688,7 @@ var AxlRuntime = class extends EventEmitter2 {
5528
5688
  policy: "finish_and_stop"
5529
5689
  }
5530
5690
  });
5691
+ ctx = wfCtx;
5531
5692
  return this.spanManager.withSpanAsync(
5532
5693
  "axl.workflow.execute",
5533
5694
  {
@@ -5537,16 +5698,23 @@ var AxlRuntime = class extends EventEmitter2 {
5537
5698
  },
5538
5699
  async (span) => {
5539
5700
  try {
5540
- const rawResult = await workflow2.handler(ctx);
5701
+ const rawResult = await workflow2.handler(wfCtx);
5541
5702
  const result = workflow2.outputSchema ? workflow2.outputSchema.parse(rawResult) : rawResult;
5542
5703
  execInfo.status = "completed";
5543
5704
  execInfo.completedAt = Date.now();
5544
5705
  execInfo.duration = execInfo.completedAt - execInfo.startedAt;
5706
+ wfCtx.log("workflow_end", {
5707
+ workflow: name,
5708
+ status: "completed",
5709
+ duration: execInfo.duration,
5710
+ cost: execInfo.totalCost
5711
+ });
5545
5712
  if (this.stateStore.deleteCheckpoints) {
5546
5713
  await this.stateStore.deleteCheckpoints(executionId);
5547
5714
  }
5548
5715
  span.setAttribute("axl.workflow.cost", execInfo.totalCost);
5549
5716
  span.setAttribute("axl.workflow.duration", execInfo.duration);
5717
+ this.persistExecution(execInfo);
5550
5718
  return result;
5551
5719
  } finally {
5552
5720
  this.abortControllers.delete(executionId);
@@ -5560,6 +5728,12 @@ var AxlRuntime = class extends EventEmitter2 {
5560
5728
  execInfo.completedAt = Date.now();
5561
5729
  execInfo.duration = execInfo.completedAt - execInfo.startedAt;
5562
5730
  execInfo.error = err instanceof Error ? err.message : String(err);
5731
+ ctx?.log("workflow_end", {
5732
+ workflow: name,
5733
+ status: "failed",
5734
+ error: execInfo.error
5735
+ });
5736
+ this.persistExecution(execInfo);
5563
5737
  }
5564
5738
  axlStream._error(err instanceof Error ? err : new Error(String(err)));
5565
5739
  });
@@ -5603,7 +5777,45 @@ var AxlRuntime = class extends EventEmitter2 {
5603
5777
  }
5604
5778
  /** Get execution details by ID. */
5605
5779
  async getExecution(executionId) {
5606
- return this.executions.get(executionId);
5780
+ const inMemory = this.executions.get(executionId);
5781
+ if (inMemory) return inMemory;
5782
+ const cached = this.historicalExecutions.get(executionId);
5783
+ if (cached) return cached;
5784
+ if (this.stateStore.getExecution) {
5785
+ const stored = await this.stateStore.getExecution(executionId);
5786
+ if (stored) {
5787
+ this.historicalExecutions.set(executionId, stored);
5788
+ return stored;
5789
+ }
5790
+ }
5791
+ return void 0;
5792
+ }
5793
+ /** Save an eval result to history. */
5794
+ async saveEvalResult(entry) {
5795
+ this.evalHistory.unshift(entry);
5796
+ if (this.stateStore.saveEvalResult) {
5797
+ await this.stateStore.saveEvalResult(entry);
5798
+ }
5799
+ }
5800
+ /** Get eval result history (most recent first). */
5801
+ async getEvalHistory() {
5802
+ if (!this.evalHistoryLoadPromise && this.stateStore.listEvalResults) {
5803
+ this.evalHistoryLoadPromise = this.stateStore.listEvalResults().then((stored) => {
5804
+ const ids = new Set(this.evalHistory.map((e) => e.id));
5805
+ for (const entry of stored) {
5806
+ if (!ids.has(entry.id)) {
5807
+ this.evalHistory.push(entry);
5808
+ }
5809
+ }
5810
+ this.evalHistory.sort((a, b) => b.timestamp - a.timestamp);
5811
+ }).catch(() => {
5812
+ this.evalHistoryLoadPromise = null;
5813
+ });
5814
+ }
5815
+ if (this.evalHistoryLoadPromise) {
5816
+ await this.evalHistoryLoadPromise;
5817
+ }
5818
+ return [...this.evalHistory];
5607
5819
  }
5608
5820
  /** List pending human decisions. */
5609
5821
  async getPendingDecisions() {
@@ -5911,6 +6123,18 @@ var RedisStore = class _RedisStore {
5911
6123
  pendingExecSetKey() {
5912
6124
  return "axl:pending-executions";
5913
6125
  }
6126
+ execHistoryKey(executionId) {
6127
+ return `axl:exec-history:${executionId}`;
6128
+ }
6129
+ execHistorySetKey() {
6130
+ return "axl:exec-history-ids";
6131
+ }
6132
+ evalHistoryKey(id) {
6133
+ return `axl:eval-history:${id}`;
6134
+ }
6135
+ evalHistorySetKey() {
6136
+ return "axl:eval-history-ids";
6137
+ }
5914
6138
  // ── Checkpoints ──────────────────────────────────────────────────────
5915
6139
  async saveCheckpoint(executionId, step, data) {
5916
6140
  await this.client.hSet(this.checkpointKey(executionId), String(step), JSON.stringify(data));
@@ -5982,6 +6206,42 @@ var RedisStore = class _RedisStore {
5982
6206
  async listPendingExecutions() {
5983
6207
  return this.client.sMembers(this.pendingExecSetKey());
5984
6208
  }
6209
+ // ── Execution History ────────────────────────────────────────────────
6210
+ async saveExecution(execution) {
6211
+ await this.client.sAdd(this.execHistorySetKey(), execution.executionId);
6212
+ await this.client.set(this.execHistoryKey(execution.executionId), JSON.stringify(execution));
6213
+ }
6214
+ async getExecution(executionId) {
6215
+ const raw = await this.client.get(this.execHistoryKey(executionId));
6216
+ return raw ? JSON.parse(raw) : null;
6217
+ }
6218
+ async listExecutions(limit) {
6219
+ const ids = await this.client.sMembers(this.execHistorySetKey());
6220
+ if (ids.length === 0) return [];
6221
+ const entries = [];
6222
+ for (const id of ids) {
6223
+ const raw = await this.client.get(this.execHistoryKey(id));
6224
+ if (raw) entries.push(JSON.parse(raw));
6225
+ }
6226
+ entries.sort((a, b) => b.startedAt - a.startedAt);
6227
+ return limit ? entries.slice(0, limit) : entries;
6228
+ }
6229
+ // ── Eval History ────────────────────────────────────────────────────
6230
+ async saveEvalResult(entry) {
6231
+ await this.client.sAdd(this.evalHistorySetKey(), entry.id);
6232
+ await this.client.set(this.evalHistoryKey(entry.id), JSON.stringify(entry));
6233
+ }
6234
+ async listEvalResults(limit) {
6235
+ const ids = await this.client.sMembers(this.evalHistorySetKey());
6236
+ if (ids.length === 0) return [];
6237
+ const entries = [];
6238
+ for (const id of ids) {
6239
+ const raw = await this.client.get(this.evalHistoryKey(id));
6240
+ if (raw) entries.push(JSON.parse(raw));
6241
+ }
6242
+ entries.sort((a, b) => b.timestamp - a.timestamp);
6243
+ return limit ? entries.slice(0, limit) : entries;
6244
+ }
5985
6245
  // ── Sessions (Studio introspection) ────────────────────────────────────
5986
6246
  async listSessions() {
5987
6247
  return this.client.sMembers("axl:session-ids");