@salesforce/sfdx-agent-harness-mastra 0.42.0 → 0.43.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 +8 -0
- package/dist/v3-stream-mapping.js +81 -8
- package/package.json +5 -4
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,14 @@
|
|
|
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.43.0] - 2026-08-05
|
|
7
|
+
|
|
8
|
+
### Tests
|
|
9
|
+
- **agent-sdk,harness-mastra**: e2e suite health — harness matrix, per-harness default model, AWS-eventstream wire parsing @W-23716382@ ([#736](https://github.com/forcedotcom/agentic-dx/pull/736))
|
|
10
|
+
|
|
11
|
+
### Chores
|
|
12
|
+
- **deps-dev**: bump @types/node from 22.20.0 to 22.20.1 in the dev-dependencies group across 1 directory ([#735](https://github.com/forcedotcom/agentic-dx/pull/735))
|
|
13
|
+
|
|
6
14
|
## [0.42.0] - 2026-08-04
|
|
7
15
|
|
|
8
16
|
### Chores
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* See LICENSE.txt for license terms.
|
|
4
4
|
*/
|
|
5
5
|
import { RealClock, UUIDGenerator } from '@salesforce/agentic-common';
|
|
6
|
+
import { EventStreamCodec } from '@smithy/eventstream-codec';
|
|
6
7
|
/**
|
|
7
8
|
* Shared V3 helpers for the Mastra harness's `LanguageModelV3` implementations
|
|
8
9
|
* — stream-shaping primitives, finish-reason / usage mappers, prompt-translation
|
|
@@ -360,6 +361,15 @@ async function observeResponseBody(options) {
|
|
|
360
361
|
let firstTokenAt;
|
|
361
362
|
let sawOutputText = false;
|
|
362
363
|
let sseBuffer = '';
|
|
364
|
+
// The Salesforce gateway streams the Bedrock-Anthropic path as AWS binary
|
|
365
|
+
// event-stream framing, not line-based SSE — the raw `sseBuffer` / `data:`
|
|
366
|
+
// parser recognizes zero text deltas there (W-23528008 follow-up, #734). A
|
|
367
|
+
// codec is constructed lazily only for that content-type; every other path
|
|
368
|
+
// keeps the text-SSE loop untouched.
|
|
369
|
+
const isAwsEventStream = options.contentType?.toLowerCase().includes('application/vnd.amazon.eventstream') ?? false;
|
|
370
|
+
const eventStreamCodec = isAwsEventStream
|
|
371
|
+
? new EventStreamCodec((buf) => decoder.decode(buf), (str) => new TextEncoder().encode(str))
|
|
372
|
+
: undefined;
|
|
363
373
|
let cancelled = false;
|
|
364
374
|
let resolveCancelled;
|
|
365
375
|
const cancellation = new Promise((resolve) => {
|
|
@@ -397,22 +407,33 @@ async function observeResponseBody(options) {
|
|
|
397
407
|
break;
|
|
398
408
|
}
|
|
399
409
|
rawBytes += next.value.byteLength;
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
410
|
+
let deltas;
|
|
411
|
+
if (eventStreamCodec) {
|
|
412
|
+
deltas = decodeEventStreamTextDeltas(eventStreamCodec, next.value, options.providerHint);
|
|
413
|
+
}
|
|
414
|
+
else {
|
|
415
|
+
sseBuffer += decoder.decode(next.value, { stream: true });
|
|
416
|
+
const parsed = takeSseEvents(sseBuffer);
|
|
417
|
+
sseBuffer = parsed.remaining;
|
|
418
|
+
deltas = parsed.events
|
|
419
|
+
.map((event) => outputTextDelta(options.providerHint, options.contentType, event))
|
|
420
|
+
.filter((d) => d !== undefined);
|
|
421
|
+
}
|
|
422
|
+
let budgetExceeded = false;
|
|
423
|
+
for (const delta of deltas) {
|
|
407
424
|
const deltaBytes = Buffer.byteLength(delta, 'utf8');
|
|
408
425
|
if (textBytes + deltaBytes > TRACE_BODY_BYTE_BUDGET) {
|
|
409
|
-
|
|
426
|
+
budgetExceeded = true;
|
|
410
427
|
break;
|
|
411
428
|
}
|
|
412
429
|
sawOutputText = true;
|
|
413
430
|
text.push(delta);
|
|
414
431
|
textBytes += deltaBytes;
|
|
415
432
|
}
|
|
433
|
+
if (budgetExceeded) {
|
|
434
|
+
cancel();
|
|
435
|
+
break;
|
|
436
|
+
}
|
|
416
437
|
}
|
|
417
438
|
}
|
|
418
439
|
catch {
|
|
@@ -460,6 +481,16 @@ function outputTextDelta(providerHint, contentType, event) {
|
|
|
460
481
|
if (!contentType?.toLowerCase().includes('text/event-stream') || typeof event !== 'object' || event === null) {
|
|
461
482
|
return undefined;
|
|
462
483
|
}
|
|
484
|
+
return outputTextDeltaFromEvent(providerHint, event);
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* Extract the assistant output-text delta from an already-parsed streaming
|
|
488
|
+
* event object, without the SSE content-type gate. Shared by the text SSE path
|
|
489
|
+
* (`outputTextDelta`) and the AWS eventstream path (`observeResponseBody`),
|
|
490
|
+
* whose decoded chunk payloads are Anthropic events but arrive under
|
|
491
|
+
* `application/vnd.amazon.eventstream`, not `text/event-stream`.
|
|
492
|
+
*/
|
|
493
|
+
function outputTextDeltaFromEvent(providerHint, event) {
|
|
463
494
|
const candidate = event;
|
|
464
495
|
if (providerHint === 'anthropic' || providerHint === 'bedrock-anthropic') {
|
|
465
496
|
const delta = candidate.delta;
|
|
@@ -480,6 +511,48 @@ function outputTextDelta(providerHint, contentType, event) {
|
|
|
480
511
|
}
|
|
481
512
|
return undefined;
|
|
482
513
|
}
|
|
514
|
+
/**
|
|
515
|
+
* Drain every complete AWS event-stream frame the codec can now assemble from
|
|
516
|
+
* `chunk` and return the assistant text deltas within.
|
|
517
|
+
*
|
|
518
|
+
* Bedrock's `rest-json1` wire nests two layers inside each binary frame: the
|
|
519
|
+
* frame body is JSON `{ "bytes": "<base64>" }`, and that base64 decodes to the
|
|
520
|
+
* Anthropic streaming event (`content_block_delta` / `text_delta`). Non-`chunk`
|
|
521
|
+
* frames (exceptions, keep-alives) and any frame that fails to decode are
|
|
522
|
+
* skipped — wire observation is best-effort and must never throw into the
|
|
523
|
+
* fetch path. The codec verifies prelude + message CRC32 internally and buffers
|
|
524
|
+
* partial frames across chunk boundaries via `feed()`.
|
|
525
|
+
*/
|
|
526
|
+
function decodeEventStreamTextDeltas(codec, chunk, providerHint) {
|
|
527
|
+
const deltas = [];
|
|
528
|
+
try {
|
|
529
|
+
codec.feed(chunk);
|
|
530
|
+
const available = codec.getAvailableMessages().getMessages();
|
|
531
|
+
for (const message of available) {
|
|
532
|
+
const eventType = message.headers[':event-type'];
|
|
533
|
+
if (eventType?.type !== 'string' || eventType.value !== 'chunk')
|
|
534
|
+
continue;
|
|
535
|
+
try {
|
|
536
|
+
const envelope = JSON.parse(new TextDecoder().decode(message.body));
|
|
537
|
+
if (typeof envelope.bytes !== 'string')
|
|
538
|
+
continue;
|
|
539
|
+
const payload = JSON.parse(Buffer.from(envelope.bytes, 'base64').toString('utf8'));
|
|
540
|
+
if (typeof payload !== 'object' || payload === null)
|
|
541
|
+
continue;
|
|
542
|
+
const delta = outputTextDeltaFromEvent(providerHint, payload);
|
|
543
|
+
if (delta !== undefined)
|
|
544
|
+
deltas.push(delta);
|
|
545
|
+
}
|
|
546
|
+
catch {
|
|
547
|
+
// Unparseable frame payload: skip, don't fail the observation.
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
catch {
|
|
552
|
+
// Codec framing error (partial/corrupt bytes): skip this chunk.
|
|
553
|
+
}
|
|
554
|
+
return deltas;
|
|
555
|
+
}
|
|
483
556
|
function tryDecodeBody(body) {
|
|
484
557
|
if (body === undefined || body === null)
|
|
485
558
|
return undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salesforce/sfdx-agent-harness-mastra",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.43.0",
|
|
4
4
|
"description": "Mastra-backed AgentHarness implementation for @salesforce/sfdx-agent-sdk",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -38,16 +38,17 @@
|
|
|
38
38
|
"@mastra/memory": "^1.24.0",
|
|
39
39
|
"@mastra/schema-compat": "^1.3.4",
|
|
40
40
|
"@salesforce/agentic-common": "0.15.0",
|
|
41
|
+
"@smithy/eventstream-codec": "^2.2.0",
|
|
41
42
|
"openai": "^7.0.0"
|
|
42
43
|
},
|
|
43
44
|
"peerDependencies": {
|
|
44
|
-
"@salesforce/sfdx-agent-sdk": "0.
|
|
45
|
+
"@salesforce/sfdx-agent-sdk": "0.44.0"
|
|
45
46
|
},
|
|
46
47
|
"devDependencies": {
|
|
47
48
|
"@eslint/js": "^10.0.1",
|
|
48
49
|
"@salesforce/harness-conformance": "0.0.1",
|
|
49
|
-
"@salesforce/sfdx-agent-sdk": "0.
|
|
50
|
-
"@types/node": "^22.20.
|
|
50
|
+
"@salesforce/sfdx-agent-sdk": "0.44.0",
|
|
51
|
+
"@types/node": "^22.20.1",
|
|
51
52
|
"@vitest/coverage-istanbul": "^4.1.10",
|
|
52
53
|
"@vitest/eslint-plugin": "^1.6.26",
|
|
53
54
|
"eslint": "^10.8.0",
|