@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/README.md +1 -1
- package/dist/index.cjs +273 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +59 -3
- package/dist/index.d.ts +59 -3
- package/dist/index.js +273 -13
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -505,7 +505,7 @@ const result = await ctx.ask(extractAgent, 'Extract user from this text', {
|
|
|
505
505
|
|
|
506
506
|
### State Stores
|
|
507
507
|
|
|
508
|
-
Three built-in implementations. All persist the same data: workflow execution checkpoints, `awaitHuman` decisions, session history, memory entries, and the execution state needed for suspend/resume.
|
|
508
|
+
Three built-in implementations. All persist the same data: workflow execution checkpoints, `awaitHuman` decisions, session history, memory entries, execution history, eval history, and the execution state needed for suspend/resume.
|
|
509
509
|
|
|
510
510
|
**Memory** (default) — in-process, no persistence. Use for development and stateless workflows.
|
|
511
511
|
|
package/dist/index.cjs
CHANGED
|
@@ -2666,6 +2666,11 @@ ${JSON.stringify(jsonSchema, null, 2)}`;
|
|
|
2666
2666
|
model: modelUri,
|
|
2667
2667
|
promptVersion: agent2._config.version,
|
|
2668
2668
|
cost: response.cost,
|
|
2669
|
+
tokens: response.usage ? {
|
|
2670
|
+
input: response.usage.prompt_tokens,
|
|
2671
|
+
output: response.usage.completion_tokens,
|
|
2672
|
+
reasoning: response.usage.reasoning_tokens
|
|
2673
|
+
} : void 0,
|
|
2669
2674
|
duration: Date.now() - startTime,
|
|
2670
2675
|
data: { prompt, response: response.content }
|
|
2671
2676
|
});
|
|
@@ -4053,6 +4058,8 @@ var MemoryStore = class {
|
|
|
4053
4058
|
decisions = /* @__PURE__ */ new Map();
|
|
4054
4059
|
executionStates = /* @__PURE__ */ new Map();
|
|
4055
4060
|
memories = /* @__PURE__ */ new Map();
|
|
4061
|
+
executionHistory = /* @__PURE__ */ new Map();
|
|
4062
|
+
evalHistory = /* @__PURE__ */ new Map();
|
|
4056
4063
|
constructor() {
|
|
4057
4064
|
this.loadPersistedState();
|
|
4058
4065
|
}
|
|
@@ -4149,11 +4156,36 @@ var MemoryStore = class {
|
|
|
4149
4156
|
async getAllMemory(scope) {
|
|
4150
4157
|
const scopeMap = this.memories.get(scope);
|
|
4151
4158
|
if (!scopeMap) return [];
|
|
4152
|
-
return Array.from(scopeMap.entries()).map(([key, value]) => ({
|
|
4159
|
+
return Array.from(scopeMap.entries()).map(([key, value]) => ({
|
|
4160
|
+
key,
|
|
4161
|
+
value: structuredClone(value)
|
|
4162
|
+
}));
|
|
4153
4163
|
}
|
|
4154
4164
|
async deleteMemory(scope, key) {
|
|
4155
4165
|
this.memories.get(scope)?.delete(key);
|
|
4156
4166
|
}
|
|
4167
|
+
// ── Execution History ──────────────────────────────────────────────
|
|
4168
|
+
async saveExecution(execution) {
|
|
4169
|
+
this.executionHistory.set(execution.executionId, structuredClone(execution));
|
|
4170
|
+
}
|
|
4171
|
+
async getExecution(executionId) {
|
|
4172
|
+
const exec = this.executionHistory.get(executionId);
|
|
4173
|
+
return exec ? structuredClone(exec) : null;
|
|
4174
|
+
}
|
|
4175
|
+
async listExecutions(limit) {
|
|
4176
|
+
const sorted = [...this.executionHistory.values()].sort((a, b) => b.startedAt - a.startedAt);
|
|
4177
|
+
const result = limit ? sorted.slice(0, limit) : sorted;
|
|
4178
|
+
return result.map((e) => structuredClone(e));
|
|
4179
|
+
}
|
|
4180
|
+
// ── Eval History ──────────────────────────────────────────────────
|
|
4181
|
+
async saveEvalResult(entry) {
|
|
4182
|
+
this.evalHistory.set(entry.id, structuredClone(entry));
|
|
4183
|
+
}
|
|
4184
|
+
async listEvalResults(limit) {
|
|
4185
|
+
const sorted = [...this.evalHistory.values()].sort((a, b) => b.timestamp - a.timestamp);
|
|
4186
|
+
const result = limit ? sorted.slice(0, limit) : sorted;
|
|
4187
|
+
return result.map((e) => structuredClone(e));
|
|
4188
|
+
}
|
|
4157
4189
|
// ── Sessions (Studio introspection) ─────────────────────────────────
|
|
4158
4190
|
async listSessions() {
|
|
4159
4191
|
return [...this.sessions.keys()];
|
|
@@ -4213,6 +4245,19 @@ function safeJsonParse(data) {
|
|
|
4213
4245
|
return null;
|
|
4214
4246
|
}
|
|
4215
4247
|
}
|
|
4248
|
+
function rowToExecutionInfo(row) {
|
|
4249
|
+
return {
|
|
4250
|
+
executionId: row.execution_id,
|
|
4251
|
+
workflow: row.workflow,
|
|
4252
|
+
status: row.status,
|
|
4253
|
+
totalCost: row.total_cost,
|
|
4254
|
+
startedAt: row.started_at,
|
|
4255
|
+
completedAt: row.completed_at ?? void 0,
|
|
4256
|
+
duration: row.duration,
|
|
4257
|
+
error: row.error ?? void 0,
|
|
4258
|
+
steps: safeJsonParse(row.steps) ?? []
|
|
4259
|
+
};
|
|
4260
|
+
}
|
|
4216
4261
|
var SQLiteStore = class {
|
|
4217
4262
|
db;
|
|
4218
4263
|
constructor(filePath) {
|
|
@@ -4273,6 +4318,28 @@ var SQLiteStore = class {
|
|
|
4273
4318
|
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
4274
4319
|
PRIMARY KEY (scope, key)
|
|
4275
4320
|
);
|
|
4321
|
+
|
|
4322
|
+
CREATE TABLE IF NOT EXISTS execution_history (
|
|
4323
|
+
execution_id TEXT PRIMARY KEY,
|
|
4324
|
+
workflow TEXT NOT NULL,
|
|
4325
|
+
status TEXT NOT NULL,
|
|
4326
|
+
total_cost REAL NOT NULL DEFAULT 0,
|
|
4327
|
+
started_at INTEGER NOT NULL,
|
|
4328
|
+
completed_at INTEGER,
|
|
4329
|
+
duration INTEGER NOT NULL DEFAULT 0,
|
|
4330
|
+
error TEXT,
|
|
4331
|
+
steps TEXT NOT NULL
|
|
4332
|
+
);
|
|
4333
|
+
|
|
4334
|
+
CREATE TABLE IF NOT EXISTS eval_history (
|
|
4335
|
+
id TEXT PRIMARY KEY,
|
|
4336
|
+
eval_name TEXT NOT NULL,
|
|
4337
|
+
timestamp INTEGER NOT NULL,
|
|
4338
|
+
data TEXT NOT NULL
|
|
4339
|
+
);
|
|
4340
|
+
|
|
4341
|
+
CREATE INDEX IF NOT EXISTS idx_exec_history_started ON execution_history (started_at DESC);
|
|
4342
|
+
CREATE INDEX IF NOT EXISTS idx_eval_history_timestamp ON eval_history (timestamp DESC);
|
|
4276
4343
|
`);
|
|
4277
4344
|
}
|
|
4278
4345
|
// ── Checkpoints ────────────────────────────────────────────────────────
|
|
@@ -4386,6 +4453,47 @@ var SQLiteStore = class {
|
|
|
4386
4453
|
const rows = stmt.all();
|
|
4387
4454
|
return rows.map((r) => r.execution_id);
|
|
4388
4455
|
}
|
|
4456
|
+
// ── Execution History ────────────────────────────────────────────────────
|
|
4457
|
+
async saveExecution(execution) {
|
|
4458
|
+
this.db.prepare(
|
|
4459
|
+
"INSERT OR REPLACE INTO execution_history (execution_id, workflow, status, total_cost, started_at, completed_at, duration, error, steps) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"
|
|
4460
|
+
).run(
|
|
4461
|
+
execution.executionId,
|
|
4462
|
+
execution.workflow,
|
|
4463
|
+
execution.status,
|
|
4464
|
+
execution.totalCost,
|
|
4465
|
+
execution.startedAt,
|
|
4466
|
+
execution.completedAt ?? null,
|
|
4467
|
+
execution.duration,
|
|
4468
|
+
execution.error ?? null,
|
|
4469
|
+
JSON.stringify(execution.steps)
|
|
4470
|
+
);
|
|
4471
|
+
}
|
|
4472
|
+
async getExecution(executionId) {
|
|
4473
|
+
const row = this.db.prepare("SELECT * FROM execution_history WHERE execution_id = ?").get(executionId);
|
|
4474
|
+
return row ? rowToExecutionInfo(row) : null;
|
|
4475
|
+
}
|
|
4476
|
+
async listExecutions(limit) {
|
|
4477
|
+
const sql = limit ? "SELECT * FROM execution_history ORDER BY started_at DESC LIMIT ?" : "SELECT * FROM execution_history ORDER BY started_at DESC";
|
|
4478
|
+
const rows = limit ? this.db.prepare(sql).all(limit) : this.db.prepare(sql).all();
|
|
4479
|
+
return rows.map(rowToExecutionInfo);
|
|
4480
|
+
}
|
|
4481
|
+
// ── Eval History ────────────────────────────────────────────────────────
|
|
4482
|
+
async saveEvalResult(entry) {
|
|
4483
|
+
this.db.prepare(
|
|
4484
|
+
"INSERT OR REPLACE INTO eval_history (id, eval_name, timestamp, data) VALUES (?, ?, ?, ?)"
|
|
4485
|
+
).run(entry.id, entry.eval, entry.timestamp, JSON.stringify(entry.data));
|
|
4486
|
+
}
|
|
4487
|
+
async listEvalResults(limit) {
|
|
4488
|
+
const sql = limit ? "SELECT * FROM eval_history ORDER BY timestamp DESC LIMIT ?" : "SELECT * FROM eval_history ORDER BY timestamp DESC";
|
|
4489
|
+
const rows = limit ? this.db.prepare(sql).all(limit) : this.db.prepare(sql).all();
|
|
4490
|
+
return rows.map((r) => ({
|
|
4491
|
+
id: r.id,
|
|
4492
|
+
eval: r.eval_name,
|
|
4493
|
+
timestamp: r.timestamp,
|
|
4494
|
+
data: safeJsonParse(r.data)
|
|
4495
|
+
}));
|
|
4496
|
+
}
|
|
4389
4497
|
// ── Sessions (Studio introspection) ────────────────────────────────────
|
|
4390
4498
|
async listSessions() {
|
|
4391
4499
|
const rows = this.db.prepare("SELECT session_id FROM sessions").all();
|
|
@@ -5271,6 +5379,10 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
5271
5379
|
mcpManager;
|
|
5272
5380
|
memoryManager;
|
|
5273
5381
|
spanManager = new NoopSpanManager();
|
|
5382
|
+
historicalExecutions = /* @__PURE__ */ new Map();
|
|
5383
|
+
historicalExecutionsLoadPromise = null;
|
|
5384
|
+
evalHistory = [];
|
|
5385
|
+
evalHistoryLoadPromise = null;
|
|
5274
5386
|
constructor(config) {
|
|
5275
5387
|
super();
|
|
5276
5388
|
this.config = resolveConfig(config ?? {});
|
|
@@ -5315,6 +5427,20 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
5315
5427
|
return new MemoryStore();
|
|
5316
5428
|
}
|
|
5317
5429
|
}
|
|
5430
|
+
/**
|
|
5431
|
+
* Persist a completed/failed execution to the state store (fire-and-forget)
|
|
5432
|
+
* and move it from the active executions map to the historical cache.
|
|
5433
|
+
*/
|
|
5434
|
+
persistExecution(execInfo) {
|
|
5435
|
+
const snapshot = structuredClone(execInfo);
|
|
5436
|
+
if (this.stateStore.saveExecution) {
|
|
5437
|
+
this.stateStore.saveExecution(snapshot).catch(() => {
|
|
5438
|
+
});
|
|
5439
|
+
}
|
|
5440
|
+
const id = execInfo.executionId;
|
|
5441
|
+
this.historicalExecutions.set(id, snapshot);
|
|
5442
|
+
this.executions.delete(id);
|
|
5443
|
+
}
|
|
5318
5444
|
/** Register a workflow with the runtime. */
|
|
5319
5445
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
5320
5446
|
register(workflow2) {
|
|
@@ -5393,6 +5519,7 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
5393
5519
|
async runRegisteredEval(name) {
|
|
5394
5520
|
const entry = this.registeredEvals.get(name);
|
|
5395
5521
|
if (!entry) throw new Error(`Eval "${name}" is not registered`);
|
|
5522
|
+
let result;
|
|
5396
5523
|
if (entry.executeWorkflow) {
|
|
5397
5524
|
let runEvalFn;
|
|
5398
5525
|
try {
|
|
@@ -5404,20 +5531,50 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
5404
5531
|
}
|
|
5405
5532
|
const originalExecuteFn = entry.executeWorkflow;
|
|
5406
5533
|
const wrappedExecuteFn = async (input, runtime) => {
|
|
5407
|
-
const { result, cost: trackedCost } = await this.trackCost(async () => {
|
|
5534
|
+
const { result: result2, cost: trackedCost } = await this.trackCost(async () => {
|
|
5408
5535
|
return originalExecuteFn(input, runtime);
|
|
5409
5536
|
});
|
|
5410
|
-
return { output:
|
|
5537
|
+
return { output: result2.output, cost: result2.cost ?? trackedCost };
|
|
5411
5538
|
};
|
|
5412
|
-
|
|
5539
|
+
result = await runEvalFn(entry.config, wrappedExecuteFn, void 0, this);
|
|
5540
|
+
} else {
|
|
5541
|
+
result = await this.eval(
|
|
5542
|
+
entry.config
|
|
5543
|
+
);
|
|
5413
5544
|
}
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
|
|
5545
|
+
const resultObj = result;
|
|
5546
|
+
try {
|
|
5547
|
+
await this.saveEvalResult({
|
|
5548
|
+
id: resultObj.id ?? (0, import_node_crypto2.randomUUID)(),
|
|
5549
|
+
eval: name,
|
|
5550
|
+
timestamp: Date.now(),
|
|
5551
|
+
data: structuredClone(result)
|
|
5552
|
+
});
|
|
5553
|
+
} catch {
|
|
5554
|
+
}
|
|
5555
|
+
return result;
|
|
5417
5556
|
}
|
|
5418
|
-
/** Get all execution info (running + completed). */
|
|
5419
|
-
getExecutions() {
|
|
5420
|
-
|
|
5557
|
+
/** Get all execution info (running + completed + historical). */
|
|
5558
|
+
async getExecutions() {
|
|
5559
|
+
if (!this.historicalExecutionsLoadPromise && this.stateStore.listExecutions) {
|
|
5560
|
+
this.historicalExecutionsLoadPromise = this.stateStore.listExecutions().then((stored) => {
|
|
5561
|
+
for (const exec of stored) {
|
|
5562
|
+
if (!this.executions.has(exec.executionId) && !this.historicalExecutions.has(exec.executionId)) {
|
|
5563
|
+
this.historicalExecutions.set(exec.executionId, exec);
|
|
5564
|
+
}
|
|
5565
|
+
}
|
|
5566
|
+
}).catch(() => {
|
|
5567
|
+
this.historicalExecutionsLoadPromise = null;
|
|
5568
|
+
});
|
|
5569
|
+
}
|
|
5570
|
+
if (this.historicalExecutionsLoadPromise) {
|
|
5571
|
+
await this.historicalExecutionsLoadPromise;
|
|
5572
|
+
}
|
|
5573
|
+
const merged = new Map(this.historicalExecutions);
|
|
5574
|
+
for (const [id, exec] of this.executions) {
|
|
5575
|
+
merged.set(id, exec);
|
|
5576
|
+
}
|
|
5577
|
+
return [...merged.values()].sort((a, b) => b.startedAt - a.startedAt);
|
|
5421
5578
|
}
|
|
5422
5579
|
/**
|
|
5423
5580
|
* Create a WorkflowContext for ad-hoc use (evals, tool testing, prototyping).
|
|
@@ -5549,6 +5706,7 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
5549
5706
|
}
|
|
5550
5707
|
span.setAttribute("axl.workflow.cost", execInfo.totalCost);
|
|
5551
5708
|
span.setAttribute("axl.workflow.duration", execInfo.duration);
|
|
5709
|
+
this.persistExecution(execInfo);
|
|
5552
5710
|
return output;
|
|
5553
5711
|
} catch (err) {
|
|
5554
5712
|
execInfo.status = "failed";
|
|
@@ -5556,6 +5714,7 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
5556
5714
|
execInfo.duration = execInfo.completedAt - execInfo.startedAt;
|
|
5557
5715
|
execInfo.error = err instanceof Error ? err.message : String(err);
|
|
5558
5716
|
ctx.log("workflow_end", { workflow: name, status: "failed", error: execInfo.error });
|
|
5717
|
+
this.persistExecution(execInfo);
|
|
5559
5718
|
throw err;
|
|
5560
5719
|
} finally {
|
|
5561
5720
|
this.abortControllers.delete(executionId);
|
|
@@ -5569,6 +5728,7 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
5569
5728
|
const controller = new AbortController();
|
|
5570
5729
|
axlStream.on("close", () => controller.abort());
|
|
5571
5730
|
let execInfo;
|
|
5731
|
+
let ctx;
|
|
5572
5732
|
const run = async () => {
|
|
5573
5733
|
const workflow2 = this.workflows.get(name);
|
|
5574
5734
|
if (!workflow2) throw new Error(`Workflow "${name}" not registered`);
|
|
@@ -5587,7 +5747,7 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
5587
5747
|
duration: 0
|
|
5588
5748
|
};
|
|
5589
5749
|
this.executions.set(executionId, execInfo);
|
|
5590
|
-
const
|
|
5750
|
+
const wfCtx = new WorkflowContext({
|
|
5591
5751
|
input: validated,
|
|
5592
5752
|
executionId,
|
|
5593
5753
|
metadata: options?.metadata,
|
|
@@ -5675,6 +5835,7 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
5675
5835
|
policy: "finish_and_stop"
|
|
5676
5836
|
}
|
|
5677
5837
|
});
|
|
5838
|
+
ctx = wfCtx;
|
|
5678
5839
|
return this.spanManager.withSpanAsync(
|
|
5679
5840
|
"axl.workflow.execute",
|
|
5680
5841
|
{
|
|
@@ -5684,16 +5845,23 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
5684
5845
|
},
|
|
5685
5846
|
async (span) => {
|
|
5686
5847
|
try {
|
|
5687
|
-
const rawResult = await workflow2.handler(
|
|
5848
|
+
const rawResult = await workflow2.handler(wfCtx);
|
|
5688
5849
|
const result = workflow2.outputSchema ? workflow2.outputSchema.parse(rawResult) : rawResult;
|
|
5689
5850
|
execInfo.status = "completed";
|
|
5690
5851
|
execInfo.completedAt = Date.now();
|
|
5691
5852
|
execInfo.duration = execInfo.completedAt - execInfo.startedAt;
|
|
5853
|
+
wfCtx.log("workflow_end", {
|
|
5854
|
+
workflow: name,
|
|
5855
|
+
status: "completed",
|
|
5856
|
+
duration: execInfo.duration,
|
|
5857
|
+
cost: execInfo.totalCost
|
|
5858
|
+
});
|
|
5692
5859
|
if (this.stateStore.deleteCheckpoints) {
|
|
5693
5860
|
await this.stateStore.deleteCheckpoints(executionId);
|
|
5694
5861
|
}
|
|
5695
5862
|
span.setAttribute("axl.workflow.cost", execInfo.totalCost);
|
|
5696
5863
|
span.setAttribute("axl.workflow.duration", execInfo.duration);
|
|
5864
|
+
this.persistExecution(execInfo);
|
|
5697
5865
|
return result;
|
|
5698
5866
|
} finally {
|
|
5699
5867
|
this.abortControllers.delete(executionId);
|
|
@@ -5707,6 +5875,12 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
5707
5875
|
execInfo.completedAt = Date.now();
|
|
5708
5876
|
execInfo.duration = execInfo.completedAt - execInfo.startedAt;
|
|
5709
5877
|
execInfo.error = err instanceof Error ? err.message : String(err);
|
|
5878
|
+
ctx?.log("workflow_end", {
|
|
5879
|
+
workflow: name,
|
|
5880
|
+
status: "failed",
|
|
5881
|
+
error: execInfo.error
|
|
5882
|
+
});
|
|
5883
|
+
this.persistExecution(execInfo);
|
|
5710
5884
|
}
|
|
5711
5885
|
axlStream._error(err instanceof Error ? err : new Error(String(err)));
|
|
5712
5886
|
});
|
|
@@ -5750,7 +5924,45 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
5750
5924
|
}
|
|
5751
5925
|
/** Get execution details by ID. */
|
|
5752
5926
|
async getExecution(executionId) {
|
|
5753
|
-
|
|
5927
|
+
const inMemory = this.executions.get(executionId);
|
|
5928
|
+
if (inMemory) return inMemory;
|
|
5929
|
+
const cached = this.historicalExecutions.get(executionId);
|
|
5930
|
+
if (cached) return cached;
|
|
5931
|
+
if (this.stateStore.getExecution) {
|
|
5932
|
+
const stored = await this.stateStore.getExecution(executionId);
|
|
5933
|
+
if (stored) {
|
|
5934
|
+
this.historicalExecutions.set(executionId, stored);
|
|
5935
|
+
return stored;
|
|
5936
|
+
}
|
|
5937
|
+
}
|
|
5938
|
+
return void 0;
|
|
5939
|
+
}
|
|
5940
|
+
/** Save an eval result to history. */
|
|
5941
|
+
async saveEvalResult(entry) {
|
|
5942
|
+
this.evalHistory.unshift(entry);
|
|
5943
|
+
if (this.stateStore.saveEvalResult) {
|
|
5944
|
+
await this.stateStore.saveEvalResult(entry);
|
|
5945
|
+
}
|
|
5946
|
+
}
|
|
5947
|
+
/** Get eval result history (most recent first). */
|
|
5948
|
+
async getEvalHistory() {
|
|
5949
|
+
if (!this.evalHistoryLoadPromise && this.stateStore.listEvalResults) {
|
|
5950
|
+
this.evalHistoryLoadPromise = this.stateStore.listEvalResults().then((stored) => {
|
|
5951
|
+
const ids = new Set(this.evalHistory.map((e) => e.id));
|
|
5952
|
+
for (const entry of stored) {
|
|
5953
|
+
if (!ids.has(entry.id)) {
|
|
5954
|
+
this.evalHistory.push(entry);
|
|
5955
|
+
}
|
|
5956
|
+
}
|
|
5957
|
+
this.evalHistory.sort((a, b) => b.timestamp - a.timestamp);
|
|
5958
|
+
}).catch(() => {
|
|
5959
|
+
this.evalHistoryLoadPromise = null;
|
|
5960
|
+
});
|
|
5961
|
+
}
|
|
5962
|
+
if (this.evalHistoryLoadPromise) {
|
|
5963
|
+
await this.evalHistoryLoadPromise;
|
|
5964
|
+
}
|
|
5965
|
+
return [...this.evalHistory];
|
|
5754
5966
|
}
|
|
5755
5967
|
/** List pending human decisions. */
|
|
5756
5968
|
async getPendingDecisions() {
|
|
@@ -6058,6 +6270,18 @@ var RedisStore = class _RedisStore {
|
|
|
6058
6270
|
pendingExecSetKey() {
|
|
6059
6271
|
return "axl:pending-executions";
|
|
6060
6272
|
}
|
|
6273
|
+
execHistoryKey(executionId) {
|
|
6274
|
+
return `axl:exec-history:${executionId}`;
|
|
6275
|
+
}
|
|
6276
|
+
execHistorySetKey() {
|
|
6277
|
+
return "axl:exec-history-ids";
|
|
6278
|
+
}
|
|
6279
|
+
evalHistoryKey(id) {
|
|
6280
|
+
return `axl:eval-history:${id}`;
|
|
6281
|
+
}
|
|
6282
|
+
evalHistorySetKey() {
|
|
6283
|
+
return "axl:eval-history-ids";
|
|
6284
|
+
}
|
|
6061
6285
|
// ── Checkpoints ──────────────────────────────────────────────────────
|
|
6062
6286
|
async saveCheckpoint(executionId, step, data) {
|
|
6063
6287
|
await this.client.hSet(this.checkpointKey(executionId), String(step), JSON.stringify(data));
|
|
@@ -6129,6 +6353,42 @@ var RedisStore = class _RedisStore {
|
|
|
6129
6353
|
async listPendingExecutions() {
|
|
6130
6354
|
return this.client.sMembers(this.pendingExecSetKey());
|
|
6131
6355
|
}
|
|
6356
|
+
// ── Execution History ────────────────────────────────────────────────
|
|
6357
|
+
async saveExecution(execution) {
|
|
6358
|
+
await this.client.sAdd(this.execHistorySetKey(), execution.executionId);
|
|
6359
|
+
await this.client.set(this.execHistoryKey(execution.executionId), JSON.stringify(execution));
|
|
6360
|
+
}
|
|
6361
|
+
async getExecution(executionId) {
|
|
6362
|
+
const raw = await this.client.get(this.execHistoryKey(executionId));
|
|
6363
|
+
return raw ? JSON.parse(raw) : null;
|
|
6364
|
+
}
|
|
6365
|
+
async listExecutions(limit) {
|
|
6366
|
+
const ids = await this.client.sMembers(this.execHistorySetKey());
|
|
6367
|
+
if (ids.length === 0) return [];
|
|
6368
|
+
const entries = [];
|
|
6369
|
+
for (const id of ids) {
|
|
6370
|
+
const raw = await this.client.get(this.execHistoryKey(id));
|
|
6371
|
+
if (raw) entries.push(JSON.parse(raw));
|
|
6372
|
+
}
|
|
6373
|
+
entries.sort((a, b) => b.startedAt - a.startedAt);
|
|
6374
|
+
return limit ? entries.slice(0, limit) : entries;
|
|
6375
|
+
}
|
|
6376
|
+
// ── Eval History ────────────────────────────────────────────────────
|
|
6377
|
+
async saveEvalResult(entry) {
|
|
6378
|
+
await this.client.sAdd(this.evalHistorySetKey(), entry.id);
|
|
6379
|
+
await this.client.set(this.evalHistoryKey(entry.id), JSON.stringify(entry));
|
|
6380
|
+
}
|
|
6381
|
+
async listEvalResults(limit) {
|
|
6382
|
+
const ids = await this.client.sMembers(this.evalHistorySetKey());
|
|
6383
|
+
if (ids.length === 0) return [];
|
|
6384
|
+
const entries = [];
|
|
6385
|
+
for (const id of ids) {
|
|
6386
|
+
const raw = await this.client.get(this.evalHistoryKey(id));
|
|
6387
|
+
if (raw) entries.push(JSON.parse(raw));
|
|
6388
|
+
}
|
|
6389
|
+
entries.sort((a, b) => b.timestamp - a.timestamp);
|
|
6390
|
+
return limit ? entries.slice(0, limit) : entries;
|
|
6391
|
+
}
|
|
6132
6392
|
// ── Sessions (Studio introspection) ────────────────────────────────────
|
|
6133
6393
|
async listSessions() {
|
|
6134
6394
|
return this.client.sMembers("axl:session-ids");
|