@axlsdk/axl 0.26.0 → 0.27.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.d.ts CHANGED
@@ -1887,9 +1887,11 @@ type ExecutionInfoV1 = {
1887
1887
  * (or `runtime.stream()`'s `options.metadata`). Persisted as part of the
1888
1888
  * `ExecutionInfo` snapshot so callers have a stable, queryable surface for
1889
1889
  * tags like `userId`, `tenantId`, or correlation ids without parsing the
1890
- * event timeline. Filter via `runtime.getExecutions()` + an in-process
1891
- * `.filter()`; secondary indexes on this field are intentionally not
1892
- * provided — `StateStore` is a persistence boundary, not a query engine. */
1890
+ * event timeline. Filter in process while paging with
1891
+ * `runtime.getExecutions({ limit, cursor })` (see
1892
+ * docs/migration/execution-history-retention.md); secondary indexes on this
1893
+ * field are intentionally not provided — `StateStore` is a persistence
1894
+ * boundary, not a query engine. */
1893
1895
  metadata?: Record<string, unknown>;
1894
1896
  };
1895
1897
  /** Stored execution written before explicit event schema versioning. */
@@ -3900,8 +3902,27 @@ interface StateStore {
3900
3902
  saveExecution?(execution: HistoricalExecutionInfo): Promise<void>;
3901
3903
  /** Get a specific execution by ID from history. */
3902
3904
  getExecution?(executionId: string): Promise<HistoricalExecutionInfo | null>;
3903
- /** List recent executions (most recent first). */
3904
- listExecutions?(limit?: number): Promise<HistoricalExecutionInfo[]>;
3905
+ /**
3906
+ * List recent executions, most recent first. The built-in stores order by
3907
+ * `startedAt` descending and break ties by `executionId` descending, so the
3908
+ * order is total and stable across calls.
3909
+ *
3910
+ * `options.cursor` asks for the page that starts strictly after that
3911
+ * position in the same order. The runtime passes it only when the store
3912
+ * sets {@link StateStore.historyPaging}; other stores are listed in full and
3913
+ * paged in process.
3914
+ */
3915
+ listExecutions?(limit?: number, options?: {
3916
+ cursor?: ExecutionCursor;
3917
+ }): Promise<HistoricalExecutionInfo[]>;
3918
+ /**
3919
+ * Set to `true` when `listExecutions` honors `options.cursor` with the
3920
+ * `(startedAt DESC, executionId DESC)` order. The built-in stores set it.
3921
+ * Leave it unset if your store ignores the cursor: the runtime then lists
3922
+ * everything and pages in process, which is correct but reads the whole
3923
+ * history on every page.
3924
+ */
3925
+ readonly historyPaging?: boolean;
3905
3926
  /**
3906
3927
  * Delete an execution from history by ID. Returns `true` if an entry was
3907
3928
  * deleted, `false` if the ID didn't exist. Used for GDPR right-to-be-
@@ -3963,8 +3984,10 @@ interface StateStore {
3963
3984
  * bytes on a timer and at startup. Physical deletion is therefore EVENTUAL,
3964
3985
  * not synchronous with the store's expiry.
3965
3986
  *
3966
- * Built-ins implement it: `MemoryStore` and `SqliteStore` never expire, so
3967
- * `expiresAt` is `undefined`; `RedisStore` reports existence and any live TTL.
3987
+ * Built-ins implement it. `MemoryStore` and `SQLiteStore` report the
3988
+ * absolute expiry they stored when `ttls.evalHistory` was configured at the
3989
+ * row's first write, and no `expiresAt` otherwise; `RedisStore` reports
3990
+ * existence and any live TTL. An expired row reports `{ exists: false }`.
3968
3991
  * A custom store WITHOUT this method cannot host managed artifact capture —
3969
3992
  * `AxlRuntime` rejects that configuration when it is constructed rather than
3970
3993
  * after a run has spent money.
@@ -3973,6 +3996,39 @@ interface StateStore {
3973
3996
  exists: boolean;
3974
3997
  expiresAt?: number;
3975
3998
  }>;
3999
+ /**
4000
+ * Apply the store's configured history retention to records that were
4001
+ * saved without one (for example before an upgrade, or before a retention
4002
+ * was set), and repair the store's listing indexes. Processes one batch and
4003
+ * returns the cursor to continue from; call again with that cursor until it
4004
+ * is `null`. Axl never calls this itself: run it when you choose to.
4005
+ *
4006
+ * For each record in the batch that has no expiry, and only when a
4007
+ * retention is configured for `options.kind`:
4008
+ * - a record whose window has already passed is deleted, with the same
4009
+ * per-run data a normal expiry removes;
4010
+ * - any other record gets the expiry it would have had: the retention
4011
+ * counted from its last save (`completedAt` for executions, falling back
4012
+ * to `startedAt`; `timestamp` for eval rows).
4013
+ *
4014
+ * Records that already have an expiry keep it. With no retention
4015
+ * configured for the kind (unset or `null`), nothing is expired or armed.
4016
+ *
4017
+ * The walk is oldest first, by `(startedAt, executionId)` for executions
4018
+ * and `(timestamp, id)` for eval rows, and the cursor is a position in that
4019
+ * order rather than an offset, so records removed behind it (by this call,
4020
+ * a concurrent delete or an expiry) never cause a record to be skipped.
4021
+ * Running it to completion twice changes nothing the second time.
4022
+ *
4023
+ * **Custom store implementors:** custom stores own their retention and may
4024
+ * implement this with the same contract, or leave it out. Built-ins:
4025
+ * - `MemoryStore` and `SQLiteStore` act on their rows directly; they keep
4026
+ * no separate index, so `indexed` and `deadIndexEntries` are always `0`.
4027
+ * - `RedisStore` also adds records that already carry a TTL to its expiry
4028
+ * index (`indexed`) and removes index entries whose record is gone
4029
+ * (`deadIndexEntries`).
4030
+ */
4031
+ applyHistoryRetention?(options: HistoryRetentionOptions): Promise<HistoryRetentionResult>;
3976
4032
  /** List all session IDs (used by Studio session browser). */
3977
4033
  listSessions?(): Promise<string[]>;
3978
4034
  /**
@@ -4003,6 +4059,71 @@ interface StateStore {
4003
4059
  close?(): Promise<void>;
4004
4060
  deleteCheckpoints?(executionId: string): Promise<void>;
4005
4061
  }
4062
+ /**
4063
+ * Position in execution history, used to fetch the next page. Pages are
4064
+ * ordered by `startedAt` descending, then `executionId` descending; a cursor
4065
+ * built from the last item of one page returns the items strictly after it.
4066
+ */
4067
+ type ExecutionCursor = {
4068
+ startedAt: number;
4069
+ executionId: string;
4070
+ };
4071
+ /**
4072
+ * History retention, in **seconds**, shared by the built-in stores
4073
+ * (`MemoryStore`, `SQLiteStore`, and the matching categories of
4074
+ * `RedisStore`'s `ttls`).
4075
+ *
4076
+ * - A positive number expires the record that many seconds after it is saved.
4077
+ * - `null` or unset keeps it forever.
4078
+ *
4079
+ * Windows differ per category:
4080
+ * - `executionHistory` counts from the record's **last** save; saving the same
4081
+ * execution again restarts the window.
4082
+ * - `evalHistory` counts from the row's **first** write. Saving the same eval
4083
+ * id again keeps the expiry it already had, and `updateEvalResult` never
4084
+ * changes it.
4085
+ *
4086
+ * Retention applies to records saved while it is configured; rows written
4087
+ * before keep whatever expiry they had until you run
4088
+ * {@link StateStore.applyHistoryRetention}. Custom stores own their retention.
4089
+ */
4090
+ type HistoryTtls = {
4091
+ executionHistory?: number | null;
4092
+ evalHistory?: number | null;
4093
+ };
4094
+ /** Which history {@link StateStore.applyHistoryRetention} processes. */
4095
+ type HistoryRetentionKind = 'executions' | 'evals';
4096
+ /**
4097
+ * Where {@link StateStore.applyHistoryRetention} continues from: the last
4098
+ * record of the previous batch, oldest-first. `time` is `startedAt` for
4099
+ * executions and `timestamp` for eval rows. Pass it back unchanged; it is
4100
+ * plain JSON, so it can be stored between runs.
4101
+ */
4102
+ type HistoryRetentionCursor = {
4103
+ time: number;
4104
+ id: string;
4105
+ };
4106
+ /** Options for {@link StateStore.applyHistoryRetention}. */
4107
+ type HistoryRetentionOptions = {
4108
+ kind: HistoryRetentionKind;
4109
+ /** Records examined per call. A positive integer; defaults to 500. */
4110
+ batchSize?: number;
4111
+ /** The `cursor` returned by the previous call. Omit or pass `null` to start. */
4112
+ cursor?: HistoryRetentionCursor | null;
4113
+ };
4114
+ /** What one {@link StateStore.applyHistoryRetention} call did. */
4115
+ type HistoryRetentionResult = {
4116
+ /** Records with no expiry whose window had already passed, now deleted. */
4117
+ expired: number;
4118
+ /** Records with no expiry that now have one. */
4119
+ armed: number;
4120
+ /** Records that already had an expiry, newly added to the store's expiry index (Redis). */
4121
+ indexed: number;
4122
+ /** Index entries whose record no longer exists, now removed (Redis). */
4123
+ deadIndexEntries: number;
4124
+ /** Pass to the next call, or `null` when the walk is finished. */
4125
+ cursor: HistoryRetentionCursor | null;
4126
+ };
4006
4127
  /** Persisted eval history entry. */
4007
4128
  type EvalHistoryEntry = {
4008
4129
  id: string;
@@ -4187,8 +4308,21 @@ type TraceConfig = {
4187
4308
  /** State store configuration */
4188
4309
  type StateConfig = {
4189
4310
  store?: StateStore | 'memory' | 'sqlite';
4311
+ /**
4312
+ * Options for the store built by `store: 'sqlite'`: the database file and
4313
+ * optional history retention (seconds; see `HistoryTtls`).
4314
+ */
4190
4315
  sqlite?: {
4191
4316
  path: string;
4317
+ ttls?: HistoryTtls;
4318
+ };
4319
+ /**
4320
+ * Options for the store built by `store: 'memory'` (the default): history
4321
+ * retention in seconds and a maximum number of execution records.
4322
+ */
4323
+ memory?: {
4324
+ ttls?: HistoryTtls;
4325
+ maxExecutions?: number;
4192
4326
  };
4193
4327
  /**
4194
4328
  * Maximum number of events retained in `ExecutionInfo.events` per
@@ -4212,6 +4346,21 @@ type StateConfig = {
4212
4346
  * integer or `Infinity`.
4213
4347
  */
4214
4348
  maxEventsPerExecution?: number;
4349
+ /**
4350
+ * How many recently finished runs the runtime keeps in process when the
4351
+ * state store cannot keep execution history itself, that is, when it lacks
4352
+ * `saveExecution` or `listExecutions`. The cache stands in for the missing
4353
+ * methods: `getExecutions()` lists from it, and `getExecution(id)` falls
4354
+ * back to it when the store cannot fetch by id either. The least recently
4355
+ * used run is dropped first. (A store that saves and lists but has no
4356
+ * `getExecution` needs no cache: by-id reads scan its listing.)
4357
+ *
4358
+ * The built-in stores have all three methods, so the runtime never keeps
4359
+ * this cache for them: history is read through the store on every call.
4360
+ *
4361
+ * Default: 1_000. `0` turns the cache off. Must be a non-negative integer.
4362
+ */
4363
+ recentExecutionsCacheSize?: number;
4215
4364
  /**
4216
4365
  * When to persist execution events to the state store.
4217
4366
  *
@@ -4275,8 +4424,11 @@ type DiagnosticsConfig = {
4275
4424
  schemaOversizedTokens?: number;
4276
4425
  /**
4277
4426
  * Silence the one-time `console.warn` diagnostics (the structured
4278
- * `schema_diagnostic` events still fire). Also silenceable process-wide via
4279
- * `AXL_DIAGNOSTICS_SILENT=true`.
4427
+ * `schema_diagnostic` events still fire). This includes the large-listing
4428
+ * warning and the retention warning of a SQLite store the runtime creates
4429
+ * from `state.store: 'sqlite'`; a store you construct yourself warns before
4430
+ * the runtime exists, so use its `ttls` or the environment variable. Also
4431
+ * silenceable process-wide via `AXL_DIAGNOSTICS_SILENT=true`.
4280
4432
  */
4281
4433
  silent?: boolean;
4282
4434
  /**
@@ -4738,10 +4890,7 @@ type WorkflowContextInit = {
4738
4890
  * fn callbacks). Composed name: `__auto/<primitive>/<n>`.
4739
4891
  *
4740
4892
  * Root contexts default to a fresh `{ byAgent: new Map(), root: 0 }`. */
4741
- autoCheckpointCounters?: {
4742
- byAgent: Map<string, number>;
4743
- root: number;
4744
- };
4893
+ autoCheckpointCounters?: AutoCheckpointState;
4745
4894
  /** Internal: root-shared state for branch continuations that must settle
4746
4895
  * before the runtime emits the workflow terminal and snapshots cost. */
4747
4896
  branchDrainState?: BranchDrainState;
@@ -4771,6 +4920,13 @@ type PendingDecisionResolver = {
4771
4920
  * deleteExecution joins this barrier before its total store sweep. */
4772
4921
  cleanupFinished: Promise<void>;
4773
4922
  };
4923
+ /** Automatic checkpoint bookkeeping, shared by an execution's child contexts. */
4924
+ type AutoCheckpointState = {
4925
+ byAgent: Map<string, number>;
4926
+ root: number;
4927
+ /** An automatic checkpoint failed to save, so none are saved from here on. */
4928
+ saveFailed?: boolean;
4929
+ };
4774
4930
  /**
4775
4931
  * The central coordination object for all Axl primitives.
4776
4932
  * Carries execution state, tracing, budget tracking, and session history.
@@ -6124,8 +6280,6 @@ declare class AxlRuntime extends EventEmitter {
6124
6280
  private mcpManager?;
6125
6281
  private memoryManager?;
6126
6282
  private spanManager;
6127
- private historicalExecutions;
6128
- private historicalExecutionsLoadPromise;
6129
6283
  /** Per-session in-process serialization. Each entry is a chain of pending
6130
6284
  * work for that session id — `Session.send` / `Session.stream` await the
6131
6285
  * prior entry before reading history, so concurrent calls produce a
@@ -6161,12 +6315,34 @@ declare class AxlRuntime extends EventEmitter {
6161
6315
  * a delete from resurrecting the (intentionally-removed) row. Entries are
6162
6316
  * removed once the would-be persist has been short-circuited. */
6163
6317
  private pendingDeletedExecutions;
6164
- /** In-flight `persistExecution` chains (save → finalize). `shutdown()`
6165
- * awaits these before closing the StateStore so a workflow whose abort
6318
+ /** In-flight `persistExecution` chains (save → finalize) and crash-
6319
+ * recovery saves. `shutdown()` awaits these before closing the StateStore so a workflow whose abort
6166
6320
  * triggers a persist-during-shutdown isn't racing a closed connection.
6167
6321
  * The Set is mutated by the chain's own `.finally` so entries clear as
6168
6322
  * soon as the work settles. */
6169
6323
  private persistInflight;
6324
+ /** Finished runs whose history save has not settled, keyed by execution
6325
+ * id. Reads serve the snapshot until the save lands, so a run is visible
6326
+ * the moment it finishes (read-your-writes). `deleteExecution()` awaits
6327
+ * the entry before deleting from the store, so a save that lands late
6328
+ * cannot re-create a deleted run. Each entry removes itself when its save
6329
+ * settles (success or failure), but only if it is still the current entry
6330
+ * for that id, so the map holds only in-flight saves. */
6331
+ private pendingSaves;
6332
+ /** Recently finished runs, kept only when the store cannot save, list, or
6333
+ * fetch execution history itself (and the cache size is not `0`).
6334
+ * `undefined` for every built-in store: execution history is read through
6335
+ * the store on every call and never cached here. */
6336
+ private readonly recentRuns;
6337
+ /** Execution ids `recoverIncompleteStreams()` is working on, each with a
6338
+ * promise that settles when recovery is done with that id.
6339
+ * `deleteExecution()` awaits it, so a recovered record cannot land after
6340
+ * the delete. */
6341
+ private recoveringExecutions;
6342
+ /** Execution ids with a `deleteExecution()` in progress, with a count of
6343
+ * concurrent deletes. Recovery skips them: their store sweep removes the
6344
+ * streaming buffer it would otherwise turn back into a record. */
6345
+ private deletingExecutions;
6170
6346
  /** Resolved diagnostic artifact backend, when `diagnostics.artifacts` is set. */
6171
6347
  private artifactStore?;
6172
6348
  private artifactLeaseMs;
@@ -6400,13 +6576,15 @@ declare class AxlRuntime extends EventEmitter {
6400
6576
  private runWorkflowBody;
6401
6577
  /**
6402
6578
  * Persist a completed/failed execution to the state store (fire-and-forget)
6403
- * and move it from the active executions map to the historical cache.
6579
+ * and remove it from the active executions map. Until the save settles the
6580
+ * run is served from `pendingSaves`; afterwards from the store (or, for a
6581
+ * store that cannot keep history, from `recentRuns`).
6404
6582
  *
6405
6583
  * Resurrection guard: if `runtime.deleteExecution(id)` was called while
6406
6584
  * this execution was still running, the id will be in
6407
- * `pendingDeletedExecutions`. In that case we skip the save AND the
6408
- * historical-cache write, and finalize the streaming buffer (if any) so
6409
- * the buffer doesn't outlive the delete.
6585
+ * `pendingDeletedExecutions`. In that case we skip the save AND every
6586
+ * in-process record of the run, and finalize the streaming buffer (if any)
6587
+ * so the buffer doesn't outlive the delete.
6410
6588
  */
6411
6589
  private persistExecution;
6412
6590
  /** Register a workflow with the runtime. */
@@ -6474,7 +6652,6 @@ declare class AxlRuntime extends EventEmitter {
6474
6652
  maxQueueBytes?: number;
6475
6653
  };
6476
6654
  }): Promise<unknown>;
6477
- /** Get all execution info (running + completed + historical). */
6478
6655
  /** Normalize an execution loaded from a `StateStore` so the
6479
6656
  * historical event union holds for downstream consumers
6480
6657
  * (Studio aggregators, REST routes, redaction). Custom or legacy stores
@@ -6483,7 +6660,37 @@ declare class AxlRuntime extends EventEmitter {
6483
6660
  * `exec.events` and takes down dependent features. Returns a normalized
6484
6661
  * copy; logs at most one warning per `executionId`. */
6485
6662
  private normalizeStoredExecution;
6486
- getExecutions(): Promise<HistoricalExecutionInfo[]>;
6663
+ /**
6664
+ * List executions, newest first: running runs, runs whose save is still in
6665
+ * flight, and the history the state store holds. Ordered by `startedAt`,
6666
+ * then `executionId`, both descending.
6667
+ *
6668
+ * With no options, returns every retained run. Pass `limit` for one page,
6669
+ * and `cursor` (built from the last item of the previous page) for the
6670
+ * page after it. Each call reads through the store, so runs finished or
6671
+ * deleted by other runtimes sharing the store are reflected immediately.
6672
+ * A call without `limit` that returns more than 1 000 runs warns once per
6673
+ * process (silenced by `diagnostics.silent`); pass `limit: Infinity` to ask
6674
+ * for everything without the warning.
6675
+ *
6676
+ * @throws {AxlError} `INVALID_ARGUMENT` for a `limit` that is not a positive
6677
+ * integer (`Infinity` means no limit) or a malformed `cursor`.
6678
+ */
6679
+ getExecutions(options?: {
6680
+ limit?: number;
6681
+ cursor?: ExecutionCursor;
6682
+ }): Promise<HistoricalExecutionInfo[]>;
6683
+ /**
6684
+ * Read one page of execution history from the store: the records strictly
6685
+ * after `cursor`, newest first, at least `limit + overlap` of them when the
6686
+ * store holds that many (all of them when `limit` is unset).
6687
+ *
6688
+ * Only a store that declares `historyPaging` is handed the cursor. Any
6689
+ * other store is listed in full and paged here: filtering the output of a
6690
+ * store that ignored the cursor would return its first page again, and an
6691
+ * empty page after that.
6692
+ */
6693
+ private listStoredExecutions;
6487
6694
  /**
6488
6695
  * Create a WorkflowContext for ad-hoc use (evals, tool testing, prototyping).
6489
6696
  * The context has access to the runtime's providers, state store, MCP manager,
@@ -6546,35 +6753,70 @@ declare class AxlRuntime extends EventEmitter {
6546
6753
  * Recover executions whose process died mid-run when `state.persist:
6547
6754
  * 'streaming'` was configured. Synthesizes a partial `ExecutionInfo`
6548
6755
  * from the streaming buffer for each — `status: 'failed'`, error
6549
- * `'process terminated'` — registers them in the historical cache so
6550
- * they show up in `getExecutions()`, persists them via the store's
6551
- * `saveExecution`, and finalizes the streaming buffer.
6756
+ * `'process terminated'` — persists them via the store's
6757
+ * `saveExecution` (so they show up in `getExecutions()`), and finalizes
6758
+ * the streaming buffer.
6552
6759
  *
6553
6760
  * Idempotent — if the streaming buffer is already gone (or
6554
6761
  * `listStreamingExecutions` is empty), this is a no-op. Returns the
6555
6762
  * list of recovered `ExecutionInfo`s for caller introspection.
6556
6763
  *
6557
- * Wire this into your process startup AFTER `runtime.getExecutions()`
6558
- * has lazy-loaded the historical cache. Order matters: completed
6559
- * executions already in the store should not be overwritten by a
6560
- * stale streaming buffer that the previous process didn't get to
6561
- * finalize. The implementation checks the historical store before
6562
- * synthesizing, so a re-run is safe.
6764
+ * A run whose final record already exists is never recovered: for each
6765
+ * buffered id this asks the store's `getExecution(id)` and, when a record
6766
+ * is there, only drops the leftover buffer. It does not list history, so
6767
+ * its cost does not grow with the size of the store. A store without
6768
+ * `getExecution` cannot be checked, and every buffered run is recovered.
6769
+ *
6770
+ * Wire this into process startup, before the process accepts new work:
6771
+ * a run live in another process looks the same as a crashed one.
6772
+ * `deleteExecution(id)` called while recovery is working on that id waits
6773
+ * for recovery to finish with it, so the recovered record cannot outlive
6774
+ * the delete.
6563
6775
  */
6564
6776
  recoverIncompleteStreams(): Promise<HistoricalExecutionInfo[]>;
6777
+ /** Recover one buffered execution. Returns the synthesized record, or
6778
+ * `undefined` when there was nothing to recover. */
6779
+ private recoverStreamingExecution;
6565
6780
  /** Abort a running execution by its ID. */
6566
6781
  abort(executionId: string): void;
6567
- /** Get execution details by ID. */
6782
+ /**
6783
+ * Get execution details by ID. Looks in order at running runs, runs whose
6784
+ * save is still in flight, the state store (its `getExecution`, or a scan
6785
+ * of its listing when it has `listExecutions` but no `getExecution`), and
6786
+ * (only for a store that cannot list or save history) the recent-runs
6787
+ * cache. Nothing read from the store is cached, so a run deleted or
6788
+ * expired elsewhere is gone on the next call, and every run
6789
+ * `getExecutions()` lists can be fetched here.
6790
+ */
6568
6791
  getExecution(executionId: string): Promise<HistoricalExecutionInfo | undefined>;
6792
+ /**
6793
+ * By-id read for a store that lists but cannot fetch by id. A paging store
6794
+ * is scanned page by page with the cursor, stopping at the match; any
6795
+ * other store is listed once in full. Returns `null` when the store cannot
6796
+ * list or does not hold the run.
6797
+ */
6798
+ private findInStoreListing;
6799
+ /** The recent-runs cache stands in for listing when the store cannot list
6800
+ * runs, or never receives them because it cannot save. */
6801
+ private recentRunsServeListing;
6802
+ /** The recent-runs cache stands in for by-id reads when the store cannot
6803
+ * fetch or list runs (it exists only then), or never receives them because
6804
+ * it cannot save. */
6805
+ private recentRunsServeReads;
6569
6806
  /** Save an eval result to history. */
6570
6807
  saveEvalResult(entry: EvalHistoryEntry): Promise<void>;
6571
6808
  /**
6572
6809
  * Delete an execution from history by id. Removes from the active map (if
6573
- * still running), the historical cache, the configured StateStore, AND
6810
+ * still running), the recent-runs cache, the configured StateStore, AND
6574
6811
  * any execution-scoped side state — pending awaitHuman decisions,
6575
6812
  * resolver maps, streaming buffer registration, in-flight abort controller.
6576
- * Returns true if an entry was actually removed from any of the three
6577
- * primary surfaces (active map, historical cache, store).
6813
+ * Returns true if the run was running, had a save in flight, or was
6814
+ * removed from the recent-runs cache or the store.
6815
+ *
6816
+ * If the run has already finished but its history save has not landed
6817
+ * yet, this method waits for that save (whether it succeeds or fails)
6818
+ * before deleting, so the save cannot re-create the deleted record. It
6819
+ * likewise waits for `recoverIncompleteStreams()` to finish with the id.
6578
6820
  *
6579
6821
  * In-flight handling: if the execution is still running, this method
6580
6822
  * **aborts** it (via the registered abort controller) AND marks the id for
@@ -6599,6 +6841,7 @@ declare class AxlRuntime extends EventEmitter {
6599
6841
  * (`defaultTtl` / `ttls.executionHistory`).
6600
6842
  */
6601
6843
  deleteExecution(id: string): Promise<boolean>;
6844
+ private deleteExecutionWhileMarked;
6602
6845
  /**
6603
6846
  * Delete an eval history entry by id. Removes from in-memory cache and
6604
6847
  * the configured StateStore. Returns true if an entry was actually removed.
@@ -9476,6 +9719,24 @@ declare class OpenRouterTranscriptionProvider implements TranscriptionProvider {
9476
9719
  private resolveKey;
9477
9720
  }
9478
9721
 
9722
+ /** Options for {@link MemoryStore}. */
9723
+ type MemoryStoreOptions = {
9724
+ /**
9725
+ * History retention in seconds. A positive number expires a record that
9726
+ * many seconds after it is saved; `null` or unset keeps it. See
9727
+ * {@link HistoryTtls} for how each category's window is counted.
9728
+ */
9729
+ ttls?: HistoryTtls;
9730
+ /**
9731
+ * Maximum number of execution-history records kept. When a save goes over
9732
+ * the limit, the oldest records in listing order (earliest `startedAt`,
9733
+ * then lowest `executionId`) are removed. A positive integer or `Infinity`
9734
+ * (no limit). Default: 1 000.
9735
+ */
9736
+ maxExecutions?: number;
9737
+ /** Clock in epoch milliseconds, used for retention. Defaults to `Date.now`. */
9738
+ now?: () => number;
9739
+ };
9479
9740
  /**
9480
9741
  * In-memory implementation of StateStore.
9481
9742
  * Fast for development and testing, but lost on process restart.
@@ -9484,6 +9745,11 @@ declare class OpenRouterTranscriptionProvider implements TranscriptionProvider {
9484
9745
  * status "waiting") is persisted to a temporary file so it survives restarts.
9485
9746
  * This is the one exception to "memory means ephemeral" — because a human
9486
9747
  * could take hours to respond, and losing that state is unacceptable.
9748
+ *
9749
+ * History can be bounded by time (`ttls`) and by count (`maxExecutions`,
9750
+ * which keeps the newest 1 000 runs by default).
9751
+ * Expired records are never returned and are physically removed, a bounded
9752
+ * batch at a time, on later saves of the same kind.
9487
9753
  */
9488
9754
  declare class MemoryStore implements StateStore {
9489
9755
  private checkpoints;
@@ -9492,9 +9758,23 @@ declare class MemoryStore implements StateStore {
9492
9758
  private decisions;
9493
9759
  private executionStates;
9494
9760
  private memories;
9761
+ /**
9762
+ * Iteration order is last-save order: a save deletes and re-inserts its key.
9763
+ * With one retention per store and a clock that does not run backwards, that
9764
+ * is also expiry order, which lets the prune stop at the first live record.
9765
+ */
9495
9766
  private executionHistory;
9767
+ /** Every key in `executionHistory`, sorted oldest first in listing order. */
9768
+ private executionOrder;
9769
+ /**
9770
+ * Iteration order is first-write order (a re-save keeps the key's position
9771
+ * and its expiry), which is also expiry order for the same reason as above.
9772
+ */
9496
9773
  private evalHistory;
9497
- constructor();
9774
+ private readonly ttls;
9775
+ private readonly maxExecutions;
9776
+ private readonly now;
9777
+ constructor(options?: MemoryStoreOptions);
9498
9778
  saveCheckpoint(executionId: string, name: string, data: unknown): Promise<void>;
9499
9779
  getCheckpoint(executionId: string, name: string): Promise<unknown | null>;
9500
9780
  saveSession(sessionId: string, history: ChatMessage[]): Promise<void>;
@@ -9517,18 +9797,64 @@ declare class MemoryStore implements StateStore {
9517
9797
  deleteMemory(scope: string, key: string): Promise<void>;
9518
9798
  saveExecution(execution: HistoricalExecutionInfo): Promise<void>;
9519
9799
  getExecution(executionId: string): Promise<HistoricalExecutionInfo | null>;
9520
- listExecutions(limit?: number): Promise<HistoricalExecutionInfo[]>;
9800
+ /** Supports `listExecutions(limit, { cursor })`. */
9801
+ readonly historyPaging = true;
9802
+ listExecutions(limit?: number, options?: {
9803
+ cursor?: ExecutionCursor;
9804
+ }): Promise<HistoricalExecutionInfo[]>;
9521
9805
  deleteExecution(executionId: string): Promise<boolean>;
9806
+ /**
9807
+ * Write an eval history row. Retention counts from the row's first write:
9808
+ * re-saving a live row keeps its expiry (and its position in expiry order).
9809
+ * A row that has already expired is replaced as a new one.
9810
+ */
9522
9811
  saveEvalResult(entry: EvalHistoryEntry): Promise<void>;
9523
- /** Update-only write: see `StateStore.updateEvalResult`. */
9812
+ /** Update-only write: see `StateStore.updateEvalResult`. Never changes retention. */
9524
9813
  updateEvalResult(entry: EvalHistoryEntry): Promise<boolean>;
9525
9814
  listEvalResults(limit?: number): Promise<EvalHistoryEntry[]>;
9526
9815
  deleteEvalResult(id: string): Promise<boolean>;
9527
- /** In-memory history never expires on its own — only existence is meaningful. */
9816
+ /** Existence plus the expiry stored at the row's first write, if any. */
9528
9817
  getEvalRetention(id: string): Promise<{
9529
9818
  exists: boolean;
9530
9819
  expiresAt?: number;
9531
9820
  }>;
9821
+ /**
9822
+ * See `StateStore.applyHistoryRetention`. Every record this store holds was
9823
+ * saved under the retention it was constructed with, so in practice a run
9824
+ * finds nothing to expire or arm; the method exists so the same maintenance
9825
+ * code works against every built-in store. There is no separate index, so
9826
+ * `indexed` and `deadIndexEntries` are always `0`.
9827
+ *
9828
+ * An armed record keeps its place in last-save order, which is also where
9829
+ * its expiry (last save + retention) belongs, so the prune-on-save order
9830
+ * still holds.
9831
+ */
9832
+ applyHistoryRetention(options: HistoryRetentionOptions): Promise<HistoryRetentionResult>;
9833
+ /** Index of the first order key not older than `key` (binary search). */
9834
+ private executionOrderLowerBound;
9835
+ private insertExecutionOrder;
9836
+ /** Remove a history record and its listing-index key. Returns whether one existed. */
9837
+ private removeExecutionRecord;
9838
+ /**
9839
+ * A run leaving history through retention (expiry or `maxExecutions`) takes
9840
+ * its leftover per-run data with it: checkpoints (kept today when a run
9841
+ * fails), saved execution state and any streaming buffer. Pending decisions
9842
+ * are not touched. The runtime never writes execution state, and a run is
9843
+ * only saved to history once it has finished, so nothing still running
9844
+ * depends on what is removed here.
9845
+ */
9846
+ private expireExecutionRecord;
9847
+ /**
9848
+ * Remove up to `PRUNE_BATCH` expired execution records, oldest save first.
9849
+ * Stops at the first live record: Map order is last-save order, which is
9850
+ * expiry order while the clock moves forward. If the clock steps back, a
9851
+ * few expired records wait for a later save; reads still filter them.
9852
+ */
9853
+ private pruneExpiredExecutions;
9854
+ /** Evict the oldest records in listing order until the count bound holds. */
9855
+ private evictOverflowExecutions;
9856
+ /** Remove up to `PRUNE_BATCH` expired eval rows, oldest first write first. */
9857
+ private pruneExpiredEvals;
9532
9858
  listSessions(): Promise<string[]>;
9533
9859
  private streamingEvents;
9534
9860
  appendStreamingEvents(executionId: string, events: AxlEvent[]): Promise<void>;
@@ -9541,6 +9867,19 @@ declare class MemoryStore implements StateStore {
9541
9867
  private loadPersistedState;
9542
9868
  }
9543
9869
 
9870
+ /** Options for {@link SQLiteStore}. */
9871
+ type SQLiteStoreOptions = {
9872
+ /**
9873
+ * History retention in seconds. A positive number expires a record that
9874
+ * many seconds after it is saved; `null` or unset keeps it. See
9875
+ * {@link HistoryTtls} for how each category's window is counted. With
9876
+ * `executionHistory` unset, the store warns once per process; set it (to
9877
+ * `null` to keep history forever) to silence the warning.
9878
+ */
9879
+ ttls?: HistoryTtls;
9880
+ /** Clock in epoch milliseconds, used for retention. Defaults to `Date.now`. */
9881
+ now?: () => number;
9882
+ };
9544
9883
  /**
9545
9884
  * SQLite-backed StateStore using better-sqlite3.
9546
9885
  *
@@ -9552,7 +9891,9 @@ declare class MemoryStore implements StateStore {
9552
9891
  */
9553
9892
  declare class SQLiteStore implements StateStore {
9554
9893
  private db;
9555
- constructor(filePath: string);
9894
+ private readonly ttls;
9895
+ private readonly now;
9896
+ constructor(filePath: string, options?: SQLiteStoreOptions);
9556
9897
  /**
9557
9898
  * Idempotent schema migration. Tracks version via `PRAGMA user_version`
9558
9899
  * so reopens skip applied steps. Wrapped in `BEGIN IMMEDIATE` so
@@ -9579,6 +9920,11 @@ declare class SQLiteStore implements StateStore {
9579
9920
  * Existing rows remain `NULL`, the documented v1 sentinel.
9580
9921
  * - v5 (observation completeness): add `execution_history.observation`.
9581
9922
  * Existing rows remain `NULL`, meaning they predate the contract.
9923
+ * - v6 (history retention): add nullable `expires_at` (epoch ms) to
9924
+ * `execution_history` and `eval_history`. Existing rows remain `NULL`
9925
+ * (never expire), so an upgrade deletes nothing. The single-column
9926
+ * `started_at` index is replaced by `(started_at, execution_id)`, which
9927
+ * serves the tie-safe listing order.
9582
9928
  *
9583
9929
  * Applied BEFORE `initTables()` so the subsequent `CREATE TABLE
9584
9930
  * IF NOT EXISTS` runs against the post-migration column name. Fresh
@@ -9611,24 +9957,52 @@ declare class SQLiteStore implements StateStore {
9611
9957
  getExecutionState(executionId: string): Promise<ExecutionState | null>;
9612
9958
  listPendingExecutions(): Promise<string[]>;
9613
9959
  saveExecution(execution: HistoricalExecutionInfo): Promise<void>;
9960
+ /**
9961
+ * Remove up to `PRUNE_BATCH` expired execution-history rows, together with
9962
+ * each run's leftover checkpoints and saved execution state, inside the
9963
+ * caller's transaction. Pending decisions are not touched. The runtime
9964
+ * never writes execution state, and a run is only saved to history once it
9965
+ * has finished, so nothing still running depends on what is removed here.
9966
+ */
9967
+ private pruneExpiredExecutions;
9968
+ /** Remove up to `PRUNE_BATCH` expired eval-history rows (caller's transaction). */
9969
+ private pruneExpiredEvals;
9614
9970
  getExecution(executionId: string): Promise<HistoricalExecutionInfo | null>;
9615
- listExecutions(limit?: number): Promise<HistoricalExecutionInfo[]>;
9971
+ /** Supports `listExecutions(limit, { cursor })`. */
9972
+ readonly historyPaging = true;
9973
+ listExecutions(limit?: number, options?: {
9974
+ cursor?: ExecutionCursor;
9975
+ }): Promise<HistoricalExecutionInfo[]>;
9616
9976
  deleteExecution(executionId: string): Promise<boolean>;
9977
+ /**
9978
+ * Write an eval history row. Retention counts from the row's first write:
9979
+ * re-saving a live row keeps its `expires_at` (including none, for a row
9980
+ * written without retention). A row that has already expired is replaced
9981
+ * as a new one.
9982
+ */
9617
9983
  saveEvalResult(entry: EvalHistoryEntry): Promise<void>;
9618
9984
  /**
9619
9985
  * Update-only write: see `StateStore.updateEvalResult`. `UPDATE ... WHERE id`
9620
- * touches nothing when the row is gone, where `INSERT OR REPLACE` would
9621
- * resurrect it — and SQLite history has no expiry, so a resurrection here is
9622
- * permanent.
9986
+ * touches nothing when the row is gone or expired, where `INSERT OR REPLACE`
9987
+ * would resurrect it. `expires_at` is left as it was.
9623
9988
  */
9624
9989
  updateEvalResult(entry: EvalHistoryEntry): Promise<boolean>;
9625
9990
  listEvalResults(limit?: number): Promise<EvalHistoryEntry[]>;
9626
9991
  deleteEvalResult(id: string): Promise<boolean>;
9627
- /** SQLite history has no automatic expiry — only existence is meaningful. */
9992
+ /** Existence plus the expiry stored at the row's first write, if any. */
9628
9993
  getEvalRetention(id: string): Promise<{
9629
9994
  exists: boolean;
9630
9995
  expiresAt?: number;
9631
9996
  }>;
9997
+ /**
9998
+ * See `StateStore.applyHistoryRetention`. One batch runs in one immediate
9999
+ * transaction, so no other writer can change a row between this call
10000
+ * reading it and acting on it. Only rows whose `expires_at` is `NULL` are
10001
+ * touched. An expired execution takes its checkpoints and saved execution
10002
+ * state with it, as a normal expiry does. There is no separate index, so
10003
+ * `indexed` and `deadIndexEntries` are always `0`.
10004
+ */
10005
+ applyHistoryRetention(options: HistoryRetentionOptions): Promise<HistoryRetentionResult>;
9632
10006
  listSessions(): Promise<string[]>;
9633
10007
  saveMemory(scope: string, key: string, value: unknown): Promise<void>;
9634
10008
  getMemory(scope: string, key: string): Promise<unknown | null>;
@@ -9642,6 +10016,20 @@ declare class SQLiteStore implements StateStore {
9642
10016
  deleteCheckpoints(executionId: string): Promise<void>;
9643
10017
  }
9644
10018
 
10019
+ /**
10020
+ * A change in a {@link RedisStore}'s connection, passed to
10021
+ * {@link RedisStoreOptions.onConnectionEvent}.
10022
+ *
10023
+ * - `error`: the connection was lost or a reconnect attempt failed. Arrives
10024
+ * once per attempt while Redis is down.
10025
+ * - `reconnected`: the store is connected again and commands work.
10026
+ */
10027
+ type RedisConnectionEvent = {
10028
+ type: 'error';
10029
+ error: Error;
10030
+ } | {
10031
+ type: 'reconnected';
10032
+ };
9645
10033
  /** Options accepted by {@link RedisStore.create}. */
9646
10034
  interface RedisStoreOptions {
9647
10035
  /** Redis connection URL (e.g. `redis://localhost:6379`). Defaults to `redis://localhost:6379`. */
@@ -9666,6 +10054,16 @@ interface RedisStoreOptions {
9666
10054
  * operator experience.
9667
10055
  */
9668
10056
  keyPrefix?: string;
10057
+ /**
10058
+ * Called when the connection is lost, a reconnect attempt fails, or the
10059
+ * connection comes back, for routing to a logger, metrics, or a health check.
10060
+ * Replaces the default logging of one line per outage and one on recovery.
10061
+ *
10062
+ * Not called for a failed `create()`, whose rejection reports the failure,
10063
+ * or after `close()`. A callback that throws or rejects is reported with
10064
+ * `console.error` and otherwise ignored.
10065
+ */
10066
+ onConnectionEvent?: (event: RedisConnectionEvent) => void;
9669
10067
  /**
9670
10068
  * Skip the lazy backfill of legacy execution / eval-history entries into
9671
10069
  * the sorted-set indexes during `RedisStore.create()`. Default `false`
@@ -9720,11 +10118,19 @@ interface RedisStoreOptions {
9720
10118
  * new ones) eventually ages out. If you need read-activity to count
9721
10119
  * as "active," issue a no-op `ctx.remember` against a sentinel key
9722
10120
  * per session turn.
9723
- * - **Fixed window**: `checkpoint`, `executionState`, `executionHistory`,
9724
- * `evalHistory`. The TTL is set on the first write and isn't extended
9725
- * on subsequent writes (via `EXPIRE ... NX`). Designed for runtime-
9726
- * controlled lifecycles like executions, where the data should age
9727
- * out on a schedule from creation.
10121
+ * - **Fixed from the first write**: `checkpoint`, `streamingEvents`
10122
+ * (`EXPIRE ... NX`) and `evalHistory` (a re-save re-applies the remaining
10123
+ * `PTTL`; `updateEvalResult` uses `KEEPTTL`). Later writes never extend
10124
+ * the window, and an eval row that exists without a TTL stays untimed.
10125
+ * - **Restarted on every save**: `executionState`, `executionHistory`
10126
+ * (`SET ... EX`). An execution record is kept for the full window after
10127
+ * its last save, which is normally when the run finished.
10128
+ *
10129
+ * `executionHistory` and `evalHistory` have the same meaning as the
10130
+ * `ttls` option of `MemoryStore` and `SQLiteStore` (see `HistoryTtls`).
10131
+ * With neither `executionHistory` (any value, `null` included) nor
10132
+ * {@link defaultTtl} set, `create()` warns once per process that
10133
+ * execution history is kept forever.
9728
10134
  */
9729
10135
  ttls?: {
9730
10136
  session?: number | null;
@@ -9761,10 +10167,40 @@ interface RedisStoreOptions {
9761
10167
  * async `RedisStore.create()` factory, which connects before returning.
9762
10168
  */
9763
10169
  declare class RedisStore implements StateStore {
9764
- private client;
10170
+ private connection;
9765
10171
  private keyPrefix;
9766
10172
  private ttls;
9767
10173
  private constructor();
10174
+ /**
10175
+ * Whether the store can send commands right now, for a health check or for
10176
+ * telling an outage from other failures when a call rejects. `false` while
10177
+ * reconnecting and after `close()`.
10178
+ */
10179
+ get isConnected(): boolean;
10180
+ /**
10181
+ * The client for sending a command, failing immediately while disconnected.
10182
+ *
10183
+ * Every command goes through here because node-redis's `disableOfflineQueue`
10184
+ * rejects single commands but not `multi().exec()`, which would wait for a
10185
+ * reconnect attempt to fail or succeed. Nothing may `await` between
10186
+ * `client.multi()` and `.exec()`, or a transaction could get past this check.
10187
+ *
10188
+ * A closed store isn't "unavailable": it falls through to node-redis's own
10189
+ * "The client is closed" rejection.
10190
+ */
10191
+ private get client();
10192
+ /**
10193
+ * History kinds with a prune running in this process. A save skips its
10194
+ * prune while another one of the same kind is in flight: the running one
10195
+ * already covers the oldest expired entries. Created lazily so the
10196
+ * instance needs no constructor state.
10197
+ */
10198
+ private prunesInFlight?;
10199
+ /**
10200
+ * Supports `listExecutions(limit, { cursor })`. A getter rather than a
10201
+ * field so it lives on the prototype.
10202
+ */
10203
+ get historyPaging(): boolean;
9768
10204
  /**
9769
10205
  * Resolve the TTL for a given storage category. Returns `undefined` when
9770
10206
  * no TTL applies (category is `null` in resolved config, the value is
@@ -9830,6 +10266,8 @@ declare class RedisStore implements StateStore {
9830
10266
  private evalHistorySetKey;
9831
10267
  /** Sorted-set index for eval history, scored by `timestamp`. See above. */
9832
10268
  private evalHistoryZsetKey;
10269
+ /** Keys and blob parsing for one kind of history. See {@link HistoryIndex}. */
10270
+ private historyIndex;
9833
10271
  private memoryKey;
9834
10272
  private streamingEventsKey;
9835
10273
  private streamingIdsKey;
@@ -9855,7 +10293,9 @@ declare class RedisStore implements StateStore {
9855
10293
  deleteMemory(scope: string, key: string): Promise<void>;
9856
10294
  saveExecution(execution: HistoricalExecutionInfo): Promise<void>;
9857
10295
  getExecution(executionId: string): Promise<HistoricalExecutionInfo | null>;
9858
- listExecutions(limit?: number): Promise<HistoricalExecutionInfo[]>;
10296
+ listExecutions(limit?: number, options?: {
10297
+ cursor?: ExecutionCursor;
10298
+ }): Promise<HistoricalExecutionInfo[]>;
9859
10299
  deleteExecution(executionId: string): Promise<boolean>;
9860
10300
  /**
9861
10301
  * Write an eval history row.
@@ -9878,7 +10318,9 @@ declare class RedisStore implements StateStore {
9878
10318
  * lifetime, so the race costs at most one window's length, never the window
9879
10319
  * itself. It also carries no Redis version floor, where `KEEPTTL` needs 6.0.
9880
10320
  *
9881
- * The index entries carry no TTL and age out lazily, as with `saveExecution`.
10321
+ * As with `saveExecution`, a timed row is also scored in the expiry sorted
10322
+ * set (by the expiry it carries, so a re-save keeps its score), an untimed
10323
+ * one is taken out of it, and a bounded prune follows the save.
9882
10324
  */
9883
10325
  saveEvalResult(entry: EvalHistoryEntry): Promise<void>;
9884
10326
  /**
@@ -9922,13 +10364,70 @@ declare class RedisStore implements StateStore {
9922
10364
  expiresAt?: number;
9923
10365
  }>;
9924
10366
  /**
9925
- * Shared list-by-sorted-set implementation. Uses ZREVRANGEBYSCORE +
9926
- * MGET when the ZSET is populated (post-migration). Falls back to the
9927
- * legacy SET + N×GET + JS-sort path when the ZSET is empty or smaller
9928
- * than the SET (covers users that constructed with `skipMigration: true`
9929
- * before calling backfill).
10367
+ * Shared list-by-sorted-set implementation, newest first by
10368
+ * `(score DESC, member DESC)`, starting strictly after `cursor` when given.
10369
+ *
10370
+ * Fast path (the time sorted set is at least as large as the id set): walk
10371
+ * the sorted set in chunks and MGET each chunk's blobs. Index entries whose
10372
+ * blob is gone (expired, or deleted before the prune caught up) are skipped,
10373
+ * and the walk keeps reading until it has `limit` live records or the set
10374
+ * runs out, however many dead entries are in the way. Each chunk restarts
10375
+ * at the last member read, by score and member, rather than at an offset,
10376
+ * so entries added or removed meanwhile cannot shift the walk.
10377
+ *
10378
+ * Slow path (the time sorted set is behind the id set: backfill has not
10379
+ * run): read every id with GET, then sort and apply the cursor in process.
9930
10380
  */
9931
10381
  private listHistoryByZset;
10382
+ /**
10383
+ * Run one prune of `index`'s kind after a save. A prune failure does not
10384
+ * fail the save, whose MULTI already committed the record: it is reported
10385
+ * once per process and the next save of that kind tries again. That also
10386
+ * covers a Redis user whose ACL denies `EVAL`.
10387
+ */
10388
+ private pruneHistoryAfterSave;
10389
+ private pruneHistory;
10390
+ /**
10391
+ * Run {@link PRUNE_HISTORY_INDEX_SCRIPT} over `ids`, at most PRUNE_BATCH
10392
+ * per call so one script never blocks the server for long.
10393
+ */
10394
+ private runPruneScript;
10395
+ /**
10396
+ * See `StateStore.applyHistoryRetention`. One call:
10397
+ *
10398
+ * 1. On the first call of a walk (no cursor), backfills the time sorted
10399
+ * set from the id set if it is behind (see `backfillExecutionIndex`).
10400
+ * 2. Reads the next `batchSize` ids from the time sorted set, oldest first
10401
+ * by `(score, member)`, strictly after the cursor. The read restarts at
10402
+ * the cursor's position by value, never by offset.
10403
+ * 3. MGETs their blobs and reads each live blob's PTTL.
10404
+ * 4. In one MULTI, for each live blob:
10405
+ * - a TTL (`PTTL > 0`): `ZADD NX` to the expiry set at now + PTTL. A
10406
+ * record already in the expiry set keeps its entry (`indexed` counts
10407
+ * only new entries).
10408
+ * - no TTL (`-1`), with a retention configured: `PEXPIREAT … NX` at last
10409
+ * save + retention. `NX` applies only to a key that still has no TTL,
10410
+ * so a record re-saved meanwhile keeps the fresh window its save gave
10411
+ * it. A window that has already passed uses a timestamp in the past,
10412
+ * which makes Redis delete the key, again only if it is still untimed.
10413
+ * 5. Adds each newly armed record to the expiry set (`ZADD NX`).
10414
+ * 6. Runs the prune script (`PRUNE_HISTORY_INDEX_SCRIPT`) over ids whose
10415
+ * blob is gone and ids just deleted in step 4. It checks each blob
10416
+ * atomically, so a record re-saved at any point stays listed and a
10417
+ * deleted one is never re-indexed.
10418
+ *
10419
+ * Blobs are never written. A record deleted while the call runs can leave
10420
+ * an entry in the expiry set only; the next prune removes it.
10421
+ */
10422
+ applyHistoryRetention(options: HistoryRetentionOptions): Promise<HistoryRetentionResult>;
10423
+ /**
10424
+ * Up to `count` members of the time sorted set strictly after `cursor`,
10425
+ * oldest first by `(score, member)`. The members sharing the cursor's score
10426
+ * are read in full and filtered by member; the rest start just above that
10427
+ * score. Neither read uses an offset, so entries removed or added
10428
+ * meanwhile cannot shift the walk.
10429
+ */
10430
+ private readRetentionBatch;
9932
10431
  listSessions(): Promise<string[]>;
9933
10432
  appendStreamingEvents(executionId: string, events: AxlEvent[]): Promise<void>;
9934
10433
  finalizeStreamingEvents(executionId: string): Promise<void>;
@@ -9942,6 +10441,11 @@ declare class RedisStore implements StateStore {
9942
10441
  * must not get a rejected promise for it. `MemoryStore.close()` and
9943
10442
  * better-sqlite3's `close()` are both no-ops the second time; node-redis
9944
10443
  * instead rejects with "The client is closed", so absorb that here.
10444
+ *
10445
+ * Bounded, so a shutdown can't hang on Redis. `QUIT` lets queued commands
10446
+ * finish on a healthy connection, but node-redis never settles it if the
10447
+ * socket closes first, and a stalled server may never answer it. While
10448
+ * disconnected it couldn't be sent at all.
9945
10449
  */
9946
10450
  close(): Promise<void>;
9947
10451
  deleteCheckpoints(executionId: string): Promise<void>;
@@ -10014,4 +10518,4 @@ declare class NoopSpanManager implements SpanManager {
10014
10518
  */
10015
10519
  declare function createSpanManager(config?: TelemetryConfig): Promise<SpanManager>;
10016
10520
 
10017
- export { AXL_EVENT_TYPES, AXL_EVENT_TYPES_V2, AXL_TOOL_LIFECYCLE_TYPES_V2, AZURE_PROFILE, type Accounting, type AccountingCompleteness, type AccountingReason, type AccountingUsage, AdmissionController, AdmissionDeniedError, type Agent, type AgentCallEndData, type AgentCallInfo, type AgentCallParams, type AgentCallStartData, type AgentConfig, AnthropicProvider, type AnyWorkflow, type ApiKeySource, type ArtifactManifest, type ArtifactOwner, type ArtifactStatus, type ArtifactWriteResult, type AskOptions, type AskScoped, type AuthHeader, type AwaitHumanData, type AwaitHumanOptions, type AwaitHumanResolvedData, type AxlConfig, AxlError, type AxlEvent, type AxlEventBase, AxlEventBus, type AxlEventOf, type AxlEventType, type AxlEventV2, type AxlEventV2Of, AxlRuntime, AxlStream, BEDROCK_PROFILE, BUILTIN_PROFILES, BudgetExceededError, type BudgetOptions, type BudgetResult, CONFIG_CANDIDATES, COST_BEARING_LEAF_TYPES, type CallTiming, type CapabilityFlags, type CaptureCorrelation, type CapturedCorrection, type CapturedError, type CapturedExternalRequestRecord, type CapturedJsonValue, type CapturedMessage, type CapturedModelRequestRecord, type CapturedOperationRef, type CapturedPhase, type CapturedRequest, type CapturedRequestRecord, type CapturedResponse, type ChatMessage, type ChatOptions, type CheckpointEventData, type CoalescedPartialObject, type CostProvenance, type CreateContextOptions, DEEPSEEK_PROFILE, DEFAULT_MAX_QUEUE_BYTES, DEFAULT_MAX_RECORD_BYTES, DEFAULT_MAX_RUN_BYTES, type DecisionCleanupFailedEvent, type DelegateData, type DelegateOptions, type DiagnosticArtifactStore, type DiagnosticArtifactsConfig, type DiagnosticsConfig, type DispatchAdmission, type Effort, type EffortResolution, type EmbedResult, type EmbedUsage, type Embedder, type EvalExecuteWorkflow, type EvalHistoryEntry, type EvalProgressEventShape, type EventStreamOptions, EventStreamOverflowError, type ExecuteOptions, type ExecutionInfo, type ExecutionInfoV1, type ExecutionInfoV2, type ExecutionState, type ExternalOperationDescriptor, type ExternalOperationRecord, type ExternalOperationReport, FileDiagnosticArtifactStore, type FinishReason, type FoldedOperationRecord, GROQ_PROFILE, GeminiProvider, GeminiTranscriptionProvider, type GuardrailBlockHandler, type GuardrailData, GuardrailError, type GuardrailResult, type GuardrailsConfig, type HandoffDescriptor, type HandoffRecord, type HandoffReturnData, type HandoffStartData, type HistoricalAxlEvent, type HistoricalExecutionInfo, type HumanDecision, InMemoryVectorStore, type InputAudioPart, type InputContentPart, type InputGuardrail, type InputImagePart, type InputMediaSource, type InputModalitySupport, type InputTextPart, InvalidModelInputError, InvalidTranscriptionInputError, LLAMACPP_PROFILE, LMSTUDIO_PROFILE, type LegacyAxlEventV1, type LegacyExecutionInfoV1, MAX_INLINE_MODEL_INPUT_BYTES, MAX_INLINE_TRANSCRIPTION_BYTES, MISTRAL_PROFILE, type MapOptions, MaxTurnsError, McpManager, type McpServer, type McpServerConfig, type McpToolDefinition, type McpToolResult, type MemoryConfig, type MemoryEventData, MemoryManager, MemoryStore, type ModelCallRecord, type ModelInput, type ModelInputDescriptor, type ModelTimingRollup, NoConsensus, NoopSpanManager, OLLAMA_PROFILE, OPENAI_PRICING, OPENAI_PROFILE, OPENROUTER_PROFILE, type ObservationStatus, type OpenAICompatibleOptions, OpenAICompatibleProvider, OpenAIEmbedder, OpenAIProvider, OpenAIResponsesProvider, OpenAITranscriptionProvider, OpenRouterTranscriptionProvider, type OpenedArtifact, type OperationKind, type OperationPurpose, type OutputGuardrail, type OutputValidator, type PendingDecision, type PerModel, type PricingSource, type PricingTable, type ProfileInputModalities, type Provider, type ProviderAdapter, type ProviderDiagnosticData, ProviderError, type ProviderInputValidationRequest, type ProviderInputValidationResult, type ProviderProfile, ProviderRegistry, type ProviderResponse, QuorumNotMet, REDACTED, REDACTION_RULES, type RaceOptions, type RateLimitConfig, RateLimiter, type ReasoningCapture, type ReasoningContextReset, type ReasoningEmit, type ReasoningProfile, type ReasoningRoundTrip, type RecallOptions, type RecallResult, type RecordedAudioSource, RedisStore, type RememberOptions, type RememberResult, RequestCaptureChannel, type RequestCaptureOptions, type RequestCaptureSink, type RequestCaptureStatus, type ResolvedThinkingOptions, type Result, type RetryFeedbackHook, type RetryFeedbackInfo, type RetryFeedbackResult, type RetryPolicy, type RuntimeEvalConfigShape, SGLANG_PROFILE, SQLiteStore, type SchemaCheckData, type SchemaDiagnosticData, type SchemaPromptOption, Session, type SessionOptions, type SpanAttributeValue, type SpanAttributes, type SpanHandle, type SpanManager, type SpawnOptions, SqliteVectorStore, type StagedArtifact, StallTimeoutError, type StateStore, type StreamChunk, type StringDeltaData, type StringStreamEvent, type StringStreamFilter, type TelemetryConfig, ThinkTagScanner, type TimeoutBreakdown, TimeoutError, type Tool, type ToolApprovalData, type ToolArgumentIssue, type ToolCallCancellation, type ToolCallData, type ToolCallEndData, type ToolCallFailure, type ToolCallMessage, type ToolCallOutcome, type ToolCallRejectedData, type ToolCallRejectedEvent, type ToolCallStartData, type ToolCallStartDataV2, type ToolChoice, type ToolConfig, type ToolDeniedData, type ToolEventError, ToolFailure, type ToolFailureConstructor, type ToolFailureOptions, type ToolHooks, type ToolLifecycleEventV2, type ToolModelOutput, ToolModelOutputError, type TrackExecutionMetadata, type TrackExecutionResult, type TrackOutcomeOptions, type TrackedOutcome, type Transcript, type TranscriptionCapabilities, TranscriptionOperationError, type TranscriptionProvider, TranscriptionProviderRegistry, type TranscriptionProviderRequest, type TranscriptionProviderResult, type TranscriptionRequest, UnsupportedEventSchemaVersionError, UnsupportedModelInputError, UnsupportedModelOptionError, UnsupportedTranscriptionInputError, VLLM_PROFILE, type ValidateData, type ValidateResult, ValidationError, type VectorEntry, type VectorResult, type VectorStore, type VerifyData, VerifyError, type VerifyOptions, type VerifyRetry, type VoteOptions, type Workflow, type WorkflowConfig, WorkflowContext, type WorkflowContextInit, type WorkflowEndData, type WorkflowStartData, XAI_PROFILE, agent, capturedJournalLineSchema, createSpanManager, defineConfig, estimateOpenAICost, eventCostContribution, expandGlob, externalOperation, externalOperationRecordSchema, extractJson, extractThinkTags, findConfig, foldJournal, foldedOperationRecordSchema, getEventSchemaVersion, getExecutionEventSchemaVersion, hasPositiveBillableWork, hasPositiveTokens, importModule, inputText, isAdmissionDeniedError, isCostBearingLeaf, isRetryableStatus, isRootLevel, isUnpricedLeaf, isUsableCost, modelCallRecordSchema, needsTsxLoader, normalizeStoredExecution, openaiReasoningEmit, parsePartialJson, pickDefault, pickExport, priceFromTable, reasoningEffortEmit, reasoningObjectEmit, redactCapturedRequest, redactEvent, redactHistoricalEvent, redactModelCallRecord, registerConditions, resolveApiKey, resolvePerModel, resolveRuntime, resolveThinkingOptions, stringStreamFromEvents, summarizeModelInput, tool, workflow, zodToJsonSchema };
10521
+ export { AXL_EVENT_TYPES, AXL_EVENT_TYPES_V2, AXL_TOOL_LIFECYCLE_TYPES_V2, AZURE_PROFILE, type Accounting, type AccountingCompleteness, type AccountingReason, type AccountingUsage, AdmissionController, AdmissionDeniedError, type Agent, type AgentCallEndData, type AgentCallInfo, type AgentCallParams, type AgentCallStartData, type AgentConfig, AnthropicProvider, type AnyWorkflow, type ApiKeySource, type ArtifactManifest, type ArtifactOwner, type ArtifactStatus, type ArtifactWriteResult, type AskOptions, type AskScoped, type AuthHeader, type AwaitHumanData, type AwaitHumanOptions, type AwaitHumanResolvedData, type AxlConfig, AxlError, type AxlEvent, type AxlEventBase, AxlEventBus, type AxlEventOf, type AxlEventType, type AxlEventV2, type AxlEventV2Of, AxlRuntime, AxlStream, BEDROCK_PROFILE, BUILTIN_PROFILES, BudgetExceededError, type BudgetOptions, type BudgetResult, CONFIG_CANDIDATES, COST_BEARING_LEAF_TYPES, type CallTiming, type CapabilityFlags, type CaptureCorrelation, type CapturedCorrection, type CapturedError, type CapturedExternalRequestRecord, type CapturedJsonValue, type CapturedMessage, type CapturedModelRequestRecord, type CapturedOperationRef, type CapturedPhase, type CapturedRequest, type CapturedRequestRecord, type CapturedResponse, type ChatMessage, type ChatOptions, type CheckpointEventData, type CoalescedPartialObject, type CostProvenance, type CreateContextOptions, DEEPSEEK_PROFILE, DEFAULT_MAX_QUEUE_BYTES, DEFAULT_MAX_RECORD_BYTES, DEFAULT_MAX_RUN_BYTES, type DecisionCleanupFailedEvent, type DelegateData, type DelegateOptions, type DiagnosticArtifactStore, type DiagnosticArtifactsConfig, type DiagnosticsConfig, type DispatchAdmission, type Effort, type EffortResolution, type EmbedResult, type EmbedUsage, type Embedder, type EvalExecuteWorkflow, type EvalHistoryEntry, type EvalProgressEventShape, type EventStreamOptions, EventStreamOverflowError, type ExecuteOptions, type ExecutionCursor, type ExecutionInfo, type ExecutionInfoV1, type ExecutionInfoV2, type ExecutionState, type ExternalOperationDescriptor, type ExternalOperationRecord, type ExternalOperationReport, FileDiagnosticArtifactStore, type FinishReason, type FoldedOperationRecord, GROQ_PROFILE, GeminiProvider, GeminiTranscriptionProvider, type GuardrailBlockHandler, type GuardrailData, GuardrailError, type GuardrailResult, type GuardrailsConfig, type HandoffDescriptor, type HandoffRecord, type HandoffReturnData, type HandoffStartData, type HistoricalAxlEvent, type HistoricalExecutionInfo, type HistoryRetentionCursor, type HistoryRetentionKind, type HistoryRetentionOptions, type HistoryRetentionResult, type HistoryTtls, type HumanDecision, InMemoryVectorStore, type InputAudioPart, type InputContentPart, type InputGuardrail, type InputImagePart, type InputMediaSource, type InputModalitySupport, type InputTextPart, InvalidModelInputError, InvalidTranscriptionInputError, LLAMACPP_PROFILE, LMSTUDIO_PROFILE, type LegacyAxlEventV1, type LegacyExecutionInfoV1, MAX_INLINE_MODEL_INPUT_BYTES, MAX_INLINE_TRANSCRIPTION_BYTES, MISTRAL_PROFILE, type MapOptions, MaxTurnsError, McpManager, type McpServer, type McpServerConfig, type McpToolDefinition, type McpToolResult, type MemoryConfig, type MemoryEventData, MemoryManager, MemoryStore, type MemoryStoreOptions, type ModelCallRecord, type ModelInput, type ModelInputDescriptor, type ModelTimingRollup, NoConsensus, NoopSpanManager, OLLAMA_PROFILE, OPENAI_PRICING, OPENAI_PROFILE, OPENROUTER_PROFILE, type ObservationStatus, type OpenAICompatibleOptions, OpenAICompatibleProvider, OpenAIEmbedder, OpenAIProvider, OpenAIResponsesProvider, OpenAITranscriptionProvider, OpenRouterTranscriptionProvider, type OpenedArtifact, type OperationKind, type OperationPurpose, type OutputGuardrail, type OutputValidator, type PendingDecision, type PerModel, type PricingSource, type PricingTable, type ProfileInputModalities, type Provider, type ProviderAdapter, type ProviderDiagnosticData, ProviderError, type ProviderInputValidationRequest, type ProviderInputValidationResult, type ProviderProfile, ProviderRegistry, type ProviderResponse, QuorumNotMet, REDACTED, REDACTION_RULES, type RaceOptions, type RateLimitConfig, RateLimiter, type ReasoningCapture, type ReasoningContextReset, type ReasoningEmit, type ReasoningProfile, type ReasoningRoundTrip, type RecallOptions, type RecallResult, type RecordedAudioSource, type RedisConnectionEvent, RedisStore, type RedisStoreOptions, type RememberOptions, type RememberResult, RequestCaptureChannel, type RequestCaptureOptions, type RequestCaptureSink, type RequestCaptureStatus, type ResolvedThinkingOptions, type Result, type RetryFeedbackHook, type RetryFeedbackInfo, type RetryFeedbackResult, type RetryPolicy, type RuntimeEvalConfigShape, SGLANG_PROFILE, SQLiteStore, type SQLiteStoreOptions, type SchemaCheckData, type SchemaDiagnosticData, type SchemaPromptOption, Session, type SessionOptions, type SpanAttributeValue, type SpanAttributes, type SpanHandle, type SpanManager, type SpawnOptions, SqliteVectorStore, type StagedArtifact, StallTimeoutError, type StateStore, type StreamChunk, type StringDeltaData, type StringStreamEvent, type StringStreamFilter, type TelemetryConfig, ThinkTagScanner, type TimeoutBreakdown, TimeoutError, type Tool, type ToolApprovalData, type ToolArgumentIssue, type ToolCallCancellation, type ToolCallData, type ToolCallEndData, type ToolCallFailure, type ToolCallMessage, type ToolCallOutcome, type ToolCallRejectedData, type ToolCallRejectedEvent, type ToolCallStartData, type ToolCallStartDataV2, type ToolChoice, type ToolConfig, type ToolDeniedData, type ToolEventError, ToolFailure, type ToolFailureConstructor, type ToolFailureOptions, type ToolHooks, type ToolLifecycleEventV2, type ToolModelOutput, ToolModelOutputError, type TrackExecutionMetadata, type TrackExecutionResult, type TrackOutcomeOptions, type TrackedOutcome, type Transcript, type TranscriptionCapabilities, TranscriptionOperationError, type TranscriptionProvider, TranscriptionProviderRegistry, type TranscriptionProviderRequest, type TranscriptionProviderResult, type TranscriptionRequest, UnsupportedEventSchemaVersionError, UnsupportedModelInputError, UnsupportedModelOptionError, UnsupportedTranscriptionInputError, VLLM_PROFILE, type ValidateData, type ValidateResult, ValidationError, type VectorEntry, type VectorResult, type VectorStore, type VerifyData, VerifyError, type VerifyOptions, type VerifyRetry, type VoteOptions, type Workflow, type WorkflowConfig, WorkflowContext, type WorkflowContextInit, type WorkflowEndData, type WorkflowStartData, XAI_PROFILE, agent, capturedJournalLineSchema, createSpanManager, defineConfig, estimateOpenAICost, eventCostContribution, expandGlob, externalOperation, externalOperationRecordSchema, extractJson, extractThinkTags, findConfig, foldJournal, foldedOperationRecordSchema, getEventSchemaVersion, getExecutionEventSchemaVersion, hasPositiveBillableWork, hasPositiveTokens, importModule, inputText, isAdmissionDeniedError, isCostBearingLeaf, isRetryableStatus, isRootLevel, isUnpricedLeaf, isUsableCost, modelCallRecordSchema, needsTsxLoader, normalizeStoredExecution, openaiReasoningEmit, parsePartialJson, pickDefault, pickExport, priceFromTable, reasoningEffortEmit, reasoningObjectEmit, redactCapturedRequest, redactEvent, redactHistoricalEvent, redactModelCallRecord, registerConditions, resolveApiKey, resolvePerModel, resolveRuntime, resolveThinkingOptions, stringStreamFromEvents, summarizeModelInput, tool, workflow, zodToJsonSchema };