@salesforce/sfdx-agent-harness-mastra 0.44.0 → 0.46.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,16 @@
|
|
|
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.46.0] - 2026-08-10
|
|
7
|
+
|
|
8
|
+
### Fixes
|
|
9
|
+
- **harness-mastra**: normalize empty tool-call args to "{}" across all passthroughs @W-23789221@ ([#745](https://github.com/forcedotcom/agentic-dx/pull/745))
|
|
10
|
+
|
|
11
|
+
## [0.45.0] - 2026-08-10
|
|
12
|
+
|
|
13
|
+
### Fixes
|
|
14
|
+
- **harness-mastra**: normalize empty streamed tool-call args to "{}" @W-23789221@ ([#743](https://github.com/forcedotcom/agentic-dx/pull/743))
|
|
15
|
+
|
|
6
16
|
## [0.44.0] - 2026-08-07
|
|
7
17
|
|
|
8
18
|
_No changes — released alongside dependent packages._
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { AnthropicBedrock } from '@anthropic-ai/bedrock-sdk';
|
|
6
6
|
import Anthropic from '@anthropic-ai/sdk';
|
|
7
|
-
import { TextSpanLifecycle, createGatewayFetch, isEmptyUsage, isParameterExcluded, mapAnthropicFinishReason, mapAnthropicUsage, toolResultOutputToString, } from './v3-stream-mapping.js';
|
|
7
|
+
import { TextSpanLifecycle, createGatewayFetch, isEmptyUsage, isParameterExcluded, mapAnthropicFinishReason, mapAnthropicUsage, normalizeToolArgsString, toolResultOutputToString, } from './v3-stream-mapping.js';
|
|
8
8
|
/**
|
|
9
9
|
* Builds the in-process `LanguageModelV3` impl for the Salesforce LLM Gateway's
|
|
10
10
|
* `/invoke-with-response-stream` (Bedrock-Anthropic pass-through) endpoint.
|
|
@@ -266,7 +266,9 @@ async function doStream(ctx, options) {
|
|
|
266
266
|
type: 'tool-call',
|
|
267
267
|
toolCallId: tu.id,
|
|
268
268
|
toolName: tu.name,
|
|
269
|
-
|
|
269
|
+
// No-arg tool → empty argsBuffer; normalize to '{}'.
|
|
270
|
+
// See normalizeToolArgsString (W-23789221).
|
|
271
|
+
input: normalizeToolArgsString(tu.argsBuffer),
|
|
270
272
|
});
|
|
271
273
|
toolUseByIndex.delete(event.index);
|
|
272
274
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* See LICENSE.txt for license terms.
|
|
4
4
|
*/
|
|
5
5
|
import OpenAI from 'openai';
|
|
6
|
-
import { TextSpanLifecycle, createGatewayFetch, isEmptyUsage, isParameterExcluded, mapOpenAIUsage, toolResultOutputToString, } from './v3-stream-mapping.js';
|
|
6
|
+
import { TextSpanLifecycle, createGatewayFetch, isEmptyUsage, isParameterExcluded, mapOpenAIUsage, normalizeToolArgsString, toolResultOutputToString, } from './v3-stream-mapping.js';
|
|
7
7
|
/**
|
|
8
8
|
* Builds the in-process `LanguageModelV3` impl that drives the `openai` SDK's
|
|
9
9
|
* `chat.completions.create({ stream: true })` against an OpenAI-compatible
|
|
@@ -94,7 +94,8 @@ async function doGenerate(ctx, options) {
|
|
|
94
94
|
type: 'tool-call',
|
|
95
95
|
toolCallId: tc.id,
|
|
96
96
|
toolName: tc.function.name,
|
|
97
|
-
|
|
97
|
+
// No-arg tool → empty arguments; normalize to '{}' (W-23789221).
|
|
98
|
+
input: normalizeToolArgsString(tc.function.arguments),
|
|
98
99
|
});
|
|
99
100
|
}
|
|
100
101
|
}
|
|
@@ -212,7 +213,8 @@ async function doStream(ctx, options) {
|
|
|
212
213
|
type: 'tool-call',
|
|
213
214
|
toolCallId: acc.id,
|
|
214
215
|
toolName: acc.name,
|
|
215
|
-
|
|
216
|
+
// No-arg tool → empty argsBuffer; normalize to '{}' (W-23789221).
|
|
217
|
+
input: normalizeToolArgsString(acc.argsBuffer),
|
|
216
218
|
});
|
|
217
219
|
}
|
|
218
220
|
toolCallsByIndex.clear();
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* See LICENSE.txt for license terms.
|
|
4
4
|
*/
|
|
5
5
|
import OpenAI from 'openai';
|
|
6
|
-
import { TextSpanLifecycle, createGatewayFetch, isEmptyUsage, isParameterExcluded, mapOpenAIFinishReason, mapOpenAIUsage, toolResultOutputToString, } from './v3-stream-mapping.js';
|
|
6
|
+
import { TextSpanLifecycle, createGatewayFetch, isEmptyUsage, isParameterExcluded, mapOpenAIFinishReason, mapOpenAIUsage, normalizeToolArgsString, toolResultOutputToString, } from './v3-stream-mapping.js';
|
|
7
7
|
/**
|
|
8
8
|
* Builds the in-process `LanguageModelV3` impl that drives the `openai` SDK's
|
|
9
9
|
* `responses.create({ stream: true })` against the Salesforce LLM Gateway's
|
|
@@ -89,7 +89,8 @@ async function doGenerate(ctx, options) {
|
|
|
89
89
|
type: 'tool-call',
|
|
90
90
|
toolCallId: item.call_id,
|
|
91
91
|
toolName: item.name,
|
|
92
|
-
|
|
92
|
+
// No-arg tool → empty arguments; normalize to '{}' (W-23789221).
|
|
93
|
+
input: normalizeToolArgsString(item.arguments),
|
|
93
94
|
});
|
|
94
95
|
}
|
|
95
96
|
// Other output-item variants (reasoning, web/file/code search, MCP) are
|
|
@@ -149,7 +150,8 @@ async function doStream(ctx, options) {
|
|
|
149
150
|
type: 'tool-call',
|
|
150
151
|
toolCallId: item.call_id,
|
|
151
152
|
toolName: item.name,
|
|
152
|
-
|
|
153
|
+
// No-arg tool → empty finalArgs; normalize to '{}' (W-23789221).
|
|
154
|
+
input: normalizeToolArgsString(finalArgs),
|
|
153
155
|
});
|
|
154
156
|
if (item.id)
|
|
155
157
|
toolCallById.delete(item.id);
|
|
@@ -265,6 +265,34 @@ type WireCommunicationLikeEvent = {
|
|
|
265
265
|
* worst place for a copy-paste drift to land.
|
|
266
266
|
*/
|
|
267
267
|
export declare function toolResultOutputToString(output: LanguageModelV3ToolResultOutput): string;
|
|
268
|
+
/**
|
|
269
|
+
* Normalize a streamed/returned tool-call arguments STRING to a
|
|
270
|
+
* schema-conforming stringified JSON object.
|
|
271
|
+
*
|
|
272
|
+
* A no-argument tool (input schema `{ type:'object', properties:{} }`) accrues
|
|
273
|
+
* no argument fragments on the wire — Anthropic streams no `input_json_delta`,
|
|
274
|
+
* the OpenAI Responses/Chat-Completions APIs send no arg deltas — so the
|
|
275
|
+
* accumulated buffer is the empty string `''`. `''` is NOT a schema-conforming
|
|
276
|
+
* stringified JSON object: a strict consumer (Mastra `secureJsonParse`, any
|
|
277
|
+
* `JSON.parse`) throws "the provided JSON could not be parsed" and the model
|
|
278
|
+
* retries, producing the empty-args retry loop (W-23789221; same class as the
|
|
279
|
+
* closed gateway-processor defect W-22696051). `'{}'` is the only valid no-arg
|
|
280
|
+
* value.
|
|
281
|
+
*
|
|
282
|
+
* Empty (including whitespace-only) → `'{}'`. Every non-empty buffer — even a
|
|
283
|
+
* malformed partial like `'{"q":'` — is returned VERBATIM: we never re-parse or
|
|
284
|
+
* re-stringify real content, so a genuinely bad payload still reaches the
|
|
285
|
+
* consumer and is still rejected. Applied at every provider's tool-call
|
|
286
|
+
* emission seam so the rule lives in one auditable place.
|
|
287
|
+
*
|
|
288
|
+
* Sibling to `sfdx-agent-chat-generations`'s `normalizeToolArguments` (the
|
|
289
|
+
* gpt5-processor path). This one additionally coerces a whitespace-only buffer
|
|
290
|
+
* — the pass-through streaming paths accumulate raw wire fragments that could in
|
|
291
|
+
* principle be whitespace, whereas the chat-generations processor normalizes
|
|
292
|
+
* earlier. The packages share no dependency, so the rule is duplicated rather
|
|
293
|
+
* than imported; keep the two in sync if the empty-definition changes.
|
|
294
|
+
*/
|
|
295
|
+
export declare function normalizeToolArgsString(args: string | null | undefined): string;
|
|
268
296
|
/**
|
|
269
297
|
* Predicate the missing-usage faithfulness invariant (W-22692131) enforces at
|
|
270
298
|
* every terminal `finish` site: when every input/output token field on the
|
|
@@ -595,6 +595,36 @@ export function toolResultOutputToString(output) {
|
|
|
595
595
|
return output.reason ?? 'tool execution denied';
|
|
596
596
|
}
|
|
597
597
|
}
|
|
598
|
+
/**
|
|
599
|
+
* Normalize a streamed/returned tool-call arguments STRING to a
|
|
600
|
+
* schema-conforming stringified JSON object.
|
|
601
|
+
*
|
|
602
|
+
* A no-argument tool (input schema `{ type:'object', properties:{} }`) accrues
|
|
603
|
+
* no argument fragments on the wire — Anthropic streams no `input_json_delta`,
|
|
604
|
+
* the OpenAI Responses/Chat-Completions APIs send no arg deltas — so the
|
|
605
|
+
* accumulated buffer is the empty string `''`. `''` is NOT a schema-conforming
|
|
606
|
+
* stringified JSON object: a strict consumer (Mastra `secureJsonParse`, any
|
|
607
|
+
* `JSON.parse`) throws "the provided JSON could not be parsed" and the model
|
|
608
|
+
* retries, producing the empty-args retry loop (W-23789221; same class as the
|
|
609
|
+
* closed gateway-processor defect W-22696051). `'{}'` is the only valid no-arg
|
|
610
|
+
* value.
|
|
611
|
+
*
|
|
612
|
+
* Empty (including whitespace-only) → `'{}'`. Every non-empty buffer — even a
|
|
613
|
+
* malformed partial like `'{"q":'` — is returned VERBATIM: we never re-parse or
|
|
614
|
+
* re-stringify real content, so a genuinely bad payload still reaches the
|
|
615
|
+
* consumer and is still rejected. Applied at every provider's tool-call
|
|
616
|
+
* emission seam so the rule lives in one auditable place.
|
|
617
|
+
*
|
|
618
|
+
* Sibling to `sfdx-agent-chat-generations`'s `normalizeToolArguments` (the
|
|
619
|
+
* gpt5-processor path). This one additionally coerces a whitespace-only buffer
|
|
620
|
+
* — the pass-through streaming paths accumulate raw wire fragments that could in
|
|
621
|
+
* principle be whitespace, whereas the chat-generations processor normalizes
|
|
622
|
+
* earlier. The packages share no dependency, so the rule is duplicated rather
|
|
623
|
+
* than imported; keep the two in sync if the empty-definition changes.
|
|
624
|
+
*/
|
|
625
|
+
export function normalizeToolArgsString(args) {
|
|
626
|
+
return args && args.trim() !== '' ? args : '{}';
|
|
627
|
+
}
|
|
598
628
|
/**
|
|
599
629
|
* Predicate the missing-usage faithfulness invariant (W-22692131) enforces at
|
|
600
630
|
* every terminal `finish` site: when every input/output token field on the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salesforce/sfdx-agent-harness-mastra",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.46.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.47.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.47.0",
|
|
51
51
|
"@types/node": "^22.20.1",
|
|
52
52
|
"@vitest/coverage-istanbul": "^4.1.10",
|
|
53
53
|
"@vitest/eslint-plugin": "^1.6.26",
|