@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 +5 -0
- package/README.md +11 -0
- package/dist/mastra-harness-factory.js +3 -0
- package/dist/mastra-harness.d.ts +10 -0
- package/dist/mastra-harness.js +9 -6
- package/dist/mcp-config-mapper.d.ts +8 -1
- package/dist/mcp-config-mapper.js +11 -4
- package/dist/pass-through-anthropic.d.ts +9 -0
- package/dist/pass-through-anthropic.js +4 -4
- package/dist/pass-through-openai-chat-completions.d.ts +2 -0
- package/dist/pass-through-openai-chat-completions.js +5 -4
- package/dist/pass-through-openai.d.ts +2 -0
- package/dist/pass-through-openai.js +5 -4
- package/dist/v3-stream-mapping.d.ts +12 -6
- package/dist/v3-stream-mapping.js +14 -8
- package/package.json +4 -4
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
|
}
|
package/dist/mastra-harness.d.ts
CHANGED
|
@@ -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
|
package/dist/mastra-harness.js
CHANGED
|
@@ -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
|
|
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
|
|
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(
|
|
36
|
-
doStream: (options) => doStream(
|
|
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(
|
|
31
|
-
doStream: (options) => doStream(
|
|
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
|
|
186
|
-
* argument may be sync or async —
|
|
187
|
-
* `Promise<Record<string, string>>`
|
|
188
|
-
* flips, and per-call rotating
|
|
189
|
-
* without rebuilding the
|
|
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
|
|
227
|
-
* argument may be sync or async —
|
|
228
|
-
* `Promise<Record<string, string>>`
|
|
229
|
-
* flips, and per-call rotating
|
|
230
|
-
* without rebuilding the
|
|
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
|
|
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
|
|
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.
|
|
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.
|
|
39
|
+
"@salesforce/agentic-common": "0.12.0",
|
|
40
40
|
"openai": "^6.0.0"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
|
-
"@salesforce/sfdx-agent-sdk": "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.
|
|
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",
|