@salesforce/sfdx-agent-harness-mastra 0.63.0 → 0.65.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,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.65.0] - 2026-09-02
|
|
7
|
+
|
|
8
|
+
_No changes — released alongside dependent packages._
|
|
9
|
+
|
|
10
|
+
## [0.64.0] - 2026-09-02
|
|
11
|
+
|
|
12
|
+
### Features
|
|
13
|
+
- **harness-mastra**: synthesize a terminal error when a Mastra stream closes without a finish @W-24026817@ ([#784](https://github.com/forcedotcom/agentic-dx/pull/784))
|
|
14
|
+
|
|
6
15
|
## [0.63.0] - 2026-09-02
|
|
7
16
|
|
|
8
17
|
### Features
|
|
@@ -407,6 +407,40 @@ export declare class MastraApprovalCoordinator {
|
|
|
407
407
|
*/
|
|
408
408
|
private driveResumeReturning;
|
|
409
409
|
private awaitContinuation;
|
|
410
|
+
/**
|
|
411
|
+
* Terminal-event guard (spike agent-offline-failure-modes #07, W-24026817).
|
|
412
|
+
*
|
|
413
|
+
* Called at the pump's silent-close sites — where Mastra's `fullStream`
|
|
414
|
+
* exhausted with neither a `finish`/`error` chunk nor a recognized
|
|
415
|
+
* suspension, so the turn produced no terminal event of its own. A healthy
|
|
416
|
+
* turn never lands here: a normal turn ends on a `finish` chunk, and an
|
|
417
|
+
* approval turn's FINAL resume produces the real continuation `finish` (see
|
|
418
|
+
* ARCHITECTURE.md → "Pump loop", step 5). The realistic trigger is a
|
|
419
|
+
* COORDINATOR-level terminating chunk the adapter intentionally skips —
|
|
420
|
+
* today `tool-call-suspended` (a tool using Mastra's native `suspend()`,
|
|
421
|
+
* which the event adapter maps to `undefined`) — and the guard also closes
|
|
422
|
+
* the class for any future Mastra chunk that ends the stream without a
|
|
423
|
+
* paired finish.
|
|
424
|
+
*
|
|
425
|
+
* Without this, the pump would `initial.end()` the sink silently and the
|
|
426
|
+
* SDK's `DefaultChatSession.wrapEventStream` — which synthesizes its own
|
|
427
|
+
* terminal `finish` ONLY when an error landed — would record the turn as a
|
|
428
|
+
* false-success `chat-stream-completed` with NO terminal `FinishEvent`,
|
|
429
|
+
* breaking the SDK invariant "every chat turn ends with exactly one terminal
|
|
430
|
+
* FinishEvent". Routing through the single `teardown` path pushes the typed
|
|
431
|
+
* `error('incomplete-stream')` + `finish('error')` pair the SDK invariant
|
|
432
|
+
* requires, so the turn fails loudly and recoverably instead. This is the
|
|
433
|
+
* harness-side complement to the SDK-side invariant item #10 pinned.
|
|
434
|
+
*
|
|
435
|
+
* `teardown` short-circuits its sink pushes once the sink is ended
|
|
436
|
+
* (`GenSink.push` no-ops after `end()`), and every terminal path either
|
|
437
|
+
* ended the sink already or set `aborted`/`disposed`/`timedOut` (which the
|
|
438
|
+
* callers check first), so this can never double-signal a turn that already
|
|
439
|
+
* reached a terminal — including 8a's pass-through `empty-stream` error,
|
|
440
|
+
* which arrives as an `error` chunk that ends the sink before the stream
|
|
441
|
+
* closes.
|
|
442
|
+
*/
|
|
443
|
+
private guardTerminalOnSilentClose;
|
|
410
444
|
/**
|
|
411
445
|
* Push an event onto the sink with MCP-server enrichment applied (when
|
|
412
446
|
* the toolName resolves to a known MCP catalog entry). Mirrors the
|
|
@@ -498,8 +498,8 @@ export class MastraApprovalCoordinator {
|
|
|
498
498
|
// returns a stream that closes silently while still
|
|
499
499
|
// suspended — the pump re-enters here to drive the next id.
|
|
500
500
|
if (this.pendingSuspendIds.size === 0) {
|
|
501
|
-
|
|
502
|
-
|
|
501
|
+
// Terminal-event guard — see guardTerminalOnSilentClose.
|
|
502
|
+
this.guardTerminalOnSilentClose();
|
|
503
503
|
return;
|
|
504
504
|
}
|
|
505
505
|
let nextId = pickIdWithDecision(this.pendingSuspendIds, this.decisions);
|
|
@@ -672,9 +672,9 @@ export class MastraApprovalCoordinator {
|
|
|
672
672
|
if (sawSuspension || this.pendingSuspendIds.size > 0) {
|
|
673
673
|
return 'suspended';
|
|
674
674
|
}
|
|
675
|
-
if (!sawTerminal
|
|
676
|
-
|
|
677
|
-
this.
|
|
675
|
+
if (!sawTerminal) {
|
|
676
|
+
// Terminal-event guard — see guardTerminalOnSilentClose.
|
|
677
|
+
this.guardTerminalOnSilentClose();
|
|
678
678
|
}
|
|
679
679
|
return 'terminal';
|
|
680
680
|
}
|
|
@@ -726,6 +726,44 @@ export class MastraApprovalCoordinator {
|
|
|
726
726
|
this.continuationResolver = resolve;
|
|
727
727
|
});
|
|
728
728
|
}
|
|
729
|
+
/**
|
|
730
|
+
* Terminal-event guard (spike agent-offline-failure-modes #07, W-24026817).
|
|
731
|
+
*
|
|
732
|
+
* Called at the pump's silent-close sites — where Mastra's `fullStream`
|
|
733
|
+
* exhausted with neither a `finish`/`error` chunk nor a recognized
|
|
734
|
+
* suspension, so the turn produced no terminal event of its own. A healthy
|
|
735
|
+
* turn never lands here: a normal turn ends on a `finish` chunk, and an
|
|
736
|
+
* approval turn's FINAL resume produces the real continuation `finish` (see
|
|
737
|
+
* ARCHITECTURE.md → "Pump loop", step 5). The realistic trigger is a
|
|
738
|
+
* COORDINATOR-level terminating chunk the adapter intentionally skips —
|
|
739
|
+
* today `tool-call-suspended` (a tool using Mastra's native `suspend()`,
|
|
740
|
+
* which the event adapter maps to `undefined`) — and the guard also closes
|
|
741
|
+
* the class for any future Mastra chunk that ends the stream without a
|
|
742
|
+
* paired finish.
|
|
743
|
+
*
|
|
744
|
+
* Without this, the pump would `initial.end()` the sink silently and the
|
|
745
|
+
* SDK's `DefaultChatSession.wrapEventStream` — which synthesizes its own
|
|
746
|
+
* terminal `finish` ONLY when an error landed — would record the turn as a
|
|
747
|
+
* false-success `chat-stream-completed` with NO terminal `FinishEvent`,
|
|
748
|
+
* breaking the SDK invariant "every chat turn ends with exactly one terminal
|
|
749
|
+
* FinishEvent". Routing through the single `teardown` path pushes the typed
|
|
750
|
+
* `error('incomplete-stream')` + `finish('error')` pair the SDK invariant
|
|
751
|
+
* requires, so the turn fails loudly and recoverably instead. This is the
|
|
752
|
+
* harness-side complement to the SDK-side invariant item #10 pinned.
|
|
753
|
+
*
|
|
754
|
+
* `teardown` short-circuits its sink pushes once the sink is ended
|
|
755
|
+
* (`GenSink.push` no-ops after `end()`), and every terminal path either
|
|
756
|
+
* ended the sink already or set `aborted`/`disposed`/`timedOut` (which the
|
|
757
|
+
* callers check first), so this can never double-signal a turn that already
|
|
758
|
+
* reached a terminal — including 8a's pass-through `empty-stream` error,
|
|
759
|
+
* which arrives as an `error` chunk that ends the sink before the stream
|
|
760
|
+
* closes.
|
|
761
|
+
*/
|
|
762
|
+
guardTerminalOnSilentClose() {
|
|
763
|
+
if (this.initial.isEnded())
|
|
764
|
+
return;
|
|
765
|
+
this.teardown('incomplete-stream', new Error('The model stream closed without a terminal finish or error event.'), 'incomplete-stream');
|
|
766
|
+
}
|
|
729
767
|
// ───────────────────────── routing helpers ──────────────────────────────
|
|
730
768
|
/**
|
|
731
769
|
* Push an event onto the sink with MCP-server enrichment applied (when
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salesforce/sfdx-agent-harness-mastra",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.65.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.66.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.66.0",
|
|
51
51
|
"@types/node": "^22.20.1",
|
|
52
52
|
"@vitest/coverage-istanbul": "^4.1.10",
|
|
53
53
|
"@vitest/eslint-plugin": "^1.6.27",
|