@salesforce/sfdx-agent-harness-mastra 0.31.0 → 0.33.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,29 @@
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.33.0] - 2026-07-15
7
+
8
+ ### Features
9
+ - **agent-sdk,harness-claude,harness-mastra**: per-model gateway config — geo-aware ids, thinking mode, param exclusion @W-23447370@ ([#672](https://github.com/forcedotcom/agentic-dx/pull/672))
10
+
11
+ ## [0.32.0] - 2026-07-14
12
+
13
+ ### Features
14
+ - **agent-sdk,harness-mastra,harness-claude**: cross-harness conformance test suite @W-23372341@ ([#663](https://github.com/forcedotcom/agentic-dx/pull/663))
15
+ - **sdk,harness-mastra,harness-claude**: delete deprecated tool-approval surfaces @W-23090034 ([#637](https://github.com/forcedotcom/agentic-dx/pull/637))
16
+
17
+ ### Fixes
18
+ - **harness-conformance**: static version instead of -dev.N so release check passes @W-23446682@ ([#670](https://github.com/forcedotcom/agentic-dx/pull/670))
19
+ - **harness-mastra**: thread model maxOutputTokens instead of flooring max_tokens at 4096 @W-23413250@ ([#656](https://github.com/forcedotcom/agentic-dx/pull/656))
20
+ - **harness-mastra**: synthesize deny tool-result for @mastra/core 1.49 ([#653](https://github.com/forcedotcom/agentic-dx/pull/653))
21
+
22
+ ### Chores
23
+ - **deps-dev**: bump typescript-eslint from 8.62.1 to 8.63.0 in the dev-dependencies group across 1 directory ([#665](https://github.com/forcedotcom/agentic-dx/pull/665))
24
+ - **deps-dev**: bump the eslint group across 1 directory with 2 updates ([#659](https://github.com/forcedotcom/agentic-dx/pull/659))
25
+ - **deps**: bump the mastra group across 1 directory with 4 updates ([#661](https://github.com/forcedotcom/agentic-dx/pull/661))
26
+ - **deps-dev**: bump @vitest/eslint-plugin from 1.6.22 to 1.6.23 in the vitest group ([#660](https://github.com/forcedotcom/agentic-dx/pull/660))
27
+ - **deps-dev**: bump the vitest group across 1 directory with 3 updates ([#655](https://github.com/forcedotcom/agentic-dx/pull/655))
28
+
6
29
  ## [0.31.0] - 2026-07-08
7
30
 
8
31
  ### Fixes
package/README.md CHANGED
@@ -44,12 +44,12 @@ Linux and macOS release the handle eagerly; the retry options are no-ops on thos
44
44
 
45
45
  ### Core
46
46
 
47
- | Export | Kind | Description |
48
- | ------------------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
49
- | `MastraHarnessFactory` | class | `HarnessFactory` that produces a Mastra-backed `AgentHarness` bound to a storage root. |
50
- | `MastraHarnessFactoryConfig` | interface | Configuration for `MastraHarnessFactory` (`toolApprovalTimeoutMs?`). |
47
+ | Export | Kind | Description |
48
+ | ------------------------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
49
+ | `MastraHarnessFactory` | class | `HarnessFactory` that produces a Mastra-backed `AgentHarness` bound to a storage root. |
50
+ | `MastraHarnessFactoryConfig` | interface | Configuration for `MastraHarnessFactory` (`toolApprovalTimeoutMs?`). |
51
51
  | `MASTRA_BUILT_IN_TOOL_POLICIES` | const | Frozen `ReadonlyArray<ToolPolicyRule>` the harness feeds the SDK's `resolveToolApprovalPolicy` as the `tiers.harness` slice — `allow` rules for Mastra's built-in `updateWorkingMemory` / `skill` / `skill_search` / `skill_read` tools. |
52
- | `MASTRA_BUILT_IN_TOOL_NAMES` | const | The four built-in tool leaf names `MASTRA_BUILT_IN_TOOL_POLICIES` covers. |
52
+ | `MASTRA_BUILT_IN_TOOL_NAMES` | const | The four built-in tool leaf names `MASTRA_BUILT_IN_TOOL_POLICIES` covers. |
53
53
 
54
54
  Connectivity (model + provider auth) is supplied by the SDK's `AgentConnectivityResolver`. Use the bundled
55
55
  `DefaultAgentConnectivityResolver` for the standard Salesforce LLM Gateway path, or `ApiKeyConnectivityResolver` for
@@ -58,11 +58,10 @@ on `ModelConnectivityInfo.providerHint` internally to pick the right pass-throug
58
58
 
59
59
  #### HTTPS proxy routing
60
60
 
61
- The harness honors `HTTPS_PROXY` / `HTTP_PROXY` / `NO_PROXY` (either casing) via
62
- `@salesforce/agentic-common`'s `resolveProxyDispatcher()`. When either proxy var is set at factory `create()` time, the
63
- harness wraps every in-process `fetch` call site (LLM gateway language-model builders + MCP remote transports) with an
64
- undici-backed routing function — no `globalThis` mutation. When no proxy env is set, `fetch` runs unwrapped (zero
65
- overhead).
61
+ The harness honors `HTTPS_PROXY` / `HTTP_PROXY` / `NO_PROXY` (either casing) via `@salesforce/agentic-common`'s
62
+ `resolveProxyDispatcher()`. When either proxy var is set at factory `create()` time, the harness wraps every in-process
63
+ `fetch` call site (LLM gateway language-model builders + MCP remote transports) with an undici-backed routing function —
64
+ no `globalThis` mutation. When no proxy env is set, `fetch` runs unwrapped (zero overhead).
66
65
 
67
66
  **Snapshot timing.** `HTTPS_PROXY` / `HTTP_PROXY` are captured at `create()` time and pinned for the harness's lifetime;
68
67
  `NO_PROXY` is re-evaluated per request. Set the proxy env vars before constructing the factory's first harness.
@@ -204,19 +203,14 @@ harness resolves a decision and acts on it:
204
203
  `tool-approval-request` surfaces.
205
204
  - **`require-approval`** — a `tool-approval-request` is emitted and the workflow suspends until the consumer settles.
206
205
 
207
- Gating is **opt-in**: it engages only when the agent's config sets `toolPolicies` or `defaultToolDecision`, or the
208
- `chat()` call sets the deprecated `requireToolApproval` or `batchApprovals`. With none configured, tools run without
209
- gating (no suspension), matching the SDK's "no policy ⇒ no gating" default. The harness adds
210
- `MASTRA_BUILT_IN_TOOL_POLICIES` (Mastra's `updateWorkingMemory` / `skill` / `skill_search` / `skill_read` built-ins) and
211
- the SDK's `skill_bridge` meta-tool rules as `allow` defaults, so capability-discovery never prompts. Consumer-executed
212
- tools (`AgentConfig.tools`) always bypass the gate — their execution is the consumer's responsibility via
213
- `submitToolResult`. Set `StreamOptions.batchApprovals: true` to surface parallel `require-approval` requests on one
206
+ Gating is **opt-in**: it engages only when the agent's config sets `toolPolicies` or `defaultToolDecision`. With neither
207
+ configured, tools run without gating (no suspension), matching the SDK's "no policy ⇒ no gating" default. The harness
208
+ adds `MASTRA_BUILT_IN_TOOL_POLICIES` (Mastra's `updateWorkingMemory` / `skill` / `skill_search` / `skill_read`
209
+ built-ins) and the SDK's `skill_bridge` meta-tool rules as `allow` defaults, so capability-discovery never prompts.
210
+ Consumer-executed tools (`AgentConfig.tools`) always bypass the gate — their execution is the consumer's responsibility
211
+ via `submitToolResult`. Set `StreamOptions.batchApprovals: true` to surface parallel `require-approval` requests on one
214
212
  stream for a batch-approval UI.
215
213
 
216
- > `StreamOptions.requireToolApproval` is **deprecated** — superseded by per-tool `AgentConfig.toolPolicies`. It still
217
- > works for one release: a truthy value engages all-or-nothing gating (every non-consumer tool requires approval), and
218
- > `'batch'` maps onto `batchApprovals`.
219
-
220
214
  ### Tool approval timeout
221
215
 
222
216
  When the approval gate is engaged and a tool resolves to `require-approval`, the harness suspends the workflow on each
@@ -50,10 +50,10 @@ export interface MastraApprovalCoordinatorOptions {
50
50
  * (front-loaded off `tool-call` when `batchApprovals`, else on the
51
51
  * natural `tool-call-approval` chunk).
52
52
  *
53
- * `undefined` means gating is **off** (the consumer configured no policy
54
- * and no `requireToolApproval` / `batchApprovals`): the harness does not
55
- * pass `requireToolApproval`, Mastra suspends nothing, and this callback is
56
- * never consulted — today's no-gating behavior.
53
+ * `undefined` means gating is **off** (the consumer configured no
54
+ * `toolPolicies` / `defaultToolDecision`): the harness does not pass
55
+ * `requireToolApproval` to Mastra, Mastra suspends nothing, and this
56
+ * callback is never consulted — the SDK's no-gating behavior.
57
57
  *
58
58
  * The coordinator builds the {@link ToolInvocation} it passes here from the
59
59
  * same identity sources it uses for event enrichment
@@ -74,8 +74,7 @@ export interface MastraApprovalCoordinatorOptions {
74
74
  * iterates to natural park). When `false`, each `require-approval` surfaces
75
75
  * on its natural `tool-call-approval` suspension chunk — one at a time.
76
76
  *
77
- * Replaces the serial/batch role of the deprecated
78
- * `StreamOptions.requireToolApproval` enum (#447). No effect when
77
+ * Read from `StreamOptions.batchApprovals` (#447). No effect when
79
78
  * {@link lookupPolicy} is `undefined` (gating off) or when no tool resolves
80
79
  * to `require-approval`.
81
80
  */
@@ -123,7 +122,8 @@ export interface MastraApprovalCoordinatorOptions {
123
122
  * Responsibilities:
124
123
  * - Policy-driven approval gating (Phase 2a): for each non-consumer tool,
125
124
  * consult `lookupPolicy` — `allow` silently approves, `deny` silently
126
- * declines (Mastra synthesizes a `tool-result(isError=true)`),
125
+ * declines (the coordinator synthesizes a `tool-result(isError=true)` and
126
+ * drives `agent.declineToolCall` so the model sees the failed call),
127
127
  * `require-approval` emits a `tool-approval-request` and drives
128
128
  * `agent.approveToolCall(...)` / `declineToolCall(...)` when the consumer
129
129
  * settles. `batchApprovals` controls whether parallel `require-approval`
@@ -166,17 +166,6 @@ export declare class MastraApprovalCoordinator {
166
166
  private readonly initial;
167
167
  /** Buffered consumer decisions keyed by `toolCallId`. */
168
168
  private readonly decisions;
169
- /**
170
- * ToolCallIds the policy resolver decided `'deny'` (Phase 2a). The pump
171
- * drives `agent.declineToolCall` for these, and Mastra emits a native
172
- * `tool-result` on the resumed stream with `isError: false` + the text
173
- * "Tool call was not approved by the user". The SDK `Decision` contract
174
- * requires a denied tool to surface as `tool-result(isError=true)`, so the
175
- * coordinator re-stamps `isError: true` on the matching result as it routes
176
- * (see {@link routeEnriched}). Tracked separately from consumer-driven
177
- * declines, which keep Mastra's native shape.
178
- */
179
- private readonly policyDeniedIds;
180
169
  /** ToolCallIds for which a `tool-approval-request` ChatEvent has been emitted. */
181
170
  private readonly emittedApprovals;
182
171
  /** ToolCallIds the consumer has settled. Rejects double-settle. */
@@ -407,18 +396,6 @@ export declare class MastraApprovalCoordinator {
407
396
  */
408
397
  private routeEnriched;
409
398
  private maybeEnrich;
410
- /**
411
- * Re-stamp `isError: true` on the `tool-result` for a policy-denied tool
412
- * call (Phase 2a). When the resolver returns `'deny'`, the coordinator
413
- * drives `agent.declineToolCall`; Mastra emits a native `tool-result` with
414
- * `isError: false` and the text "Tool call was not approved by the user".
415
- * The SDK `Decision` contract requires a denied tool to surface as
416
- * `tool-result(isError=true)` so consumers and the model treat it as a
417
- * failed call, not a successful one — so we flip the flag exactly once, on
418
- * the matching result. Consumer-driven declines (not policy denials) are not
419
- * in `policyDeniedIds` and keep Mastra's native shape.
420
- */
421
- private policyDenyStamp;
422
399
  /**
423
400
  * Build the {@link ToolInvocation} the policy resolver matches against, and
424
401
  * the single source of the `serverName` / `annotations` identity stamped on
@@ -450,11 +427,10 @@ export declare class MastraApprovalCoordinator {
450
427
  * Resolve the approval decision for a non-consumer tool via the harness-
451
428
  * supplied {@link MastraApprovalCoordinatorOptions.lookupPolicy} callback.
452
429
  * Falls back to `'require-approval'` when no policy callback is wired — a
453
- * defensive default so a future caller that passes `requireToolApproval`
454
- * without a resolver surfaces the request rather than silently allowing.
455
- * In practice this fallback is unreachable: gating is active iff
456
- * `lookupPolicy` is defined, and only then does Mastra suspend non-consumer
457
- * tools.
430
+ * defensive default so a future caller that activates gating without a
431
+ * resolver surfaces the request rather than silently allowing. In practice
432
+ * this fallback is unreachable: gating is active iff `lookupPolicy` is
433
+ * defined, and only then does Mastra suspend non-consumer tools.
458
434
  */
459
435
  private policyFor;
460
436
  private emitApprovalRequest;
@@ -22,6 +22,13 @@ function pickIdWithDecision(pendingIds, decisions) {
22
22
  }
23
23
  return undefined;
24
24
  }
25
+ /**
26
+ * Result text the coordinator stamps on the synthesized `tool-result` for a
27
+ * policy-denied tool call. Matches the text Mastra folds into the model's
28
+ * context on decline ("Tool call was not approved by the user") so the
29
+ * consumer-facing event and the model's transcript stay consistent.
30
+ */
31
+ const POLICY_DENIED_RESULT_TEXT = 'Tool call was not approved by the user';
25
32
  /**
26
33
  * Per-`(agentId, threadId)` coordinator that subsumes the pre-#529
27
34
  * `MastraStreamBroker` + `activeRuns` pair into one object owning a single
@@ -30,7 +37,8 @@ function pickIdWithDecision(pendingIds, decisions) {
30
37
  * Responsibilities:
31
38
  * - Policy-driven approval gating (Phase 2a): for each non-consumer tool,
32
39
  * consult `lookupPolicy` — `allow` silently approves, `deny` silently
33
- * declines (Mastra synthesizes a `tool-result(isError=true)`),
40
+ * declines (the coordinator synthesizes a `tool-result(isError=true)` and
41
+ * drives `agent.declineToolCall` so the model sees the failed call),
34
42
  * `require-approval` emits a `tool-approval-request` and drives
35
43
  * `agent.approveToolCall(...)` / `declineToolCall(...)` when the consumer
36
44
  * settles. `batchApprovals` controls whether parallel `require-approval`
@@ -73,17 +81,6 @@ export class MastraApprovalCoordinator {
73
81
  initial = new GenSink();
74
82
  /** Buffered consumer decisions keyed by `toolCallId`. */
75
83
  decisions = new Map();
76
- /**
77
- * ToolCallIds the policy resolver decided `'deny'` (Phase 2a). The pump
78
- * drives `agent.declineToolCall` for these, and Mastra emits a native
79
- * `tool-result` on the resumed stream with `isError: false` + the text
80
- * "Tool call was not approved by the user". The SDK `Decision` contract
81
- * requires a denied tool to surface as `tool-result(isError=true)`, so the
82
- * coordinator re-stamps `isError: true` on the matching result as it routes
83
- * (see {@link routeEnriched}). Tracked separately from consumer-driven
84
- * declines, which keep Mastra's native shape.
85
- */
86
- policyDeniedIds = new Set();
87
84
  /** ToolCallIds for which a `tool-approval-request` ChatEvent has been emitted. */
88
85
  emittedApprovals = new Set();
89
86
  /** ToolCallIds the consumer has settled. Rejects double-settle. */
@@ -342,10 +339,6 @@ export class MastraApprovalCoordinator {
342
339
  this.initial.push({ type: 'finish', finishReason: 'error' });
343
340
  this.initial.end();
344
341
  this.pendingSuspendIds.clear();
345
- // The turn is over; any deny re-stamp that never landed (the declined
346
- // tool-result will never arrive now) is moot. Clear so the set doesn't
347
- // outlive the turn.
348
- this.policyDeniedIds.clear();
349
342
  this.runTeardownCallbacks();
350
343
  }
351
344
  // ───────────────────────── settle methods ───────────────────────────────
@@ -592,9 +585,9 @@ export class MastraApprovalCoordinator {
592
585
  this.pendingSuspendIds.add(tc.toolCallId);
593
586
  sawSuspension = true;
594
587
  if (this.consumerToolNames.has(tc.toolName)) {
595
- // Consumer tools bypass the gate per the
596
- // StreamOptions.requireToolApproval JSDoc. Auto-
597
- // approve silently — no ChatEvent surfaces, and the
588
+ // Consumer tools bypass the approval gate by
589
+ // construction. Auto-approve silently — no ChatEvent
590
+ // surfaces, and the
598
591
  // pump loop drives `agent.approveToolCall` when it
599
592
  // picks up the buffered decision. After Mastra
600
593
  // resumes, the bridge `execute` parker runs and
@@ -616,19 +609,36 @@ export class MastraApprovalCoordinator {
616
609
  }
617
610
  else if (decision === 'deny') {
618
611
  // Silent decline — no `tool-approval-request` surfaces.
619
- // The pump drives `agent.declineToolCall`; Mastra emits a
620
- // native `tool-result` on the resumed stream (with
621
- // `isError: false` + "Tool call was not approved by the
622
- // user") so the model can recover. We mark the id
623
- // `policyDeniedIds` so `routeEnriched` re-stamps that
624
- // result `isError: true` to honor the SDK `Decision`
625
- // contract. The un-emitted approval is marked
612
+ // The pump drives `agent.declineToolCall` so Mastra folds
613
+ // the denial into the model's context (persisted as the
614
+ // `role: 'tool'` message "Tool call was not approved by the
615
+ // user"), letting the model recover on the resume stream.
616
+ //
617
+ // We synthesize the consumer-facing `tool-result(isError=true)`
618
+ // ourselves rather than relying on Mastra to emit one:
619
+ // @mastra/core 1.49 no longer emits a standalone
620
+ // `tool-result` chunk on the decline-resume `fullStream`
621
+ // (the denial text lives only inside the persisted
622
+ // `messages.all` payload embedded in `step-finish`), so the
623
+ // old "re-stamp Mastra's native result" path never fired and
624
+ // the SDK `Decision` contract "a denied tool surfaces as
625
+ // `tool-result(isError=true)`" was silently broken. Routing
626
+ // it here — immediately after the already-routed `tool-call`
627
+ // and before the decline resume — keeps the natural
628
+ // tool-call → tool-result ordering the SDK telemetry deriver
629
+ // matches on. The un-emitted approval is marked
626
630
  // coordinator-settled so a (spurious) consumer
627
631
  // `approveToolCall` for this id is a no-op rather than a
628
632
  // TOOL_CALL_NOT_FOUND throw.
629
633
  this.settledByCoordinator.add(tc.toolCallId);
630
- this.policyDeniedIds.add(tc.toolCallId);
631
634
  this.decisions.set(tc.toolCallId, { kind: 'decline' });
635
+ this.routeEnriched({
636
+ type: 'tool-result',
637
+ toolCallId: tc.toolCallId,
638
+ toolName: tc.toolName,
639
+ result: POLICY_DENIED_RESULT_TEXT,
640
+ isError: true,
641
+ });
632
642
  }
633
643
  else if (!this.batchApprovals && !this.emittedApprovals.has(tc.toolCallId)) {
634
644
  // require-approval, serial: emit on the natural chunk
@@ -727,7 +737,7 @@ export class MastraApprovalCoordinator {
727
737
  routeEnriched(event) {
728
738
  if (this.aborted || this.disposed)
729
739
  return;
730
- const enriched = this.policyDenyStamp(this.maybeEnrich(event));
740
+ const enriched = this.maybeEnrich(event);
731
741
  // Issue #591: track in-flight MCP tool call so progress notifications
732
742
  // arriving via `progress.onUpdate(...)` can be attributed to the
733
743
  // right `toolCallId`. Set on a genuinely MCP-discovered tool-call;
@@ -782,29 +792,6 @@ export class MastraApprovalCoordinator {
782
792
  const out = { ...event, ...fields };
783
793
  return out;
784
794
  }
785
- /**
786
- * Re-stamp `isError: true` on the `tool-result` for a policy-denied tool
787
- * call (Phase 2a). When the resolver returns `'deny'`, the coordinator
788
- * drives `agent.declineToolCall`; Mastra emits a native `tool-result` with
789
- * `isError: false` and the text "Tool call was not approved by the user".
790
- * The SDK `Decision` contract requires a denied tool to surface as
791
- * `tool-result(isError=true)` so consumers and the model treat it as a
792
- * failed call, not a successful one — so we flip the flag exactly once, on
793
- * the matching result. Consumer-driven declines (not policy denials) are not
794
- * in `policyDeniedIds` and keep Mastra's native shape.
795
- */
796
- policyDenyStamp(event) {
797
- if (event.type !== 'tool-result' || !this.policyDeniedIds.has(event.toolCallId)) {
798
- return event;
799
- }
800
- // One-shot: clear so a later same-id result (shouldn't happen, but be
801
- // safe) isn't mislabeled, and the set doesn't grow unbounded.
802
- this.policyDeniedIds.delete(event.toolCallId);
803
- if (event.isError)
804
- return event;
805
- const out = { ...event, isError: true };
806
- return out;
807
- }
808
795
  /**
809
796
  * Build the {@link ToolInvocation} the policy resolver matches against, and
810
797
  * the single source of the `serverName` / `annotations` identity stamped on
@@ -852,11 +839,10 @@ export class MastraApprovalCoordinator {
852
839
  * Resolve the approval decision for a non-consumer tool via the harness-
853
840
  * supplied {@link MastraApprovalCoordinatorOptions.lookupPolicy} callback.
854
841
  * Falls back to `'require-approval'` when no policy callback is wired — a
855
- * defensive default so a future caller that passes `requireToolApproval`
856
- * without a resolver surfaces the request rather than silently allowing.
857
- * In practice this fallback is unreachable: gating is active iff
858
- * `lookupPolicy` is defined, and only then does Mastra suspend non-consumer
859
- * tools.
842
+ * defensive default so a future caller that activates gating without a
843
+ * resolver surfaces the request rather than silently allowing. In practice
844
+ * this fallback is unreachable: gating is active iff `lookupPolicy` is
845
+ * defined, and only then does Mastra suspend non-consumer tools.
860
846
  */
861
847
  policyFor(toolName) {
862
848
  if (!this.lookupPolicy)
@@ -2,7 +2,7 @@ import type { HarnessFactory, ProviderHint } from '@salesforce/sfdx-agent-sdk';
2
2
  import { type MastraAgentHarness } from './mastra-harness.js';
3
3
  export interface MastraHarnessFactoryConfig {
4
4
  /**
5
- * How long to wait for a `requireToolApproval` consumer to call
5
+ * How long to wait for a consumer to call
6
6
  * `approveToolCall` / `declineToolCall` before auto-denying via
7
7
  * `agent.abortRunStream(...)` and yielding a synthetic `error`
8
8
  * (`code: 'tool-approval-timeout'`) + `finish` (`finishReason: 'error'`)
@@ -122,9 +122,8 @@ interface MastraAgentState {
122
122
  coordinators: Map<string, MastraApprovalCoordinator>;
123
123
  /**
124
124
  * Names of consumer-executed tools registered via `AgentConfig.tools`.
125
- * The coordinator uses this set to bypass the approval gate for them
126
- * (per the `StreamOptions.requireToolApproval` JSDoc) — when Mastra
127
- * suspends on a `tool-call-approval` for a name in this set the
125
+ * The coordinator uses this set to bypass the approval gate for them —
126
+ * when Mastra suspends on a `tool-call-approval` for a name in this set the
128
127
  * coordinator silently calls `agent.approveToolCall(...)` and never
129
128
  * surfaces a `tool-approval-request` ChatEvent.
130
129
  */
@@ -424,31 +423,16 @@ export declare class MastraHarness implements MastraAgentHarness {
424
423
  * during pending approval (#409)".
425
424
  */
426
425
  /**
427
- * Phase 2a: decide whether the approval gate engages this turn and, if so,
428
- * build the per-tool policy resolver the coordinator consults.
426
+ * Decide whether the approval gate engages this turn and, if so, build the
427
+ * per-tool policy resolver the coordinator consults.
429
428
  *
430
- * Gating is **opt-in**. It engages when the consumer set any of:
431
- * - `config.toolPolicies` (non-empty),
432
- * - `config.defaultToolDecision`,
433
- * - the deprecated `StreamOptions.requireToolApproval` (any non-`false`
434
- * value — `deprecatedMode` is `'serial'`/`'batch'`/`undefined`), or
435
- * - `StreamOptions.batchApprovals: true`.
429
+ * Gating is **opt-in**. It engages when the consumer set either:
430
+ * - `config.toolPolicies` (non-empty), or
431
+ * - `config.defaultToolDecision`.
436
432
  *
437
- * When none are set, returns `{ lookupPolicy: undefined }` — the caller
433
+ * When neither is set, returns `{ lookupPolicy: undefined }` — the caller
438
434
  * omits `requireToolApproval` from Mastra's `stream()` and tools run free
439
- * (today's behavior).
440
- *
441
- * **Deprecation bridge.** When the gate engages *only* because the deprecated
442
- * `requireToolApproval` was truthy (no `toolPolicies`, no
443
- * `defaultToolDecision`), the effective default must be `'require-approval'`
444
- * — legacy all-or-none — not the resolver's `'allow'` default, or the old
445
- * behavior would silently flip to "auto-approve everything." We apply this
446
- * by resolving against a shallow-overridden config carrying
447
- * `defaultToolDecision: 'require-approval'`. Built-in allow-rules
448
- * (skill_bridge meta-tools, the Mastra built-ins) and any consumer
449
- * `toolPolicies` still win over the default via within-tier last-wins /
450
- * cross-tier deny-wins, so the capability-discovery meta-tools stay
451
- * auto-approved even under legacy gating.
435
+ * (the SDK's "no policy ⇒ no gating" back-compat invariant).
452
436
  *
453
437
  * **Live config read (#638).** `lookupPolicy` reads `agentState.config` on
454
438
  * every call rather than closing over a snapshot taken at stream start. A
@@ -459,22 +443,14 @@ export declare class MastraHarness implements MastraAgentHarness {
459
443
  * that persisted the rule. Reading live makes the remembered decision
460
444
  * auto-resolve subsequent matching tool calls within the same turn.
461
445
  *
462
- * Two decisions stay **frozen at stream start** and MUST NOT be recomputed
463
- * live:
464
- * - `gatingActive` — the turn either told Mastra to suspend tools
465
- * (`requireToolApproval: true`) or it didn't; gating cannot begin
466
- * mid-turn. The closure is built (and returned) iff gating engaged now.
467
- * - `applyLegacyBridge` (`!hasPolicy && legacyGating`) — whether the
468
- * deprecation bridge injects `defaultToolDecision: 'require-approval'`.
469
- * If this were recomputed live, the FIRST remembered rule would flip
470
- * `hasPolicy` false→true, turn the bridge OFF mid-turn, and silently drop
471
- * the effective default back to the resolver's `'allow'` — auto-approving
472
- * every remaining legacy-gated tool. Freezing the boolean and applying it
473
- * to the live config preserves legacy all-or-none semantics while still
474
- * honoring any rule the turn appends.
446
+ * `gatingActive` stays **frozen at stream start** — the turn either told
447
+ * Mastra to suspend tools (`requireToolApproval: true`) or it didn't; gating
448
+ * cannot begin mid-turn. The closure is built (and returned) iff gating
449
+ * engaged now.
475
450
  *
476
- * `batchApprovals` is the serial-vs-batch emission axis: the explicit
477
- * `StreamOptions.batchApprovals` OR the legacy `requireToolApproval: 'batch'`.
451
+ * `batchApprovals` is the serial-vs-batch emission axis, read straight off
452
+ * `StreamOptions.batchApprovals`. It has no effect when the gate is inactive
453
+ * (nothing resolves to `'require-approval'`, so there is nothing to batch).
478
454
  */
479
455
  private buildPolicyGate;
480
456
  /**
@@ -11,7 +11,7 @@ import { SkillSearchProcessor, ToolSearchProcessor } from '@mastra/core/processo
11
11
  import { MASTRA_RESOURCE_ID_KEY, MASTRA_THREAD_ID_KEY, RequestContext } from '@mastra/core/request-context';
12
12
  import { LibSQLStore } from '@mastra/libsql';
13
13
  import { MCPClient } from '@mastra/mcp';
14
- import { AgentSDKError, AgentSDKErrorType, DEFAULT_MAX_STEPS, McpServerStatus, resolveToolApprovalMode, resolveToolApprovalPolicy, } from '@salesforce/sfdx-agent-sdk';
14
+ import { AgentSDKError, AgentSDKErrorType, DEFAULT_MAX_STEPS, McpServerStatus, resolveToolApprovalPolicy, } from '@salesforce/sfdx-agent-sdk';
15
15
  import { HarnessBusOwner, lowerStreamInput, mcpServerConfigEqual, mergeToolResultsIntoAssistant, splitToolResultsIntoToolMessages, } from '@salesforce/sfdx-agent-sdk/harness';
16
16
  import { MastraApprovalCoordinator } from './mastra-approval-coordinator.js';
17
17
  import { MASTRA_BUILT_IN_TOOL_POLICIES } from './mastra-built-in-policies.js';
@@ -1339,31 +1339,16 @@ export class MastraHarness {
1339
1339
  * during pending approval (#409)".
1340
1340
  */
1341
1341
  /**
1342
- * Phase 2a: decide whether the approval gate engages this turn and, if so,
1343
- * build the per-tool policy resolver the coordinator consults.
1342
+ * Decide whether the approval gate engages this turn and, if so, build the
1343
+ * per-tool policy resolver the coordinator consults.
1344
1344
  *
1345
- * Gating is **opt-in**. It engages when the consumer set any of:
1346
- * - `config.toolPolicies` (non-empty),
1347
- * - `config.defaultToolDecision`,
1348
- * - the deprecated `StreamOptions.requireToolApproval` (any non-`false`
1349
- * value — `deprecatedMode` is `'serial'`/`'batch'`/`undefined`), or
1350
- * - `StreamOptions.batchApprovals: true`.
1345
+ * Gating is **opt-in**. It engages when the consumer set either:
1346
+ * - `config.toolPolicies` (non-empty), or
1347
+ * - `config.defaultToolDecision`.
1351
1348
  *
1352
- * When none are set, returns `{ lookupPolicy: undefined }` — the caller
1349
+ * When neither is set, returns `{ lookupPolicy: undefined }` — the caller
1353
1350
  * omits `requireToolApproval` from Mastra's `stream()` and tools run free
1354
- * (today's behavior).
1355
- *
1356
- * **Deprecation bridge.** When the gate engages *only* because the deprecated
1357
- * `requireToolApproval` was truthy (no `toolPolicies`, no
1358
- * `defaultToolDecision`), the effective default must be `'require-approval'`
1359
- * — legacy all-or-none — not the resolver's `'allow'` default, or the old
1360
- * behavior would silently flip to "auto-approve everything." We apply this
1361
- * by resolving against a shallow-overridden config carrying
1362
- * `defaultToolDecision: 'require-approval'`. Built-in allow-rules
1363
- * (skill_bridge meta-tools, the Mastra built-ins) and any consumer
1364
- * `toolPolicies` still win over the default via within-tier last-wins /
1365
- * cross-tier deny-wins, so the capability-discovery meta-tools stay
1366
- * auto-approved even under legacy gating.
1351
+ * (the SDK's "no policy ⇒ no gating" back-compat invariant).
1367
1352
  *
1368
1353
  * **Live config read (#638).** `lookupPolicy` reads `agentState.config` on
1369
1354
  * every call rather than closing over a snapshot taken at stream start. A
@@ -1374,47 +1359,27 @@ export class MastraHarness {
1374
1359
  * that persisted the rule. Reading live makes the remembered decision
1375
1360
  * auto-resolve subsequent matching tool calls within the same turn.
1376
1361
  *
1377
- * Two decisions stay **frozen at stream start** and MUST NOT be recomputed
1378
- * live:
1379
- * - `gatingActive` — the turn either told Mastra to suspend tools
1380
- * (`requireToolApproval: true`) or it didn't; gating cannot begin
1381
- * mid-turn. The closure is built (and returned) iff gating engaged now.
1382
- * - `applyLegacyBridge` (`!hasPolicy && legacyGating`) — whether the
1383
- * deprecation bridge injects `defaultToolDecision: 'require-approval'`.
1384
- * If this were recomputed live, the FIRST remembered rule would flip
1385
- * `hasPolicy` false→true, turn the bridge OFF mid-turn, and silently drop
1386
- * the effective default back to the resolver's `'allow'` — auto-approving
1387
- * every remaining legacy-gated tool. Freezing the boolean and applying it
1388
- * to the live config preserves legacy all-or-none semantics while still
1389
- * honoring any rule the turn appends.
1362
+ * `gatingActive` stays **frozen at stream start** — the turn either told
1363
+ * Mastra to suspend tools (`requireToolApproval: true`) or it didn't; gating
1364
+ * cannot begin mid-turn. The closure is built (and returned) iff gating
1365
+ * engaged now.
1390
1366
  *
1391
- * `batchApprovals` is the serial-vs-batch emission axis: the explicit
1392
- * `StreamOptions.batchApprovals` OR the legacy `requireToolApproval: 'batch'`.
1367
+ * `batchApprovals` is the serial-vs-batch emission axis, read straight off
1368
+ * `StreamOptions.batchApprovals`. It has no effect when the gate is inactive
1369
+ * (nothing resolves to `'require-approval'`, so there is nothing to batch).
1393
1370
  */
1394
- buildPolicyGate(agentState, options, deprecatedMode) {
1371
+ buildPolicyGate(agentState, options) {
1395
1372
  const config = agentState.config;
1396
- const hasPolicy = (config.toolPolicies?.length ?? 0) > 0 || config.defaultToolDecision !== undefined;
1397
- const legacyGating = deprecatedMode !== undefined;
1398
- const batchApprovals = options?.batchApprovals === true || deprecatedMode === 'batch';
1399
- const gatingActive = hasPolicy || legacyGating || batchApprovals;
1373
+ const gatingActive = (config.toolPolicies?.length ?? 0) > 0 || config.defaultToolDecision !== undefined;
1400
1374
  if (!gatingActive) {
1401
1375
  return { batchApprovals: false };
1402
1376
  }
1403
- // Freeze the deprecation-bridge decision at stream start (see JSDoc) —
1404
- // whether to inject `defaultToolDecision: 'require-approval'` must not
1405
- // change when a mid-turn remember rule flips `hasPolicy`.
1406
- const applyLegacyBridge = !hasPolicy && legacyGating;
1407
- const lookupPolicy = (invocation) => {
1408
- // Read the config live so a mid-turn `updateAgentConfig` (e.g. a
1409
- // remembered rule) is honored for the rest of this turn.
1410
- const liveConfig = agentState.config;
1411
- const resolverConfig = applyLegacyBridge
1412
- ? { ...liveConfig, defaultToolDecision: 'require-approval' }
1413
- : liveConfig;
1414
- return resolveToolApprovalPolicy(invocation, resolverConfig, { harness: MASTRA_BUILT_IN_TOOL_POLICIES })
1415
- .decision;
1416
- };
1417
- return { lookupPolicy, batchApprovals };
1377
+ const lookupPolicy = (invocation) =>
1378
+ // Read the config live so a mid-turn `updateAgentConfig` (e.g. a
1379
+ // remembered rule) is honored for the rest of this turn (#638).
1380
+ resolveToolApprovalPolicy(invocation, agentState.config, { harness: MASTRA_BUILT_IN_TOOL_POLICIES })
1381
+ .decision;
1382
+ return { lookupPolicy, batchApprovals: options?.batchApprovals === true };
1418
1383
  }
1419
1384
  /**
1420
1385
  * Maps an SDK `MessagePart[]` into the single AI SDK v5 user `ModelMessage` that
@@ -1440,20 +1405,14 @@ export class MastraHarness {
1440
1405
  }
1441
1406
  async stream(agentId, threadId, message, options) {
1442
1407
  const agentState = this.getAgentStateOrThrow(agentId);
1443
- // Phase 2a: resolve whether the approval gate engages this turn, and if
1444
- // so, build the per-tool policy resolver the coordinator consults.
1445
- //
1446
- // `resolveToolApprovalMode` still validates the deprecated
1447
- // `requireToolApproval` union (throws on garbage) and tells us whether
1448
- // the consumer asked for batch UX (`'batch'`). The gate is **opt-in**:
1449
- // it engages only when the consumer configured `toolPolicies`,
1450
- // `defaultToolDecision`, the deprecated `requireToolApproval`, or
1451
- // `batchApprovals`. With none set, behavior is unchanged from today —
1452
- // no suspension, no resolver, tools run free (the SDK's "no policy ⇒ no
1453
- // gating" back-compat invariant), avoiding a suspend/resume round-trip
1454
- // per tool call.
1455
- const deprecatedMode = resolveToolApprovalMode(options?.requireToolApproval);
1456
- const { lookupPolicy, batchApprovals } = this.buildPolicyGate(agentState, options, deprecatedMode);
1408
+ // Resolve whether the approval gate engages this turn, and if so, build
1409
+ // the per-tool policy resolver the coordinator consults. The gate is
1410
+ // **opt-in**: it engages only when the consumer configured
1411
+ // `toolPolicies` or `defaultToolDecision`. With neither set, behavior is
1412
+ // unchanged — no suspension, no resolver, tools run free (the SDK's "no
1413
+ // policy ⇒ no gating" back-compat invariant), avoiding a suspend/resume
1414
+ // round-trip per tool call.
1415
+ const { lookupPolicy, batchApprovals } = this.buildPolicyGate(agentState, options);
1457
1416
  const gatingActive = lookupPolicy !== undefined;
1458
1417
  // Dispose any coordinator the prior turn on this thread allocated.
1459
1418
  // Without this a stale coordinator from the prior turn would still
@@ -39,10 +39,10 @@ export function mapToolDefinitionsToMastraTools(tools, parkerLookup) {
39
39
  description: tool.description ?? '',
40
40
  inputSchema: ensureJsonSchemaMarker(tool.inputSchema),
41
41
  // Issue #529 step 2c (Path A) — consumer tools bypass the
42
- // approval gate per the StreamOptions.requireToolApproval
43
- // JSDoc. With `requireApproval: false`, Mastra parks the
44
- // agentic run on the parker Promise without suspending the
45
- // workflow. The coordinator handles the parker lifecycle.
42
+ // approval gate by construction. With `requireApproval: false`,
43
+ // Mastra parks the agentic run on the parker Promise without
44
+ // suspending the workflow. The coordinator handles the parker
45
+ // lifecycle.
46
46
  //
47
47
  // Phase 2a dual-enforcement contract: this `requireApproval: false`
48
48
  // is the PRIMARY, structural gate for consumer tools — it is
@@ -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, mapAnthropicFinishReason, mapAnthropicUsage, toolResultOutputToString, } from './v3-stream-mapping.js';
7
+ import { TextSpanLifecycle, createGatewayFetch, isEmptyUsage, isParameterExcluded, mapAnthropicFinishReason, mapAnthropicUsage, 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.
@@ -143,15 +143,24 @@ async function buildAnthropicRequest(ctx, options) {
143
143
  const tools = translateToolsToAnthropic(options);
144
144
  const commonParams = {
145
145
  model: info.nativeModelId,
146
- max_tokens: options.maxOutputTokens ?? DEFAULT_MAX_TOKENS,
146
+ // Precedence: an explicit per-request `maxOutputTokens` wins; otherwise
147
+ // thread the bound model's published cap (`info.model.maxOutputTokens`)
148
+ // so large completions aren't silently truncated at the 4096 floor
149
+ // (W-23413250). `DEFAULT_MAX_TOKENS` is only the last-resort fallback for
150
+ // a model shape that somehow lacks the cap — Bedrock requires the field,
151
+ // so this path must always yield a number.
152
+ max_tokens: options.maxOutputTokens ?? info.model.maxOutputTokens ?? DEFAULT_MAX_TOKENS,
147
153
  messages,
148
154
  };
149
155
  if (system !== undefined)
150
156
  commonParams.system = system;
151
157
  if (tools.length > 0)
152
158
  commonParams.tools = tools;
153
- if (options.temperature !== undefined)
159
+ // The gateway deprecated `temperature` for Claude Opus 4.7+ and rejects the
160
+ // request if it's present; drop it when the bound model excludes it.
161
+ if (options.temperature !== undefined && !isParameterExcluded(info.model, 'temperature')) {
154
162
  commonParams.temperature = options.temperature;
163
+ }
155
164
  if (options.stopSequences !== undefined)
156
165
  commonParams.stop_sequences = [...options.stopSequences];
157
166
  return { client, commonParams };
@@ -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, mapOpenAIUsage, toolResultOutputToString, } from './v3-stream-mapping.js';
6
+ import { TextSpanLifecycle, createGatewayFetch, isEmptyUsage, isParameterExcluded, mapOpenAIUsage, 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
@@ -62,7 +62,11 @@ async function buildRequest(ctx, options) {
62
62
  model: info.nativeModelId,
63
63
  messages,
64
64
  ...(tools.length > 0 ? { tools } : {}),
65
- ...(options.temperature !== undefined ? { temperature: options.temperature } : {}),
65
+ // The gateway deprecated `temperature` for Claude Opus 4.7+ and rejects
66
+ // the request if it's present; drop it when the bound model excludes it.
67
+ ...(options.temperature !== undefined && !isParameterExcluded(info.model, 'temperature')
68
+ ? { temperature: options.temperature }
69
+ : {}),
66
70
  ...(options.maxOutputTokens !== undefined ? { max_tokens: options.maxOutputTokens } : {}),
67
71
  ...(options.stopSequences !== undefined && options.stopSequences.length > 0
68
72
  ? { stop: [...options.stopSequences] }
@@ -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, mapOpenAIFinishReason, mapOpenAIUsage, toolResultOutputToString, } from './v3-stream-mapping.js';
6
+ import { TextSpanLifecycle, createGatewayFetch, isEmptyUsage, isParameterExcluded, mapOpenAIFinishReason, mapOpenAIUsage, 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
@@ -63,7 +63,11 @@ async function buildOpenAIRequest(ctx, options) {
63
63
  input,
64
64
  ...(instructions !== undefined ? { instructions } : {}),
65
65
  ...(tools.length > 0 ? { tools } : {}),
66
- ...(options.temperature !== undefined ? { temperature: options.temperature } : {}),
66
+ // The gateway deprecated `temperature` for Claude Opus 4.7+ and rejects
67
+ // the request if it's present; drop it when the bound model excludes it.
68
+ ...(options.temperature !== undefined && !isParameterExcluded(info.model, 'temperature')
69
+ ? { temperature: options.temperature }
70
+ : {}),
67
71
  ...(options.maxOutputTokens !== undefined ? { max_output_tokens: options.maxOutputTokens } : {}),
68
72
  };
69
73
  return { client, commonParams };
@@ -1,5 +1,6 @@
1
1
  import type { LanguageModelV3FinishReason, LanguageModelV3StreamPart, LanguageModelV3ToolResultOutput, LanguageModelV3Usage } from '@ai-sdk/provider';
2
2
  import { Clock } from '@salesforce/agentic-common';
3
+ import type { Model } from '@salesforce/sfdx-agent-sdk';
3
4
  /**
4
5
  * Shared V3 helpers for the Mastra harness's `LanguageModelV3` implementations
5
6
  * — stream-shaping primitives, finish-reason / usage mappers, prompt-translation
@@ -11,6 +12,21 @@ import { Clock } from '@salesforce/agentic-common';
11
12
  * the lifecycle invariants those parts have to honor — drift between any two
12
13
  * paths is a TS compile error rather than a silent wire-shape divergence.
13
14
  */
15
+ /**
16
+ * Whether a generation parameter must be stripped from the outbound request
17
+ * for this model.
18
+ *
19
+ * `Model.excludedParameters` is a denylist of generation parameters the LLM
20
+ * Gateway rejects for the model. The pass-through builders consult it before
21
+ * forwarding a parameter — today only `temperature`, which the gateway
22
+ * deprecated for Claude Opus 4.7+ and now rejects. A model that lists a
23
+ * parameter here has it dropped from the request.
24
+ *
25
+ * `undefined` (the default) means nothing is excluded — every parameter the
26
+ * harness would send is forwarded. Only models that need to suppress a
27
+ * deprecated parameter declare `excludedParameters`.
28
+ */
29
+ export declare function isParameterExcluded(model: Model, parameter: string): boolean;
14
30
  /**
15
31
  * Encodes the AI SDK V3 text-span lifecycle (`text-start` / `text-delta` /
16
32
  * `text-end`) and the rotation that must happen around tool calls.
@@ -14,6 +14,23 @@ import { Clock, RealClock } from '@salesforce/agentic-common';
14
14
  * the lifecycle invariants those parts have to honor — drift between any two
15
15
  * paths is a TS compile error rather than a silent wire-shape divergence.
16
16
  */
17
+ /**
18
+ * Whether a generation parameter must be stripped from the outbound request
19
+ * for this model.
20
+ *
21
+ * `Model.excludedParameters` is a denylist of generation parameters the LLM
22
+ * Gateway rejects for the model. The pass-through builders consult it before
23
+ * forwarding a parameter — today only `temperature`, which the gateway
24
+ * deprecated for Claude Opus 4.7+ and now rejects. A model that lists a
25
+ * parameter here has it dropped from the request.
26
+ *
27
+ * `undefined` (the default) means nothing is excluded — every parameter the
28
+ * harness would send is forwarded. Only models that need to suppress a
29
+ * deprecated parameter declare `excludedParameters`.
30
+ */
31
+ export function isParameterExcluded(model, parameter) {
32
+ return model.excludedParameters?.includes(parameter) ?? false;
33
+ }
17
34
  /**
18
35
  * Encodes the AI SDK V3 text-span lifecycle (`text-start` / `text-delta` /
19
36
  * `text-end`) and the rotation that must happen around tool calls.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/sfdx-agent-harness-mastra",
3
- "version": "0.31.0",
3
+ "version": "0.33.0",
4
4
  "description": "Mastra-backed AgentHarness implementation for @salesforce/sfdx-agent-sdk",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -17,7 +17,8 @@
17
17
  "lint": "eslint .",
18
18
  "lint:fix": "eslint . --fix",
19
19
  "showcoverage": "open ./coverage/lcov-report/index.html",
20
- "test": "tsc --build ./test/tsconfig.json && vitest run --coverage"
20
+ "test": "tsc --build ./test/tsconfig.json && vitest run --coverage",
21
+ "test:conformance": "tsc --build ./test/tsconfig.json && vitest run test/conformance"
21
22
  },
22
23
  "license": "SEE LICENSE IN LICENSE.txt",
23
24
  "files": [
@@ -31,35 +32,36 @@
31
32
  "@ai-sdk/provider": "^3.0.10",
32
33
  "@anthropic-ai/bedrock-sdk": "^0.32.0",
33
34
  "@anthropic-ai/sdk": "^0.106.0",
34
- "@mastra/core": "^1.49.0",
35
- "@mastra/libsql": "^1.15.0",
36
- "@mastra/mcp": "^1.13.0",
37
- "@mastra/memory": "^1.22.1",
35
+ "@mastra/core": "^1.50.1",
36
+ "@mastra/libsql": "^1.15.1",
37
+ "@mastra/mcp": "^1.13.1",
38
+ "@mastra/memory": "^1.22.2",
38
39
  "@mastra/schema-compat": "^1.2.11",
39
40
  "@salesforce/agentic-common": "0.13.0",
40
41
  "openai": "^6.0.0"
41
42
  },
42
43
  "peerDependencies": {
43
- "@salesforce/sfdx-agent-sdk": "0.32.0"
44
+ "@salesforce/sfdx-agent-sdk": "0.34.0"
44
45
  },
45
46
  "devDependencies": {
46
47
  "@eslint/js": "^10.0.1",
47
- "@salesforce/sfdx-agent-sdk": "0.32.0",
48
+ "@salesforce/harness-conformance": "0.0.1",
49
+ "@salesforce/sfdx-agent-sdk": "0.34.0",
48
50
  "@types/node": "^22.20.0",
49
- "@vitest/coverage-istanbul": "^4.1.8",
50
- "@vitest/eslint-plugin": "^1.6.20",
51
- "eslint": "^10.6.0",
51
+ "@vitest/coverage-istanbul": "^4.1.10",
52
+ "@vitest/eslint-plugin": "^1.6.23",
53
+ "eslint": "^10.7.0",
52
54
  "eslint-config-prettier": "^10.1.8",
53
55
  "eslint-import-resolver-typescript": "^4.4.5",
54
56
  "eslint-plugin-import": "^2.32.0",
55
- "eslint-plugin-n": "^18.2.1",
57
+ "eslint-plugin-n": "^18.2.2",
56
58
  "globals": "^17.6.0",
57
59
  "lint-staged": "^17.0.7",
58
60
  "prettier": "^3.9.4",
59
61
  "rimraf": "^6.1.3",
60
62
  "tsx": "^4.23.0",
61
63
  "typescript": "^6.0.3",
62
- "typescript-eslint": "^8.62.1",
64
+ "typescript-eslint": "^8.63.0",
63
65
  "vitest": "^4.1.8"
64
66
  },
65
67
  "engines": {