@salesforce/sfdx-agent-harness-mastra 0.21.0 → 0.22.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.22.0] - 2026-06-22
7
+
8
+ ### Features
9
+ - **agentic-common,harnesses**: route HTTPS_PROXY via injectable undici Dispatcher @W-23121596 ([#610](https://github.com/forcedotcom/agentic-dx/pull/610))
10
+
6
11
  ## [0.21.0] - 2026-06-19
7
12
 
8
13
  ### Features
package/README.md CHANGED
@@ -54,6 +54,17 @@ Connectivity (model + provider auth) is supplied by the SDK's `AgentConnectivity
54
54
  direct-provider / LLMG Express api-key endpoints — both ship from `@salesforce/sfdx-agent-sdk`. The harness dispatches
55
55
  on `ModelConnectivityInfo.providerHint` internally to pick the right pass-through builder for each provider.
56
56
 
57
+ #### HTTPS proxy routing
58
+
59
+ The harness honors `HTTPS_PROXY` / `HTTP_PROXY` / `NO_PROXY` (either casing) via
60
+ `@salesforce/agentic-common`'s `resolveProxyDispatcher()`. When either proxy var is set at factory `create()` time, the
61
+ harness wraps every in-process `fetch` call site (LLM gateway language-model builders + MCP remote transports) with an
62
+ undici-backed routing function — no `globalThis` mutation. When no proxy env is set, `fetch` runs unwrapped (zero
63
+ overhead).
64
+
65
+ **Snapshot timing.** `HTTPS_PROXY` / `HTTP_PROXY` are captured at `create()` time and pinned for the harness's lifetime;
66
+ `NO_PROXY` is re-evaluated per request. Set the proxy env vars before constructing the factory's first harness.
67
+
57
68
  ### Extension surface
58
69
 
59
70
  These types let consumers reach Mastra-specific features through the SDK's typed `extensions` slot. The SDK infers
@@ -2,6 +2,7 @@
2
2
  * Copyright 2026, Salesforce, Inc. All rights reserved.
3
3
  * See LICENSE.txt for license terms.
4
4
  */
5
+ import { createProxyAwareFetch, resolveProxyDispatcher } from '@salesforce/agentic-common';
5
6
  import { MastraHarness } from './mastra-harness.js';
6
7
  const DEFAULT_TOOL_APPROVAL_TIMEOUT_MS = 600_000;
7
8
  /** Mastra-backed factory for creating AgentHarness instances. */
@@ -20,8 +21,10 @@ export class MastraHarnessFactory {
20
21
  this.config = config;
21
22
  }
22
23
  async create(storageRootFolder) {
24
+ const innerFetch = createProxyAwareFetch(resolveProxyDispatcher());
23
25
  return new MastraHarness(storageRootFolder, {
24
26
  toolApprovalTimeoutMs: this.config.toolApprovalTimeoutMs ?? DEFAULT_TOOL_APPROVAL_TIMEOUT_MS,
27
+ innerFetch,
25
28
  });
26
29
  }
27
30
  }
@@ -197,6 +197,15 @@ export interface MastraHarnessOptions {
197
197
  clock?: Clock;
198
198
  idGenerator?: UniqueIDGenerator;
199
199
  toolApprovalTimeoutMs?: number;
200
+ /**
201
+ * Inner fetch the harness threads into every in-process HTTP call site
202
+ * (LLM gateway language-model builders + MCP remote transports). Defaults
203
+ * to `globalThis.fetch` when omitted. The harness factory builds a
204
+ * proxy-aware fetch via `createProxyAwareFetch(dispatcher)` once per
205
+ * `create()` and passes it here, so `HTTPS_PROXY` routing happens without
206
+ * any `globalThis` mutation.
207
+ */
208
+ innerFetch?: typeof fetch;
200
209
  }
201
210
  export declare class MastraHarness implements MastraAgentHarness {
202
211
  private readonly storageRootFolder;
@@ -229,6 +238,7 @@ export declare class MastraHarness implements MastraAgentHarness {
229
238
  private readonly clock;
230
239
  private readonly idGenerator;
231
240
  private readonly toolApprovalTimeoutMs;
241
+ private readonly innerFetch;
232
242
  constructor(storageRootFolder: string, opts?: MastraHarnessOptions);
233
243
  /**
234
244
  * @requirements
@@ -376,11 +376,13 @@ export class MastraHarness {
376
376
  clock;
377
377
  idGenerator;
378
378
  toolApprovalTimeoutMs;
379
+ innerFetch;
379
380
  constructor(storageRootFolder, opts = {}) {
380
381
  this.storageRootFolder = storageRootFolder;
381
382
  this.clock = opts.clock ?? new RealClock();
382
383
  this.idGenerator = opts.idGenerator ?? new UUIDGenerator();
383
384
  this.toolApprovalTimeoutMs = opts.toolApprovalTimeoutMs ?? 600_000;
385
+ this.innerFetch = opts.innerFetch ?? globalThis.fetch;
384
386
  this.storage = new LibSQLStore({
385
387
  id: 'mastra-storage',
386
388
  url: `file:${this.storageRootFolder}/mastra.db`,
@@ -467,7 +469,7 @@ export class MastraHarness {
467
469
  const mcpServerStates = new Map();
468
470
  if (hasEnabledServers(config.mcpServers)) {
469
471
  options?.abortSignal?.throwIfAborted();
470
- const enabledServers = mapToMastraMCPServers(config.mcpServers, config.orgJwt);
472
+ const enabledServers = mapToMastraMCPServers(config.mcpServers, config.orgJwt, this.innerFetch);
471
473
  for (const [serverName, serverDef] of Object.entries(enabledServers)) {
472
474
  const client = new MCPClient({
473
475
  id: `${agentId}:${serverName}`,
@@ -909,7 +911,7 @@ export class MastraHarness {
909
911
  const nextConfig = config.mcpServers ?? {};
910
912
  const allNames = new Set([...Object.keys(prevConfig), ...Object.keys(nextConfig)]);
911
913
  const nextEnabledServers = hasEnabledServers(nextConfig)
912
- ? mapToMastraMCPServers(nextConfig, config.orgJwt)
914
+ ? mapToMastraMCPServers(nextConfig, config.orgJwt, this.innerFetch)
913
915
  : {};
914
916
  for (const name of allNames) {
915
917
  const prev = prevConfig[name];
@@ -1245,25 +1247,26 @@ export class MastraHarness {
1245
1247
  throw err;
1246
1248
  }
1247
1249
  };
1250
+ const innerFetch = this.innerFetch;
1248
1251
  switch (initial.providerHint) {
1249
1252
  case 'bedrock-anthropic':
1250
1253
  // Salesforce LLM Gateway's `/invoke-with-response-stream` (Bedrock pass-through).
1251
- return buildAnthropicLanguageModel({ getInfo, logBus, emitWireCommunication });
1254
+ return buildAnthropicLanguageModel({ getInfo, logBus, emitWireCommunication, innerFetch });
1252
1255
  case 'anthropic':
1253
1256
  // Direct Anthropic Messages API (`api.anthropic.com/v1/messages`) —
1254
1257
  // different wire shape (`x-api-key` auth, body without `anthropic_version`).
1255
- return buildAnthropicDirectLanguageModel({ getInfo, logBus, emitWireCommunication });
1258
+ return buildAnthropicDirectLanguageModel({ getInfo, logBus, emitWireCommunication, innerFetch });
1256
1259
  case 'openai-responses':
1257
1260
  case 'openai':
1258
1261
  // OpenAI Responses API. Salesforce LLM Gateway's `/responses` pass-through
1259
1262
  // for `'openai-responses'`; `api.openai.com/v1/responses` for `'openai'` direct.
1260
1263
  // Same wire shape; the only difference is the `baseUrl` the resolver supplies.
1261
- return buildOpenAILanguageModel({ getInfo, logBus, emitWireCommunication });
1264
+ return buildOpenAILanguageModel({ getInfo, logBus, emitWireCommunication, innerFetch });
1262
1265
  case 'openai-compatible':
1263
1266
  // OpenAI-compatible Chat Completions API (LLMG Express, LiteLLM, etc.).
1264
1267
  // Different wire shape from Responses — `/v1/chat/completions` with
1265
1268
  // `choices[0].delta` streaming events.
1266
- return buildOpenAIChatCompletionsLanguageModel({ getInfo, logBus, emitWireCommunication });
1269
+ return buildOpenAIChatCompletionsLanguageModel({ getInfo, logBus, emitWireCommunication, innerFetch });
1267
1270
  default:
1268
1271
  // Defensive — manager-side G8 should have caught this before
1269
1272
  // we got here. Make the error typed so consumers can branch
@@ -24,4 +24,11 @@ export declare function hasEnabledServers(config: MCPConfiguration | undefined):
24
24
  * - When `orgJwt` is provided, remote servers get a `fetch` that delegates to
25
25
  * {@link resolveMcpServerHeaders} per-request to inject auth for Salesforce Platform URLs.
26
26
  */
27
- export declare function mapToMastraMCPServers(config: MCPConfiguration, orgJwt?: JSONWebToken): Record<string, MastraMCPServerDefinition>;
27
+ export declare function mapToMastraMCPServers(config: MCPConfiguration, orgJwt?: JSONWebToken,
28
+ /**
29
+ * Inner fetch routed through the harness's `Dispatcher`. Defaults to
30
+ * `globalThis.fetch`; the harness factory threads a proxy-aware fetch
31
+ * here so MCP remote transports honor `HTTPS_PROXY` without `globalThis`
32
+ * mutation. Stdio transports ignore it (they're child processes).
33
+ */
34
+ innerFetch?: typeof fetch): Record<string, MastraMCPServerDefinition>;
@@ -44,7 +44,14 @@ const DEFAULT_RECONNECTION_OPTIONS = {
44
44
  * - When `orgJwt` is provided, remote servers get a `fetch` that delegates to
45
45
  * {@link resolveMcpServerHeaders} per-request to inject auth for Salesforce Platform URLs.
46
46
  */
47
- export function mapToMastraMCPServers(config, orgJwt) {
47
+ export function mapToMastraMCPServers(config, orgJwt,
48
+ /**
49
+ * Inner fetch routed through the harness's `Dispatcher`. Defaults to
50
+ * `globalThis.fetch`; the harness factory threads a proxy-aware fetch
51
+ * here so MCP remote transports honor `HTTPS_PROXY` without `globalThis`
52
+ * mutation. Stdio transports ignore it (they're child processes).
53
+ */
54
+ innerFetch = globalThis.fetch) {
48
55
  const result = {};
49
56
  for (const [name, serverConfig] of Object.entries(config)) {
50
57
  if (serverConfig.enabled === false) {
@@ -73,7 +80,7 @@ export function mapToMastraMCPServers(config, orgJwt) {
73
80
  ...(serverConfig.headers !== undefined && {
74
81
  requestInit: { headers: serverConfig.headers },
75
82
  }),
76
- ...(orgJwt && { fetch: createAuthFetch(serverConfig.url, orgJwt, serverConfig.headers) }),
83
+ ...(orgJwt && { fetch: createAuthFetch(serverConfig.url, orgJwt, innerFetch, serverConfig.headers) }),
77
84
  ...(serverConfig.timeout !== undefined && {
78
85
  timeout: serverConfig.timeout,
79
86
  connectTimeout: serverConfig.timeout,
@@ -99,14 +106,14 @@ export function mapToMastraMCPServers(config, orgJwt) {
99
106
  }
100
107
  return result;
101
108
  }
102
- function createAuthFetch(serverUrl, orgJwt, existingHeaders) {
109
+ function createAuthFetch(serverUrl, orgJwt, innerFetch, existingHeaders) {
103
110
  return async (url, init) => {
104
111
  const authHeaders = await resolveMcpServerHeaders(serverUrl, orgJwt, existingHeaders);
105
112
  const merged = new Headers(init?.headers);
106
113
  for (const [key, value] of Object.entries(authHeaders)) {
107
114
  merged.set(key, value);
108
115
  }
109
- return globalThis.fetch(url, { ...init, headers: merged });
116
+ return innerFetch(url, { ...init, headers: merged });
110
117
  };
111
118
  }
112
119
  //# sourceMappingURL=mcp-config-mapper.js.map
@@ -20,6 +20,13 @@ export declare function buildAnthropicLanguageModel(opts: {
20
20
  getInfo: () => ModelConnectivityInfo;
21
21
  logBus?: LogBus;
22
22
  emitWireCommunication?: (event: WireCommunicationEvent) => void;
23
+ /**
24
+ * Inner fetch routed through the harness's `Dispatcher` (built from
25
+ * `HTTPS_PROXY` / `HTTP_PROXY` or a consumer-supplied one). Defaults to
26
+ * `globalThis.fetch` so unit tests that don't care about proxy routing
27
+ * don't need to thread it.
28
+ */
29
+ innerFetch?: typeof fetch;
23
30
  }): LanguageModelV3;
24
31
  /**
25
32
  * Builds the in-process `LanguageModelV3` impl for direct-Anthropic API access
@@ -44,6 +51,8 @@ export declare function buildAnthropicDirectLanguageModel(opts: {
44
51
  getInfo: () => ModelConnectivityInfo;
45
52
  logBus?: LogBus;
46
53
  emitWireCommunication?: (event: WireCommunicationEvent) => void;
54
+ /** See {@link buildAnthropicLanguageModel} `innerFetch`. */
55
+ innerFetch?: typeof fetch;
47
56
  }): LanguageModelV3;
48
57
  /**
49
58
  * Translates a V3 `LanguageModelV3Prompt` into Anthropic's Messages API shape
@@ -20,7 +20,7 @@ import { TextSpanLifecycle, createGatewayFetch, isEmptyUsage, mapAnthropicFinish
20
20
  * the next request.
21
21
  */
22
22
  export function buildAnthropicLanguageModel(opts) {
23
- const { getInfo, logBus, emitWireCommunication } = opts;
23
+ const { getInfo, logBus, emitWireCommunication, innerFetch } = opts;
24
24
  const initialInfo = getInfo();
25
25
  const ctx = {
26
26
  getInfo,
@@ -32,7 +32,7 @@ export function buildAnthropicLanguageModel(opts) {
32
32
  // The fetch wrapper re-evaluates `getHeaders()` on every outbound call —
33
33
  // JWT rotation, premium-rate-limit gate flips, BYOK provider rotation
34
34
  // all land on the next request without rebuilding the bridge.
35
- fetch: createGatewayFetch(() => info.getHeaders(), buildWireHooks(ctx)),
35
+ fetch: createGatewayFetch(() => info.getHeaders(), buildWireHooks(ctx), innerFetch),
36
36
  }),
37
37
  };
38
38
  return {
@@ -68,7 +68,7 @@ export function buildAnthropicLanguageModel(opts) {
68
68
  * language model.
69
69
  */
70
70
  export function buildAnthropicDirectLanguageModel(opts) {
71
- const { getInfo, logBus, emitWireCommunication } = opts;
71
+ const { getInfo, logBus, emitWireCommunication, innerFetch } = opts;
72
72
  const initialInfo = getInfo();
73
73
  const ctx = {
74
74
  getInfo,
@@ -85,7 +85,7 @@ export function buildAnthropicDirectLanguageModel(opts) {
85
85
  new Anthropic({
86
86
  apiKey: 'anthropic-direct-resolver-managed',
87
87
  baseURL: info.baseUrl,
88
- fetch: createGatewayFetch(() => translateBearerToXApiKey(info), buildWireHooks(ctx)),
88
+ fetch: createGatewayFetch(() => translateBearerToXApiKey(info), buildWireHooks(ctx), innerFetch),
89
89
  }),
90
90
  };
91
91
  return {
@@ -26,6 +26,8 @@ export declare function buildOpenAIChatCompletionsLanguageModel(opts: {
26
26
  getInfo: () => ModelConnectivityInfo;
27
27
  logBus?: LogBus;
28
28
  emitWireCommunication?: (event: WireCommunicationEvent) => void;
29
+ /** See `buildAnthropicLanguageModel` `innerFetch`. */
30
+ innerFetch?: typeof fetch;
29
31
  }): LanguageModelV3;
30
32
  /**
31
33
  * Translates a V3 `LanguageModelV3Prompt` into the OpenAI Chat Completions
@@ -25,15 +25,16 @@ import { TextSpanLifecycle, createGatewayFetch, isEmptyUsage, mapOpenAIUsage, to
25
25
  * without rebuilding the language model.
26
26
  */
27
27
  export function buildOpenAIChatCompletionsLanguageModel(opts) {
28
- const { getInfo, logBus, emitWireCommunication } = opts;
28
+ const { getInfo, logBus, emitWireCommunication, innerFetch } = opts;
29
29
  const initialInfo = getInfo();
30
+ const ctx = { getInfo, logBus, emitWireCommunication, innerFetch };
30
31
  return {
31
32
  specificationVersion: 'v3',
32
33
  provider: 'openai-compatible-chat-completions-pass-through',
33
34
  modelId: initialInfo.nativeModelId,
34
35
  supportedUrls: {},
35
- doGenerate: (options) => doGenerate({ getInfo, logBus, emitWireCommunication }, options),
36
- doStream: (options) => doStream({ getInfo, logBus, emitWireCommunication }, options),
36
+ doGenerate: (options) => doGenerate(ctx, options),
37
+ doStream: (options) => doStream(ctx, options),
37
38
  };
38
39
  }
39
40
  /**
@@ -53,7 +54,7 @@ async function buildRequest(ctx, options) {
53
54
  // request. The SDK still wants a non-empty `apiKey` at construction;
54
55
  // an opaque sentinel keeps it from reading `process.env['OPENAI_API_KEY']`.
55
56
  apiKey: 'openai-compatible-resolver-managed',
56
- fetch: createGatewayFetch(() => info.getHeaders(), wireHooks),
57
+ fetch: createGatewayFetch(() => info.getHeaders(), wireHooks, ctx.innerFetch),
57
58
  });
58
59
  const messages = translatePromptToChatCompletions(options.prompt);
59
60
  const tools = translateToolsToChatCompletions(options);
@@ -18,6 +18,8 @@ export declare function buildOpenAILanguageModel(opts: {
18
18
  getInfo: () => ModelConnectivityInfo;
19
19
  logBus?: LogBus;
20
20
  emitWireCommunication?: (event: WireCommunicationEvent) => void;
21
+ /** See `buildAnthropicLanguageModel` `innerFetch`. */
22
+ innerFetch?: typeof fetch;
21
23
  }): LanguageModelV3;
22
24
  /**
23
25
  * Translates a V3 `LanguageModelV3Prompt` into OpenAI's Responses input shape:
@@ -17,8 +17,9 @@ import { TextSpanLifecycle, createGatewayFetch, isEmptyUsage, mapOpenAIFinishRea
17
17
  * next request.
18
18
  */
19
19
  export function buildOpenAILanguageModel(opts) {
20
- const { getInfo, logBus, emitWireCommunication } = opts;
20
+ const { getInfo, logBus, emitWireCommunication, innerFetch } = opts;
21
21
  const initialInfo = getInfo();
22
+ const ctx = { getInfo, logBus, emitWireCommunication, innerFetch };
22
23
  return {
23
24
  specificationVersion: 'v3',
24
25
  provider: 'salesforce-llm-gateway-pass-through-openai',
@@ -27,8 +28,8 @@ export function buildOpenAILanguageModel(opts) {
27
28
  // bag inside `commonParams.model`.
28
29
  modelId: initialInfo.nativeModelId,
29
30
  supportedUrls: {},
30
- doGenerate: (options) => doGenerate({ getInfo, logBus, emitWireCommunication }, options),
31
- doStream: (options) => doStream({ getInfo, logBus, emitWireCommunication }, options),
31
+ doGenerate: (options) => doGenerate(ctx, options),
32
+ doStream: (options) => doStream(ctx, options),
32
33
  };
33
34
  }
34
35
  /**
@@ -53,7 +54,7 @@ async function buildOpenAIRequest(ctx, options) {
53
54
  // Re-evaluates `getHeaders()` per outbound call so JWT rotation /
54
55
  // premium-rate-limit gate flips / BYOK provider rotation land on the
55
56
  // next request without rebuilding this bridge.
56
- fetch: createGatewayFetch(() => info.getHeaders(), wireHooks),
57
+ fetch: createGatewayFetch(() => info.getHeaders(), wireHooks, ctx.innerFetch),
57
58
  });
58
59
  const { input, instructions } = translatePromptToOpenAI(options.prompt);
59
60
  const tools = translateToolsToOpenAI(options);
@@ -182,11 +182,17 @@ export type GatewayFetchWireHooks = {
182
182
  clock?: Clock;
183
183
  };
184
184
  /**
185
- * Wraps `globalThis.fetch` with a per-request header merger. The `getHeaders`
186
- * argument may be sync or async — `ModelConnectivityInfo.getHeaders` returns a
187
- * `Promise<Record<string, string>>` so JWT freshness, premium-rate-limit gate
188
- * flips, and per-call rotating credentials all land on the next outbound call
189
- * without rebuilding the fetch wrapper.
185
+ * Wraps an `innerFetch` (defaults to `globalThis.fetch`) with a per-request
186
+ * header merger. The `getHeaders` argument may be sync or async —
187
+ * `ModelConnectivityInfo.getHeaders` returns a `Promise<Record<string, string>>`
188
+ * so JWT freshness, premium-rate-limit gate flips, and per-call rotating
189
+ * credentials all land on the next outbound call without rebuilding the
190
+ * fetch wrapper.
191
+ *
192
+ * The `innerFetch` arg is what makes proxy support compose: the harness
193
+ * factory builds a proxy-aware fetch via `createProxyAwareFetch(dispatcher)`
194
+ * once per `create()` and passes it through, so this wrapper inherits the
195
+ * dispatcher without any `globalThis` mutation.
190
196
  *
191
197
  * When `wireHooks` is supplied, every outbound call emits one
192
198
  * `WireCommunicationEvent` `request` event and one `response` event onto the
@@ -197,7 +203,7 @@ export type GatewayFetchWireHooks = {
197
203
  * <n> }` so wire-communication captures don't blow the consumer's log file
198
204
  * budget.
199
205
  */
200
- export declare function createGatewayFetch(getHeaders: () => Record<string, string> | Promise<Record<string, string>>, wireHooks?: GatewayFetchWireHooks): typeof fetch;
206
+ export declare function createGatewayFetch(getHeaders: () => Record<string, string> | Promise<Record<string, string>>, wireHooks?: GatewayFetchWireHooks, innerFetch?: typeof fetch): typeof fetch;
201
207
  /**
202
208
  * The `WireCommunicationEvent` shape from `@salesforce/sfdx-agent-sdk` minus the
203
209
  * harness-injected fields. Kept structural so this module doesn't import
@@ -223,11 +223,17 @@ export function mapOpenAIUsage(usage) {
223
223
  };
224
224
  }
225
225
  /**
226
- * Wraps `globalThis.fetch` with a per-request header merger. The `getHeaders`
227
- * argument may be sync or async — `ModelConnectivityInfo.getHeaders` returns a
228
- * `Promise<Record<string, string>>` so JWT freshness, premium-rate-limit gate
229
- * flips, and per-call rotating credentials all land on the next outbound call
230
- * without rebuilding the fetch wrapper.
226
+ * Wraps an `innerFetch` (defaults to `globalThis.fetch`) with a per-request
227
+ * header merger. The `getHeaders` argument may be sync or async —
228
+ * `ModelConnectivityInfo.getHeaders` returns a `Promise<Record<string, string>>`
229
+ * so JWT freshness, premium-rate-limit gate flips, and per-call rotating
230
+ * credentials all land on the next outbound call without rebuilding the
231
+ * fetch wrapper.
232
+ *
233
+ * The `innerFetch` arg is what makes proxy support compose: the harness
234
+ * factory builds a proxy-aware fetch via `createProxyAwareFetch(dispatcher)`
235
+ * once per `create()` and passes it through, so this wrapper inherits the
236
+ * dispatcher without any `globalThis` mutation.
231
237
  *
232
238
  * When `wireHooks` is supplied, every outbound call emits one
233
239
  * `WireCommunicationEvent` `request` event and one `response` event onto the
@@ -238,7 +244,7 @@ export function mapOpenAIUsage(usage) {
238
244
  * <n> }` so wire-communication captures don't blow the consumer's log file
239
245
  * budget.
240
246
  */
241
- export function createGatewayFetch(getHeaders, wireHooks) {
247
+ export function createGatewayFetch(getHeaders, wireHooks, innerFetch = globalThis.fetch) {
242
248
  return async (input, init) => {
243
249
  const resolved = await getHeaders();
244
250
  // Per WHATWG Fetch, `init.headers` is `HeadersInit = Headers | Record<string, string> | [string, string][]`.
@@ -253,7 +259,7 @@ export function createGatewayFetch(getHeaders, wireHooks) {
253
259
  }
254
260
  const finalInit = { ...init, headers: finalHeaders };
255
261
  if (!wireHooks) {
256
- return globalThis.fetch(input, finalInit);
262
+ return innerFetch(input, finalInit);
257
263
  }
258
264
  const url = typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url;
259
265
  const method = (finalInit.method ?? 'GET').toUpperCase();
@@ -270,7 +276,7 @@ export function createGatewayFetch(getHeaders, wireHooks) {
270
276
  body: tryDecodeBody(finalInit.body),
271
277
  });
272
278
  try {
273
- const response = await globalThis.fetch(input, finalInit);
279
+ const response = await innerFetch(input, finalInit);
274
280
  wireHooks.emitWireCommunication({
275
281
  type: 'llm-response',
276
282
  timestamp: clock.now(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/sfdx-agent-harness-mastra",
3
- "version": "0.21.0",
3
+ "version": "0.22.0",
4
4
  "description": "Mastra-backed AgentHarness implementation for @salesforce/sfdx-agent-sdk",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -36,15 +36,15 @@
36
36
  "@mastra/mcp": "^1.10.0",
37
37
  "@mastra/memory": "^1.20.3",
38
38
  "@mastra/schema-compat": "^1.2.11",
39
- "@salesforce/agentic-common": "0.11.0",
39
+ "@salesforce/agentic-common": "0.12.0",
40
40
  "openai": "^6.0.0"
41
41
  },
42
42
  "peerDependencies": {
43
- "@salesforce/sfdx-agent-sdk": "0.22.0"
43
+ "@salesforce/sfdx-agent-sdk": "0.23.0"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@eslint/js": "^10.0.1",
47
- "@salesforce/sfdx-agent-sdk": "0.22.0",
47
+ "@salesforce/sfdx-agent-sdk": "0.23.0",
48
48
  "@types/node": "^22.19.21",
49
49
  "@vitest/coverage-istanbul": "^4.1.8",
50
50
  "@vitest/eslint-plugin": "^1.6.20",