@mastra/libsql 1.27.1 → 1.28.0-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/README.md CHANGED
@@ -49,7 +49,7 @@ const results = await vectorStore.query({
49
49
 
50
50
  ## Documentation
51
51
 
52
- - [@mastra/libsql documentation](https://mastra.ai/reference/vectors/libsql)
52
+ - [@mastra/libsql documentation](https://mastra.ai/integrations/vectors/libsql)
53
53
 
54
54
  ## Changelog
55
55
 
@@ -3,7 +3,7 @@ name: mastra-libsql
3
3
  description: Documentation for @mastra/libsql. Use when working with @mastra/libsql APIs, configuration, or implementation.
4
4
  metadata:
5
5
  package: "@mastra/libsql"
6
- version: "1.27.1"
6
+ version: "1.28.0-alpha.0"
7
7
  ---
8
8
 
9
9
  ## When to use
@@ -35,6 +35,7 @@ Read the individual reference documents for detailed explanations and code examp
35
35
  - [GitHub](references/integrations-channels-github.md) - Subscribe Mastra agents to GitHub pull requests with @mastra/github-signals, polling modes, notification signals, and subscription management tools.
36
36
  - [DynamoDB](references/integrations-databases-dynamodb.md) - Persist Mastra data in Amazon DynamoDB with a single-table ElectroDB design, configurable indexes, TTL support, credentials, and table initialization.
37
37
  - [libSQL](references/integrations-databases-libsql.md) - Persist Mastra messages, workflow snapshots, traces, and eval scores in local or remote libSQL with managed tables and initialization options.
38
+ - [libSQL](references/integrations-vectors-libsql.md) - Store embeddings in a local libSQL database or Turso and run similarity search with metadata filtering from Mastra.
38
39
 
39
40
  ### Reference
40
41
 
@@ -48,7 +49,6 @@ Read the individual reference documents for detailed explanations and code examp
48
49
  - [Retrieval, semantic search, reranking](references/reference-rag-retrieval.md) - After storing embeddings, you need to retrieve relevant chunks to answer user queries.
49
50
  - [Reference: Composite storage](references/reference-storage-composite.md) - MastraCompositeStore can compose storage domains from different providers. Use it when you need different databases for different purposes.
50
51
  - [Reference: Storage retention (prune)](references/reference-storage-retention.md) - prune() deletes rows. It caps growth and is safe to run against large tables (batched, bounded, resumable, cancellable).
51
- - [Reference: libSQL vector store](references/reference-vectors-libsql.md) - Configure LibSQLVector for similarity search and metadata filtering with local libSQL, SQLite-compatible vector extensions, or Turso databases.
52
52
 
53
53
 
54
54
  Read [assets/SOURCE_MAP.json](assets/SOURCE_MAP.json) for source code references.
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.27.1",
2
+ "version": "1.28.0-alpha.0",
3
3
  "package": "@mastra/libsql",
4
4
  "exports": {},
5
5
  "modules": {}
@@ -116,7 +116,7 @@ const stream = await agent.stream('Clean up old records', {
116
116
  })
117
117
  ```
118
118
 
119
- The function can be asynchronous. For decisions that depend on the tool name and arguments, use a [classifier](https://mastra.ai/reference/classifier/classifier) to estimate whether the call needs human review:
119
+ The function can be asynchronous. For decisions that depend on the tool name and arguments, use a [classifier](https://mastra.ai/docs/classifier) to estimate whether the call needs human review:
120
120
 
121
121
  ```typescript
122
122
  import { Classifier } from '@mastra/core/classifier'
@@ -589,6 +589,31 @@ A [supervisor agent](https://mastra.ai/docs/subagents) coordinates multiple suba
589
589
 
590
590
  Tool approvals also propagate through multiple levels of delegation. If a supervisor delegates to subagent A, which delegates to subagent B that has a tool with `requireApproval: true`, the approval request still surfaces at the top-level supervisor.
591
591
 
592
+ ### Identify which subagent tool paused
593
+
594
+ The supervisor raises a subagent's pause on its own delegation call, so `toolCallId` on the `tool-call-approval` or `tool-call-suspended` chunk is the supervisor's call to the subagent. Always pass that `toolCallId` when you approve, decline, or resume.
595
+
596
+ To show which of the subagent's tool calls paused, read these fields from the same payload:
597
+
598
+ - `delegatedToolCallId`: The ID of the subagent's tool call that paused. Use it to tell apart two pauses from the same delegation, such as a question followed by an approval.
599
+ - `delegatedToolName`: The name of the subagent's tool that paused.
600
+
601
+ With several levels of delegation, both fields name the deepest tool call, the one that actually paused. Memory saves them with the pause, so they're still there when you reload the conversation, and `generate()` returns them on `suspendPayload`.
602
+
603
+ ```typescript
604
+ const stream = await supervisorAgent.stream('Draft and publish the report')
605
+
606
+ for await (const chunk of stream.fullStream) {
607
+ if (chunk.type === 'tool-call-approval' || chunk.type === 'tool-call-suspended') {
608
+ // Label the pause with the subagent tool call that caused it
609
+ console.log('Paused:', chunk.payload.delegatedToolName, chunk.payload.delegatedToolCallId)
610
+
611
+ // Pass the supervisor's delegation call when you approve, decline, or resume
612
+ console.log('Resume with:', chunk.payload.toolCallId)
613
+ }
614
+ }
615
+ ```
616
+
592
617
  ### Approve and decline in supervisor agents
593
618
 
594
619
  The example below creates a subagent with a tool requiring approval. When the tool triggers an approval request, it surfaces in the supervisor's stream as a `tool-call-approval` chunk:
@@ -12,13 +12,13 @@ Memory processors are [processors](https://mastra.ai/docs/agents/processors) tha
12
12
 
13
13
  ## Built-in memory processors
14
14
 
15
- Mastra automatically adds these processors when memory is enabled:
15
+ Mastra automatically adds these processors when memory is enabled. `MemoryInputFilter` is added only when a memory store is available and stored history will be loaded:
16
16
 
17
17
  ### `MemoryInputFilter`
18
18
 
19
- Trims client-echoed history before memory loaders run. For an existing thread, it keeps only the current user turn or new tool results. For an empty thread, it keeps the full initial input and removes provider item metadata from assistant parts that could refer to items that don't exist in storage.
19
+ Trims client-echoed history before memory loaders run. For an existing thread, it keeps only the current user turn or new tool results. For an empty thread, it keeps the full initial input and removes provider item metadata from assistant parts that could refer to items that don't exist in storage. When `retainFullInput: true` is set, it skips both the trim and the metadata strip and processes the input exactly as supplied, while stored history still loads.
20
20
 
21
- This processor runs first so `MessageHistory`, `SemanticRecall`, and Observational Memory receive only the new input they need.
21
+ By default, this processor runs first so `MessageHistory`, `SemanticRecall`, and Observational Memory receive only the new input they need.
22
22
 
23
23
  ### `MessageHistory`
24
24
 
@@ -320,6 +320,52 @@ const agent = new Agent({
320
320
 
321
321
  Both scenarios are safe - guardrails prevent inappropriate content from being persisted to memory
322
322
 
323
+ ## Aborted runs and saved messages
324
+
325
+ Cancelling a run and blocking it with a processor `abort()` both stop generation, but they save different things:
326
+
327
+ | What stops the run | Memory processors | What's persisted |
328
+ | ----------------------------- | ----------------- | ------------------------------------------------------------ |
329
+ | `abortSignal` from the caller | Still run | The user message and the assistant text the user already saw |
330
+ | `abort()` inside a processor | Skipped | Nothing; the tripwire blocks the save |
331
+
332
+ A caller cancellation goes through output processors like any other run, so the built-in `MessageHistory` processor saves the thread. The partial answer is stored as an ordinary assistant message with no error part, which means the next turn starts from what the user actually read.
333
+
334
+ ```typescript
335
+ import { Agent } from '@mastra/core/agent'
336
+ import { Memory } from '@mastra/memory'
337
+
338
+ const memory = new Memory({ options: { lastMessages: 20 } })
339
+ const agent = new Agent({
340
+ id: 'writer',
341
+ name: 'writer',
342
+ instructions: 'You write step-by-step migration guides.',
343
+ model: 'openai/gpt-6.1-sol',
344
+ memory,
345
+ })
346
+
347
+ const controller = new AbortController()
348
+ const stream = await agent.stream('Write the migration guide.', {
349
+ memory: { thread: 'thread-1', resource: 'user-1' },
350
+ abortSignal: controller.signal,
351
+ onAbort: ({ steps, text }) => {
352
+ console.log(`Aborted after ${steps.length} steps with ${text?.length ?? 0} characters streamed`)
353
+ },
354
+ })
355
+
356
+ let streamed = ''
357
+ for await (const chunk of stream.fullStream) {
358
+ if (chunk.type !== 'text-delta') continue
359
+ streamed += chunk.payload.text
360
+ if (streamed.length > 200) controller.abort()
361
+ }
362
+
363
+ // The partial answer is already in the thread
364
+ const { messages } = await memory.recall({ threadId: 'thread-1', resourceId: 'user-1' })
365
+ ```
366
+
367
+ `onAbort` receives the steps that completed before the cancellation and the assistant text streamed for the in-flight step. For the abort lifecycle itself, including durable runs aborted by `runId`, see the [`stream()` reference](https://mastra.ai/reference/streaming/agents/stream).
368
+
323
369
  ## Handling large attachments
324
370
 
325
371
  Some storage providers enforce record size limits that base64-encoded file attachments can exceed:
@@ -141,24 +141,24 @@ const agent = new Agent({
141
141
 
142
142
  Each vector store page below includes installation instructions, configuration parameters, and usage examples:
143
143
 
144
- - [Astra](https://mastra.ai/reference/vectors/astra)
145
- - [Chroma](https://mastra.ai/reference/vectors/chroma)
146
- - [Cloudflare Vectorize](https://mastra.ai/reference/vectors/vectorize)
147
- - [Convex](https://mastra.ai/reference/vectors/convex)
148
- - [Couchbase](https://mastra.ai/reference/vectors/couchbase)
149
- - [DuckDB](https://mastra.ai/reference/vectors/duckdb)
150
- - [Elasticsearch](https://mastra.ai/reference/vectors/elasticsearch)
151
- - [LanceDB](https://mastra.ai/reference/vectors/lance)
152
- - [libSQL](https://mastra.ai/reference/vectors/libsql)
153
- - [MongoDB](https://mastra.ai/reference/vectors/mongodb)
154
- - [OpenSearch](https://mastra.ai/reference/vectors/opensearch)
155
- - [OracleDB](https://mastra.ai/reference/vectors/oracledb)
156
- - [Pinecone](https://mastra.ai/reference/vectors/pinecone)
157
- - [PostgreSQL](https://mastra.ai/reference/vectors/pg)
158
- - [Qdrant](https://mastra.ai/reference/vectors/qdrant)
159
- - [S3 Vectors](https://mastra.ai/reference/vectors/s3vectors)
160
- - [Turbopuffer](https://mastra.ai/reference/vectors/turbopuffer)
161
- - [Upstash](https://mastra.ai/reference/vectors/upstash)
144
+ - [Astra](https://mastra.ai/integrations/vectors/astra)
145
+ - [Chroma](https://mastra.ai/integrations/vectors/chroma)
146
+ - [Cloudflare Vectorize](https://mastra.ai/integrations/vectors/vectorize)
147
+ - [Convex](https://mastra.ai/integrations/vectors/convex)
148
+ - [Couchbase](https://mastra.ai/integrations/vectors/couchbase)
149
+ - [DuckDB](https://mastra.ai/integrations/vectors/duckdb)
150
+ - [Elasticsearch](https://mastra.ai/integrations/vectors/elasticsearch)
151
+ - [LanceDB](https://mastra.ai/integrations/vectors/lance)
152
+ - [libSQL](https://mastra.ai/integrations/vectors/libsql)
153
+ - [MongoDB](https://mastra.ai/integrations/vectors/mongodb)
154
+ - [OpenSearch](https://mastra.ai/integrations/vectors/opensearch)
155
+ - [OracleDB](https://mastra.ai/integrations/vectors/oracledb)
156
+ - [Pinecone](https://mastra.ai/integrations/vectors/pinecone)
157
+ - [PostgreSQL](https://mastra.ai/integrations/vectors/pg)
158
+ - [Qdrant](https://mastra.ai/integrations/vectors/qdrant)
159
+ - [S3 Vectors](https://mastra.ai/integrations/vectors/s3vectors)
160
+ - [Turbopuffer](https://mastra.ai/integrations/vectors/turbopuffer)
161
+ - [Upstash](https://mastra.ai/integrations/vectors/upstash)
162
162
 
163
163
  ## Recall configuration
164
164
 
@@ -250,7 +250,7 @@ const options = {
250
250
 
251
251
  Some vector stores generate the embeddings themselves. With one of those, semantic recall runs without an `embedder`: the database embeds each message on write and the search string on arrival. The embedding provider stays out of your application entirely.
252
252
 
253
- MongoDB does this through [Automated Embedding](https://mastra.ai/reference/vectors/mongodb): name a Voyage AI model on the vector store and leave the `embedder` out.
253
+ MongoDB does this through [Automated Embedding](https://mastra.ai/integrations/vectors/mongodb): name a Voyage AI model on the vector store and leave the `embedder` out.
254
254
 
255
255
  ```typescript
256
256
  import { Memory } from '@mastra/memory'
@@ -467,7 +467,7 @@ const agent = new Agent({
467
467
  })
468
468
  ```
469
469
 
470
- For detailed information about index configuration options and performance tuning, see the [PgVector configuration guide](https://mastra.ai/reference/vectors/pg).
470
+ For detailed information about index configuration options and performance tuning, see the [PgVector configuration guide](https://mastra.ai/integrations/vectors/pg).
471
471
 
472
472
  ## Disable semantic recall
473
473
 
@@ -6,7 +6,7 @@
6
6
 
7
7
  [libSQL](https://docs.turso.tech/libsql) is an open-source, SQLite-compatible database that supports both local and remote deployments. It can be used to store message history, workflow snapshots, traces, and eval scores.
8
8
 
9
- For vectors like semantic recall or traditional RAG, use [libSQL Vector](https://mastra.ai/reference/vectors/libsql) which covers embeddings and vector search.
9
+ For vectors like semantic recall or traditional RAG, use [libSQL Vector](https://mastra.ai/integrations/vectors/libsql) which covers embeddings and vector search.
10
10
 
11
11
  ## Installation
12
12
 
@@ -75,6 +75,8 @@ const results = await store.query({
75
75
 
76
76
  ## Constructor options
77
77
 
78
+ **id** (`string`): Unique identifier for this vector store instance
79
+
78
80
  **url** (`string`): libSQL database URL. Use ':memory:' for in-memory database, 'file:dbname.db' for local file, or a libSQL-compatible connection string like 'libsql://your-database.turso.io'.
79
81
 
80
82
  **authToken** (`string`): Authentication token for Turso cloud databases
@@ -83,6 +85,12 @@ const results = await store.query({
83
85
 
84
86
  **syncInterval** (`number`): Interval in milliseconds for database sync (Turso specific)
85
87
 
88
+ **maxRetries** (`number`): Total attempts allowed for a write that hits a database lock (SQLITE_BUSY, SQLITE_LOCKED, or a "database is locked" error). The default of 5 means one attempt plus at most four retries. Use at least 1: with 0 the write is never attempted and the call throws (Default: `5`)
89
+
90
+ **initialBackoffMs** (`number`): Backoff before the first retry, in milliseconds. Each later retry waits twice as long (Default: `100`)
91
+
92
+ **vectorTopKOverFetchMultiplier** (`number`): Over-fetch multiplier for vector_top_k queries that have metadata filters. Higher values improve recall at the cost of scanning more rows. Must be a positive integer, or the constructor throws (Default: `10`)
93
+
86
94
  ## Methods
87
95
 
88
96
  ### `createIndex()`
@@ -191,6 +199,14 @@ Delete multiple vectors by IDs or by metadata filter. Either `ids` or `filter` m
191
199
 
192
200
  **filter** (`Record<string, any>`): Metadata filter to identify vectors to delete (mutually exclusive with ids)
193
201
 
202
+ ### `close()`
203
+
204
+ Closes the underlying libSQL client and releases the file handles this vector store holds. Calling it more than once is a no-op.
205
+
206
+ ```typescript
207
+ await store.close()
208
+ ```
209
+
194
210
  ## Response types
195
211
 
196
212
  Query results are returned in this format:
@@ -77,7 +77,7 @@ Visit the [Configuration reference](https://mastra.ai/reference/configuration) f
77
77
 
78
78
  **environment** (`string`): Deployment environment name (e.g. production, staging, development). When set, automatically attached to all observability signals so they can be filtered by environment without passing tracingOptions.metadata.environment on each call. When unset, resolves to development for mastra dev runs, then falls back to process.env.NODE_ENV; left undefined if none are set. Per-call tracingOptions.metadata.environment always takes precedence.
79
79
 
80
- **deployer** (`MastraDeployer`): An instance of a MastraDeployer for managing deployments.
80
+ **deployer** (`IDeployer`): A deployer for managing deployments, such as VercelDeployer from @mastra/deployer-vercel.
81
81
 
82
82
  **server** (`ServerConfig`): Server configuration including port, host, timeout, API routes, middleware, CORS settings, and build options for Swagger UI, API request logging, and OpenAPI docs.
83
83
 
@@ -123,6 +123,8 @@ Visit the [Configuration reference](https://mastra.ai/reference/configuration) f
123
123
 
124
124
  **backgroundTasks.perAgentConcurrency** (`number`): Max concurrent tasks per agent.
125
125
 
126
+ **backgroundTasks.perOwnerConcurrency** (`Record<string, number>`): Per-owner overrides of perAgentConcurrency, keyed by agentId. Overridden owners still count toward globalConcurrency.
127
+
126
128
  **backgroundTasks.backpressure** (`'queue' | 'reject' | 'fallback-sync'`): Behavior when concurrency limit is reached.
127
129
 
128
130
  **backgroundTasks.defaultTimeoutMs** (`number`): Default task timeout in milliseconds.
@@ -61,6 +61,83 @@ export const agent = new Agent({
61
61
 
62
62
  **memory** (`Memory`): A new Memory instance with the specified configuration.
63
63
 
64
+ ## `saveMessages()`
65
+
66
+ `agent.generate()` and `agent.stream()` persist messages for you. Call `memory.saveMessages()` when you need to write messages yourself, for example to seed a thread, to import a transcript, or to store messages that originated outside Mastra. Each message needs a `threadId` and a `resourceId`, so create the thread with [`createThread()`](https://mastra.ai/reference/memory/createThread) before saving into it.
67
+
68
+ ```typescript
69
+ await memory.saveMessages({
70
+ messages: [
71
+ {
72
+ id: 'msg-1',
73
+ threadId: 'thread-123',
74
+ resourceId: 'user-456',
75
+ role: 'user',
76
+ createdAt: new Date(),
77
+ content: {
78
+ format: 2,
79
+ parts: [{ type: 'text', text: 'Where is my order?' }],
80
+ },
81
+ },
82
+ ],
83
+ })
84
+ ```
85
+
86
+ ### Parameters
87
+
88
+ **messages** (`MastraDBMessage[]`): The messages to save. Every message must include a threadId and a resourceId.
89
+
90
+ **memoryConfig** (`MemoryConfig`): Per-call memory options. Merged over the options passed to the constructor.
91
+
92
+ **observabilityContext** (`Partial<ObservabilityContext>`): Tracing context attached to the save operation.
93
+
94
+ Returns `Promise<{ messages: MastraDBMessage[]; usage?: { tokens: number } }>`. `usage` is present when the embedder reports token usage while indexing messages for semantic recall.
95
+
96
+ ### `MastraDBMessage`
97
+
98
+ Storage uses a single message shape, `MastraDBMessage`. The methods that hand messages back or take them in use this shape, including `saveMessages()` and [`recall()`](https://mastra.ai/reference/memory/recall).
99
+
100
+ **id** (`string`): Message identifier.
101
+
102
+ **role** (`'user' | 'assistant' | 'system' | 'signal'`): Who produced the message. System messages and transient signals are not persisted.
103
+
104
+ **threadId** (`string`): Thread the message belongs to. Required when saving.
105
+
106
+ **resourceId** (`string`): Resource (usually a user or tenant) the message belongs to. Required when saving.
107
+
108
+ **createdAt** (`Date`): Message timestamp.
109
+
110
+ **content** (`MastraMessageContentV2`): The message body. Its parts array holds the text, reasoning, tool calls, and attachments.
111
+
112
+ `content.parts` is the part the agent actually reads. `content.format` is always `2` for stored messages, and `content.metadata` carries arbitrary custom data. `content.toolInvocations`, `content.reasoning`, and `content.experimental_attachments` are flat mirrors kept for AI SDK v4 compatibility; write to `parts` instead.
113
+
114
+ ```typescript
115
+ const assistantMessage = {
116
+ id: 'msg-2',
117
+ threadId: 'thread-123',
118
+ resourceId: 'user-456',
119
+ role: 'assistant',
120
+ createdAt: new Date(),
121
+ content: {
122
+ format: 2,
123
+ parts: [
124
+ { type: 'text', text: 'It ships tomorrow.' },
125
+ {
126
+ type: 'tool-invocation',
127
+ toolInvocation: {
128
+ state: 'result',
129
+ toolCallId: 'call-1',
130
+ toolName: 'lookupOrder',
131
+ args: { orderId: 'A-1' },
132
+ result: { eta: '2026-10-30' },
133
+ },
134
+ },
135
+ ],
136
+ metadata: { source: 'import' },
137
+ },
138
+ }
139
+ ```
140
+
64
141
  ## Extended usage example
65
142
 
66
143
  ```typescript
package/dist/index.cjs CHANGED
@@ -3426,10 +3426,13 @@ function rowToTask(row) {
3426
3426
  suspendedAt: row.suspendedAt ? new Date(String(row.suspendedAt)) : void 0,
3427
3427
  completedAt: row.completedAt ? new Date(String(row.completedAt)) : void 0,
3428
3428
  ownerId: row.ownerId != null ? String(row.ownerId) : void 0,
3429
- leaseExpiresAt: row.leaseExpiresAt ? new Date(String(row.leaseExpiresAt)) : void 0
3429
+ leaseExpiresAt: row.leaseExpiresAt ? new Date(String(row.leaseExpiresAt)) : void 0,
3430
+ expiresAt: row.expiresAt ? new Date(String(row.expiresAt)) : void 0,
3431
+ metadata: parseJson$4(row.metadata)
3430
3432
  };
3431
3433
  }
3432
3434
  var BackgroundTasksLibSQL = class BackgroundTasksLibSQL extends _mastra_core_storage.BackgroundTasksStorage {
3435
+ supportsTaskExpiry = true;
3433
3436
  /**
3434
3437
  * Completed/failed task records accumulate. Anchored on `completedAt`, which
3435
3438
  * is NULL while a task is in-flight (pending/running/suspended) — so `completedAt
@@ -3464,7 +3467,9 @@ var BackgroundTasksLibSQL = class BackgroundTasksLibSQL extends _mastra_core_sto
3464
3467
  "suspend_payload",
3465
3468
  "suspendedAt",
3466
3469
  "ownerId",
3467
- "leaseExpiresAt"
3470
+ "leaseExpiresAt",
3471
+ "expiresAt",
3472
+ "metadata"
3468
3473
  ]
3469
3474
  });
3470
3475
  }
@@ -3510,7 +3515,9 @@ var BackgroundTasksLibSQL = class BackgroundTasksLibSQL extends _mastra_core_sto
3510
3515
  suspendedAt: task.suspendedAt?.toISOString() ?? null,
3511
3516
  completedAt: task.completedAt?.toISOString() ?? null,
3512
3517
  ownerId: task.ownerId ?? null,
3513
- leaseExpiresAt: task.leaseExpiresAt?.toISOString() ?? null
3518
+ leaseExpiresAt: task.leaseExpiresAt?.toISOString() ?? null,
3519
+ expiresAt: task.expiresAt?.toISOString() ?? null,
3520
+ metadata: task.metadata ?? null
3514
3521
  }
3515
3522
  });
3516
3523
  }
@@ -3557,6 +3564,14 @@ var BackgroundTasksLibSQL = class BackgroundTasksLibSQL extends _mastra_core_sto
3557
3564
  setClauses.push("leaseExpiresAt = ?");
3558
3565
  params.push(update.leaseExpiresAt?.toISOString() ?? null);
3559
3566
  }
3567
+ if ("expiresAt" in update) {
3568
+ setClauses.push("expiresAt = ?");
3569
+ params.push(update.expiresAt?.toISOString() ?? null);
3570
+ }
3571
+ if ("metadata" in update) {
3572
+ setClauses.push("metadata = jsonb(?)");
3573
+ params.push(serializeJson(update.metadata));
3574
+ }
3560
3575
  if (setClauses.length === 0) return false;
3561
3576
  params.push(taskId);
3562
3577
  let where = "id = ?";
@@ -3578,6 +3593,13 @@ var BackgroundTasksLibSQL = class BackgroundTasksLibSQL extends _mastra_core_sto
3578
3593
  params.push(options.expectedLeaseExpiresAt.toISOString());
3579
3594
  }
3580
3595
  }
3596
+ if (options?.expectedSuspendedAt !== void 0) {
3597
+ if (options.expectedSuspendedAt === null) where += " AND suspendedAt IS NULL";
3598
+ else {
3599
+ where += " AND suspendedAt = ?";
3600
+ params.push(options.expectedSuspendedAt.toISOString());
3601
+ }
3602
+ }
3581
3603
  return (await this.#client.execute({
3582
3604
  sql: `UPDATE ${_mastra_core_storage.TABLE_BACKGROUND_TASKS} SET ${setClauses.join(", ")} WHERE ${where}`,
3583
3605
  args: params
@@ -3622,7 +3644,8 @@ var BackgroundTasksLibSQL = class BackgroundTasksLibSQL extends _mastra_core_sto
3622
3644
  conditions.push("tool_call_id = ?");
3623
3645
  params.push(filter.toolCallId);
3624
3646
  }
3625
- const dateCol = filter.dateFilterBy === "startedAt" ? "startedAt" : filter.dateFilterBy === "suspendedAt" ? "suspendedAt" : filter.dateFilterBy === "completedAt" ? "completedAt" : "createdAt";
3647
+ const dateCol = filter.dateFilterBy === "startedAt" ? "startedAt" : filter.dateFilterBy === "suspendedAt" ? "suspendedAt" : filter.dateFilterBy === "completedAt" ? "completedAt" : filter.dateFilterBy === "expiresAt" ? "expiresAt" : "createdAt";
3648
+ if (filter.hasExpiresAt !== void 0) conditions.push(filter.hasExpiresAt ? "expiresAt IS NOT NULL" : "expiresAt IS NULL");
3626
3649
  if (filter.fromDate) {
3627
3650
  conditions.push(`${dateCol} >= ?`);
3628
3651
  params.push(filter.fromDate.toISOString());
@@ -3670,7 +3693,8 @@ var BackgroundTasksLibSQL = class BackgroundTasksLibSQL extends _mastra_core_sto
3670
3693
  conditions.push(`status IN (${statuses.map(() => "?").join(", ")})`);
3671
3694
  params.push(...statuses);
3672
3695
  }
3673
- const dateCol = filter.dateFilterBy === "startedAt" ? "startedAt" : filter.dateFilterBy === "suspendedAt" ? "suspendedAt" : filter.dateFilterBy === "completedAt" ? "completedAt" : "createdAt";
3696
+ const dateCol = filter.dateFilterBy === "startedAt" ? "startedAt" : filter.dateFilterBy === "suspendedAt" ? "suspendedAt" : filter.dateFilterBy === "completedAt" ? "completedAt" : filter.dateFilterBy === "expiresAt" ? "expiresAt" : "createdAt";
3697
+ if (filter.hasExpiresAt !== void 0) conditions.push(filter.hasExpiresAt ? "expiresAt IS NOT NULL" : "expiresAt IS NULL");
3674
3698
  if (filter.fromDate) {
3675
3699
  conditions.push(`${dateCol} >= ?`);
3676
3700
  params.push(filter.fromDate.toISOString());