@salesforce/sfdx-agent-harness-claude 0.17.0 → 0.19.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,21 @@
3
3
  All notable changes to `@salesforce/sfdx-agent-harness-claude` are documented in this file.
4
4
  Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
5
5
 
6
+ ## [0.19.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
+
11
+ ## [0.18.0] - 2026-06-19
12
+
13
+ ### Features
14
+ - **BREAKING** Major refactor to Unify Connectivity via ModelConnectivityInfo @W-22782317 ([#607](https://github.com/forcedotcom/agentic-dx/pull/607))
15
+
16
+ ### Chores
17
+ - **deps-dev**: bump @vitest/eslint-plugin from 1.6.19 to 1.6.20 in the vitest group across 1 directory ([#601](https://github.com/forcedotcom/agentic-dx/pull/601))
18
+ - **deps-dev**: bump eslint from 10.4.1 to 10.5.0 in the eslint group across 1 directory ([#600](https://github.com/forcedotcom/agentic-dx/pull/600))
19
+ - **deps-dev**: bump @types/node from 22.19.20 to 22.19.21 in the dev-dependencies group ([#599](https://github.com/forcedotcom/agentic-dx/pull/599))
20
+
6
21
  ## [0.17.0] - 2026-06-15
7
22
 
8
23
  ### Fixes
package/README.md CHANGED
@@ -15,37 +15,39 @@ npm install @salesforce/sfdx-agent-sdk @salesforce/sfdx-agent-harness-claude
15
15
  ## Quick start
16
16
 
17
17
  ```ts
18
- import { createAgentManager } from '@salesforce/sfdx-agent-sdk';
19
- import { ClaudeHarnessFactory, DirectClaudeTransportResolver } from '@salesforce/sfdx-agent-harness-claude';
18
+ import { createAgentManager, DefaultAgentConnectivityResolver } from '@salesforce/sfdx-agent-sdk';
19
+ import { ClaudeHarnessFactory } from '@salesforce/sfdx-agent-harness-claude';
20
20
 
21
21
  const harnessFactory = new ClaudeHarnessFactory({
22
- resolver: new DirectClaudeTransportResolver(() => process.env.ANTHROPIC_API_KEY!),
23
22
  permissionMode: 'bypassPermissions',
24
23
  });
25
24
 
26
- const manager = await createAgentManager('/path/to/storage', harnessFactory);
25
+ const manager = await createAgentManager('/path/to/storage', harnessFactory, {
26
+ connectivityResolver: new DefaultAgentConnectivityResolver(),
27
+ });
27
28
  ```
28
29
 
30
+ Connectivity (gateway URL, JWT, headers, native model id) is resolved by an `AgentConnectivityResolver` on the SDK and
31
+ handed to the harness as a `ModelConnectivityInfo` bag. The harness factory itself owns harness-only concerns
32
+ (permission mode, tool-approval timeout, bypass list, query defaults). For BYOK / direct-Anthropic / LLMG-Express
33
+ deployments use `ApiKeyConnectivityResolver`; for non-Salesforce hosts implement your own resolver. See the
34
+ [`@salesforce/sfdx-agent-sdk` README](../sfdx-agent-sdk/README.md) for the full resolver surface.
35
+
29
36
  See the [`@salesforce/sfdx-agent-sdk` README](../sfdx-agent-sdk/README.md) for the full consumer-facing API. For
30
37
  internal architecture, see [ARCHITECTURE.md](ARCHITECTURE.md).
31
38
 
32
39
  ## Public API
33
40
 
34
- | Export | Kind | Description |
35
- | ------------------------------------------------ | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
36
- | `ClaudeHarnessFactory` | class | `HarnessFactory` that produces a Claude-backed `AgentHarness` bound to a storage root. |
37
- | `ClaudeHarnessFactoryConfig` | interface | Configuration for `ClaudeHarnessFactory` (`resolver`, `permissionMode`, optional `queryDefaults`, `toolApprovalTimeoutMs`, `bypassApprovalTools`). |
38
- | `ClaudeHarnessPermissionMode` | type | Narrowed permission mode: `'bypassPermissions' \| 'acceptEdits' \| 'plan'`. |
39
- | `ClaudeAgentHarness` | type | Branded `AgentHarness` subtype carrying the Claude-specific config inference. Inferred at `manager.createAgent` automatically — annotation rarely needed. |
40
- | `ClaudeAgentConfig` | type | `AgentConfig` extended with Claude-only fields (`skillSearch`, `toolSearch`). |
41
- | `ClaudeSkillSearchConfig` | type | Shape of `ClaudeAgentConfig.skillSearch` (`topK`). Opts the in-process `skill_bridge` MCP server into search-on-demand mode. |
42
- | `ClaudeToolSearchConfig` | type | Shape of `ClaudeAgentConfig.toolSearch` (`alwaysActive`). Stamps `_meta['anthropic/alwaysLoad']` on matching tools so they bypass Claude tool-search deferral. |
43
- | `ClaudeTransportResolver` | interface | Strategy for resolving transport env, auth, and native model ID for the subprocess. |
44
- | `ClaudeTransportResolverResult` | type | Return type of `ClaudeTransportResolver.resolve()` (`env` + `nativeModelId`). |
45
- | `ClaudeQueryDefaults` | type | Consumer-overridable defaults for the Claude `query()` call (excludes `model`). |
46
- | `DirectClaudeTransportResolver` | class | Resolver for direct Anthropic API access (sets `ANTHROPIC_API_KEY`). Suitable for local dev with a personal API key; not the production path. |
47
- | `SalesforceBedrockClaudeTransportResolver` | class | Resolver that routes the Claude subprocess through the Salesforce LLM Gateway's Bedrock proxy. Canonical production path for Salesforce developer-tooling consumers. |
48
- | `SalesforceBedrockClaudeTransportResolverConfig` | interface | Configuration for `SalesforceBedrockClaudeTransportResolver` (`gatewayBaseUrl`, optional `modelIdMap`, `appContext`, `featureId`, `ideContext`). |
41
+ | Export | Kind | Description |
42
+ | ----------------------------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
43
+ | `ClaudeHarnessFactory` | class | `HarnessFactory` that produces a Claude-backed `AgentHarness` bound to a storage root. |
44
+ | `ClaudeHarnessFactoryConfig` | interface | Configuration for `ClaudeHarnessFactory` (`permissionMode`, optional `queryDefaults`, `toolApprovalTimeoutMs`, `bypassApprovalTools`, `dispatcher`). |
45
+ | `ClaudeHarnessPermissionMode` | type | Narrowed permission mode: `'bypassPermissions' \| 'acceptEdits' \| 'plan'`. |
46
+ | `ClaudeAgentHarness` | type | Branded `AgentHarness` subtype carrying the Claude-specific config inference. Inferred at `manager.createAgent` automatically — annotation rarely needed. |
47
+ | `ClaudeAgentConfig` | type | `AgentConfig` extended with Claude-only fields (`skillSearch`, `toolSearch`). |
48
+ | `ClaudeSkillSearchConfig` | type | Shape of `ClaudeAgentConfig.skillSearch` (`topK`). Opts the in-process `skill_bridge` MCP server into search-on-demand mode. |
49
+ | `ClaudeToolSearchConfig` | type | Shape of `ClaudeAgentConfig.toolSearch` (`alwaysActive`). Stamps `_meta['anthropic/alwaysLoad']` on matching tools so they bypass Claude tool-search deferral. |
50
+ | `ClaudeQueryDefaults` | type | Consumer-overridable defaults for the Claude `query()` call (excludes `model`). |
49
51
 
50
52
  ### Permission modes
51
53
 
@@ -61,6 +63,19 @@ provide. For human-in-the-loop gating, set `StreamOptions.requireToolApproval: t
61
63
  installs a `PreToolUse` SDK hook for that turn that gates every tool call through the SDK's `tool-approval-request` flow
62
64
  regardless of the factory-pinned permission mode.
63
65
 
66
+ ### HTTPS proxy routing
67
+
68
+ The harness honors `HTTPS_PROXY` / `HTTP_PROXY` / `NO_PROXY` (either casing) for the host-side MCP HTTP transport via
69
+ `@salesforce/agentic-common`'s `resolveProxyDispatcher()`. When either proxy var is set at factory `create()` time, the
70
+ harness wraps the transport `fetch` with an undici-backed routing function — no `globalThis` mutation. When no proxy
71
+ env is set, `fetch` runs unwrapped (zero overhead).
72
+
73
+ The Claude subprocess inherits `HTTPS_PROXY` from the host env separately and is unaffected by the host-side wrapping.
74
+ Subprocess-side proxy routing follows whatever env was set when the subprocess spawned.
75
+
76
+ **Snapshot timing.** `HTTPS_PROXY` / `HTTP_PROXY` are captured at `create()` time and pinned for the harness's lifetime;
77
+ `NO_PROXY` is re-evaluated per request. Set the proxy env vars before constructing the factory's first harness.
78
+
64
79
  ### Skill search
65
80
 
66
81
  `AgentConfig.skills` is a cross-harness field; both Mastra and Claude surface configured skills to the model. By default
@@ -201,65 +216,25 @@ afterwards.
201
216
  > (which `break` does inside `for await`) finalizes the harness's source generator before the terminal event can reach
202
217
  > you. Settle in-line and keep iterating; the loop exits naturally when the terminal `finish` arrives.
203
218
 
204
- ### Using the Salesforce Bedrock resolver
219
+ ### Connectivity (gateway URL, auth, native model id)
205
220
 
206
- The canonical production path for Salesforce developer-tooling consumers. Routes the Claude subprocess through the
207
- Salesforce LLM Gateway's Bedrock proxy using an org JWT for auth.
221
+ The Claude harness reads connectivity per-spawn from a `ModelConnectivityInfo` bag the SDK hands it. The bag carries the
222
+ gateway base URL, the native model id (e.g. `anthropic.claude-sonnet-4-6`), the provider hint (`bedrock-anthropic` or
223
+ `anthropic`), and a `getHeaders()` callback the harness invokes once per subprocess spawn. JWT rotation rides on that
224
+ callback — the next `stream()` call reads the rotated JWT through the same closure without rebuilding anything.
208
225
 
209
- ```ts
210
- import { createAgentManager } from '@salesforce/sfdx-agent-sdk';
211
- import { ClaudeHarnessFactory, SalesforceBedrockClaudeTransportResolver } from '@salesforce/sfdx-agent-harness-claude';
226
+ The SDK ships two bundled resolvers and a documented extension point — see the
227
+ [`@salesforce/sfdx-agent-sdk` README](../sfdx-agent-sdk/README.md):
212
228
 
213
- const harnessFactory = new ClaudeHarnessFactory({
214
- resolver: new SalesforceBedrockClaudeTransportResolver({
215
- // Pick the gateway URL for your environment — e.g. test:
216
- gatewayBaseUrl: 'https://test.api.salesforce.com/ai/gpt/v1',
217
- // No `modelIdMap` needed in the common case: the LLM Gateway's invoke API
218
- // accepts the gateway-prefixed canonical id (`llmgateway__Bedrock*`) for
219
- // every Anthropic Claude model, and the SDK's `Model.name` is already that
220
- // canonical id, so the resolver's default passthrough is correct. Set the
221
- // map only if your consumer needs to translate to a different shape (e.g.
222
- // a self-hosted Bedrock proxy that expects the bare-native ids).
223
- // Optional: override the `x-client-feature-id` header. Defaults to `orgJwt.getFeatureId()`.
224
- featureId: 'EinsteinGptForDevelopers',
225
- // Optional: getter for the `x-aip-user-ctx-tag` header. Invoked on every `resolve()`;
226
- // returning `undefined` (or omitting the option) drops the header. Use when the tag is
227
- // derived from post-JWT-mint state that changes between turns.
228
- ideContext: () => orgEntitlement.ideUserContext,
229
- }),
230
- permissionMode: 'bypassPermissions',
231
- });
229
+ - **`DefaultAgentConnectivityResolver`** — Salesforce-org gateway path. Targets the LLM Gateway's pass-through Bedrock
230
+ endpoint (`/invoke-with-response-stream`) using an org JWT.
231
+ - **`ApiKeyConnectivityResolver`** — direct-Anthropic / OpenAI-direct / LLMG-Express. Constructor takes
232
+ `{ getApiKey, baseUrl, providerHint, ... }`.
233
+ - **Custom `AgentConnectivityResolver`** — for deployments neither bundled resolver covers, implement the SDK's
234
+ `AgentConnectivityResolver` interface and pass it to `createAgentManager({ ..., connectivityResolver })`.
232
235
 
233
- const manager = await createAgentManager('/path/to/storage', harnessFactory);
234
- ```
235
-
236
- JWT refresh works the same as elsewhere in the harness: each `resolve()` call eagerly reads `orgJwt.getValue()` /
237
- `orgJwt.getTenantKey()` and freezes the result into the static subprocess headers. `Agent.updateAgentConfig({})` runs
238
- `harness.updateAgent`, the resolver re-reads the fresh JWT on the next `stream()`, and the next subprocess spawns with
239
- the new headers — connected MCP clients are preserved through the rotation.
240
-
241
- ### Using a custom resolver
242
-
243
- For deployment topologies neither built-in resolver covers (alternative gateways, custom auth flows, etc.), implement
244
- the `ClaudeTransportResolver` interface directly.
245
-
246
- ```ts
247
- import { createAgentManager } from '@salesforce/sfdx-agent-sdk';
248
- import { ClaudeHarnessFactory } from '@salesforce/sfdx-agent-harness-claude';
249
- import type { ClaudeTransportResolver } from '@salesforce/sfdx-agent-harness-claude';
250
-
251
- const resolver: ClaudeTransportResolver = {
252
- async resolve(orgJwt, modelId) {
253
- return {
254
- env: { ANTHROPIC_API_KEY: '...' },
255
- nativeModelId: 'anthropic.claude-sonnet-4-6',
256
- };
257
- },
258
- };
259
-
260
- const harnessFactory = new ClaudeHarnessFactory({ resolver, permissionMode: 'bypassPermissions' });
261
- const manager = await createAgentManager('/path/to/storage', harnessFactory);
262
- ```
236
+ The harness factory itself takes no resolver, gateway URL, or auth field — those concerns live entirely on the
237
+ connectivity resolver.
263
238
 
264
239
  ### Per-server MCP reconnect (eager semantics)
265
240
 
@@ -287,7 +262,6 @@ reject.
287
262
 
288
263
  ```ts
289
264
  const harnessFactory = new ClaudeHarnessFactory({
290
- resolver: new DirectClaudeTransportResolver(() => process.env.ANTHROPIC_API_KEY!),
291
265
  permissionMode: 'bypassPermissions',
292
266
  queryDefaults: {
293
267
  maxTurns: 5,
@@ -307,11 +281,10 @@ across harnesses.
307
281
  `ClaudeQueryDefaults` does not strip). It blocks the model from emitting `tool_use` for the named built-ins entirely.
308
282
 
309
283
  ```ts
310
- import { createAgentManager } from '@salesforce/sfdx-agent-sdk';
311
- import { ClaudeHarnessFactory, SalesforceBedrockClaudeTransportResolver } from '@salesforce/sfdx-agent-harness-claude';
284
+ import { createAgentManager, DefaultAgentConnectivityResolver } from '@salesforce/sfdx-agent-sdk';
285
+ import { ClaudeHarnessFactory } from '@salesforce/sfdx-agent-harness-claude';
312
286
 
313
287
  const harnessFactory = new ClaudeHarnessFactory({
314
- resolver: new SalesforceBedrockClaudeTransportResolver({ gatewayBaseUrl: '...' }),
315
288
  permissionMode: 'bypassPermissions',
316
289
  // Suppress the built-in `AskUserQuestion` so the cross-harness consumer
317
290
  // tool below is the only ask-the-user surface the model sees on Claude
@@ -321,7 +294,9 @@ const harnessFactory = new ClaudeHarnessFactory({
321
294
  },
322
295
  });
323
296
 
324
- const manager = await createAgentManager('/path/to/storage', harnessFactory);
297
+ const manager = await createAgentManager('/path/to/storage', harnessFactory, {
298
+ connectivityResolver: new DefaultAgentConnectivityResolver(),
299
+ });
325
300
  const agent = await manager.createAgent(projectRoot, {
326
301
  instructions: '...',
327
302
  tools: [
@@ -360,7 +335,6 @@ additional tool names out of the gate.
360
335
 
361
336
  ```ts
362
337
  const harnessFactory = new ClaudeHarnessFactory({
363
- resolver: new SalesforceBedrockClaudeTransportResolver({ gatewayBaseUrl: '...' }),
364
338
  permissionMode: 'bypassPermissions',
365
339
  // Even when `requireToolApproval: true`, calls to these tools bypass the
366
340
  // PreToolUse hook. They still emit `tool-call` / `tool-result` ChatEvents,
@@ -1,5 +1,4 @@
1
- import type { HarnessFactory } from '@salesforce/sfdx-agent-sdk';
2
- import type { ClaudeTransportResolver } from './claude-transport-resolver.js';
1
+ import type { HarnessFactory, ProviderHint } from '@salesforce/sfdx-agent-sdk';
3
2
  import type { ClaudeQueryDefaults } from './claude-query-options-builder.js';
4
3
  import { type ClaudeAgentHarness } from './claude-harness.js';
5
4
  /**
@@ -13,7 +12,6 @@ import { type ClaudeAgentHarness } from './claude-harness.js';
13
12
  */
14
13
  export type ClaudeHarnessPermissionMode = 'bypassPermissions' | 'acceptEdits' | 'plan';
15
14
  export interface ClaudeHarnessFactoryConfig {
16
- resolver: ClaudeTransportResolver;
17
15
  permissionMode: ClaudeHarnessPermissionMode;
18
16
  queryDefaults?: ClaudeQueryDefaults;
19
17
  /**
@@ -42,6 +40,14 @@ export declare class ClaudeHarnessFactory implements HarnessFactory<ClaudeAgentH
42
40
  private readonly config;
43
41
  readonly harnessId = "claude";
44
42
  readonly protocolVersion = 1;
43
+ /**
44
+ * The Claude CLI binary speaks Anthropic-shaped protocols only — both the
45
+ * Bedrock pass-through (`'bedrock-anthropic'`) and direct Anthropic API
46
+ * (`'anthropic'`). Routing it at a non-Anthropic model is a programming
47
+ * error caught at `createAgent` / `updateAgentConfig` by the manager's
48
+ * G8 validation.
49
+ */
50
+ readonly supportedProviderHints: readonly ProviderHint[];
45
51
  constructor(config: ClaudeHarnessFactoryConfig);
46
52
  create(storageRootFolder: string): Promise<ClaudeAgentHarness>;
47
53
  }
@@ -2,19 +2,30 @@
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 { ClaudeQueryEnvBuilder } from './claude-query-env-builder.js';
6
7
  import { ClaudeQueryOptionsBuilder } from './claude-query-options-builder.js';
7
8
  import { ClaudeHarness } from './claude-harness.js';
9
+ import { buildMcpClients } from './mcp-client-factory.js';
8
10
  const DEFAULT_TOOL_APPROVAL_TIMEOUT_MS = 600_000;
9
11
  export class ClaudeHarnessFactory {
10
12
  config;
11
13
  harnessId = 'claude';
12
14
  protocolVersion = 1;
15
+ /**
16
+ * The Claude CLI binary speaks Anthropic-shaped protocols only — both the
17
+ * Bedrock pass-through (`'bedrock-anthropic'`) and direct Anthropic API
18
+ * (`'anthropic'`). Routing it at a non-Anthropic model is a programming
19
+ * error caught at `createAgent` / `updateAgentConfig` by the manager's
20
+ * G8 validation.
21
+ */
22
+ supportedProviderHints = ['anthropic', 'bedrock-anthropic'];
13
23
  constructor(config) {
14
24
  this.config = config;
15
25
  }
16
26
  async create(storageRootFolder) {
17
- const envBuilder = new ClaudeQueryEnvBuilder(this.config.resolver, storageRootFolder);
27
+ const innerFetch = createProxyAwareFetch(resolveProxyDispatcher());
28
+ const envBuilder = new ClaudeQueryEnvBuilder(storageRootFolder);
18
29
  const permissionDefaults = {
19
30
  permissionMode: this.config.permissionMode,
20
31
  ...(this.config.permissionMode === 'bypassPermissions'
@@ -27,7 +38,11 @@ export class ClaudeHarnessFactory {
27
38
  // under issue #529 — every event flows through the single
28
39
  // `initial` sink.
29
40
  const optionsBuilder = new ClaudeQueryOptionsBuilder({ ...permissionDefaults, ...this.config.queryDefaults }, new Set(this.config.bypassApprovalTools ?? []));
30
- return new ClaudeHarness(envBuilder, optionsBuilder, this.config.toolApprovalTimeoutMs ?? DEFAULT_TOOL_APPROVAL_TIMEOUT_MS, storageRootFolder);
41
+ // Bind `innerFetch` into the mcp-client factory closure so every
42
+ // call site inside `ClaudeHarness` (createAgent, reconnect, updateAgent)
43
+ // routes MCP HTTP transports through the same proxy-aware fetch.
44
+ const mcpClientFactory = (config, orgJwt) => buildMcpClients(config, orgJwt, innerFetch);
45
+ return new ClaudeHarness(envBuilder, optionsBuilder, this.config.toolApprovalTimeoutMs ?? DEFAULT_TOOL_APPROVAL_TIMEOUT_MS, storageRootFolder, { mcpClientFactory });
31
46
  }
32
47
  }
33
48
  //# sourceMappingURL=claude-harness-factory.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"claude-harness-factory.js","sourceRoot":"","sources":["../src/claude-harness-factory.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,EAAE,yBAAyB,EAAE,MAAM,mCAAmC,CAAC;AAC9E,OAAO,EAAE,aAAa,EAA2B,MAAM,qBAAqB,CAAC;AAa7E,MAAM,gCAAgC,GAAG,OAAO,CAAC;AA6BjD,MAAM,OAAO,oBAAoB;IAIA;IAHpB,SAAS,GAAG,QAAQ,CAAC;IACrB,eAAe,GAAG,CAAC,CAAC;IAE7B,YAA6B,MAAkC;QAAlC,WAAM,GAAN,MAAM,CAA4B;IAAG,CAAC;IAEnE,KAAK,CAAC,MAAM,CAAC,iBAAyB;QAClC,MAAM,UAAU,GAAG,IAAI,qBAAqB,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,iBAAiB,CAAC,CAAC;QACtF,MAAM,kBAAkB,GAAG;YACvB,cAAc,EAAE,IAAI,CAAC,MAAM,CAAC,cAAc;YAC1C,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,cAAc,KAAK,mBAAmB;gBAClD,CAAC,CAAC,EAAE,+BAA+B,EAAE,IAAa,EAAE;gBACpD,CAAC,CAAC,EAAE,CAAC;SACZ,CAAC;QACF,iEAAiE;QACjE,iEAAiE;QACjE,iEAAiE;QACjE,0DAA0D;QAC1D,kBAAkB;QAClB,MAAM,cAAc,GAAG,IAAI,yBAAyB,CAChD,EAAE,GAAG,kBAAkB,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,EACvD,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,mBAAmB,IAAI,EAAE,CAAC,CACjD,CAAC;QACF,OAAO,IAAI,aAAa,CACpB,UAAU,EACV,cAAc,EACd,IAAI,CAAC,MAAM,CAAC,qBAAqB,IAAI,gCAAgC,EACrE,iBAAiB,CACpB,CAAC;IACN,CAAC;CACJ"}
1
+ {"version":3,"file":"claude-harness-factory.js","sourceRoot":"","sources":["../src/claude-harness-factory.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAG3F,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,EAAE,yBAAyB,EAAE,MAAM,mCAAmC,CAAC;AAC9E,OAAO,EAAE,aAAa,EAA2B,MAAM,qBAAqB,CAAC;AAC7E,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAa1D,MAAM,gCAAgC,GAAG,OAAO,CAAC;AA4BjD,MAAM,OAAO,oBAAoB;IAYA;IAXpB,SAAS,GAAG,QAAQ,CAAC;IACrB,eAAe,GAAG,CAAC,CAAC;IAC7B;;;;;;OAMG;IACM,sBAAsB,GAA4B,CAAC,WAAW,EAAE,mBAAmB,CAAC,CAAC;IAE9F,YAA6B,MAAkC;QAAlC,WAAM,GAAN,MAAM,CAA4B;IAAG,CAAC;IAEnE,KAAK,CAAC,MAAM,CAAC,iBAAyB;QAClC,MAAM,UAAU,GAAG,qBAAqB,CAAC,sBAAsB,EAAE,CAAC,CAAC;QACnE,MAAM,UAAU,GAAG,IAAI,qBAAqB,CAAC,iBAAiB,CAAC,CAAC;QAChE,MAAM,kBAAkB,GAAG;YACvB,cAAc,EAAE,IAAI,CAAC,MAAM,CAAC,cAAc;YAC1C,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,cAAc,KAAK,mBAAmB;gBAClD,CAAC,CAAC,EAAE,+BAA+B,EAAE,IAAa,EAAE;gBACpD,CAAC,CAAC,EAAE,CAAC;SACZ,CAAC;QACF,iEAAiE;QACjE,iEAAiE;QACjE,iEAAiE;QACjE,0DAA0D;QAC1D,kBAAkB;QAClB,MAAM,cAAc,GAAG,IAAI,yBAAyB,CAChD,EAAE,GAAG,kBAAkB,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,EACvD,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,mBAAmB,IAAI,EAAE,CAAC,CACjD,CAAC;QACF,iEAAiE;QACjE,yEAAyE;QACzE,iEAAiE;QACjE,MAAM,gBAAgB,GAA2B,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,CAChE,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC;QAChD,OAAO,IAAI,aAAa,CACpB,UAAU,EACV,cAAc,EACd,IAAI,CAAC,MAAM,CAAC,qBAAqB,IAAI,gCAAgC,EACrE,iBAAiB,EACjB,EAAE,gBAAgB,EAAE,CACvB,CAAC;IACN,CAAC;CACJ"}
@@ -1,8 +1,7 @@
1
1
  import { type Clock, type LogRecord, type UniqueIDGenerator, type Unsubscribe } from '@salesforce/agentic-common';
2
- import { type AgentHarness, type ChatStreamResult, type HarnessAgentConfig, type McpServerInfo, type Message, type MessagePart, type StreamOptions, type TelemetryEventCallback, type ToolResultInfo } from '@salesforce/sfdx-agent-sdk';
2
+ import { type AgentHarness, type ChatStreamResult, type HarnessAgentConfig, type McpServerInfo, type Message, type MessagePart, type ModelConnectivityInfo, type StreamOptions, type TelemetryEventCallback, type ToolResultInfo, type WireCommunicationEventCallback } from '@salesforce/sfdx-agent-sdk';
3
3
  import { type AgentHooks, type WithAgentConfig } from '@salesforce/sfdx-agent-sdk/harness';
4
4
  import type { ClaudeAgentConfig } from './claude-config.js';
5
- import { type LLMGatewayClient } from '@salesforce/llm-gateway-sdk';
6
5
  import type { ClaudeQueryEnvBuilder } from './claude-query-env-builder.js';
7
6
  import type { ClaudeQueryOptionsBuilder } from './claude-query-options-builder.js';
8
7
  import { buildMcpClients } from './mcp-client-factory.js';
@@ -35,6 +34,20 @@ import { ThreadContextStore } from './thread-context-store.js';
35
34
  export type ClaudeAgentHarness = WithAgentConfig<AgentHarness & {
36
35
  readonly harnessId: 'claude';
37
36
  }, ClaudeAgentConfig>;
37
+ /**
38
+ * Construction options for {@link ClaudeHarness}. Internal — consumers
39
+ * configure the harness via {@link ClaudeHarnessFactory}. The factory threads
40
+ * `mcpClientFactory` (bound with the proxy-aware `innerFetch`); tests pass
41
+ * stubs (`clock`, `idGenerator`, `sessionStore`, `mcpClientFactory`,
42
+ * `threadContextStore`) directly.
43
+ */
44
+ export interface ClaudeHarnessOptions {
45
+ clock?: Clock;
46
+ idGenerator?: UniqueIDGenerator;
47
+ sessionStore?: DiskBackedSessionStore;
48
+ mcpClientFactory?: typeof buildMcpClients;
49
+ threadContextStore?: ThreadContextStore;
50
+ }
38
51
  export declare class ClaudeHarness implements ClaudeAgentHarness {
39
52
  private readonly envBuilder;
40
53
  private readonly optionsBuilder;
@@ -71,13 +84,52 @@ export declare class ClaudeHarness implements ClaudeAgentHarness {
71
84
  * collaborators on this constructor.
72
85
  */
73
86
  private readonly mcpClientFactory;
87
+ private readonly storageRootFolder;
74
88
  private shuttingDown;
75
- constructor(envBuilder: ClaudeQueryEnvBuilder, optionsBuilder: ClaudeQueryOptionsBuilder, toolApprovalTimeoutMs: number, storageRootFolder: string, idGenerator?: UniqueIDGenerator, clock?: Clock, sessionStore?: DiskBackedSessionStore, mcpClientFactory?: typeof buildMcpClients, threadContextStore?: ThreadContextStore);
89
+ constructor(envBuilder: ClaudeQueryEnvBuilder, optionsBuilder: ClaudeQueryOptionsBuilder, toolApprovalTimeoutMs: number, storageRootFolder: string, opts?: ClaudeHarnessOptions);
76
90
  private coordinatorKey;
91
+ /**
92
+ * Returns a per-thread debug-log file path under the SDK storage root when a
93
+ * wire-communication consumer is subscribed; otherwise `undefined`. Callers pass the
94
+ * returned path as `query({ options: { debugFile } })` so the Claude subprocess writes
95
+ * its debug log to a file we own — and emit a `WireMonitoringNotSupportedEvent`
96
+ * pointing at the path immediately, before the subprocess starts.
97
+ *
98
+ * The path is **per-thread** (not per-turn): turns within the same conversation share
99
+ * a single file, accumulating wire data via the SDK's append-on-write behavior. A
100
+ * banner line written by {@link writeTurnBanner} delimits each turn within the file.
101
+ *
102
+ * Listener-count gating: relies on the wire-communication chain
103
+ * (`harness.wireBus → router slice → manager.wireBus`) being wired with
104
+ * `forwardWhileSubscribed`, so a consumer-less manager leaves
105
+ * `wireBus.listenerCount === 0` and this method returns `undefined` (no file written,
106
+ * no debug overhead in the subprocess).
107
+ */
108
+ private maybeAllocateWireDebugLogPath;
109
+ /**
110
+ * Append a turn-start banner to the per-thread wire-debug log file so consumers
111
+ * grepping the accumulated content can find each turn's boundary. The Claude SDK
112
+ * appends to `debugFile` across `query()` calls with no separator of its own; this
113
+ * banner makes turn boundaries explicit and version-stable.
114
+ */
115
+ private writeTurnBanner;
77
116
  shutdown(): Promise<void>;
78
117
  onTelemetry(callback: TelemetryEventCallback): Unsubscribe;
79
118
  onLog(callback: (record: LogRecord) => void): Unsubscribe;
80
- createAgent(agentId: string, projectRoot: string, llmGatewayClient: LLMGatewayClient, config?: HarnessAgentConfig, options?: {
119
+ /**
120
+ * The Claude harness emits `WireMonitoringNotSupportedEvent` (one per stream) when at
121
+ * least one consumer is subscribed at stream-start. The Claude Agent SDK runs the model
122
+ * client in a subprocess with no programmatic seam for HTTP-level monitoring, so per-call
123
+ * `LlmRequestEvent` / `LlmResponseEvent` (Mastra's contract) are not produced here;
124
+ * instead, the harness writes the subprocess's debug log to a file under the SDK storage
125
+ * root and points at it from the event's `message` field.
126
+ *
127
+ * **Subscribe-before-stream**: subscriptions added mid-stream miss the in-flight
128
+ * subprocess on Claude — the listener-count check happens once at `stream()` start. The
129
+ * next subprocess spawn (next `chat()` call) will pick up the subscriber.
130
+ */
131
+ onWireCommunication(callback: WireCommunicationEventCallback): Unsubscribe;
132
+ createAgent(agentId: string, projectRoot: string, modelConnectivityInfo: ModelConnectivityInfo, config?: HarnessAgentConfig, options?: {
81
133
  abortSignal?: AbortSignal;
82
134
  hooks?: AgentHooks;
83
135
  }): Promise<void>;
@@ -146,7 +198,7 @@ export declare class ClaudeHarness implements ClaudeAgentHarness {
146
198
  * `state.threads` is intentionally untouched — sessions are config-
147
199
  * independent, so an `updateAgent` cannot invalidate them.
148
200
  */
149
- updateAgent(agentId: string, llmGatewayClient: LLMGatewayClient, config?: HarnessAgentConfig, options?: {
201
+ updateAgent(agentId: string, modelConnectivityInfo: ModelConnectivityInfo, config?: HarnessAgentConfig, options?: {
150
202
  abortSignal?: AbortSignal;
151
203
  hooks?: AgentHooks;
152
204
  }): Promise<void>;