@salesforce/sfdx-agent-harness-mastra 0.78.0 → 0.79.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.79.0] - 2026-09-14
|
|
7
|
+
|
|
8
|
+
### Fixes
|
|
9
|
+
- **harness-mastra**: stamp Anthropic cache_control in the Mastra pass-through @W-24130072@ ([#811](https://github.com/forcedotcom/agentic-dx/pull/811))
|
|
10
|
+
|
|
6
11
|
## [0.78.0] - 2026-09-14
|
|
7
12
|
|
|
8
13
|
### Features
|
|
@@ -105,6 +105,19 @@ export declare function buildAnthropicDirectLanguageModel(opts: {
|
|
|
105
105
|
/** Per-read idle timeout (ms) for the gateway response body; see `createGatewayFetch`. */
|
|
106
106
|
idleReadTimeoutMs?: number;
|
|
107
107
|
}): LanguageModelV3;
|
|
108
|
+
/**
|
|
109
|
+
* Stamps `cache_control: {type:'ephemeral'}` on up to three of Anthropic's four
|
|
110
|
+
* allowed breakpoints: the last tool (caches the constant tools block), the
|
|
111
|
+
* system prompt (sent as a `text` block carrying the breakpoint), and a
|
|
112
|
+
* rolling breakpoint on the last cache-eligible block of the last message —
|
|
113
|
+
* the growing history is the majority of per-step input on a multi-step turn,
|
|
114
|
+
* so this breakpoint is load-bearing, not incidental: dropping it would leave
|
|
115
|
+
* only the turn-constant tools/system cached and forfeit most of the win.
|
|
116
|
+
* Mutates only `commonParams`'s own arrays — which
|
|
117
|
+
* `translate*ToAnthropic` builds fresh per request — never the caller's
|
|
118
|
+
* original `LanguageModelV3` prompt/tool objects.
|
|
119
|
+
*/
|
|
120
|
+
export declare function applyCacheBreakpoints(commonParams: Omit<Anthropic.Messages.MessageCreateParamsStreaming, 'stream'>): void;
|
|
108
121
|
/**
|
|
109
122
|
* Translates a V3 `LanguageModelV3Prompt` into Anthropic's Messages API shape
|
|
110
123
|
* — top-level `system` string + `MessageParam[]` of role-tagged content blocks.
|
|
@@ -177,8 +177,76 @@ async function buildAnthropicRequest(ctx, options) {
|
|
|
177
177
|
}
|
|
178
178
|
if (options.stopSequences !== undefined)
|
|
179
179
|
commonParams.stop_sequences = [...options.stopSequences];
|
|
180
|
+
// Anthropic caching is opt-in per request (unlike OpenAI's automatic
|
|
181
|
+
// prefix cache) — nothing caches without an explicit `cache_control`
|
|
182
|
+
// breakpoint. `Model.supportsPromptCache` is the gate (`true` for the
|
|
183
|
+
// Claude family, `false` for GPT); see `applyCacheBreakpoints`.
|
|
184
|
+
if (info.model.supportsPromptCache && !isPromptCacheKillSwitchEngaged())
|
|
185
|
+
applyCacheBreakpoints(commonParams);
|
|
180
186
|
return { client, commonParams, toolSchemas };
|
|
181
187
|
}
|
|
188
|
+
/**
|
|
189
|
+
* Internal, undocumented escape hatch — NOT a consumer-facing `AgentConfig`
|
|
190
|
+
* field or a documented environment variable. Exists solely so an operator can
|
|
191
|
+
* disable cache-control stamping process-wide if a provider-side caching bug
|
|
192
|
+
* surfaces in production, without a code rollback. Read fresh on every request
|
|
193
|
+
* (not cached) so it can be flipped at runtime.
|
|
194
|
+
*/
|
|
195
|
+
function isPromptCacheKillSwitchEngaged() {
|
|
196
|
+
return process.env['SF_MASTRA_DISABLE_PROMPT_CACHE'] === 'true';
|
|
197
|
+
}
|
|
198
|
+
/** Content-block types that accept `cache_control` on the Anthropic Messages
|
|
199
|
+
* API. Deliberately excludes `thinking` / `redacted_thinking`, which reject it. */
|
|
200
|
+
const CACHE_ELIGIBLE_BLOCK_TYPES = new Set([
|
|
201
|
+
'text',
|
|
202
|
+
'tool_use',
|
|
203
|
+
'tool_result',
|
|
204
|
+
'image',
|
|
205
|
+
'document',
|
|
206
|
+
]);
|
|
207
|
+
/** Shared marker object stamped onto up to 3 locations per request, reused
|
|
208
|
+
* across every request and every agent for the process lifetime — never
|
|
209
|
+
* mutate this in place; construct a new object if a call site ever needs a
|
|
210
|
+
* different breakpoint shape (e.g. a `ttl`). */
|
|
211
|
+
const CACHE_CONTROL_EPHEMERAL = Object.freeze({ type: 'ephemeral' });
|
|
212
|
+
/**
|
|
213
|
+
* Stamps `cache_control: {type:'ephemeral'}` on up to three of Anthropic's four
|
|
214
|
+
* allowed breakpoints: the last tool (caches the constant tools block), the
|
|
215
|
+
* system prompt (sent as a `text` block carrying the breakpoint), and a
|
|
216
|
+
* rolling breakpoint on the last cache-eligible block of the last message —
|
|
217
|
+
* the growing history is the majority of per-step input on a multi-step turn,
|
|
218
|
+
* so this breakpoint is load-bearing, not incidental: dropping it would leave
|
|
219
|
+
* only the turn-constant tools/system cached and forfeit most of the win.
|
|
220
|
+
* Mutates only `commonParams`'s own arrays — which
|
|
221
|
+
* `translate*ToAnthropic` builds fresh per request — never the caller's
|
|
222
|
+
* original `LanguageModelV3` prompt/tool objects.
|
|
223
|
+
*/
|
|
224
|
+
export function applyCacheBreakpoints(commonParams) {
|
|
225
|
+
const tools = commonParams.tools;
|
|
226
|
+
if (tools !== undefined && tools.length > 0) {
|
|
227
|
+
const lastTool = tools[tools.length - 1];
|
|
228
|
+
if (lastTool !== undefined)
|
|
229
|
+
lastTool.cache_control = CACHE_CONTROL_EPHEMERAL;
|
|
230
|
+
}
|
|
231
|
+
if (typeof commonParams.system === 'string') {
|
|
232
|
+
commonParams.system = [{ type: 'text', text: commonParams.system, cache_control: CACHE_CONTROL_EPHEMERAL }];
|
|
233
|
+
}
|
|
234
|
+
else if (Array.isArray(commonParams.system) && commonParams.system.length > 0) {
|
|
235
|
+
commonParams.system[commonParams.system.length - 1].cache_control = CACHE_CONTROL_EPHEMERAL;
|
|
236
|
+
}
|
|
237
|
+
const lastMessage = commonParams.messages[commonParams.messages.length - 1];
|
|
238
|
+
const content = lastMessage?.content;
|
|
239
|
+
if (Array.isArray(content)) {
|
|
240
|
+
for (let i = content.length - 1; i >= 0; i--) {
|
|
241
|
+
const block = content[i];
|
|
242
|
+
if (block !== undefined && CACHE_ELIGIBLE_BLOCK_TYPES.has(block.type)) {
|
|
243
|
+
block.cache_control =
|
|
244
|
+
CACHE_CONTROL_EPHEMERAL;
|
|
245
|
+
break;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
182
250
|
async function doGenerate(ctx, options) {
|
|
183
251
|
const { client, commonParams, toolSchemas } = await buildAnthropicRequest(ctx, options);
|
|
184
252
|
// Forward the turn's AbortSignal into the SDK's RequestOptions so firing 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.79.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.80.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.80.0",
|
|
51
51
|
"@types/node": "^22.20.1",
|
|
52
52
|
"@vitest/coverage-istanbul": "^4.1.11",
|
|
53
53
|
"@vitest/eslint-plugin": "^1.6.27",
|