@mastra/redis 1.1.0-alpha.0 → 1.1.1-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,146 @@
1
1
  # @mastra/redis
2
2
 
3
+ ## 1.1.1-alpha.0
4
+
5
+ ### Patch Changes
6
+
7
+ - **Per-key TTL support in `RedisCache`** ([#16283](https://github.com/mastra-ai/mastra/pull/16283))
8
+
9
+ `RedisCache.set()` now accepts an optional `ttlMs` argument that overrides the configured default TTL for a single entry. Sub-second values are rounded up to one second (Redis `EXPIRE` granularity); a non-positive value persists the entry without expiry.
10
+
11
+ ```ts
12
+ const cache = new RedisCache({ url: 'redis://...' });
13
+ await cache.set('weather:nyc', payload, 60_000); // expires in 60s
14
+ await cache.set('manifest', payload, 0); // persists indefinitely
15
+ ```
16
+
17
+ - Respect optional `resourceId` in `getThreadById` so scoped thread lookups return `null` when the thread belongs to a different resource. ([#14237](https://github.com/mastra-ai/mastra/pull/14237))
18
+
19
+ Example:
20
+
21
+ ```typescript
22
+ const thread = await memory.getThreadById({
23
+ threadId: 'my-thread-id',
24
+ resourceId: 'my-user-id',
25
+ });
26
+ // Returns null if the thread does not belong to 'my-user-id'.
27
+ ```
28
+
29
+ - Updated dependencies [[`7c275a8`](https://github.com/mastra-ai/mastra/commit/7c275a810595e1a6c41ccc39720531ab65734700), [`890b24c`](https://github.com/mastra-ai/mastra/commit/890b24cc7d32ed6aa4dfe253e54dc6bf4099f690), [`0f48ebf`](https://github.com/mastra-ai/mastra/commit/0f48ebfc7ac7897b2092a189f45751924cf56d1c), [`f180e49`](https://github.com/mastra-ai/mastra/commit/f180e4990e71b04c9a475b523584071712f0048f), [`9260e01`](https://github.com/mastra-ai/mastra/commit/9260e015276fb1b500f7878ee452b47476bf1583), [`2f6c54e`](https://github.com/mastra-ai/mastra/commit/2f6c54e17c041cac1def54baaa6b771647836414), [`e06a159`](https://github.com/mastra-ai/mastra/commit/e06a1598ca07a6c3778aefc2a2d288363c6294ff), [`db34bc6`](https://github.com/mastra-ai/mastra/commit/db34bc6fb36cf125bda0c46be4d3fdc774b70cc4)]:
30
+ - @mastra/core@1.33.0-alpha.8
31
+
32
+ ## 1.1.0
33
+
34
+ ### Minor Changes
35
+
36
+ - Update peer dependencies to match core package version bump (1.0.5) ([#12557](https://github.com/mastra-ai/mastra/pull/12557))
37
+
38
+ ### Patch Changes
39
+
40
+ - Add durable agents with resumable streams ([#12557](https://github.com/mastra-ai/mastra/pull/12557))
41
+
42
+ Durable agents make agent execution resilient to disconnections, crashes, and long-running operations.
43
+
44
+ ### The Problem
45
+
46
+ Standard agent streaming has two fragility points:
47
+ 1. **Connection drops** - If a client disconnects mid-stream (network blip, browser refresh, mobile app backgrounded), all subsequent events are lost. The client has no way to "catch up" on what they missed.
48
+ 2. **Long-running operations** - Agent loops with tool calls can take minutes. Holding an HTTP connection open that long is unreliable. If the server restarts or the connection times out, the work is lost.
49
+
50
+ ### The Solution
51
+
52
+ **Resumable streams** solve connection drops. Every event is cached with a sequential index. If a client disconnects at event 5, they can reconnect and request events starting from index 6. They receive cached events immediately, then continue with live events as they arrive.
53
+
54
+ **Durable execution** solves long-running operations. Instead of executing the agent loop directly in the HTTP request, execution happens in a workflow engine (built-in evented engine or Inngest). The HTTP request just subscribes to events. If the connection drops, execution continues. The client can reconnect anytime to observe progress.
55
+
56
+ ### Usage
57
+
58
+ Wrap any existing `Agent` with durability using factory functions:
59
+
60
+ ```typescript
61
+ import { Agent } from '@mastra/core/agent';
62
+ import { createDurableAgent } from '@mastra/core/agent/durable';
63
+
64
+ const agent = new Agent({
65
+ id: 'my-agent',
66
+ model: openai('gpt-4'),
67
+ instructions: 'You are helpful',
68
+ });
69
+
70
+ const durableAgent = createDurableAgent({ agent });
71
+ ```
72
+
73
+ **Factory functions for different execution strategies:**
74
+
75
+ | Factory | Execution | Use Case |
76
+ | ---------------------------------------- | ----------------------------------- | ------------------------------- |
77
+ | `createDurableAgent({ agent })` | Local, synchronous | Development, simple deployments |
78
+ | `createEventedAgent({ agent })` | Fire-and-forget via workflow engine | Long-running operations |
79
+ | `createInngestAgent({ agent, inngest })` | Inngest-powered | Production, distributed systems |
80
+
81
+ ### Resumable Streams
82
+
83
+ ```typescript
84
+ // Start streaming
85
+ const { runId, output } = await durableAgent.stream('Analyze this data...');
86
+
87
+ // Client disconnects at event 5...
88
+
89
+ // Reconnect and resume from where we left off
90
+ const { output: resumed } = await durableAgent.observe(runId, { offset: 6 });
91
+ // Receives events 6, 7, 8... from cache, then continues with live events
92
+ ```
93
+
94
+ ### PubSub and Cache
95
+
96
+ Durable agents use two infrastructure components:
97
+
98
+ | Component | Purpose | Default |
99
+ | ---------- | ----------------------------------------- | --------------------- |
100
+ | **PubSub** | Real-time event delivery during streaming | `EventEmitterPubSub` |
101
+ | **Cache** | Stores events for replay on reconnection | `InMemoryServerCache` |
102
+
103
+ When `stream()` is called, events flow through pubsub in real-time. The cache stores each event with a sequential index. When `observe()` is called, missed events replay from cache before continuing with live events.
104
+
105
+ **Configure via Mastra instance (recommended):**
106
+
107
+ ```typescript
108
+ const mastra = new Mastra({
109
+ cache: new RedisServerCache({ url: 'redis://...' }),
110
+ pubsub: new RedisPubSub({ url: 'redis://...' }),
111
+ agents: {
112
+ // Inherits cache and pubsub from Mastra
113
+ myAgent: createDurableAgent({ agent }),
114
+ },
115
+ });
116
+ ```
117
+
118
+ **Configure per-agent (overrides Mastra):**
119
+
120
+ ```typescript
121
+ const durableAgent = createDurableAgent({
122
+ agent,
123
+ cache: new RedisServerCache({ url: 'redis://...' }),
124
+ pubsub: new RedisPubSub({ url: 'redis://...' }),
125
+ });
126
+ ```
127
+
128
+ **Disable caching (streams won't be resumable):**
129
+
130
+ ```typescript
131
+ const durableAgent = createDurableAgent({ agent, cache: false });
132
+ ```
133
+
134
+ For single-instance deployments, the defaults work fine. For multi-instance deployments (load balancer, horizontal scaling), use Redis-backed implementations so any instance can serve reconnection requests.
135
+
136
+ ### Class Hierarchy
137
+ - `DurableAgent` extends `Agent` - base class with resumable streams
138
+ - `EventedAgent` extends `DurableAgent` - fire-and-forget execution
139
+ - `InngestAgent` extends `DurableAgent` - Inngest-powered execution
140
+
141
+ - Updated dependencies [[`920c757`](https://github.com/mastra-ai/mastra/commit/920c75799c6bd71787d86deaf654a35af4c839ca), [`d587199`](https://github.com/mastra-ai/mastra/commit/d5871993c0371bde2b0717d6b47194755baa1443), [`1fe2533`](https://github.com/mastra-ai/mastra/commit/1fe2533c4382ca6858aac7c4b63e888c2eac6541), [`f8694b6`](https://github.com/mastra-ai/mastra/commit/f8694b6fa0b7a5cde71d794c3bbef4957c55bcb8)]:
142
+ - @mastra/core@1.30.0
143
+
3
144
  ## 1.1.0-alpha.0
4
145
 
5
146
  ### Minor Changes
package/dist/cache.d.ts CHANGED
@@ -29,7 +29,7 @@ export declare class RedisServerCache extends MastraServerCache {
29
29
  private serialize;
30
30
  private deserialize;
31
31
  get(key: string): Promise<unknown>;
32
- set(key: string, value: unknown): Promise<void>;
32
+ set(key: string, value: unknown, ttlMs?: number): Promise<void>;
33
33
  listLength(key: string): Promise<number>;
34
34
  listPush(key: string, value: unknown): Promise<void>;
35
35
  listFromTo(key: string, from: number, to?: number): Promise<unknown[]>;
@@ -1 +1 @@
1
- {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"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"}
@@ -3,7 +3,7 @@ name: mastra-redis
3
3
  description: Documentation for @mastra/redis. Use when working with @mastra/redis APIs, configuration, or implementation.
4
4
  metadata:
5
5
  package: "@mastra/redis"
6
- version: "1.1.0-alpha.0"
6
+ version: "1.1.1-alpha.0"
7
7
  ---
8
8
 
9
9
  ## When to use
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.1.0-alpha.0",
2
+ "version": "1.1.1-alpha.0",
3
3
  "package": "@mastra/redis",
4
4
  "exports": {},
5
5
  "modules": {}
package/dist/index.cjs CHANGED
@@ -152,13 +152,16 @@ var StoreMemoryRedis = class extends storage.MemoryStorage {
152
152
  await this.db.scanAndDelete("msg-idx:*");
153
153
  await this.db.scanAndDelete("thread:*:messages");
154
154
  }
155
- async getThreadById({ threadId }) {
155
+ async getThreadById({
156
+ threadId,
157
+ resourceId
158
+ }) {
156
159
  try {
157
160
  const thread = await this.db.get({
158
161
  tableName: storage.TABLE_THREADS,
159
162
  keys: { id: threadId }
160
163
  });
161
- if (!thread) {
164
+ if (!thread || resourceId !== void 0 && thread.resourceId !== resourceId) {
162
165
  return null;
163
166
  }
164
167
  return {
@@ -1840,11 +1843,13 @@ var RedisServerCache = class extends cache.MastraServerCache {
1840
1843
  }
1841
1844
  return this.deserialize(value);
1842
1845
  }
1843
- async set(key, value) {
1846
+ async set(key, value, ttlMs) {
1844
1847
  const fullKey = this.getKey(key);
1845
1848
  const serialized = this.serialize(value);
1846
- if (this.ttlSeconds > 0) {
1847
- await this.setWithExpiry(this.client, fullKey, serialized, this.ttlSeconds);
1849
+ const overrideSeconds = ttlMs !== void 0 ? Math.max(1, Math.ceil(ttlMs / 1e3)) : void 0;
1850
+ const effectiveSeconds = overrideSeconds ?? this.ttlSeconds;
1851
+ if (effectiveSeconds > 0) {
1852
+ await this.setWithExpiry(this.client, fullKey, serialized, effectiveSeconds);
1848
1853
  } else {
1849
1854
  await this.client.set(fullKey, serialized);
1850
1855
  }