@salesforce/sfdx-agent-harness-mastra 0.62.0 → 0.63.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,11 @@
|
|
|
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.63.0] - 2026-09-02
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
- **agent-sdk,harness-mastra,harness-claude,harness-openai**: reject concurrent turns with a typed SESSION_BUSY error @W-24044572@ ([#783](https://github.com/forcedotcom/agentic-dx/pull/783))
|
|
10
|
+
|
|
6
11
|
## [0.62.0] - 2026-09-02
|
|
7
12
|
|
|
8
13
|
### Fixes
|
|
@@ -261,6 +261,18 @@ export declare class MastraApprovalCoordinator {
|
|
|
261
261
|
private timedOut;
|
|
262
262
|
constructor(opts: MastraApprovalCoordinatorOptions);
|
|
263
263
|
initialSink(): GenSink<ChatEvent>;
|
|
264
|
+
/**
|
|
265
|
+
* True once this turn has reached a terminal event — a natural `finish`
|
|
266
|
+
* pushed by the pump, or the synthetic `error` + `finish('error')` pair a
|
|
267
|
+
* `teardown` (abort / dispose / timeout / pump-error) emits. Both end the
|
|
268
|
+
* single per-turn sink, so `initial.isEnded()` is the authoritative "this
|
|
269
|
+
* turn is over" signal. A coordinator still pumping — including one PARKED on
|
|
270
|
+
* a `tool-approval-request` or a consumer-tool `submitToolResult` — has not
|
|
271
|
+
* ended its sink and is therefore NOT settled. `MastraHarness.stream()` reads
|
|
272
|
+
* this to reject a concurrent turn with `SESSION_BUSY` (a still-active
|
|
273
|
+
* coordinator) versus safely replace a settled one.
|
|
274
|
+
*/
|
|
275
|
+
isSettled(): boolean;
|
|
264
276
|
/**
|
|
265
277
|
* Mastra's current run id. The harness's MCP-progress dispatcher uses
|
|
266
278
|
* this to filter incoming `notifications/progress` to the coordinator
|
|
@@ -184,6 +184,20 @@ export class MastraApprovalCoordinator {
|
|
|
184
184
|
initialSink() {
|
|
185
185
|
return this.initial;
|
|
186
186
|
}
|
|
187
|
+
/**
|
|
188
|
+
* True once this turn has reached a terminal event — a natural `finish`
|
|
189
|
+
* pushed by the pump, or the synthetic `error` + `finish('error')` pair a
|
|
190
|
+
* `teardown` (abort / dispose / timeout / pump-error) emits. Both end the
|
|
191
|
+
* single per-turn sink, so `initial.isEnded()` is the authoritative "this
|
|
192
|
+
* turn is over" signal. A coordinator still pumping — including one PARKED on
|
|
193
|
+
* a `tool-approval-request` or a consumer-tool `submitToolResult` — has not
|
|
194
|
+
* ended its sink and is therefore NOT settled. `MastraHarness.stream()` reads
|
|
195
|
+
* this to reject a concurrent turn with `SESSION_BUSY` (a still-active
|
|
196
|
+
* coordinator) versus safely replace a settled one.
|
|
197
|
+
*/
|
|
198
|
+
isSettled() {
|
|
199
|
+
return this.initial.isEnded();
|
|
200
|
+
}
|
|
187
201
|
/**
|
|
188
202
|
* Mastra's current run id. The harness's MCP-progress dispatcher uses
|
|
189
203
|
* this to filter incoming `notifications/progress` to the coordinator
|
package/dist/mastra-harness.js
CHANGED
|
@@ -1618,12 +1618,20 @@ export class MastraHarness {
|
|
|
1618
1618
|
// round-trip per tool call.
|
|
1619
1619
|
const { lookupPolicy, batchApprovals } = this.buildPolicyGate(agentState, options);
|
|
1620
1620
|
const gatingActive = lookupPolicy !== undefined;
|
|
1621
|
-
//
|
|
1622
|
-
//
|
|
1623
|
-
//
|
|
1624
|
-
//
|
|
1625
|
-
// turn
|
|
1621
|
+
// Concurrency guard (spike agent-offline-failure-modes #10, W-24044572):
|
|
1622
|
+
// reject a second turn on a thread whose prior turn is still ACTIVE with a
|
|
1623
|
+
// typed `SESSION_BUSY` error, instead of silently disposing the first
|
|
1624
|
+
// (which surfaced to the first consumer as a spurious `DISPOSED` — a
|
|
1625
|
+
// "silent lost turn"). A coordinator is active until its sink ends (natural
|
|
1626
|
+
// finish or teardown), so one still pumping — including one parked on an
|
|
1627
|
+
// approval / consumer-tool result — blocks a new turn. An already-settled
|
|
1628
|
+
// coordinator (finished or errored) is safe to dispose-and-replace, so the
|
|
1629
|
+
// idempotent settling of an already-terminal turn is preserved.
|
|
1626
1630
|
const priorCoordinator = agentState.coordinators.get(threadId);
|
|
1631
|
+
if (priorCoordinator && !priorCoordinator.isSettled()) {
|
|
1632
|
+
throw new AgentSDKError(`A turn is already in progress for thread "${threadId}" on agent "${agentId}". ` +
|
|
1633
|
+
'Only one turn may run at a time per thread; await the in-flight turn before starting another.', AgentSDKErrorType.SESSION_BUSY);
|
|
1634
|
+
}
|
|
1627
1635
|
if (priorCoordinator) {
|
|
1628
1636
|
priorCoordinator.dispose();
|
|
1629
1637
|
agentState.coordinators.delete(threadId);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salesforce/sfdx-agent-harness-mastra",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.63.0",
|
|
4
4
|
"description": "Mastra-backed AgentHarness implementation for @salesforce/sfdx-agent-sdk",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -42,12 +42,12 @@
|
|
|
42
42
|
"openai": "^7.0.0"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
|
-
"@salesforce/sfdx-agent-sdk": "0.
|
|
45
|
+
"@salesforce/sfdx-agent-sdk": "0.64.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.64.0",
|
|
51
51
|
"@types/node": "^22.20.1",
|
|
52
52
|
"@vitest/coverage-istanbul": "^4.1.10",
|
|
53
53
|
"@vitest/eslint-plugin": "^1.6.27",
|