@axlsdk/axl 0.17.6 → 0.17.7
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 +62 -0
- package/dist/index.cjs +917 -63
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +379 -2
- package/dist/index.d.ts +379 -2
- package/dist/index.js +917 -63
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -855,6 +855,14 @@ type ExecutionInfo = {
|
|
|
855
855
|
duration: number;
|
|
856
856
|
result?: unknown;
|
|
857
857
|
error?: string;
|
|
858
|
+
/** Caller-supplied metadata threaded verbatim from `ExecuteOptions.metadata`
|
|
859
|
+
* (or `runtime.stream()`'s `options.metadata`). Persisted as part of the
|
|
860
|
+
* `ExecutionInfo` snapshot so callers have a stable, queryable surface for
|
|
861
|
+
* tags like `userId`, `tenantId`, or correlation ids without parsing the
|
|
862
|
+
* event timeline. Filter via `runtime.getExecutions()` + an in-process
|
|
863
|
+
* `.filter()`; secondary indexes on this field are intentionally not
|
|
864
|
+
* provided — `StateStore` is a persistence boundary, not a query engine. */
|
|
865
|
+
metadata?: Record<string, unknown>;
|
|
858
866
|
};
|
|
859
867
|
/** Record of an agent handoff event (persisted in session metadata).
|
|
860
868
|
*
|
|
@@ -1447,6 +1455,30 @@ interface StateStore {
|
|
|
1447
1455
|
getExecution?(executionId: string): Promise<ExecutionInfo | null>;
|
|
1448
1456
|
/** List recent executions (most recent first). */
|
|
1449
1457
|
listExecutions?(limit?: number): Promise<ExecutionInfo[]>;
|
|
1458
|
+
/**
|
|
1459
|
+
* Delete an execution from history by ID. Returns `true` if an entry was
|
|
1460
|
+
* deleted, `false` if the ID didn't exist. Used for GDPR right-to-be-
|
|
1461
|
+
* forgotten and operator-driven cleanup. Symmetric to `deleteEvalResult`.
|
|
1462
|
+
*
|
|
1463
|
+
* **Custom store implementors:** This delete must be **total** —
|
|
1464
|
+
* sweep every per-execution surface your store maintains. The runtime's
|
|
1465
|
+
* `runtime.deleteExecution` does NOT call separate `deleteCheckpoints` /
|
|
1466
|
+
* `deleteExecutionState` / `finalizeStreamingEvents` after this; one
|
|
1467
|
+
* call should clean EVERYTHING owned by the executionId. The built-in
|
|
1468
|
+
* stores remove:
|
|
1469
|
+
* - the canonical execution-history row and its indexes,
|
|
1470
|
+
* - all checkpoints for the id,
|
|
1471
|
+
* - the suspended-state row + its pending-set membership,
|
|
1472
|
+
* - the streaming-event buffer + its in-flight-ids membership,
|
|
1473
|
+
* - any pending awaitHuman decision row.
|
|
1474
|
+
* Skipping any of these leaks PII through `recoverIncompleteStreams()`
|
|
1475
|
+
* or `getPendingDecisions()`, breaking the GDPR delete contract.
|
|
1476
|
+
*
|
|
1477
|
+
* For multi-table SQL stores: wrap the deletes in a transaction. For
|
|
1478
|
+
* Redis-style stores: use MULTI/EXEC. Idempotency is required —
|
|
1479
|
+
* deleting an unknown id returns `false` without throwing.
|
|
1480
|
+
*/
|
|
1481
|
+
deleteExecution?(executionId: string): Promise<boolean>;
|
|
1450
1482
|
/** Save an eval result to history. */
|
|
1451
1483
|
saveEvalResult?(entry: EvalHistoryEntry): Promise<void>;
|
|
1452
1484
|
/** List eval history entries (most recent first). */
|
|
@@ -1455,6 +1487,31 @@ interface StateStore {
|
|
|
1455
1487
|
deleteEvalResult?(id: string): Promise<boolean>;
|
|
1456
1488
|
/** List all session IDs (used by Studio session browser). */
|
|
1457
1489
|
listSessions?(): Promise<string[]>;
|
|
1490
|
+
/**
|
|
1491
|
+
* Append a batch of events to the streaming buffer for an execution.
|
|
1492
|
+
* Should be idempotent w.r.t. the executionId being new vs existing
|
|
1493
|
+
* (first call also registers the id in the "in-flight" index used by
|
|
1494
|
+
* `listStreamingExecutions`).
|
|
1495
|
+
*/
|
|
1496
|
+
appendStreamingEvents?(executionId: string, events: AxlEvent[]): Promise<void>;
|
|
1497
|
+
/**
|
|
1498
|
+
* Finalize a streaming execution — delete its buffer + un-register it
|
|
1499
|
+
* from the in-flight index. Called by the runtime after the canonical
|
|
1500
|
+
* `executionHistory` blob has been written, so the streaming buffer
|
|
1501
|
+
* is no longer the source of truth for this execution.
|
|
1502
|
+
*/
|
|
1503
|
+
finalizeStreamingEvents?(executionId: string): Promise<void>;
|
|
1504
|
+
/**
|
|
1505
|
+
* List execution IDs that have a streaming buffer but no corresponding
|
|
1506
|
+
* completed `executionHistory` — i.e., runs whose process died mid-flight.
|
|
1507
|
+
* The runtime calls this from `recoverIncompleteStreams()`.
|
|
1508
|
+
*/
|
|
1509
|
+
listStreamingExecutions?(): Promise<string[]>;
|
|
1510
|
+
/**
|
|
1511
|
+
* Read the events accumulated in the streaming buffer for an execution.
|
|
1512
|
+
* Returns `[]` if no buffer exists.
|
|
1513
|
+
*/
|
|
1514
|
+
getStreamingEvents?(executionId: string): Promise<AxlEvent[]>;
|
|
1458
1515
|
close?(): Promise<void>;
|
|
1459
1516
|
deleteCheckpoints?(executionId: string): Promise<void>;
|
|
1460
1517
|
}
|
|
@@ -1632,6 +1689,44 @@ type StateConfig = {
|
|
|
1632
1689
|
* integer or `Infinity`.
|
|
1633
1690
|
*/
|
|
1634
1691
|
maxEventsPerExecution?: number;
|
|
1692
|
+
/**
|
|
1693
|
+
* When to persist execution events to the state store.
|
|
1694
|
+
*
|
|
1695
|
+
* - `'terminal'` (default, back-compat) — events are buffered in memory
|
|
1696
|
+
* and written to `executionHistory` only at the terminal `done`/`error`
|
|
1697
|
+
* event. Fastest path; if the process crashes mid-run, the events are
|
|
1698
|
+
* lost. Fine for development and short-running workflows.
|
|
1699
|
+
*
|
|
1700
|
+
* - `'streaming'` — events are batched and written to a streaming buffer
|
|
1701
|
+
* throughout the run (via `StateStore.appendStreamingEvents`), then the
|
|
1702
|
+
* buffer is finalized when the canonical `executionHistory` is saved at
|
|
1703
|
+
* terminal exit. If the process crashes mid-run, call
|
|
1704
|
+
* `runtime.recoverIncompleteStreams()` on the next process to reconstruct
|
|
1705
|
+
* the partial `ExecutionInfo` from the streaming buffer (status: `'failed'`,
|
|
1706
|
+
* error: `'process terminated'`). Use this in production when you need to
|
|
1707
|
+
* know "what did the workflow do up to the crash."
|
|
1708
|
+
*
|
|
1709
|
+
* Excluded from streaming flush even in `'streaming'` mode: `token`,
|
|
1710
|
+
* `partial_object`, `string_delta` (high-volume, reconstructable from
|
|
1711
|
+
* the persisted `agent_call_end.data`).
|
|
1712
|
+
*/
|
|
1713
|
+
persist?: 'terminal' | 'streaming';
|
|
1714
|
+
/**
|
|
1715
|
+
* Number of events buffered before flushing to the streaming store.
|
|
1716
|
+
* Default `100`. Set to `1` for per-event flush (highest durability,
|
|
1717
|
+
* one Redis round-trip per emit — adds latency proportional to RTT).
|
|
1718
|
+
*
|
|
1719
|
+
* Only meaningful when `persist === 'streaming'`.
|
|
1720
|
+
*/
|
|
1721
|
+
streamingBatchSize?: number;
|
|
1722
|
+
/**
|
|
1723
|
+
* Max milliseconds between flushes when the batch hasn't filled up.
|
|
1724
|
+
* Default `1000`. Together with `streamingBatchSize`, bounds the
|
|
1725
|
+
* "events lost on crash" window to `min(batchSize-of-events, interval-of-time)`.
|
|
1726
|
+
*
|
|
1727
|
+
* Only meaningful when `persist === 'streaming'`.
|
|
1728
|
+
*/
|
|
1729
|
+
streamingBatchInterval?: number;
|
|
1635
1730
|
};
|
|
1636
1731
|
/** Global defaults */
|
|
1637
1732
|
type DefaultsConfig = {
|
|
@@ -2762,6 +2857,30 @@ declare class AxlRuntime extends EventEmitter {
|
|
|
2762
2857
|
* `events` field — keeps the log to one line per row instead of one per
|
|
2763
2858
|
* rebuild tick. */
|
|
2764
2859
|
private warnedMalformedExecutions;
|
|
2860
|
+
/** Streaming-mode flusher. `undefined` when persist === 'terminal'
|
|
2861
|
+
* (back-compat default — no buffering overhead, no batching timer). */
|
|
2862
|
+
private streamingFlusher?;
|
|
2863
|
+
/** Execution IDs that are eligible for streaming-buffer durability —
|
|
2864
|
+
* populated by `execute()` / `stream()`. `createContext()` flows are
|
|
2865
|
+
* deliberately NOT added: ad-hoc contexts (tool tests, Studio playground,
|
|
2866
|
+
* evals) have no terminal `persistExecution` path that would finalize the
|
|
2867
|
+
* streaming buffer, so allowing them to write would leave phantom orphan
|
|
2868
|
+
* buffers that `recoverIncompleteStreams()` later mis-recovers as failed
|
|
2869
|
+
* executions. Membership is removed in `persistExecution` once the
|
|
2870
|
+
* canonical save + finalize chain has been scheduled. */
|
|
2871
|
+
private streamableExecutionIds;
|
|
2872
|
+
/** Execution IDs deleted by `runtime.deleteExecution()` while still in
|
|
2873
|
+
* flight. `persistExecution` skips its save + cache write when the
|
|
2874
|
+
* executionId is in this set — preventing a workflow that completes after
|
|
2875
|
+
* a delete from resurrecting the (intentionally-removed) row. Entries are
|
|
2876
|
+
* removed once the would-be persist has been short-circuited. */
|
|
2877
|
+
private pendingDeletedExecutions;
|
|
2878
|
+
/** In-flight `persistExecution` chains (save → finalize). `shutdown()`
|
|
2879
|
+
* awaits these before closing the StateStore so a workflow whose abort
|
|
2880
|
+
* triggers a persist-during-shutdown isn't racing a closed connection.
|
|
2881
|
+
* The Set is mutated by the chain's own `.finally` so entries clear as
|
|
2882
|
+
* soon as the work settles. */
|
|
2883
|
+
private persistInflight;
|
|
2765
2884
|
constructor(config?: AxlConfig);
|
|
2766
2885
|
/**
|
|
2767
2886
|
* Whether `config.trace.redact` is enabled on this runtime. A narrow
|
|
@@ -2824,6 +2943,12 @@ declare class AxlRuntime extends EventEmitter {
|
|
|
2824
2943
|
/**
|
|
2825
2944
|
* Persist a completed/failed execution to the state store (fire-and-forget)
|
|
2826
2945
|
* and move it from the active executions map to the historical cache.
|
|
2946
|
+
*
|
|
2947
|
+
* Resurrection guard: if `runtime.deleteExecution(id)` was called while
|
|
2948
|
+
* this execution was still running, the id will be in
|
|
2949
|
+
* `pendingDeletedExecutions`. In that case we skip the save AND the
|
|
2950
|
+
* historical-cache write, and finalize the streaming buffer (if any) so
|
|
2951
|
+
* the buffer doesn't outlive the delete.
|
|
2827
2952
|
*/
|
|
2828
2953
|
private persistExecution;
|
|
2829
2954
|
/** Register a workflow with the runtime. */
|
|
@@ -2932,18 +3057,76 @@ declare class AxlRuntime extends EventEmitter {
|
|
|
2932
3057
|
* before invoking `shutdown()`. A `Session.send` invoked after the
|
|
2933
3058
|
* drain snapshot is taken can still race a closed state store. */
|
|
2934
3059
|
shutdown(): Promise<void>;
|
|
3060
|
+
/**
|
|
3061
|
+
* Recover executions whose process died mid-run when `state.persist:
|
|
3062
|
+
* 'streaming'` was configured. Synthesizes a partial `ExecutionInfo`
|
|
3063
|
+
* from the streaming buffer for each — `status: 'failed'`, error
|
|
3064
|
+
* `'process terminated'` — registers them in the historical cache so
|
|
3065
|
+
* they show up in `getExecutions()`, persists them via the store's
|
|
3066
|
+
* `saveExecution`, and finalizes the streaming buffer.
|
|
3067
|
+
*
|
|
3068
|
+
* Idempotent — if the streaming buffer is already gone (or
|
|
3069
|
+
* `listStreamingExecutions` is empty), this is a no-op. Returns the
|
|
3070
|
+
* list of recovered `ExecutionInfo`s for caller introspection.
|
|
3071
|
+
*
|
|
3072
|
+
* Wire this into your process startup AFTER `runtime.getExecutions()`
|
|
3073
|
+
* has lazy-loaded the historical cache. Order matters: completed
|
|
3074
|
+
* executions already in the store should not be overwritten by a
|
|
3075
|
+
* stale streaming buffer that the previous process didn't get to
|
|
3076
|
+
* finalize. The implementation checks the historical store before
|
|
3077
|
+
* synthesizing, so a re-run is safe.
|
|
3078
|
+
*/
|
|
3079
|
+
recoverIncompleteStreams(): Promise<ExecutionInfo[]>;
|
|
2935
3080
|
/** Abort a running execution by its ID. */
|
|
2936
3081
|
abort(executionId: string): void;
|
|
2937
3082
|
/** Get execution details by ID. */
|
|
2938
3083
|
getExecution(executionId: string): Promise<ExecutionInfo | undefined>;
|
|
2939
3084
|
/** Save an eval result to history. */
|
|
2940
3085
|
saveEvalResult(entry: EvalHistoryEntry): Promise<void>;
|
|
3086
|
+
/**
|
|
3087
|
+
* Delete an execution from history by id. Removes from the active map (if
|
|
3088
|
+
* still running), the historical cache, the configured StateStore, AND
|
|
3089
|
+
* any execution-scoped side state — pending awaitHuman decisions,
|
|
3090
|
+
* resolver maps, streaming buffer registration, in-flight abort controller.
|
|
3091
|
+
* Returns true if an entry was actually removed from any of the three
|
|
3092
|
+
* primary surfaces (active map, historical cache, store).
|
|
3093
|
+
*
|
|
3094
|
+
* In-flight handling: if the execution is still running, this method
|
|
3095
|
+
* **aborts** it (via the registered abort controller) AND marks the id for
|
|
3096
|
+
* skip-on-persist so the workflow's eventual `workflow_end` doesn't
|
|
3097
|
+
* resurrect the row. The workflow itself terminates normally — callers
|
|
3098
|
+
* iterating the returned `AxlStream` will see the abort flow through. If
|
|
3099
|
+
* you need a hard hand-off (delete completes before the workflow tears
|
|
3100
|
+
* down), call `runtime.abort(id)` and `await` the stream/execute promise
|
|
3101
|
+
* before calling this. `streamableExecutionIds` is cleared eagerly so
|
|
3102
|
+
* events emitted during the wind-down don't re-create the streaming
|
|
3103
|
+
* buffer in the store after the deleteExecution-issued DEL.
|
|
3104
|
+
*
|
|
3105
|
+
* Audit trail: emits `execution_deleted` on the runtime's `EventEmitter`
|
|
3106
|
+
* with `{ executionId, wasActive, hadPendingDecision }` so compliance
|
|
3107
|
+
* apps can subscribe via `runtime.on('execution_deleted', ...)` for a
|
|
3108
|
+
* "who deleted what when" log without wrapping this method. Fires
|
|
3109
|
+
* regardless of whether the id was actually present.
|
|
3110
|
+
*
|
|
3111
|
+
* Use cases: GDPR right-to-be-forgotten, operator-driven cleanup of
|
|
3112
|
+
* specific runs (e.g. a workflow that recorded PII the user requested
|
|
3113
|
+
* scrubbed). For bulk eviction by age, use a `RedisStore` TTL instead
|
|
3114
|
+
* (`defaultTtl` / `ttls.executionHistory`).
|
|
3115
|
+
*/
|
|
3116
|
+
deleteExecution(id: string): Promise<boolean>;
|
|
2941
3117
|
/**
|
|
2942
3118
|
* Delete an eval history entry by id. Removes from in-memory cache and
|
|
2943
3119
|
* the configured StateStore. Returns true if an entry was actually removed.
|
|
2944
3120
|
*
|
|
2945
3121
|
* Ensures lazy-loaded history is loaded first so the in-memory cache and
|
|
2946
3122
|
* the store can't drift apart on the deletion path.
|
|
3123
|
+
*
|
|
3124
|
+
* Emits `eval_deleted` on the runtime's `EventEmitter` with
|
|
3125
|
+
* `{ id, eval, removed }`. Fires on every call — including attempts
|
|
3126
|
+
* against unknown ids (`removed: false`) — symmetric to `execution_deleted`.
|
|
3127
|
+
* Studio's eval-trends aggregator subscribes to this event so deleted
|
|
3128
|
+
* runs don't linger in the trends panel for up to 5 minutes (until the
|
|
3129
|
+
* next periodic rebuild).
|
|
2947
3130
|
*/
|
|
2948
3131
|
deleteEvalResult(id: string): Promise<boolean>;
|
|
2949
3132
|
/** Get eval result history (most recent first). */
|
|
@@ -3775,10 +3958,16 @@ declare class MemoryStore implements StateStore {
|
|
|
3775
3958
|
saveExecution(execution: ExecutionInfo): Promise<void>;
|
|
3776
3959
|
getExecution(executionId: string): Promise<ExecutionInfo | null>;
|
|
3777
3960
|
listExecutions(limit?: number): Promise<ExecutionInfo[]>;
|
|
3961
|
+
deleteExecution(executionId: string): Promise<boolean>;
|
|
3778
3962
|
saveEvalResult(entry: EvalHistoryEntry): Promise<void>;
|
|
3779
3963
|
listEvalResults(limit?: number): Promise<EvalHistoryEntry[]>;
|
|
3780
3964
|
deleteEvalResult(id: string): Promise<boolean>;
|
|
3781
3965
|
listSessions(): Promise<string[]>;
|
|
3966
|
+
private streamingEvents;
|
|
3967
|
+
appendStreamingEvents(executionId: string, events: AxlEvent[]): Promise<void>;
|
|
3968
|
+
finalizeStreamingEvents(executionId: string): Promise<void>;
|
|
3969
|
+
listStreamingExecutions(): Promise<string[]>;
|
|
3970
|
+
getStreamingEvents(executionId: string): Promise<AxlEvent[]>;
|
|
3782
3971
|
close(): Promise<void>;
|
|
3783
3972
|
deleteCheckpoints(executionId: string): Promise<void>;
|
|
3784
3973
|
private persistAwaitHumanState;
|
|
@@ -3815,6 +4004,10 @@ declare class SQLiteStore implements StateStore {
|
|
|
3815
4004
|
* numeric step values become stringified names ("0", "1", …) to
|
|
3816
4005
|
* preserve replay continuity for in-flight executions across the
|
|
3817
4006
|
* upgrade boundary.
|
|
4007
|
+
* - v3 (ExecutionInfo.metadata): add `execution_history.metadata`
|
|
4008
|
+
* column (TEXT, nullable, JSON-serialized) so caller-supplied
|
|
4009
|
+
* `ExecuteOptions.metadata` round-trips through history. Existing
|
|
4010
|
+
* rows get `NULL`, deserialized as `metadata: undefined`.
|
|
3818
4011
|
*
|
|
3819
4012
|
* Applied BEFORE `initTables()` so the subsequent `CREATE TABLE
|
|
3820
4013
|
* IF NOT EXISTS` runs against the post-migration column name. Fresh
|
|
@@ -3849,6 +4042,7 @@ declare class SQLiteStore implements StateStore {
|
|
|
3849
4042
|
saveExecution(execution: ExecutionInfo): Promise<void>;
|
|
3850
4043
|
getExecution(executionId: string): Promise<ExecutionInfo | null>;
|
|
3851
4044
|
listExecutions(limit?: number): Promise<ExecutionInfo[]>;
|
|
4045
|
+
deleteExecution(executionId: string): Promise<boolean>;
|
|
3852
4046
|
saveEvalResult(entry: EvalHistoryEntry): Promise<void>;
|
|
3853
4047
|
listEvalResults(limit?: number): Promise<EvalHistoryEntry[]>;
|
|
3854
4048
|
deleteEvalResult(id: string): Promise<boolean>;
|
|
@@ -3865,6 +4059,115 @@ declare class SQLiteStore implements StateStore {
|
|
|
3865
4059
|
deleteCheckpoints(executionId: string): Promise<void>;
|
|
3866
4060
|
}
|
|
3867
4061
|
|
|
4062
|
+
/** Options accepted by {@link RedisStore.create}. */
|
|
4063
|
+
interface RedisStoreOptions {
|
|
4064
|
+
/** Redis connection URL (e.g. `redis://localhost:6379`). Defaults to `redis://localhost:6379`. */
|
|
4065
|
+
url?: string;
|
|
4066
|
+
/**
|
|
4067
|
+
* Prefix prepended to every Redis key written by this store. Defaults to
|
|
4068
|
+
* `'axl:'`. Useful when multiple Axl deployments share a Redis cluster
|
|
4069
|
+
* (e.g. `'axl:prod:'` vs `'axl:staging:'`) or when coexisting with other
|
|
4070
|
+
* applications' keys.
|
|
4071
|
+
*
|
|
4072
|
+
* The prefix is concatenated as-given — no normalization. If you want a
|
|
4073
|
+
* trailing colon, include it. Whitespace and unusual characters are
|
|
4074
|
+
* accepted as-is. Only the empty string is rejected, to prevent accidental
|
|
4075
|
+
* collisions with non-Axl keys.
|
|
4076
|
+
*
|
|
4077
|
+
* Operator note: Redis SCAN/KEYS patterns interpret `*`, `?`, and `[`/`]`
|
|
4078
|
+
* as glob meta-characters. If your prefix contains any of these, the
|
|
4079
|
+
* runtime accepts it (literal SET/HSET/etc. don't use globs and round-
|
|
4080
|
+
* trip cleanly), but operators trying to clean up with
|
|
4081
|
+
* `redis-cli SCAN 0 MATCH '<prefix>*'` will need to escape them. Stick
|
|
4082
|
+
* to alphanumerics, hyphens, underscores, and colons for the best
|
|
4083
|
+
* operator experience.
|
|
4084
|
+
*/
|
|
4085
|
+
keyPrefix?: string;
|
|
4086
|
+
/**
|
|
4087
|
+
* Skip the lazy backfill of legacy execution / eval-history entries into
|
|
4088
|
+
* the sorted-set indexes during `RedisStore.create()`. Default `false`
|
|
4089
|
+
* (backfill runs on startup, one MULTI per 500-entry chunk plus two
|
|
4090
|
+
* probes — cheap when there's nothing to do).
|
|
4091
|
+
*
|
|
4092
|
+
* Set to `true` for installs at six-figure execution counts where the
|
|
4093
|
+
* startup cost is unacceptable. Until you call `backfillExecutionIndex()` /
|
|
4094
|
+
* `backfillEvalIndex()` manually (e.g. during a maintenance window),
|
|
4095
|
+
* `listExecutions` / `listEvalResults` fall back to the legacy
|
|
4096
|
+
* SET + N×GET + JS-sort read path — correct but slow at scale.
|
|
4097
|
+
*/
|
|
4098
|
+
skipMigration?: boolean;
|
|
4099
|
+
/**
|
|
4100
|
+
* Default TTL in **seconds** applied to every storage category that has
|
|
4101
|
+
* a TTL-capable storage shape. Omitted by default — keys never expire,
|
|
4102
|
+
* matching pre-#3 behavior.
|
|
4103
|
+
*
|
|
4104
|
+
* Per-category overrides via {@link ttls} take precedence. Set a category
|
|
4105
|
+
* to `null` in {@link ttls} to opt that category out of the default.
|
|
4106
|
+
*
|
|
4107
|
+
* Strongly recommended in production: without a TTL every save accumulates
|
|
4108
|
+
* forever and your Redis instance will eventually OOM.
|
|
4109
|
+
*
|
|
4110
|
+
* @example
|
|
4111
|
+
* ```ts
|
|
4112
|
+
* await RedisStore.create({
|
|
4113
|
+
* url: 'redis://localhost:6379',
|
|
4114
|
+
* defaultTtl: 60 * 60 * 24 * 30, // 30 days
|
|
4115
|
+
* });
|
|
4116
|
+
* ```
|
|
4117
|
+
*/
|
|
4118
|
+
defaultTtl?: number;
|
|
4119
|
+
/**
|
|
4120
|
+
* Per-category TTL overrides in **seconds**. Use to assign different
|
|
4121
|
+
* lifetimes per category (e.g. short-lived checkpoints, long-lived eval
|
|
4122
|
+
* history). Set a category to `null` to explicitly disable TTL for it
|
|
4123
|
+
* even when {@link defaultTtl} is set. Omitting a category means it falls
|
|
4124
|
+
* back to {@link defaultTtl} (or no TTL if neither is set).
|
|
4125
|
+
*
|
|
4126
|
+
* `pendingDecision` is intentionally NOT in this map — pending decisions
|
|
4127
|
+
* are stored as fields of a shared hash (`axl:decisions`), so a TTL on
|
|
4128
|
+
* that key would expire ALL pending decisions at once, which is almost
|
|
4129
|
+
* never what you want. Resolve decisions individually via
|
|
4130
|
+
* `resolveDecision()` instead.
|
|
4131
|
+
*
|
|
4132
|
+
* Window semantics:
|
|
4133
|
+
* - **Sliding window** (write-only refresh): `memory`, `session`,
|
|
4134
|
+
* `sessionMeta`. Every write resets the TTL, so active *writers* keep
|
|
4135
|
+
* their data and inactive ones forget. Reads do NOT refresh the TTL —
|
|
4136
|
+
* a read-only workload (e.g. agent recalling memories without writing
|
|
4137
|
+
* new ones) eventually ages out. If you need read-activity to count
|
|
4138
|
+
* as "active," issue a no-op `ctx.remember` against a sentinel key
|
|
4139
|
+
* per session turn.
|
|
4140
|
+
* - **Fixed window**: `checkpoint`, `executionState`, `executionHistory`,
|
|
4141
|
+
* `evalHistory`. The TTL is set on the first write and isn't extended
|
|
4142
|
+
* on subsequent writes (via `EXPIRE ... NX`). Designed for runtime-
|
|
4143
|
+
* controlled lifecycles like executions, where the data should age
|
|
4144
|
+
* out on a schedule from creation.
|
|
4145
|
+
*/
|
|
4146
|
+
ttls?: {
|
|
4147
|
+
session?: number | null;
|
|
4148
|
+
sessionMeta?: number | null;
|
|
4149
|
+
checkpoint?: number | null;
|
|
4150
|
+
executionState?: number | null;
|
|
4151
|
+
executionHistory?: number | null;
|
|
4152
|
+
evalHistory?: number | null;
|
|
4153
|
+
memory?: number | null;
|
|
4154
|
+
/**
|
|
4155
|
+
* TTL for the `state.persist: 'streaming'` per-execution event buffer
|
|
4156
|
+
* (`exec-events:{id}` list). Safety net for the scenario where the
|
|
4157
|
+
* operator forgets to wire `runtime.recoverIncompleteStreams()` into
|
|
4158
|
+
* startup — without this TTL, orphaned buffers from crashed
|
|
4159
|
+
* processes accumulate indefinitely. Default `null` (no TTL —
|
|
4160
|
+
* matches pre-streaming-mode behavior; explicit opt-in for the
|
|
4161
|
+
* safety net so recovery isn't surprised by buffers that aged out).
|
|
4162
|
+
*
|
|
4163
|
+
* **MUST be longer than your recovery cadence.** If you restart
|
|
4164
|
+
* processes every 12h and the buffer TTL is 1h, any crash whose
|
|
4165
|
+
* recovery hasn't fired within the hour loses its events to TTL
|
|
4166
|
+
* eviction. Recommended floor: 24× your max time-between-restarts.
|
|
4167
|
+
*/
|
|
4168
|
+
streamingEvents?: number | null;
|
|
4169
|
+
};
|
|
4170
|
+
}
|
|
3868
4171
|
/**
|
|
3869
4172
|
* Redis-backed StateStore using the official `redis` (node-redis) client.
|
|
3870
4173
|
*
|
|
@@ -3876,23 +4179,77 @@ declare class SQLiteStore implements StateStore {
|
|
|
3876
4179
|
*/
|
|
3877
4180
|
declare class RedisStore implements StateStore {
|
|
3878
4181
|
private client;
|
|
4182
|
+
private keyPrefix;
|
|
4183
|
+
private ttls;
|
|
3879
4184
|
private constructor();
|
|
4185
|
+
/**
|
|
4186
|
+
* Resolve the TTL for a given storage category. Returns `undefined` when
|
|
4187
|
+
* no TTL applies (category is `null` in resolved config, the value is
|
|
4188
|
+
* non-positive — Redis EXPIRE 0 immediately deletes, which is almost
|
|
4189
|
+
* never intended — or the value is non-finite).
|
|
4190
|
+
*
|
|
4191
|
+
* Non-finite values (`NaN`, `±Infinity`) defensively map to `undefined`
|
|
4192
|
+
* rather than being passed to Redis, where they'd surface as a wire-level
|
|
4193
|
+
* protocol error at the worst possible time (first write under load).
|
|
4194
|
+
*/
|
|
4195
|
+
private ttlFor;
|
|
3880
4196
|
/**
|
|
3881
4197
|
* Create a connected RedisStore instance.
|
|
3882
4198
|
*
|
|
3883
|
-
*
|
|
4199
|
+
* Accepts either a URL string (back-compat) or a {@link RedisStoreOptions}
|
|
4200
|
+
* object. The object form is required to set a custom `keyPrefix`.
|
|
4201
|
+
*
|
|
4202
|
+
* @example
|
|
4203
|
+
* ```ts
|
|
4204
|
+
* // Default prefix 'axl:'
|
|
4205
|
+
* const store = await RedisStore.create('redis://localhost:6379');
|
|
4206
|
+
*
|
|
4207
|
+
* // Custom prefix for shared-cluster deployments
|
|
4208
|
+
* const store = await RedisStore.create({
|
|
4209
|
+
* url: 'redis://localhost:6379',
|
|
4210
|
+
* keyPrefix: 'axl:prod:',
|
|
4211
|
+
* });
|
|
4212
|
+
* ```
|
|
4213
|
+
*/
|
|
4214
|
+
static create(options?: string | RedisStoreOptions): Promise<RedisStore>;
|
|
4215
|
+
/**
|
|
4216
|
+
* Backfill the execution-history sorted-set from the legacy ID set.
|
|
4217
|
+
* Idempotent — runs only if the ZSET is missing entries the SET has.
|
|
4218
|
+
* Safe to call manually if you constructed the store with
|
|
4219
|
+
* `skipMigration: true`.
|
|
3884
4220
|
*/
|
|
3885
|
-
|
|
4221
|
+
backfillExecutionIndex(): Promise<void>;
|
|
4222
|
+
/** Backfill the eval-history sorted-set. Symmetric to `backfillExecutionIndex`. */
|
|
4223
|
+
backfillEvalIndex(): Promise<void>;
|
|
4224
|
+
/**
|
|
4225
|
+
* Shared implementation for both backfill paths. Reads SET members,
|
|
4226
|
+
* batch-MGETs their data blobs in chunks, and ZADDs each into the new
|
|
4227
|
+
* sorted-set inside a single MULTI per chunk.
|
|
4228
|
+
*/
|
|
4229
|
+
private backfillHistoryIndex;
|
|
3886
4230
|
private checkpointKey;
|
|
3887
4231
|
private sessionKey;
|
|
3888
4232
|
private sessionMetaKey;
|
|
4233
|
+
private sessionIdsKey;
|
|
3889
4234
|
private decisionsKey;
|
|
3890
4235
|
private executionStateKey;
|
|
3891
4236
|
private pendingExecSetKey;
|
|
3892
4237
|
private execHistoryKey;
|
|
3893
4238
|
private execHistorySetKey;
|
|
4239
|
+
/**
|
|
4240
|
+
* Sorted-set index for execution history, scored by `startedAt`. Drives
|
|
4241
|
+
* the O(log N) `listExecutions` fast path; the legacy SET above is kept
|
|
4242
|
+
* as a fallback for users running with `skipMigration: true` before
|
|
4243
|
+
* they've called `backfillExecutionIndex()` manually.
|
|
4244
|
+
*/
|
|
4245
|
+
private execHistoryZsetKey;
|
|
3894
4246
|
private evalHistoryKey;
|
|
3895
4247
|
private evalHistorySetKey;
|
|
4248
|
+
/** Sorted-set index for eval history, scored by `timestamp`. See above. */
|
|
4249
|
+
private evalHistoryZsetKey;
|
|
4250
|
+
private memoryKey;
|
|
4251
|
+
private streamingEventsKey;
|
|
4252
|
+
private streamingIdsKey;
|
|
3896
4253
|
saveCheckpoint(executionId: string, name: string, data: unknown): Promise<void>;
|
|
3897
4254
|
getCheckpoint(executionId: string, name: string): Promise<unknown | null>;
|
|
3898
4255
|
saveSession(sessionId: string, history: ChatMessage[]): Promise<void>;
|
|
@@ -3906,13 +4263,33 @@ declare class RedisStore implements StateStore {
|
|
|
3906
4263
|
saveExecutionState(executionId: string, state: ExecutionState): Promise<void>;
|
|
3907
4264
|
getExecutionState(executionId: string): Promise<ExecutionState | null>;
|
|
3908
4265
|
listPendingExecutions(): Promise<string[]>;
|
|
4266
|
+
saveMemory(scope: string, key: string, value: unknown): Promise<void>;
|
|
4267
|
+
getMemory(scope: string, key: string): Promise<unknown | null>;
|
|
4268
|
+
getAllMemory(scope: string): Promise<Array<{
|
|
4269
|
+
key: string;
|
|
4270
|
+
value: unknown;
|
|
4271
|
+
}>>;
|
|
4272
|
+
deleteMemory(scope: string, key: string): Promise<void>;
|
|
3909
4273
|
saveExecution(execution: ExecutionInfo): Promise<void>;
|
|
3910
4274
|
getExecution(executionId: string): Promise<ExecutionInfo | null>;
|
|
3911
4275
|
listExecutions(limit?: number): Promise<ExecutionInfo[]>;
|
|
4276
|
+
deleteExecution(executionId: string): Promise<boolean>;
|
|
3912
4277
|
saveEvalResult(entry: EvalHistoryEntry): Promise<void>;
|
|
3913
4278
|
listEvalResults(limit?: number): Promise<EvalHistoryEntry[]>;
|
|
3914
4279
|
deleteEvalResult(id: string): Promise<boolean>;
|
|
4280
|
+
/**
|
|
4281
|
+
* Shared list-by-sorted-set implementation. Uses ZREVRANGEBYSCORE +
|
|
4282
|
+
* MGET when the ZSET is populated (post-migration). Falls back to the
|
|
4283
|
+
* legacy SET + N×GET + JS-sort path when the ZSET is empty or smaller
|
|
4284
|
+
* than the SET (covers users that constructed with `skipMigration: true`
|
|
4285
|
+
* before calling backfill).
|
|
4286
|
+
*/
|
|
4287
|
+
private listHistoryByZset;
|
|
3915
4288
|
listSessions(): Promise<string[]>;
|
|
4289
|
+
appendStreamingEvents(executionId: string, events: AxlEvent[]): Promise<void>;
|
|
4290
|
+
finalizeStreamingEvents(executionId: string): Promise<void>;
|
|
4291
|
+
listStreamingExecutions(): Promise<string[]>;
|
|
4292
|
+
getStreamingEvents(executionId: string): Promise<AxlEvent[]>;
|
|
3916
4293
|
/** Close the Redis connection. */
|
|
3917
4294
|
close(): Promise<void>;
|
|
3918
4295
|
deleteCheckpoints(executionId: string): Promise<void>;
|