@salesforce/sfdx-agent-harness-mastra 0.29.0 → 0.30.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 +10 -0
- package/dist/mastra-message-mapper.d.ts +13 -3
- package/dist/mastra-message-mapper.js +59 -26
- package/package.json +11 -11
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.30.0] - 2026-07-07
|
|
7
|
+
|
|
8
|
+
### Fixes
|
|
9
|
+
- **harness-mastra**: getMessages() round-trips the tool-call part @W-23351939 ([#648](https://github.com/forcedotcom/agentic-dx/pull/648))
|
|
10
|
+
|
|
11
|
+
### Chores
|
|
12
|
+
- **deps**: bump the mastra group across 1 directory with 5 updates ([#644](https://github.com/forcedotcom/agentic-dx/pull/644))
|
|
13
|
+
- **deps**: bump @anthropic-ai/bedrock-sdk from 0.31.0 to 0.32.0 ([#645](https://github.com/forcedotcom/agentic-dx/pull/645))
|
|
14
|
+
- **deps-dev**: bump the dev-dependencies group with 3 updates ([#642](https://github.com/forcedotcom/agentic-dx/pull/642))
|
|
15
|
+
|
|
6
16
|
## [0.29.0] - 2026-07-03
|
|
7
17
|
|
|
8
18
|
_No changes — released alongside dependent packages._
|
|
@@ -15,13 +15,23 @@ import type { LogBus } from '@salesforce/agentic-common';
|
|
|
15
15
|
*/
|
|
16
16
|
export declare function mapMastraToMessage(msg: MastraDBMessage, logBus?: LogBus): Message;
|
|
17
17
|
/**
|
|
18
|
-
* Converts a single Mastra message part to
|
|
19
|
-
*
|
|
18
|
+
* Converts a single Mastra message part to zero or more SDK {@link MessagePart}s.
|
|
19
|
+
*
|
|
20
|
+
* Most Mastra parts map one-to-one, but a **completed** `tool-invocation` maps to
|
|
21
|
+
* *two* SDK parts — a `tool-call` (name + args) followed by a `tool-result` — because
|
|
22
|
+
* a single Mastra object carries both halves once its `state` transitions to `'result'`.
|
|
23
|
+
* Emitting both keeps `getMessages()` history harness-agnostic: the model's original
|
|
24
|
+
* tool call (name + args) survives on read exactly as it does on the Claude harness,
|
|
25
|
+
* which stores the call and result as distinct blocks (#647). Returning an array is what
|
|
26
|
+
* lets one input part expand to two output parts without a special case in the caller.
|
|
27
|
+
*
|
|
28
|
+
* Returns an empty array for part types that exist in Mastra but have no SDK
|
|
29
|
+
* representation (e.g. `source`, `step-start`, `partial-call`, `data-*`).
|
|
20
30
|
*
|
|
21
31
|
* @param logBus - When supplied, unrecognized part types emit a `warn` log via this bus.
|
|
22
32
|
* Tests that don't care about logging can omit it.
|
|
23
33
|
*/
|
|
24
|
-
export declare function mapMastraPartToMessagePart(part: MastraMessagePart, logBus?: LogBus): MessagePart
|
|
34
|
+
export declare function mapMastraPartToMessagePart(part: MastraMessagePart, logBus?: LogBus): MessagePart[];
|
|
25
35
|
/**
|
|
26
36
|
* Converts an SDK {@link Message} into the Mastra persisted message format.
|
|
27
37
|
* The `role` field is normalised: `'tool'` role (which has no Mastra equivalent)
|
|
@@ -47,10 +47,7 @@ export function mapMastraToMessage(msg, logBus) {
|
|
|
47
47
|
}
|
|
48
48
|
const parts = [];
|
|
49
49
|
for (const part of msg.content.parts) {
|
|
50
|
-
|
|
51
|
-
if (mapped) {
|
|
52
|
-
parts.push(mapped);
|
|
53
|
-
}
|
|
50
|
+
parts.push(...mapMastraPartToMessagePart(part, logBus));
|
|
54
51
|
}
|
|
55
52
|
const consolidated = consolidateAdjacentTextParts(parts);
|
|
56
53
|
const isSingleText = consolidated.length === 1 && consolidated[0].type === 'text';
|
|
@@ -85,8 +82,18 @@ function consolidateAdjacentTextParts(parts) {
|
|
|
85
82
|
return result;
|
|
86
83
|
}
|
|
87
84
|
/**
|
|
88
|
-
* Converts a single Mastra message part to
|
|
89
|
-
*
|
|
85
|
+
* Converts a single Mastra message part to zero or more SDK {@link MessagePart}s.
|
|
86
|
+
*
|
|
87
|
+
* Most Mastra parts map one-to-one, but a **completed** `tool-invocation` maps to
|
|
88
|
+
* *two* SDK parts — a `tool-call` (name + args) followed by a `tool-result` — because
|
|
89
|
+
* a single Mastra object carries both halves once its `state` transitions to `'result'`.
|
|
90
|
+
* Emitting both keeps `getMessages()` history harness-agnostic: the model's original
|
|
91
|
+
* tool call (name + args) survives on read exactly as it does on the Claude harness,
|
|
92
|
+
* which stores the call and result as distinct blocks (#647). Returning an array is what
|
|
93
|
+
* lets one input part expand to two output parts without a special case in the caller.
|
|
94
|
+
*
|
|
95
|
+
* Returns an empty array for part types that exist in Mastra but have no SDK
|
|
96
|
+
* representation (e.g. `source`, `step-start`, `partial-call`, `data-*`).
|
|
90
97
|
*
|
|
91
98
|
* @param logBus - When supplied, unrecognized part types emit a `warn` log via this bus.
|
|
92
99
|
* Tests that don't care about logging can omit it.
|
|
@@ -94,60 +101,62 @@ function consolidateAdjacentTextParts(parts) {
|
|
|
94
101
|
export function mapMastraPartToMessagePart(part, logBus) {
|
|
95
102
|
switch (part.type) {
|
|
96
103
|
case 'text':
|
|
97
|
-
return { type: 'text', text: part.text };
|
|
104
|
+
return [{ type: 'text', text: part.text }];
|
|
98
105
|
case 'reasoning':
|
|
99
|
-
return { type: 'reasoning', text: part.reasoning };
|
|
106
|
+
return [{ type: 'reasoning', text: part.reasoning }];
|
|
100
107
|
case 'tool-invocation': {
|
|
101
108
|
const inv = part.toolInvocation;
|
|
102
109
|
if (inv.state === 'partial-call') {
|
|
103
|
-
return
|
|
104
|
-
}
|
|
105
|
-
if (inv.state === 'result') {
|
|
106
|
-
return {
|
|
107
|
-
type: 'tool-result',
|
|
108
|
-
toolCallId: inv.toolCallId,
|
|
109
|
-
toolName: inv.toolName,
|
|
110
|
-
result: inv.result,
|
|
111
|
-
};
|
|
110
|
+
return [];
|
|
112
111
|
}
|
|
113
|
-
|
|
112
|
+
const toolCall = {
|
|
114
113
|
type: 'tool-call',
|
|
115
114
|
toolCallId: inv.toolCallId,
|
|
116
115
|
toolName: inv.toolName,
|
|
117
|
-
args: inv.args,
|
|
116
|
+
args: (inv.args ?? {}),
|
|
118
117
|
};
|
|
118
|
+
if (inv.state === 'result') {
|
|
119
|
+
// A completed invocation carries both the original call (name + args)
|
|
120
|
+
// and its result; surface both so history round-trips the call the
|
|
121
|
+
// model made, not just its outcome.
|
|
122
|
+
return [
|
|
123
|
+
toolCall,
|
|
124
|
+
{ type: 'tool-result', toolCallId: inv.toolCallId, toolName: inv.toolName, result: inv.result },
|
|
125
|
+
];
|
|
126
|
+
}
|
|
127
|
+
return [toolCall];
|
|
119
128
|
}
|
|
120
129
|
case 'file': {
|
|
121
130
|
// Read path: base64 data is returned verbatim from LibSQL — see ARCHITECTURE.md → "Memory Strategy"
|
|
122
131
|
// for the storage-cost caveat (no blob offloading; full encoded size per attachment per row).
|
|
123
132
|
const fileName = readSalesforceFileName(part.providerMetadata);
|
|
124
133
|
if (part.mimeType === 'image/png' || part.mimeType === 'image/jpeg') {
|
|
125
|
-
return { type: 'image', mimeType: part.mimeType, data: part.data, ...(fileName ? { fileName } : {}) };
|
|
134
|
+
return [{ type: 'image', mimeType: part.mimeType, data: part.data, ...(fileName ? { fileName } : {}) }];
|
|
126
135
|
}
|
|
127
136
|
if (part.mimeType === 'application/pdf') {
|
|
128
|
-
return { type: 'file', mimeType: part.mimeType, data: part.data, ...(fileName ? { fileName } : {}) };
|
|
137
|
+
return [{ type: 'file', mimeType: part.mimeType, data: part.data, ...(fileName ? { fileName } : {}) }];
|
|
129
138
|
}
|
|
130
139
|
logBus?.warn('Skipping file MastraMessagePart with unsupported mimeType; omitted from message history', {
|
|
131
140
|
mimeType: part.mimeType,
|
|
132
141
|
hint: 'Only image/png, image/jpeg, and application/pdf map to SDK message parts.',
|
|
133
142
|
});
|
|
134
|
-
return
|
|
143
|
+
return [];
|
|
135
144
|
}
|
|
136
145
|
// Part types that exist in Mastra but have no SDK equivalent yet.
|
|
137
146
|
case 'source':
|
|
138
147
|
case 'step-start':
|
|
139
|
-
return
|
|
148
|
+
return [];
|
|
140
149
|
default:
|
|
141
150
|
// AI SDK v5 data parts use a `data-*` type prefix for framework-specific
|
|
142
151
|
// metadata (e.g. workspace info). These are harness-internal and expected.
|
|
143
152
|
if (part.type.startsWith('data-')) {
|
|
144
|
-
return
|
|
153
|
+
return [];
|
|
145
154
|
}
|
|
146
155
|
logBus?.warn('Skipping unrecognized MastraMessagePart; omitted from message history', {
|
|
147
156
|
partType: part.type,
|
|
148
157
|
hint: 'Update mapMastraPartToMessagePart to handle this type if it should be mapped.',
|
|
149
158
|
});
|
|
150
|
-
return
|
|
159
|
+
return [];
|
|
151
160
|
}
|
|
152
161
|
}
|
|
153
162
|
/**
|
|
@@ -161,7 +170,31 @@ export function mapMessageToMastra(msg, threadId, resourceId) {
|
|
|
161
170
|
parts.push({ type: 'text', text: msg.content });
|
|
162
171
|
}
|
|
163
172
|
else {
|
|
164
|
-
|
|
173
|
+
const content = msg.content;
|
|
174
|
+
for (let i = 0; i < content.length; i++) {
|
|
175
|
+
const part = content[i];
|
|
176
|
+
const next = content[i + 1];
|
|
177
|
+
// Coalesce an adjacent tool-call + tool-result for the same toolCallId
|
|
178
|
+
// into ONE Mastra `tool-invocation(result)` carrying both `args` and
|
|
179
|
+
// `result`. This mirrors how Mastra natively persists a completed call
|
|
180
|
+
// (its MessageList merges the call and result into a single object) and
|
|
181
|
+
// is the exact inverse of the read-path expansion in
|
|
182
|
+
// `mapMastraPartToMessagePart`, so a `getMessages()` → `addContext()`
|
|
183
|
+
// round-trip is lossless and never double-counts the call (#647).
|
|
184
|
+
if (part.type === 'tool-call' && next?.type === 'tool-result' && next.toolCallId === part.toolCallId) {
|
|
185
|
+
parts.push({
|
|
186
|
+
type: 'tool-invocation',
|
|
187
|
+
toolInvocation: {
|
|
188
|
+
state: 'result',
|
|
189
|
+
toolCallId: part.toolCallId,
|
|
190
|
+
toolName: part.toolName,
|
|
191
|
+
args: part.args,
|
|
192
|
+
result: next.result,
|
|
193
|
+
},
|
|
194
|
+
});
|
|
195
|
+
i++; // consumed the paired tool-result
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
165
198
|
parts.push(mapMessagePartToMastra(part));
|
|
166
199
|
}
|
|
167
200
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salesforce/sfdx-agent-harness-mastra",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.30.0",
|
|
4
4
|
"description": "Mastra-backed AgentHarness implementation for @salesforce/sfdx-agent-sdk",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -29,22 +29,22 @@
|
|
|
29
29
|
],
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@ai-sdk/provider": "^3.0.10",
|
|
32
|
-
"@anthropic-ai/bedrock-sdk": "^0.
|
|
32
|
+
"@anthropic-ai/bedrock-sdk": "^0.32.0",
|
|
33
33
|
"@anthropic-ai/sdk": "^0.106.0",
|
|
34
|
-
"@mastra/core": "^1.
|
|
35
|
-
"@mastra/libsql": "^1.
|
|
36
|
-
"@mastra/mcp": "^1.
|
|
37
|
-
"@mastra/memory": "^1.
|
|
34
|
+
"@mastra/core": "^1.49.0",
|
|
35
|
+
"@mastra/libsql": "^1.15.0",
|
|
36
|
+
"@mastra/mcp": "^1.13.0",
|
|
37
|
+
"@mastra/memory": "^1.22.1",
|
|
38
38
|
"@mastra/schema-compat": "^1.2.11",
|
|
39
39
|
"@salesforce/agentic-common": "0.13.0",
|
|
40
40
|
"openai": "^6.0.0"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
|
-
"@salesforce/sfdx-agent-sdk": "0.
|
|
43
|
+
"@salesforce/sfdx-agent-sdk": "0.31.0"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@eslint/js": "^10.0.1",
|
|
47
|
-
"@salesforce/sfdx-agent-sdk": "0.
|
|
47
|
+
"@salesforce/sfdx-agent-sdk": "0.31.0",
|
|
48
48
|
"@types/node": "^22.20.0",
|
|
49
49
|
"@vitest/coverage-istanbul": "^4.1.8",
|
|
50
50
|
"@vitest/eslint-plugin": "^1.6.20",
|
|
@@ -55,11 +55,11 @@
|
|
|
55
55
|
"eslint-plugin-n": "^18.2.1",
|
|
56
56
|
"globals": "^17.6.0",
|
|
57
57
|
"lint-staged": "^17.0.7",
|
|
58
|
-
"prettier": "^3.9.
|
|
58
|
+
"prettier": "^3.9.4",
|
|
59
59
|
"rimraf": "^6.1.3",
|
|
60
|
-
"tsx": "^4.
|
|
60
|
+
"tsx": "^4.23.0",
|
|
61
61
|
"typescript": "^6.0.3",
|
|
62
|
-
"typescript-eslint": "^8.
|
|
62
|
+
"typescript-eslint": "^8.62.1",
|
|
63
63
|
"vitest": "^4.1.8"
|
|
64
64
|
},
|
|
65
65
|
"engines": {
|