@salesforce/sfdx-agent-harness-mastra 0.78.0 → 0.80.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,18 @@
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.80.0] - 2026-09-15
7
+
8
+ ### Chores
9
+ - **deps**: bump the mastra group across 1 directory with 5 updates ([#817](https://github.com/forcedotcom/agentic-dx/pull/817))
10
+ - **deps-dev**: bump eslint from 10.9.1 to 10.10.0 in the eslint group across 1 directory ([#815](https://github.com/forcedotcom/agentic-dx/pull/815))
11
+ - **deps-dev**: bump the dev-dependencies group with 2 updates ([#814](https://github.com/forcedotcom/agentic-dx/pull/814))
12
+
13
+ ## [0.79.0] - 2026-09-14
14
+
15
+ ### Fixes
16
+ - **harness-mastra**: stamp Anthropic cache_control in the Mastra pass-through @W-24130072@ ([#811](https://github.com/forcedotcom/agentic-dx/pull/811))
17
+
6
18
  ## [0.78.0] - 2026-09-14
7
19
 
8
20
  ### 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.78.0",
3
+ "version": "0.80.0",
4
4
  "description": "Mastra-backed AgentHarness implementation for @salesforce/sfdx-agent-sdk",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -32,37 +32,37 @@
32
32
  "@ai-sdk/provider": "^4.0.10",
33
33
  "@anthropic-ai/bedrock-sdk": "^0.32.0",
34
34
  "@anthropic-ai/sdk": "^0.122.0",
35
- "@mastra/core": "^1.63.2",
36
- "@mastra/libsql": "^1.22.2",
37
- "@mastra/mcp": "^1.17.2",
38
- "@mastra/memory": "^1.28.1",
35
+ "@mastra/core": "^1.64.0",
36
+ "@mastra/libsql": "^1.22.3",
37
+ "@mastra/mcp": "^1.17.3",
38
+ "@mastra/memory": "^1.28.2",
39
39
  "@mastra/schema-compat": "^1.3.7",
40
40
  "@salesforce/agentic-common": "0.19.0",
41
41
  "@smithy/eventstream-codec": "^4.5.2",
42
42
  "openai": "^7.0.0"
43
43
  },
44
44
  "peerDependencies": {
45
- "@salesforce/sfdx-agent-sdk": "0.79.0"
45
+ "@salesforce/sfdx-agent-sdk": "0.81.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.79.0",
50
+ "@salesforce/sfdx-agent-sdk": "0.81.0",
51
51
  "@types/node": "^22.20.1",
52
52
  "@vitest/coverage-istanbul": "^4.1.11",
53
53
  "@vitest/eslint-plugin": "^1.6.27",
54
- "eslint": "^10.9.1",
54
+ "eslint": "^10.10.0",
55
55
  "eslint-config-prettier": "^10.1.8",
56
56
  "eslint-import-resolver-typescript": "^4.4.5",
57
57
  "eslint-plugin-import": "^2.32.0",
58
58
  "eslint-plugin-n": "^18.3.0",
59
59
  "globals": "^17.12.0",
60
- "lint-staged": "^17.4.1",
60
+ "lint-staged": "^17.5.0",
61
61
  "prettier": "^3.9.6",
62
62
  "rimraf": "^6.1.3",
63
63
  "tsx": "^4.23.13",
64
64
  "typescript": "^7.0.2",
65
- "typescript-eslint": "^8.69.0",
65
+ "typescript-eslint": "^8.70.0",
66
66
  "vitest": "^4.1.11"
67
67
  },
68
68
  "engines": {