@salesforce/sfdx-agent-harness-mastra 0.41.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 CHANGED
@@ -3,6 +3,20 @@
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
+
14
+ ## [0.42.0] - 2026-08-04
15
+
16
+ ### Chores
17
+ - **deps**: sync all workspace dependencies to latest @W-23369873 ([#733](https://github.com/forcedotcom/agentic-dx/pull/733))
18
+ - **deps-dev**: bump @vitest/eslint-plugin from 1.6.23 to 1.6.24 in the vitest group across 1 directory ([#729](https://github.com/forcedotcom/agentic-dx/pull/729))
19
+
6
20
  ## [0.41.0] - 2026-08-04
7
21
 
8
22
  ### Chores
@@ -33,7 +33,7 @@ import type { ToolPolicyRule } from '@salesforce/sfdx-agent-sdk';
33
33
  * `test/mastra-built-in-policies.test.ts` pins only the shape (all `allow`, all
34
34
  * `builtin` matchers), not the names; this caveat is the names' guard rail.
35
35
  */
36
- export declare const MASTRA_BUILT_IN_TOOL_NAMES: readonly ["updateWorkingMemory", "skill", "skill_search", "skill_read"];
36
+ export declare const MASTRA_BUILT_IN_TOOL_NAMES: readonly ['updateWorkingMemory', 'skill', 'skill_search', 'skill_read'];
37
37
  /**
38
38
  * Mastra harness-specific built-in tool-approval rules — the `tiers.harness`
39
39
  * slice passed to `resolveToolApprovalPolicy`. Per the SDK's R4 layering
@@ -48,7 +48,7 @@ export declare class ToolResultRedactionProcessor implements Processor<'sfdx-too
48
48
  private readonly agentId;
49
49
  private readonly redactor;
50
50
  private readonly lookupMcpToolMetadata;
51
- readonly id: "sfdx-tool-result-redaction";
51
+ readonly id: 'sfdx-tool-result-redaction';
52
52
  constructor(agentId: string, redactor: ToolResultRedactor, lookupMcpToolMetadata: McpServerLookup);
53
53
  processInputStep(args: ProcessInputStepArgs): Promise<void>;
54
54
  }
@@ -19,9 +19,9 @@ import { SKILL_BRIDGE_SERVER_ID } from '@salesforce/sfdx-agent-sdk';
19
19
  * sank the host-side bare-leaf workaround in AFV PR #2455.
20
20
  */
21
21
  /** Leaf names injected by Mastra's `ToolSearchProcessor` (no `autoLoad`). */
22
- export declare const TOOL_SEARCH_META_TOOL_NAMES: readonly ["search_tools", "load_tool"];
22
+ export declare const TOOL_SEARCH_META_TOOL_NAMES: readonly ['search_tools', 'load_tool'];
23
23
  /** Leaf names injected by Mastra's `SkillSearchProcessor`. */
24
- export declare const SKILL_SEARCH_META_TOOL_NAMES: readonly ["search_skills", "load_skill"];
24
+ export declare const SKILL_SEARCH_META_TOOL_NAMES: readonly ['search_skills', 'load_skill'];
25
25
  /**
26
26
  * Every capability-discovery meta-tool leaf the harness gives the
27
27
  * `skill_bridge` identity. Kept in lockstep with the SDK's `mcp:skill_bridge:*`
@@ -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
- sseBuffer += decoder.decode(next.value, { stream: true });
401
- const parsed = takeSseEvents(sseBuffer);
402
- sseBuffer = parsed.remaining;
403
- for (const event of parsed.events) {
404
- const delta = outputTextDelta(options.providerHint, options.contentType, event);
405
- if (delta === undefined)
406
- continue;
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
- cancel();
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.41.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",
@@ -29,43 +29,44 @@
29
29
  "LICENSE.txt"
30
30
  ],
31
31
  "dependencies": {
32
- "@ai-sdk/provider": "^3.0.10",
32
+ "@ai-sdk/provider": "^4.0.5",
33
33
  "@anthropic-ai/bedrock-sdk": "^0.32.0",
34
- "@anthropic-ai/sdk": "^0.111.0",
35
- "@mastra/core": "^1.53.0",
36
- "@mastra/libsql": "^1.17.1",
34
+ "@anthropic-ai/sdk": "^0.115.0",
35
+ "@mastra/core": "^1.55.0",
36
+ "@mastra/libsql": "^1.18.0",
37
37
  "@mastra/mcp": "^1.15.0",
38
- "@mastra/memory": "^1.23.1",
39
- "@mastra/schema-compat": "^1.2.11",
40
- "@salesforce/agentic-common": "0.14.0",
41
- "openai": "^6.0.0"
38
+ "@mastra/memory": "^1.24.0",
39
+ "@mastra/schema-compat": "^1.3.4",
40
+ "@salesforce/agentic-common": "0.15.0",
41
+ "@smithy/eventstream-codec": "^2.2.0",
42
+ "openai": "^7.0.0"
42
43
  },
43
44
  "peerDependencies": {
44
- "@salesforce/sfdx-agent-sdk": "0.42.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.42.0",
50
- "@types/node": "^22.20.0",
50
+ "@salesforce/sfdx-agent-sdk": "0.44.0",
51
+ "@types/node": "^22.20.1",
51
52
  "@vitest/coverage-istanbul": "^4.1.10",
52
- "@vitest/eslint-plugin": "^1.6.22",
53
+ "@vitest/eslint-plugin": "^1.6.26",
53
54
  "eslint": "^10.8.0",
54
55
  "eslint-config-prettier": "^10.1.8",
55
56
  "eslint-import-resolver-typescript": "^4.4.5",
56
57
  "eslint-plugin-import": "^2.32.0",
57
- "eslint-plugin-n": "^18.2.1",
58
- "globals": "^17.6.0",
59
- "lint-staged": "^17.0.7",
60
- "prettier": "^3.9.4",
58
+ "eslint-plugin-n": "^18.2.2",
59
+ "globals": "^17.9.0",
60
+ "lint-staged": "^17.3.0",
61
+ "prettier": "^3.9.6",
61
62
  "rimraf": "^6.1.3",
62
- "tsx": "^4.23.0",
63
- "typescript": "^6.0.3",
64
- "typescript-eslint": "^8.63.0",
63
+ "tsx": "^4.23.5",
64
+ "typescript": "^7.0.2",
65
+ "typescript-eslint": "^8.66.0",
65
66
  "vitest": "^4.1.8"
66
67
  },
67
68
  "engines": {
68
- "node": ">=22.22.0"
69
+ "node": ">=22.22.1"
69
70
  },
70
71
  "lint-staged": {
71
72
  "*.{js,jsx,ts,tsx,json,md}": [