@salesforce/sfdx-agent-harness-mastra 0.83.0 → 0.84.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.84.0] - 2026-09-16
7
+
8
+ ### Fixes
9
+ - **harness-mastra**: carry toolName on tool-call-delta so AG-UI TOOL_CALL_START isn't blank @W-23954558@ ([#826](https://github.com/forcedotcom/agentic-dx/pull/826))
10
+
6
11
  ## [0.83.0] - 2026-09-16
7
12
 
8
13
  _No changes — released alongside dependent packages._
@@ -56,6 +56,7 @@ import type { ChatEvent } from '@salesforce/sfdx-agent-sdk';
56
56
  export declare class MastraEventAdapter {
57
57
  private readonly logBus?;
58
58
  private stepIndex;
59
+ private readonly toolNamesByCallId;
59
60
  /**
60
61
  * @param logBus - Optional structured-log sink. When supplied, the adapter emits a `debug`
61
62
  * log for every chunk it intentionally drops — informational lifecycle events, known-but-
@@ -60,6 +60,12 @@ import { isClassifiedStreamError } from './stream-errors.js';
60
60
  export class MastraEventAdapter {
61
61
  logBus;
62
62
  stepIndex = 0;
63
+ // `toolCallId → toolName`, populated from each `tool-call-input-streaming-start`
64
+ // chunk. Mastra omits `toolName` from the `tool-call-delta` chunks that follow
65
+ // (it rides only on the streaming-start), so this lets `tool-call-delta` recover
66
+ // it — see the `tool-call-delta` case. Per-stream instance, so it never grows
67
+ // unbounded or leaks across turns.
68
+ toolNamesByCallId = new Map();
63
69
  /**
64
70
  * @param logBus - Optional structured-log sink. When supplied, the adapter emits a `debug`
65
71
  * log for every chunk it intentionally drops — informational lifecycle events, known-but-
@@ -208,26 +214,46 @@ export class MastraEventAdapter {
208
214
  toolName: chunk.payload.toolName ?? 'unknown',
209
215
  output: chunk.payload.output,
210
216
  };
211
- case 'tool-call-delta':
217
+ case 'tool-call-delta': {
218
+ // Mastra drops `toolName` from this chunk — it carries it only on the
219
+ // preceding `tool-call-input-streaming-start` — so recover it from the
220
+ // per-stream map. Downstream AG-UI opens its TOOL_CALL_START off the
221
+ // first delta and can't correct a blank name from the terminal
222
+ // `tool-call`, so a missing name here surfaces as a blank tool name.
223
+ // Strict `!== undefined` (not truthy) so an empty-string toolName is
224
+ // forwarded honestly instead of silently swallowed.
225
+ const toolName = chunk.payload.toolName ?? this.toolNamesByCallId.get(chunk.payload.toolCallId);
212
226
  return {
213
227
  type: 'tool-call-delta',
214
228
  toolCallId: chunk.payload.toolCallId,
215
- // Strict `!== undefined` (not truthy) so an empty-string
216
- // toolName from upstream — unlikely but possible — is
217
- // forwarded honestly instead of silently swallowed.
218
- ...(chunk.payload.toolName !== undefined ? { toolName: chunk.payload.toolName } : {}),
229
+ ...(toolName !== undefined ? { toolName } : {}),
219
230
  argsTextDelta: chunk.payload.argsTextDelta,
220
231
  };
232
+ }
221
233
  // Known harness chunk types not yet supported in the SDK's ChatEvent
222
234
  // model. Silently dropped to avoid leaking harness internals onto the
223
235
  // public stream. Candidates for future ChatEvent variants when the
224
236
  // corresponding features (citations, file generation, tool input
225
237
  // lifecycle bookends, tool suspension, structured output) are
226
238
  // supported.
227
- case 'source':
228
- case 'file':
229
239
  case 'tool-call-input-streaming-start':
240
+ // Not surfaced on the public stream, but its `toolName` is the only
241
+ // place Mastra carries it for the streamed args — stash it so the
242
+ // following `tool-call-delta` chunks can recover it (see that case).
243
+ if (chunk.payload.toolName !== undefined) {
244
+ this.toolNamesByCallId.set(chunk.payload.toolCallId, chunk.payload.toolName);
245
+ }
246
+ this.logBus?.debug('Skipping unsupported Mastra chunk type', { chunkType: chunk.type });
247
+ return undefined;
230
248
  case 'tool-call-input-streaming-end':
249
+ // End of the streamed-args span: no more deltas for this id will
250
+ // arrive, and the terminal `tool-call` carries its own name, so drop
251
+ // the tracked entry to keep the map bounded within a long stream.
252
+ this.toolNamesByCallId.delete(chunk.payload.toolCallId);
253
+ this.logBus?.debug('Skipping unsupported Mastra chunk type', { chunkType: chunk.type });
254
+ return undefined;
255
+ case 'source':
256
+ case 'file':
231
257
  case 'tool-call-suspended':
232
258
  case 'object':
233
259
  this.logBus?.debug('Skipping unsupported Mastra chunk type', { chunkType: chunk.type });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/sfdx-agent-harness-mastra",
3
- "version": "0.83.0",
3
+ "version": "0.84.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.84.0"
45
+ "@salesforce/sfdx-agent-sdk": "0.85.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.84.0",
50
+ "@salesforce/sfdx-agent-sdk": "0.85.0",
51
51
  "@types/node": "^22.20.1",
52
52
  "@vitest/coverage-istanbul": "^4.1.11",
53
53
  "@vitest/eslint-plugin": "^1.6.27",