@salesforce/sfdx-agent-harness-mastra 0.16.0 → 0.18.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
@@ -3,6 +3,22 @@
3
3
  All notable changes to `@salesforce/sfdx-agent-harness-mastra` are documented in this file.
4
4
  Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
5
5
 
6
+ ## [0.18.0] - 2026-06-11
7
+
8
+ ### Fixes
9
+ - **harness-mastra**: drive one resume per pending approval for parallel batches ([#587](https://github.com/forcedotcom/agentic-dx/pull/587))
10
+
11
+ ## [0.17.0] - 2026-06-09
12
+
13
+ ### Fixes
14
+ - **harness-mastra**: drop inline WAL-pragma workaround now that LibSQLStore.close() ships stable ([#576](https://github.com/forcedotcom/agentic-dx/pull/576))
15
+
16
+ ### Chores
17
+ - **deps**: bump dependencies across packages @W-22695116@ ([#577](https://github.com/forcedotcom/agentic-dx/pull/577))
18
+ - **deps**: bump the mastra group across 1 directory with 5 updates ([#569](https://github.com/forcedotcom/agentic-dx/pull/569))
19
+ - **deps**: bump @libsql/client from 0.15.15 to 0.17.3 ([#570](https://github.com/forcedotcom/agentic-dx/pull/570))
20
+ - **deps-dev**: bump @types/node from 22.19.19 to 22.19.20 in the dev-dependencies group ([#568](https://github.com/forcedotcom/agentic-dx/pull/568))
21
+
6
22
  ## [0.16.0] - 2026-06-09
7
23
 
8
24
  ### Features
package/README.md CHANGED
@@ -26,10 +26,10 @@ internal architecture, see [ARCHITECTURE.md](ARCHITECTURE.md).
26
26
 
27
27
  ### Removing the storage directory on Windows
28
28
 
29
- `manager.shutdown()` closes the libsql client (forcedotcom/agentic-dx#429), but the underlying SQLite/WAL handle release
30
- on Windows is asynchronous at the OS level. After `shutdown()` returns, Windows can take anywhere from ~60 ms to several
31
- seconds to actually release the file lock — so a naïve `fs.rm(storageRoot)` immediately afterward will fail with
32
- `EBUSY`. Pass `maxRetries` and `retryDelay` so Node retries until the OS lets go:
29
+ `manager.shutdown()` checkpoints the WAL and closes the libsql client (forcedotcom/agentic-dx#429,
30
+ mastra-ai/mastra#17285), but the underlying SQLite handle release on Windows is asynchronous at the OS level. After
31
+ `shutdown()` returns, Windows can briefly hold the file lock — so a naïve `fs.rm(storageRoot)` immediately afterward can
32
+ fail with `EBUSY`. Pass `maxRetries` and `retryDelay` so Node retries until the OS lets go:
33
33
 
34
34
  ```ts
35
35
  import { rm } from 'node:fs/promises';
@@ -38,9 +38,7 @@ await manager.shutdown();
38
38
  await rm(storageRoot, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 });
39
39
  ```
40
40
 
41
- Linux and macOS release the handle eagerly; the retry options are no-ops on those platforms. Tracking upstream at
42
- [mastra-ai/mastra#17285](https://github.com/mastra-ai/mastra/issues/17285) — when `LibSQLStore` exposes a public
43
- `close()` that includes a WAL checkpoint, this delay should shrink or disappear.
41
+ Linux and macOS release the handle eagerly; the retry options are no-ops on those platforms.
44
42
 
45
43
  ## Public API
46
44
 
@@ -143,12 +143,11 @@ export declare class MastraApprovalCoordinator {
143
143
  */
144
144
  private currentRunId;
145
145
  /**
146
- * The toolCallId Mastra is currently suspended on (after the natural
147
- * `tool-call-approval` chunk). `null` when no suspension. The
148
- * coordinator's pump loop reads this when the current `fullStream`
149
- * closes to decide whether to drive a resume.
146
+ * ToolCallIds Mastra is currently suspended on. Mastra retains a separate
147
+ * workflow snapshot step per id, so the pump must drive one resume per
148
+ * pending id with `{ toolCallId }` to disambiguate (#586).
150
149
  */
151
- private currentSuspendId;
150
+ private readonly pendingSuspendIds;
152
151
  /** Consumer-tool: bound parkers, keyed by `toolCallId`. `submitToolResult` resolves these. */
153
152
  private readonly pendingByToolCallId;
154
153
  /**
@@ -5,6 +5,13 @@
5
5
  import { AgentSDKError, AgentSDKErrorType, } from '@salesforce/sfdx-agent-sdk';
6
6
  import { GenSink } from '@salesforce/sfdx-agent-sdk/harness';
7
7
  import { MastraEventAdapter } from './mastra-event-adapter.js';
8
+ function pickIdWithDecision(pendingIds, decisions) {
9
+ for (const id of pendingIds) {
10
+ if (decisions.has(id))
11
+ return id;
12
+ }
13
+ return undefined;
14
+ }
8
15
  /**
9
16
  * Per-`(agentId, threadId)` coordinator that subsumes the pre-#529
10
17
  * `MastraStreamBroker` + `activeRuns` pair into one object owning a single
@@ -72,12 +79,11 @@ export class MastraApprovalCoordinator {
72
79
  */
73
80
  currentRunId = '';
74
81
  /**
75
- * The toolCallId Mastra is currently suspended on (after the natural
76
- * `tool-call-approval` chunk). `null` when no suspension. The
77
- * coordinator's pump loop reads this when the current `fullStream`
78
- * closes to decide whether to drive a resume.
82
+ * ToolCallIds Mastra is currently suspended on. Mastra retains a separate
83
+ * workflow snapshot step per id, so the pump must drive one resume per
84
+ * pending id with `{ toolCallId }` to disambiguate (#586).
79
85
  */
80
- currentSuspendId = null;
86
+ pendingSuspendIds = new Set();
81
87
  /** Consumer-tool: bound parkers, keyed by `toolCallId`. `submitToolResult` resolves these. */
82
88
  pendingByToolCallId = new Map();
83
89
  /**
@@ -210,7 +216,7 @@ export class MastraApprovalCoordinator {
210
216
  this.initial.push({ type: 'finish', finishReason: 'error' });
211
217
  this.initial.end();
212
218
  }
213
- this.currentSuspendId = null;
219
+ this.pendingSuspendIds.clear();
214
220
  this.runTeardownCallbacks();
215
221
  }
216
222
  // ───────────────────────── settle methods ───────────────────────────────
@@ -233,14 +239,9 @@ export class MastraApprovalCoordinator {
233
239
  this.consumerSettled.add(toolCallId);
234
240
  this.cancelTimer(toolCallId);
235
241
  this.decisions.set(toolCallId, decision);
236
- // The pump loop is the single owner of `agent.approveToolCall(...)`
237
- // / `declineToolCall(...)` invocation. recordDecision buffers the
238
- // decision; the pump's outer loop reads `decisions.get(suspendId)`
239
- // when it sees the matching suspension and drives the resume. If
240
- // the pump is currently parked on `awaitContinuation` (saw the
241
- // suspension first, decision arrived second), wake it so it
242
- // re-checks `decisions`.
243
- if (this.continuationResolver && this.currentSuspendId === toolCallId) {
242
+ // Wake the pump if it's parked on `awaitContinuation` waiting for
243
+ // a decision on a pending id.
244
+ if (this.continuationResolver && this.pendingSuspendIds.has(toolCallId)) {
244
245
  const resolve = this.continuationResolver;
245
246
  this.continuationResolver = undefined;
246
247
  resolve('continued');
@@ -330,33 +331,29 @@ export class MastraApprovalCoordinator {
330
331
  }
331
332
  if (this.aborted || this.disposed || this.timedOut)
332
333
  return;
333
- // outcome === 'suspended': Mastra suspended after a
334
- // `tool-call-approval` chunk. `currentSuspendId` carries
335
- // the id Mastra is suspended on. Either the consumer
336
- // already settled (decision buffered) and we drive the
337
- // resume now, or we park until a settle arrives.
338
- const suspendId = this.currentSuspendId;
339
- if (suspendId === null) {
340
- // Defensive: stream ended without a suspension. Treat
341
- // as terminal.
334
+ // outcome === 'suspended': drive one resume per outer
335
+ // iteration. For parallel batches, all but the last resume
336
+ // returns a stream that closes silently while still
337
+ // suspended — the pump re-enters here to drive the next id.
338
+ if (this.pendingSuspendIds.size === 0) {
342
339
  if (!this.initial.isEnded())
343
340
  this.initial.end();
344
341
  return;
345
342
  }
346
- let buffered = this.decisions.get(suspendId);
347
- if (buffered === undefined) {
348
- // Park for a decision (or teardown).
343
+ let nextId = pickIdWithDecision(this.pendingSuspendIds, this.decisions);
344
+ if (nextId === undefined) {
349
345
  const cont = await this.awaitContinuation();
350
346
  if (cont === 'done')
351
347
  return;
352
- // Re-read `decisions` post-wake; recordDecision sets it
353
- // before signaling, so we must see it here.
354
- buffered = this.decisions.get(suspendId);
355
- if (buffered === undefined)
348
+ nextId = pickIdWithDecision(this.pendingSuspendIds, this.decisions);
349
+ if (nextId === undefined)
356
350
  return;
357
351
  }
358
- this.currentSuspendId = null;
359
- const next = await this.driveResumeReturning(suspendId, buffered);
352
+ const decision = this.decisions.get(nextId);
353
+ if (decision === undefined)
354
+ return;
355
+ this.pendingSuspendIds.delete(nextId);
356
+ const next = await this.driveResumeReturning(nextId, decision);
360
357
  if (next === 'done')
361
358
  return;
362
359
  currentStream = next;
@@ -434,22 +431,21 @@ export class MastraApprovalCoordinator {
434
431
  continue;
435
432
  }
436
433
  if (event.type === 'tool-approval-request') {
437
- // Mastra's natural `tool-call-approval` chunk → ChatEvent.
438
- // Mastra has suspended on this toolCallId. Decide whether
439
- // to surface a `tool-approval-request` to the consumer or
440
- // auto-approve silently.
434
+ // Parallel batches emit one chunk per suspension before
435
+ // the stream closes; accumulate every id and let the
436
+ // read loop continue to the natural close.
441
437
  const tc = event.toolCall;
442
438
  this.toolCallArgs.set(tc.toolCallId, tc.args);
443
- this.currentSuspendId = tc.toolCallId;
439
+ this.pendingSuspendIds.add(tc.toolCallId);
444
440
  sawSuspension = true;
445
441
  if (this.consumerToolNames.has(tc.toolName)) {
446
442
  // Consumer tools bypass the gate per the
447
443
  // StreamOptions.requireToolApproval JSDoc. Auto-
448
444
  // approve silently — no ChatEvent surfaces, and the
449
- // outer pump loop drives `agent.approveToolCall`
450
- // when it picks up the buffered decision. After
451
- // Mastra resumes, the bridge `execute` parker runs
452
- // and `submitToolResult` settles it — same shape as
445
+ // pump loop drives `agent.approveToolCall` when it
446
+ // picks up the buffered decision. After Mastra
447
+ // resumes, the bridge `execute` parker runs and
448
+ // `submitToolResult` settles it — same shape as
453
449
  // non-approval-mode consumer-tool flow.
454
450
  this.settledByCoordinator.add(tc.toolCallId);
455
451
  this.decisions.set(tc.toolCallId, { kind: 'approve' });
@@ -459,14 +455,7 @@ export class MastraApprovalCoordinator {
459
455
  // pre-emit in tool-call routing).
460
456
  this.emitApprovalRequest(tc.toolCallId, tc.toolName, tc.args);
461
457
  }
462
- // Drain the rest of the stream so Mastra's workflow
463
- // snapshot persists before we attempt a resume.
464
- while (true) {
465
- const { done: d } = await reader.read();
466
- if (d)
467
- break;
468
- }
469
- return 'suspended';
458
+ continue;
470
459
  }
471
460
  this.routeEnriched(event);
472
461
  }
@@ -490,17 +479,13 @@ export class MastraApprovalCoordinator {
490
479
  /* best-effort: lock may already be released after a thrown read */
491
480
  }
492
481
  }
493
- // Stream closed naturally without a `finish` chunk and without a
494
- // suspension. This happens when:
495
- // (a) abort/dispose/timeout fired and Mastra closed the stream
496
- // silently (no chunk emitted).
497
- // (b) consumer-tool flow: the tool's parker resolved, Mastra
498
- // continued the same fullStream, and the next assistant turn
499
- // ran to terminal `finish` — but in that case `finish` would
500
- // have arrived in the loop above. So (b) shouldn't reach here.
501
- // Treat as terminal: sink is either ended already (teardown) or
502
- // we must end it now.
503
- if (!sawTerminal && !sawSuspension && !this.initial.isEnded()) {
482
+ // Stream closed without `finish`/`error`. If any suspension is
483
+ // pending (just observed, or carried over from a prior non-final
484
+ // resume that closed silently), defer to the pump loop's drive.
485
+ if (sawSuspension || this.pendingSuspendIds.size > 0) {
486
+ return 'suspended';
487
+ }
488
+ if (!sawTerminal && !this.initial.isEnded()) {
504
489
  this.initial.end();
505
490
  }
506
491
  return 'terminal';
@@ -540,10 +525,10 @@ export class MastraApprovalCoordinator {
540
525
  return output.fullStream;
541
526
  }
542
527
  catch (err) {
528
+ // Route through teardown so siblings in `pendingSuspendIds`
529
+ // settle as auto-resolved rather than silently orphaning.
543
530
  const error = err instanceof Error ? err : new Error(String(err));
544
- this.initial.push({ type: 'error', error });
545
- this.initial.push({ type: 'finish', finishReason: 'error' });
546
- this.initial.end();
531
+ this.teardown('pump-error', error, 'pump-error');
547
532
  return 'done';
548
533
  }
549
534
  }
@@ -207,18 +207,10 @@ export declare class MastraHarness implements MastraAgentHarness {
207
207
  * would mix process state.
208
208
  */
209
209
  protected readonly activeAgents: Map<string, MastraAgentState>;
210
- private readonly libsqlClient;
211
210
  private readonly storage;
212
211
  private readonly memory;
213
212
  private readonly mastra;
214
213
  private readonly buses;
215
- /**
216
- * Tracks the in-flight storage init so {@link shutdown} can settle it
217
- * before closing the libsql client. Without this, Mastra's lazy storage
218
- * init can fire after `client.close()` and surface as an unhandled
219
- * `CLIENT_CLOSED` rejection (#429 follow-up).
220
- */
221
- private readonly storageReady;
222
214
  /**
223
215
  * Mastra-specific extension surface, typed off `MastraAgentHarness`. The
224
216
  * SDK never reads this; consumers reach through `manager.extensions.mastra`.
@@ -233,10 +225,10 @@ export declare class MastraHarness implements MastraAgentHarness {
233
225
  * @requirements
234
226
  * - MUST disconnect the mcpClient for every active agent before clearing state.
235
227
  * - MUST clear the `activeAgents` map.
236
- * - MUST shut down the global `this.mastra` orchestrator.
237
- * - MUST close the libsql client so the underlying SQLite handle is released
238
- * (Windows blocks fs.rm of the storage dir while the WAL/shm files are open;
239
- * see forcedotcom/agentic-dx#429).
228
+ * - MUST shut down the global `this.mastra` orchestrator. Mastra.shutdown() awaits
229
+ * `storage.close()`, which checkpoints the WAL and unlinks the -wal/-shm sidecars
230
+ * so a subsequent `fs.rm` of the storage dir succeeds on Windows
231
+ * (forcedotcom/agentic-dx#429, mastra-ai/mastra#17285).
240
232
  */
241
233
  shutdown(): Promise<void>;
242
234
  onTelemetry(callback: TelemetryEventCallback): Unsubscribe;
@@ -10,7 +10,6 @@ import { Memory } from '@mastra/memory';
10
10
  import { SkillSearchProcessor, ToolSearchProcessor } from '@mastra/core/processors';
11
11
  import { MASTRA_RESOURCE_ID_KEY, MASTRA_THREAD_ID_KEY, RequestContext } from '@mastra/core/request-context';
12
12
  import { LibSQLStore } from '@mastra/libsql';
13
- import { createClient } from '@libsql/client';
14
13
  import { MCPClient } from '@mastra/mcp';
15
14
  import { AgentSDKError, AgentSDKErrorType, DEFAULT_MAX_STEPS, McpServerStatus, resolveToolApprovalMode, } from '@salesforce/sfdx-agent-sdk';
16
15
  import { HarnessBusOwner, lowerStreamInput, mcpServerConfigEqual, } from '@salesforce/sfdx-agent-sdk/harness';
@@ -340,18 +339,10 @@ export class MastraHarness {
340
339
  * would mix process state.
341
340
  */
342
341
  activeAgents = new Map();
343
- libsqlClient;
344
342
  storage;
345
343
  memory;
346
344
  mastra;
347
345
  buses = new HarnessBusOwner();
348
- /**
349
- * Tracks the in-flight storage init so {@link shutdown} can settle it
350
- * before closing the libsql client. Without this, Mastra's lazy storage
351
- * init can fire after `client.close()` and surface as an unhandled
352
- * `CLIENT_CLOSED` rejection (#429 follow-up).
353
- */
354
- storageReady;
355
346
  /**
356
347
  * Mastra-specific extension surface, typed off `MastraAgentHarness`. The
357
348
  * SDK never reads this; consumers reach through `manager.extensions.mastra`.
@@ -381,24 +372,9 @@ export class MastraHarness {
381
372
  this.clock = opts.clock ?? new RealClock();
382
373
  this.idGenerator = opts.idGenerator ?? new UUIDGenerator();
383
374
  this.toolApprovalTimeoutMs = opts.toolApprovalTimeoutMs ?? 600_000;
384
- // Construct the libsql Client ourselves and inject it into LibSQLStore
385
- // via the `{ client }` constructor form. We keep our own reference so
386
- // shutdown() can call client.close() — Mastra's shutdown() does not
387
- // close the storage, and LibSQLStore declares `client` as private, so
388
- // we cannot reach in. Without an explicit close, the SQLite WAL/shm
389
- // handles stay open on Windows and block fs.rm of the storage dir
390
- // (forcedotcom/agentic-dx#429).
391
- //
392
- // Upstream tracking: https://github.com/mastra-ai/mastra/issues/17285
393
- // — when LibSQLStore exposes a public close(), revert to the simpler
394
- // `new LibSQLStore({ id, url })` form and call store.close() in
395
- // shutdown().
396
- this.libsqlClient = createClient({
397
- url: `file:${this.storageRootFolder}/mastra.db`,
398
- });
399
375
  this.storage = new LibSQLStore({
400
376
  id: 'mastra-storage',
401
- client: this.libsqlClient,
377
+ url: `file:${this.storageRootFolder}/mastra.db`,
402
378
  });
403
379
  this.memory = new Memory({
404
380
  storage: this.storage,
@@ -415,24 +391,15 @@ export class MastraHarness {
415
391
  storage: this.storage,
416
392
  memory: { shared: this.memory },
417
393
  });
418
- // Eagerly run storage init so Mastra's augment-with-init proxy caches
419
- // the result. Without this, any lazy storage call deferred until after
420
- // shutdown() (e.g. background tool/agent registration paths in Mastra)
421
- // races our libsqlClient.close() and surfaces as an unhandled
422
- // CLIENT_CLOSED rejection. We retain the promise so shutdown() can
423
- // settle it before closing the client (a swallowed rejection here is
424
- // fine — Mastra retains its own init proxy and surfaces real init
425
- // errors at the next storage call site).
426
- this.storageReady = this.storage.init().catch(() => undefined);
427
394
  }
428
395
  /**
429
396
  * @requirements
430
397
  * - MUST disconnect the mcpClient for every active agent before clearing state.
431
398
  * - MUST clear the `activeAgents` map.
432
- * - MUST shut down the global `this.mastra` orchestrator.
433
- * - MUST close the libsql client so the underlying SQLite handle is released
434
- * (Windows blocks fs.rm of the storage dir while the WAL/shm files are open;
435
- * see forcedotcom/agentic-dx#429).
399
+ * - MUST shut down the global `this.mastra` orchestrator. Mastra.shutdown() awaits
400
+ * `storage.close()`, which checkpoints the WAL and unlinks the -wal/-shm sidecars
401
+ * so a subsequent `fs.rm` of the storage dir succeeds on Windows
402
+ * (forcedotcom/agentic-dx#429, mastra-ai/mastra#17285).
436
403
  */
437
404
  async shutdown() {
438
405
  await Promise.all([...this.activeAgents.values()].map(async (state) => {
@@ -448,29 +415,6 @@ export class MastraHarness {
448
415
  }));
449
416
  this.activeAgents.clear();
450
417
  await this.mastra?.shutdown();
451
- // Settle the constructor's eager init before closing — see the
452
- // `storageReady` field comment for the why.
453
- await this.storageReady;
454
- // Fold the WAL into the main DB and switch journal_mode=DELETE
455
- // so SQLite unlinks the -wal/-shm sidecars on close. Without this,
456
- // Windows holds the sidecars open well past `client.close()` and
457
- // racing `fs.rm` calls (e.g. vitest's per-test cleanup) crash the
458
- // worker. Mirrors the fix Mastra shipped on `LibSQLStore.close()`
459
- // in `@mastra/libsql@1.12.0-alpha.0`. Replace this block with a
460
- // call to `storage.close()` once that release ships stable — see
461
- // https://github.com/forcedotcom/agentic-dx/issues/475.
462
- try {
463
- await this.libsqlClient.execute('PRAGMA wal_checkpoint(TRUNCATE);');
464
- await this.libsqlClient.execute('PRAGMA journal_mode=DELETE;');
465
- }
466
- catch (err) {
467
- // Best-effort: still close even if checkpoint fails (e.g. the
468
- // client was already closed by a prior shutdown path).
469
- this.buses.getLogBus()?.warn('libsql WAL checkpoint failed before close', {
470
- error: getErrorMessage(err),
471
- });
472
- }
473
- this.libsqlClient.close();
474
418
  this.buses.dispose();
475
419
  }
476
420
  onTelemetry(callback) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/sfdx-agent-harness-mastra",
3
- "version": "0.16.0",
3
+ "version": "0.18.0",
4
4
  "description": "Mastra-backed AgentHarness implementation for @salesforce/sfdx-agent-sdk",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -29,37 +29,36 @@
29
29
  ],
30
30
  "dependencies": {
31
31
  "@ai-sdk/provider": "^3.0.10",
32
- "@libsql/client": "^0.15.15",
33
- "@mastra/core": "^1.37.1",
34
- "@mastra/libsql": "^1.11.1",
35
- "@mastra/mcp": "^1.8.1",
36
- "@mastra/memory": "^1.20.0",
37
- "@mastra/schema-compat": "^1.2.10",
38
- "@salesforce/agentic-common": "0.9.0",
39
- "@salesforce/llm-gateway-sdk": "0.13.0"
32
+ "@mastra/core": "^1.41.0",
33
+ "@mastra/libsql": "^1.12.1",
34
+ "@mastra/mcp": "^1.9.1",
35
+ "@mastra/memory": "^1.20.2",
36
+ "@mastra/schema-compat": "^1.2.11",
37
+ "@salesforce/agentic-common": "0.10.0",
38
+ "@salesforce/llm-gateway-sdk": "0.14.0"
40
39
  },
41
40
  "peerDependencies": {
42
- "@salesforce/sfdx-agent-sdk": "0.17.0"
41
+ "@salesforce/sfdx-agent-sdk": "0.19.0"
43
42
  },
44
43
  "devDependencies": {
45
44
  "@eslint/js": "^10.0.1",
46
- "@salesforce/sfdx-agent-sdk": "0.17.0",
47
- "@types/node": "^22.19.19",
45
+ "@salesforce/sfdx-agent-sdk": "0.19.0",
46
+ "@types/node": "^22.19.20",
48
47
  "@vitest/coverage-istanbul": "^4.1.8",
49
48
  "@vitest/eslint-plugin": "^1.6.19",
50
49
  "eslint": "^10.4.1",
51
50
  "eslint-config-prettier": "^10.1.8",
52
51
  "eslint-import-resolver-typescript": "^4.4.5",
53
52
  "eslint-plugin-import": "^2.32.0",
54
- "eslint-plugin-n": "^18.0.1",
53
+ "eslint-plugin-n": "^18.1.0",
55
54
  "globals": "^17.6.0",
56
55
  "lint-staged": "^17.0.7",
57
- "prettier": "^3.8.3",
56
+ "prettier": "^3.8.4",
58
57
  "rimraf": "^6.1.3",
59
58
  "tsx": "^4.22.4",
60
59
  "typescript": "^6.0.3",
61
- "typescript-eslint": "^8.60.1",
62
- "vitest": "^4.1.7"
60
+ "typescript-eslint": "^8.61.0",
61
+ "vitest": "^4.1.8"
63
62
  },
64
63
  "engines": {
65
64
  "node": ">=22.19.0"