@salesforce/sfdx-agent-harness-mastra 0.56.0 → 0.58.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 +9 -0
- package/dist/mastra-harness.d.ts +27 -0
- package/dist/mastra-harness.js +71 -4
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,15 @@
|
|
|
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.58.0] - 2026-08-28
|
|
7
|
+
|
|
8
|
+
_No changes — released alongside dependent packages._
|
|
9
|
+
|
|
10
|
+
## [0.57.0] - 2026-08-28
|
|
11
|
+
|
|
12
|
+
### Fixes
|
|
13
|
+
- **harness-mastra,agent-sdk**: resilient thread enumeration + exception-safe destroyChatSession @W-24014120@ ([#774](https://github.com/forcedotcom/agentic-dx/pull/774))
|
|
14
|
+
|
|
6
15
|
## [0.56.0] - 2026-08-27
|
|
7
16
|
|
|
8
17
|
_No changes — released alongside dependent packages._
|
package/dist/mastra-harness.d.ts
CHANGED
|
@@ -393,8 +393,35 @@ export declare class MastraHarness implements MastraAgentHarness {
|
|
|
393
393
|
* @requirements
|
|
394
394
|
* - MUST throw an error if the specified `agentId` is not found in `activeAgents`.
|
|
395
395
|
* - MUST query threads scoped to this agent and return their IDs.
|
|
396
|
+
* - MUST enumerate resiliently: a single thread whose persisted `metadata` is
|
|
397
|
+
* corrupt MUST NOT suppress the agent's other thread IDs.
|
|
398
|
+
*
|
|
399
|
+
* Resilience rationale (spike agent-offline-failure-modes #11, W-24014120):
|
|
400
|
+
* `memory.listThreads` builds its SELECT as `json(metadata)` over every row, so one
|
|
401
|
+
* malformed-JSON row makes SQLite throw `SQLITE_ERROR: malformed JSON`; Mastra's
|
|
402
|
+
* catch swallows that and returns an EMPTY list — silently un-restoring every thread
|
|
403
|
+
* on the agent (the "one bad thread bricks the whole agent" symptom). We instead
|
|
404
|
+
* enumerate with a column-only `SELECT id` (both `id` and `resourceId` are plain
|
|
405
|
+
* TEXT, never parsed), which a corrupt `metadata` on another row cannot break. The
|
|
406
|
+
* raw query couples to Mastra's `mastra_threads` schema, so it is fallback-guarded —
|
|
407
|
+
* but ONLY for the failures the fallback is meant to survive:
|
|
408
|
+
* - the libsql client isn't reachable (a future Mastra internals change), or
|
|
409
|
+
* - the raw query hits a schema-shape drift (`no such table` / `no such column`).
|
|
410
|
+
* Both degrade to the high-level `listThreads` API with a warning. Any OTHER failure
|
|
411
|
+
* (I/O error, corrupt/locked DB — a genuine whole-store fault) is rethrown unchanged,
|
|
412
|
+
* so the SDK's boot-restore full-failure invariant fires rather than papering over an
|
|
413
|
+
* unreadable store as healthy-but-empty (see `isRecoverableThreadEnumerationError`).
|
|
396
414
|
*/
|
|
397
415
|
getThreadIds(agentId: string): Promise<string[]>;
|
|
416
|
+
/**
|
|
417
|
+
* Degraded thread enumeration via Mastra's high-level `listThreads` API, used when the
|
|
418
|
+
* resilient raw `SELECT id` path is unavailable for a recoverable reason (client absent
|
|
419
|
+
* or schema-shape drift). Logs a warning so the degradation is observable. NOTE: unlike
|
|
420
|
+
* the raw path, `listThreads` is itself vulnerable to a corrupt `metadata` row (the very
|
|
421
|
+
* defect `getThreadIds` exists to route around) — this is a best-effort fallback for the
|
|
422
|
+
* Mastra-rename scenario, not a second line of defense against metadata corruption.
|
|
423
|
+
*/
|
|
424
|
+
private listThreadIdsFallback;
|
|
398
425
|
/**
|
|
399
426
|
* @requirements
|
|
400
427
|
* - MUST throw an error if the specified `agentId` is not found in `activeAgents`.
|
package/dist/mastra-harness.js
CHANGED
|
@@ -131,6 +131,27 @@ export function renderSessionContextSystemText(context) {
|
|
|
131
131
|
* API surface (src/index.ts).
|
|
132
132
|
*/
|
|
133
133
|
const LAST_MESSAGES_WINDOW = 40;
|
|
134
|
+
/**
|
|
135
|
+
* Mastra's thread table (`@mastra/core`'s `TABLE_THREADS`). `getThreadIds` enumerates
|
|
136
|
+
* it with a column-only `SELECT id` to stay immune to a single corrupt `metadata` row
|
|
137
|
+
* (see the resilient-enumeration path in `getThreadIds`). Coupling to this name is the
|
|
138
|
+
* price of that immunity; the raw query is fallback-guarded so a future Mastra rename
|
|
139
|
+
* degrades to the high-level API rather than crashing enumeration.
|
|
140
|
+
*/
|
|
141
|
+
const MASTRA_THREADS_TABLE = 'mastra_threads';
|
|
142
|
+
/**
|
|
143
|
+
* Decides whether a raw `getThreadIds` enumeration failure is a *recoverable* schema-shape
|
|
144
|
+
* drift (our column-only `SELECT id FROM mastra_threads` references a table/column a future
|
|
145
|
+
* Mastra renamed — libsql reports `no such table` / `no such column`), in which case the
|
|
146
|
+
* harness degrades to the high-level `listThreads` API. Every other failure (I/O error,
|
|
147
|
+
* corrupt or locked DB — a genuine whole-store fault) is deliberately NOT recoverable: it
|
|
148
|
+
* MUST propagate so the SDK's boot-restore full-failure invariant fires and the agent
|
|
149
|
+
* surfaces the fault rather than coming up silently empty. (spike #11, W-24014120)
|
|
150
|
+
*/
|
|
151
|
+
function isRecoverableThreadEnumerationError(err) {
|
|
152
|
+
const message = getErrorMessage(err).toLowerCase();
|
|
153
|
+
return message.includes('no such table') || message.includes('no such column');
|
|
154
|
+
}
|
|
134
155
|
/**
|
|
135
156
|
* Maximum flattened-transcript length (in characters) sent to the model in a
|
|
136
157
|
* single summarization call. Transcripts above this size are split into
|
|
@@ -1240,13 +1261,59 @@ export class MastraHarness {
|
|
|
1240
1261
|
* @requirements
|
|
1241
1262
|
* - MUST throw an error if the specified `agentId` is not found in `activeAgents`.
|
|
1242
1263
|
* - MUST query threads scoped to this agent and return their IDs.
|
|
1264
|
+
* - MUST enumerate resiliently: a single thread whose persisted `metadata` is
|
|
1265
|
+
* corrupt MUST NOT suppress the agent's other thread IDs.
|
|
1266
|
+
*
|
|
1267
|
+
* Resilience rationale (spike agent-offline-failure-modes #11, W-24014120):
|
|
1268
|
+
* `memory.listThreads` builds its SELECT as `json(metadata)` over every row, so one
|
|
1269
|
+
* malformed-JSON row makes SQLite throw `SQLITE_ERROR: malformed JSON`; Mastra's
|
|
1270
|
+
* catch swallows that and returns an EMPTY list — silently un-restoring every thread
|
|
1271
|
+
* on the agent (the "one bad thread bricks the whole agent" symptom). We instead
|
|
1272
|
+
* enumerate with a column-only `SELECT id` (both `id` and `resourceId` are plain
|
|
1273
|
+
* TEXT, never parsed), which a corrupt `metadata` on another row cannot break. The
|
|
1274
|
+
* raw query couples to Mastra's `mastra_threads` schema, so it is fallback-guarded —
|
|
1275
|
+
* but ONLY for the failures the fallback is meant to survive:
|
|
1276
|
+
* - the libsql client isn't reachable (a future Mastra internals change), or
|
|
1277
|
+
* - the raw query hits a schema-shape drift (`no such table` / `no such column`).
|
|
1278
|
+
* Both degrade to the high-level `listThreads` API with a warning. Any OTHER failure
|
|
1279
|
+
* (I/O error, corrupt/locked DB — a genuine whole-store fault) is rethrown unchanged,
|
|
1280
|
+
* so the SDK's boot-restore full-failure invariant fires rather than papering over an
|
|
1281
|
+
* unreadable store as healthy-but-empty (see `isRecoverableThreadEnumerationError`).
|
|
1243
1282
|
*/
|
|
1244
1283
|
async getThreadIds(agentId) {
|
|
1245
1284
|
const memory = await this.getMemoryOrThrow(agentId);
|
|
1246
|
-
const
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1285
|
+
const resourceId = this.agentResourceId(agentId);
|
|
1286
|
+
const client = this.storage.client;
|
|
1287
|
+
if (typeof client?.execute !== 'function') {
|
|
1288
|
+
return this.listThreadIdsFallback(agentId, memory, resourceId, 'LibSQLStore client is not accessible');
|
|
1289
|
+
}
|
|
1290
|
+
try {
|
|
1291
|
+
const { rows } = await client.execute({
|
|
1292
|
+
sql: `SELECT id FROM ${MASTRA_THREADS_TABLE} WHERE resourceId = ?`,
|
|
1293
|
+
args: [resourceId],
|
|
1294
|
+
});
|
|
1295
|
+
return rows.map((row) => String(row['id']));
|
|
1296
|
+
}
|
|
1297
|
+
catch (err) {
|
|
1298
|
+
if (!isRecoverableThreadEnumerationError(err)) {
|
|
1299
|
+
throw err;
|
|
1300
|
+
}
|
|
1301
|
+
return this.listThreadIdsFallback(agentId, memory, resourceId, getErrorMessage(err));
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
/**
|
|
1305
|
+
* Degraded thread enumeration via Mastra's high-level `listThreads` API, used when the
|
|
1306
|
+
* resilient raw `SELECT id` path is unavailable for a recoverable reason (client absent
|
|
1307
|
+
* or schema-shape drift). Logs a warning so the degradation is observable. NOTE: unlike
|
|
1308
|
+
* the raw path, `listThreads` is itself vulnerable to a corrupt `metadata` row (the very
|
|
1309
|
+
* defect `getThreadIds` exists to route around) — this is a best-effort fallback for the
|
|
1310
|
+
* Mastra-rename scenario, not a second line of defense against metadata corruption.
|
|
1311
|
+
*/
|
|
1312
|
+
async listThreadIdsFallback(agentId, memory, resourceId, reason) {
|
|
1313
|
+
this.buses
|
|
1314
|
+
.getLogBus()
|
|
1315
|
+
?.warn('getThreadIds: raw enumeration unavailable; falling back to listThreads', { agentId, reason });
|
|
1316
|
+
const { threads } = await memory.listThreads({ filter: { resourceId }, perPage: false });
|
|
1250
1317
|
return threads.map((t) => t.id);
|
|
1251
1318
|
}
|
|
1252
1319
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salesforce/sfdx-agent-harness-mastra",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.58.0",
|
|
4
4
|
"description": "Mastra-backed AgentHarness implementation for @salesforce/sfdx-agent-sdk",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -37,17 +37,17 @@
|
|
|
37
37
|
"@mastra/mcp": "^1.15.0",
|
|
38
38
|
"@mastra/memory": "^1.24.0",
|
|
39
39
|
"@mastra/schema-compat": "^1.3.4",
|
|
40
|
-
"@salesforce/agentic-common": "0.
|
|
40
|
+
"@salesforce/agentic-common": "0.18.0",
|
|
41
41
|
"@smithy/eventstream-codec": "^2.2.0",
|
|
42
42
|
"openai": "^7.0.0"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
|
-
"@salesforce/sfdx-agent-sdk": "0.
|
|
45
|
+
"@salesforce/sfdx-agent-sdk": "0.59.0"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@eslint/js": "^10.0.1",
|
|
49
49
|
"@salesforce/harness-conformance": "0.0.1",
|
|
50
|
-
"@salesforce/sfdx-agent-sdk": "0.
|
|
50
|
+
"@salesforce/sfdx-agent-sdk": "0.59.0",
|
|
51
51
|
"@types/node": "^22.20.1",
|
|
52
52
|
"@vitest/coverage-istanbul": "^4.1.10",
|
|
53
53
|
"@vitest/eslint-plugin": "^1.6.27",
|