@salesforce/sfdx-agent-sdk 0.21.0 → 0.23.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 +18 -0
- package/README.md +336 -64
- package/dist/agent-connectivity-resolver.d.ts +47 -25
- package/dist/agent-connectivity-resolver.js +92 -15
- package/dist/agent-manager.d.ts +17 -1
- package/dist/agent-manager.js +40 -7
- package/dist/agent.d.ts +33 -10
- package/dist/agent.js +38 -50
- package/dist/api-key-connectivity-resolver.d.ts +110 -0
- package/dist/api-key-connectivity-resolver.js +114 -0
- package/dist/errors.d.ts +1 -0
- package/dist/errors.js +1 -0
- package/dist/harness/agent-harness.d.ts +27 -5
- package/dist/harness/harness-bus-owner.d.ts +17 -0
- package/dist/harness/harness-bus-owner.js +27 -0
- package/dist/harness/harness-config.d.ts +3 -2
- package/dist/harness/harness-factory.d.ts +13 -0
- package/dist/harness/stream-input.d.ts +9 -5
- package/dist/harness/stream-input.js +12 -14
- package/dist/index.d.ts +8 -2
- package/dist/index.js +4 -1
- package/dist/internal/wire-communication-router.d.ts +43 -0
- package/dist/internal/wire-communication-router.js +119 -0
- package/dist/mcp-auth.d.ts +1 -1
- package/dist/models/claude-opus-4-5.d.ts +11 -0
- package/dist/models/claude-opus-4-5.js +21 -0
- package/dist/models/claude-opus-4-6.d.ts +11 -0
- package/dist/models/claude-opus-4-6.js +22 -0
- package/dist/models/claude-opus-4-7.d.ts +11 -0
- package/dist/models/claude-opus-4-7.js +22 -0
- package/dist/models/claude-sonnet-4-5.d.ts +11 -0
- package/dist/models/claude-sonnet-4-5.js +23 -0
- package/dist/models/claude-sonnet-4-6.d.ts +11 -0
- package/dist/models/claude-sonnet-4-6.js +22 -0
- package/dist/models/create-claude-model.d.ts +54 -0
- package/dist/models/create-claude-model.js +62 -0
- package/dist/models/gpt-5-4.d.ts +11 -0
- package/dist/models/gpt-5-4.js +21 -0
- package/dist/models/gpt-5-5.d.ts +15 -0
- package/dist/models/gpt-5-5.js +24 -0
- package/dist/models/gpt-5.d.ts +11 -0
- package/dist/models/gpt-5.js +23 -0
- package/dist/models/index.d.ts +19 -0
- package/dist/models/index.js +49 -0
- package/dist/models/model.d.ts +69 -0
- package/dist/models/model.js +63 -0
- package/dist/models/multimodal.d.ts +35 -0
- package/dist/models/multimodal.js +78 -0
- package/dist/models/types.d.ts +49 -0
- package/dist/models/types.js +18 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/model-connectivity-info.d.ts +87 -0
- package/dist/types/model-connectivity-info.js +6 -0
- package/dist/types/usage.d.ts +3 -3
- package/dist/types/wire-communication-event.d.ts +124 -0
- package/dist/types/wire-communication-event.js +6 -0
- package/dist/wire-communication-file-writer.d.ts +39 -0
- package/dist/wire-communication-file-writer.js +142 -0
- package/package.json +7 -8
|
@@ -1,54 +1,76 @@
|
|
|
1
|
-
import { Model
|
|
2
|
-
import { type OrgConnection, type OrgConnectionFactory } from '@salesforce/agentic-common';
|
|
1
|
+
import { Model } from './models/index.js';
|
|
2
|
+
import { type JSONWebToken, type OrgConnection, type OrgConnectionFactory } from '@salesforce/agentic-common';
|
|
3
3
|
import type { AgentConfig } from './harness/harness-config.js';
|
|
4
|
+
import type { ModelConnectivityInfo } from './types/model-connectivity-info.js';
|
|
4
5
|
/**
|
|
5
|
-
* The result of resolving an agent's
|
|
6
|
-
*
|
|
6
|
+
* The result of resolving an agent's connectivity.
|
|
7
|
+
*
|
|
8
|
+
* `modelConnectivityInfo` is the harness-facing bag. `orgConnection` and
|
|
9
|
+
* `orgJwt` are Salesforce-org-specific and only present when the resolver targets a
|
|
10
|
+
* Salesforce-org host; non-Salesforce hosts (MuleSoft, Commerce Vibes — see
|
|
11
|
+
* [#605](https://github.com/forcedotcom/agentic-dx/issues/605)) omit them. Downstream
|
|
12
|
+
* consumers (MCP auth, identity-derived headers) treat them as optional.
|
|
7
13
|
*/
|
|
8
14
|
export type ResolvedConnectivity = {
|
|
9
|
-
/**
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Connectivity facts the harness needs to make an LLM request: model, base URL,
|
|
17
|
+
* native model id, provider hint, and a per-call `getHeaders()`. See
|
|
18
|
+
* {@link ModelConnectivityInfo} for the contract.
|
|
19
|
+
*/
|
|
20
|
+
modelConnectivityInfo: ModelConnectivityInfo;
|
|
21
|
+
/**
|
|
22
|
+
* Authenticated org connection carrying identity and env inference. Optional —
|
|
23
|
+
* non-Salesforce hosts (MuleSoft, Commerce Vibes) omit it.
|
|
24
|
+
*/
|
|
25
|
+
orgConnection?: OrgConnection;
|
|
26
|
+
/**
|
|
27
|
+
* Self-refreshing JWT for the resolved org. Used for MCP auth injection and any
|
|
28
|
+
* other Salesforce-platform-auth need (identity, future Apex). Optional —
|
|
29
|
+
* non-Salesforce hosts (or hosts that authenticate the LLM via api-key without
|
|
30
|
+
* needing org auth for MCP) omit it.
|
|
31
|
+
*/
|
|
32
|
+
orgJwt?: JSONWebToken;
|
|
15
33
|
};
|
|
16
34
|
/**
|
|
17
|
-
* Resolves the
|
|
35
|
+
* Resolves the connectivity needed to create an agent for a given project and
|
|
18
36
|
* configuration.
|
|
19
37
|
*
|
|
20
|
-
* Implementations are responsible for
|
|
21
|
-
*
|
|
22
|
-
* {@link
|
|
38
|
+
* Implementations are responsible for producing a {@link ModelConnectivityInfo} bag
|
|
39
|
+
* the harness can use to make LLM requests. Salesforce-org hosts additionally produce
|
|
40
|
+
* an {@link OrgConnection} + {@link JSONWebToken} for MCP auth and identity; non-SF
|
|
41
|
+
* hosts omit those fields.
|
|
42
|
+
*
|
|
43
|
+
* The same `AgentConnectivityResolver` instance is invoked at `createAgent` time and
|
|
44
|
+
* at `updateAgentConfig` time when `orgAlias` or `modelId` change — implementations
|
|
45
|
+
* MUST be safe to call repeatedly with different `AgentConfig` values.
|
|
23
46
|
*/
|
|
24
47
|
export interface AgentConnectivityResolver {
|
|
25
48
|
/**
|
|
26
|
-
* Resolve the
|
|
49
|
+
* Resolve the connectivity for an agent.
|
|
27
50
|
*
|
|
28
51
|
* @param projectRoot - The root directory of the project the agent operates within.
|
|
29
|
-
* Used to find a project-local `target-org` when no `orgAlias` is configured
|
|
52
|
+
* Used to find a project-local `target-org` when no `orgAlias` is configured
|
|
53
|
+
* (Salesforce-org hosts only).
|
|
30
54
|
* @param config - The agent's configuration, including optional org alias and model selection.
|
|
31
|
-
* @returns The resolved
|
|
55
|
+
* @returns The resolved connectivity bag plus optional org handle / JWT.
|
|
32
56
|
*/
|
|
33
57
|
resolve(projectRoot: string, config: AgentConfig): Promise<ResolvedConnectivity>;
|
|
34
58
|
}
|
|
35
59
|
/**
|
|
36
60
|
* Default implementation of {@link AgentConnectivityResolver}.
|
|
37
61
|
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
62
|
+
* Targets the Salesforce LLM Gateway with a Salesforce-org JWT. Produces a
|
|
63
|
+
* `ModelConnectivityInfo` bag (the harness-facing seam). The connection
|
|
64
|
+
* factory is injectable for testing.
|
|
40
65
|
*/
|
|
41
66
|
export declare class DefaultAgentConnectivityResolver implements AgentConnectivityResolver {
|
|
42
67
|
private readonly connectionFactory;
|
|
43
|
-
private readonly gatewayClientFactory;
|
|
44
68
|
/**
|
|
45
69
|
* @param connectionFactory - Creates authenticated Salesforce org connections.
|
|
46
|
-
* @param gatewayClientFactory - Creates authenticated LLM gateway clients.
|
|
47
70
|
*/
|
|
48
|
-
constructor(connectionFactory?: OrgConnectionFactory
|
|
71
|
+
constructor(connectionFactory?: OrgConnectionFactory);
|
|
49
72
|
/**
|
|
50
|
-
* Resolves the org
|
|
51
|
-
* returns the underlying connection.
|
|
73
|
+
* Resolves the org and produces the harness-facing `ModelConnectivityInfo` bag.
|
|
52
74
|
*
|
|
53
75
|
* If `config.orgAlias` is set, resolves that alias explicitly; otherwise falls back
|
|
54
76
|
* to the project-local or machine-default org via
|
|
@@ -56,7 +78,7 @@ export declare class DefaultAgentConnectivityResolver implements AgentConnectivi
|
|
|
56
78
|
*
|
|
57
79
|
* @param projectRoot - Project root for project-local org resolution.
|
|
58
80
|
* @param config - Agent configuration with optional org alias and model ID.
|
|
59
|
-
* @returns The resolved
|
|
81
|
+
* @returns The resolved connectivity bag, org connection, and JWT.
|
|
60
82
|
*/
|
|
61
83
|
resolve(projectRoot: string, config: AgentConfig): Promise<ResolvedConnectivity>;
|
|
62
84
|
}
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* Copyright 2026, Salesforce, Inc. All rights reserved.
|
|
3
3
|
* See LICENSE.txt for license terms.
|
|
4
4
|
*/
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { Model, ModelName, Models, createClaudeModel } from './models/index.js';
|
|
6
|
+
import { createJWTFromConnection, RealOrgConnectionFactory, SfApiEnv, } from '@salesforce/agentic-common';
|
|
7
7
|
// TODO(@W-22782317): Temporary workaround — only on prod orgs the LLM Gateway must
|
|
8
8
|
// route requests through AgentforceVibes rather than the default VibesService. Remove once a
|
|
9
9
|
// long-term feature ID configuration strategy is in place.
|
|
@@ -11,23 +11,20 @@ const PROD_ORG_FEATURE_ID = 'AgentforceVibes';
|
|
|
11
11
|
/**
|
|
12
12
|
* Default implementation of {@link AgentConnectivityResolver}.
|
|
13
13
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
14
|
+
* Targets the Salesforce LLM Gateway with a Salesforce-org JWT. Produces a
|
|
15
|
+
* `ModelConnectivityInfo` bag (the harness-facing seam). The connection
|
|
16
|
+
* factory is injectable for testing.
|
|
16
17
|
*/
|
|
17
18
|
export class DefaultAgentConnectivityResolver {
|
|
18
19
|
connectionFactory;
|
|
19
|
-
gatewayClientFactory;
|
|
20
20
|
/**
|
|
21
21
|
* @param connectionFactory - Creates authenticated Salesforce org connections.
|
|
22
|
-
* @param gatewayClientFactory - Creates authenticated LLM gateway clients.
|
|
23
22
|
*/
|
|
24
|
-
constructor(connectionFactory = new RealOrgConnectionFactory()
|
|
23
|
+
constructor(connectionFactory = new RealOrgConnectionFactory()) {
|
|
25
24
|
this.connectionFactory = connectionFactory;
|
|
26
|
-
this.gatewayClientFactory = gatewayClientFactory;
|
|
27
25
|
}
|
|
28
26
|
/**
|
|
29
|
-
* Resolves the org
|
|
30
|
-
* returns the underlying connection.
|
|
27
|
+
* Resolves the org and produces the harness-facing `ModelConnectivityInfo` bag.
|
|
31
28
|
*
|
|
32
29
|
* If `config.orgAlias` is set, resolves that alias explicitly; otherwise falls back
|
|
33
30
|
* to the project-local or machine-default org via
|
|
@@ -35,7 +32,7 @@ export class DefaultAgentConnectivityResolver {
|
|
|
35
32
|
*
|
|
36
33
|
* @param projectRoot - Project root for project-local org resolution.
|
|
37
34
|
* @param config - Agent configuration with optional org alias and model ID.
|
|
38
|
-
* @returns The resolved
|
|
35
|
+
* @returns The resolved connectivity bag, org connection, and JWT.
|
|
39
36
|
*/
|
|
40
37
|
async resolve(projectRoot, config) {
|
|
41
38
|
const orgConnection = config.orgAlias !== undefined
|
|
@@ -45,11 +42,91 @@ export class DefaultAgentConnectivityResolver {
|
|
|
45
42
|
const env = orgConnection.getInferredSfApiEnv();
|
|
46
43
|
const featureId = env === SfApiEnv.Prod ? PROD_ORG_FEATURE_ID : undefined;
|
|
47
44
|
const orgJwt = await createJWTFromConnection(orgConnection, { featureId });
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
45
|
+
const model = resolveAgentConfigModel(config.modelId);
|
|
46
|
+
const modelConnectivityInfo = {
|
|
47
|
+
model,
|
|
48
|
+
baseUrl: salesforceGatewayBaseUrl(env),
|
|
49
|
+
// The Salesforce gateway accepts the canonical `llmgateway__*` model id verbatim,
|
|
50
|
+
// so `nativeModelId === model.name` for every gateway-routed call.
|
|
51
|
+
nativeModelId: model.name,
|
|
52
|
+
providerHint: pickProviderHintForGatewayModel(model),
|
|
53
|
+
getHeaders: async () => buildSalesforceGatewayHeaders(orgJwt, model),
|
|
54
|
+
};
|
|
55
|
+
return { modelConnectivityInfo, orgConnection, orgJwt };
|
|
51
56
|
}
|
|
52
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* Maps a Salesforce gateway-routed {@link Model} to the wire shape the harness will
|
|
60
|
+
* speak. Anthropic models go to the `/invoke-with-response-stream` (Bedrock-Anthropic)
|
|
61
|
+
* pass-through endpoint; OpenAI models go to the `/responses` (OpenAI Responses)
|
|
62
|
+
* pass-through endpoint. Throws for any model whose family cannot be inferred from
|
|
63
|
+
* its `name` — a programming error worth surfacing eagerly rather than silently
|
|
64
|
+
* defaulting.
|
|
65
|
+
*
|
|
66
|
+
* **Scope:** This helper is for the Salesforce gateway path only — both
|
|
67
|
+
* `llmgateway__BedrockAnthropic*` and `llmgateway__Anthropic*` prefixes go to the
|
|
68
|
+
* gateway's Bedrock pass-through, so both collapse to `'bedrock-anthropic'`. A
|
|
69
|
+
* consumer wanting direct-Anthropic auth (`providerHint: 'anthropic'`) drives that
|
|
70
|
+
* path through {@link ApiKeyConnectivityResolver} with an explicit `providerHint`
|
|
71
|
+
* argument, not through this resolver.
|
|
72
|
+
*/
|
|
73
|
+
function pickProviderHintForGatewayModel(model) {
|
|
74
|
+
const name = model.name;
|
|
75
|
+
if (name.startsWith('llmgateway__BedrockAnthropic') || name.startsWith('llmgateway__Anthropic')) {
|
|
76
|
+
return 'bedrock-anthropic';
|
|
77
|
+
}
|
|
78
|
+
if (name.startsWith('llmgateway__OpenAI')) {
|
|
79
|
+
return 'openai-responses';
|
|
80
|
+
}
|
|
81
|
+
throw new Error(`Cannot infer providerHint for model "${name}". Salesforce gateway models must start with "llmgateway__BedrockAnthropic*" or "llmgateway__OpenAI*".`);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Returns the Salesforce LLM Gateway base URL for the given environment.
|
|
85
|
+
*
|
|
86
|
+
* Includes the `/ai/gpt/v1` API root: the pass-through endpoints
|
|
87
|
+
* (`/responses` for OpenAI Responses, `/model/<id>/invoke-with-response-stream`
|
|
88
|
+
* for Bedrock-Anthropic) live under this prefix, and provider SDKs
|
|
89
|
+
* (`@anthropic-ai/bedrock-sdk`, `openai`) construct their per-model paths by
|
|
90
|
+
* concatenation onto the base URL the consumer supplies.
|
|
91
|
+
*/
|
|
92
|
+
function salesforceGatewayBaseUrl(env) {
|
|
93
|
+
switch (env) {
|
|
94
|
+
case SfApiEnv.Dev:
|
|
95
|
+
return 'https://dev.api.salesforce.com/ai/gpt/v1';
|
|
96
|
+
case SfApiEnv.Perf:
|
|
97
|
+
return 'https://perf.api.salesforce.com/ai/gpt/v1';
|
|
98
|
+
case SfApiEnv.Stage:
|
|
99
|
+
return 'https://stage.api.salesforce.com/ai/gpt/v1';
|
|
100
|
+
case SfApiEnv.Test:
|
|
101
|
+
return 'https://test.api.salesforce.com/ai/gpt/v1';
|
|
102
|
+
case SfApiEnv.Prod:
|
|
103
|
+
default:
|
|
104
|
+
return 'https://api.salesforce.com/ai/gpt/v1';
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Builds the full header set for a Salesforce LLM Gateway request, including the
|
|
109
|
+
* Authorization Bearer JWT, tenant-key, feature-id, and any model-specific custom
|
|
110
|
+
* headers. Re-evaluated on every harness call so JWT rotations land on the next
|
|
111
|
+
* request without rebuilding the bag.
|
|
112
|
+
*/
|
|
113
|
+
async function buildSalesforceGatewayHeaders(orgJwt, model) {
|
|
114
|
+
const [jwtValue, tenantKey] = await Promise.all([orgJwt.getValue(), orgJwt.getTenantKey()]);
|
|
115
|
+
// Order matters: spread `customHeaders` FIRST so the resolver-set auth /
|
|
116
|
+
// tenant / feature-id headers win on conflict. `customHeaders` is for
|
|
117
|
+
// vendor-specific labels (`anthropic-version`, model-tag headers, etc.) —
|
|
118
|
+
// not for auth or tenancy. A `Model` instance built with
|
|
119
|
+
// `customHeaders: { Authorization: '...' }` must NOT shadow the
|
|
120
|
+
// resolver's freshly-fetched JWT.
|
|
121
|
+
return {
|
|
122
|
+
...(model.customHeaders ?? {}),
|
|
123
|
+
Authorization: `Bearer ${jwtValue}`,
|
|
124
|
+
'Content-Type': 'application/json;charset=utf-8',
|
|
125
|
+
'x-client-feature-id': orgJwt.getFeatureId(),
|
|
126
|
+
'x-sfdc-app-context': 'EinsteinGPT',
|
|
127
|
+
'x-sfdc-core-tenant-id': tenantKey,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
53
130
|
/**
|
|
54
131
|
* Resolves an `AgentConfig.modelId` value (which may be a {@link ModelName} enum value, a
|
|
55
132
|
* pre-built {@link Model} instance, or `undefined`) to a concrete {@link Model}.
|
|
@@ -68,7 +145,7 @@ export function resolveAgentConfigModel(modelId) {
|
|
|
68
145
|
if (modelId === undefined)
|
|
69
146
|
return Models.getDefault();
|
|
70
147
|
// Known limitation: `instanceof Model` is realm-scoped — a consumer that ends up with two copies
|
|
71
|
-
// of `@salesforce/
|
|
148
|
+
// of `@salesforce/sfdx-agent-sdk` resolved in their dependency tree will have their `Model`
|
|
72
149
|
// instance fail this check and fall through to `rehydratePersistedModel`. That branch handles
|
|
73
150
|
// it correctly for Claude variants but throws for anything else. The duplicate-package case is
|
|
74
151
|
// a packaging bug at the consumer; we don't paper over it here.
|
package/dist/agent-manager.d.ts
CHANGED
|
@@ -5,6 +5,8 @@ import { type AgentConfig } from './harness/harness-config.js';
|
|
|
5
5
|
import { type Agent } from './agent.js';
|
|
6
6
|
import type { HooksForAgent } from './types/redaction.js';
|
|
7
7
|
import type { TelemetryEventCallback } from './types/telemetry-events.js';
|
|
8
|
+
import type { WireCommunicationEventCallback } from './types/wire-communication-event.js';
|
|
9
|
+
import type { ProviderHint } from './types/model-connectivity-info.js';
|
|
8
10
|
import { type AgentConnectivityResolver } from './agent-connectivity-resolver.js';
|
|
9
11
|
/**
|
|
10
12
|
* Per-agent failure recorded during the boot-time restore pass.
|
|
@@ -104,6 +106,16 @@ export interface AgentManager<H extends AgentHarness = AgentHarness> {
|
|
|
104
106
|
onTelemetry(callback: TelemetryEventCallback): Unsubscribe;
|
|
105
107
|
/** Subscribe to structured log records across every managed agent. */
|
|
106
108
|
onLog(callback: (record: LogRecord) => void): Unsubscribe;
|
|
109
|
+
/**
|
|
110
|
+
* Subscribe to wire-communication events emitted by the harness across
|
|
111
|
+
* every managed agent — raw HTTP request/response captures of the LLM
|
|
112
|
+
* provider's traffic. The events may contain user prompts and PII; the
|
|
113
|
+
* channel is opt-in and not flowed to log files by default. Use
|
|
114
|
+
* {@link WireCommunicationFileWriter} to dump events to disk for
|
|
115
|
+
* debugging. Fidelity differs by harness — see
|
|
116
|
+
* `AgentHarness.onWireCommunication` for the cross-harness contract.
|
|
117
|
+
*/
|
|
118
|
+
onWireCommunication(callback: WireCommunicationEventCallback): Unsubscribe;
|
|
107
119
|
/**
|
|
108
120
|
* Returns a snapshot of the boot-time restore failures the SDK has not
|
|
109
121
|
* yet been told to forget. Each entry is cleared on a successful
|
|
@@ -123,6 +135,7 @@ export interface AgentManager<H extends AgentHarness = AgentHarness> {
|
|
|
123
135
|
*/
|
|
124
136
|
export declare class DefaultAgentManager<H extends AgentHarness = AgentHarness> implements AgentManager<H> {
|
|
125
137
|
private readonly harness;
|
|
138
|
+
private readonly harnessSupportedProviderHints;
|
|
126
139
|
private readonly agentIdGenerator;
|
|
127
140
|
private readonly agentConnectivityResolver;
|
|
128
141
|
private readonly hooksForAgent;
|
|
@@ -132,7 +145,9 @@ export declare class DefaultAgentManager<H extends AgentHarness = AgentHarness>
|
|
|
132
145
|
private restoreFailures;
|
|
133
146
|
private readonly telemetryBus;
|
|
134
147
|
private readonly logBus;
|
|
148
|
+
private readonly wireBus;
|
|
135
149
|
private readonly router;
|
|
150
|
+
private readonly wireRouter;
|
|
136
151
|
private readonly unroutedUnsubs;
|
|
137
152
|
private disposed;
|
|
138
153
|
private constructor();
|
|
@@ -146,7 +161,7 @@ export declare class DefaultAgentManager<H extends AgentHarness = AgentHarness>
|
|
|
146
161
|
* is private, so this is the only way to obtain an instance, but
|
|
147
162
|
* consumers should always go through {@link createAgentManager}.
|
|
148
163
|
*/
|
|
149
|
-
static __build<H extends AgentHarness>(harness: H, agentConnectivityResolver: AgentConnectivityResolver, hooksForAgent: HooksForAgent | undefined, storageRootFolder: string, agentIdGenerator: UniqueIDGenerator, clock: Clock, logBus: LogBus): Promise<DefaultAgentManager<H>>;
|
|
164
|
+
static __build<H extends AgentHarness>(harness: H, harnessSupportedProviderHints: readonly ProviderHint[], agentConnectivityResolver: AgentConnectivityResolver, hooksForAgent: HooksForAgent | undefined, storageRootFolder: string, agentIdGenerator: UniqueIDGenerator, clock: Clock, logBus: LogBus): Promise<DefaultAgentManager<H>>;
|
|
150
165
|
private init;
|
|
151
166
|
shutdown(): Promise<void>;
|
|
152
167
|
createAgent(projectRoot: string, config?: ConfigOf<H> & {
|
|
@@ -172,6 +187,7 @@ export declare class DefaultAgentManager<H extends AgentHarness = AgentHarness>
|
|
|
172
187
|
destroyAgent(agentId: string): Promise<void>;
|
|
173
188
|
onTelemetry(callback: TelemetryEventCallback): Unsubscribe;
|
|
174
189
|
onLog(callback: (record: LogRecord) => void): Unsubscribe;
|
|
190
|
+
onWireCommunication(callback: WireCommunicationEventCallback): Unsubscribe;
|
|
175
191
|
getRestoreFailures(): RestoreFailure[];
|
|
176
192
|
/**
|
|
177
193
|
* Re-exposes `harness.extensions` typed off `H`. Read-only; the SDK never
|
package/dist/agent-manager.js
CHANGED
|
@@ -10,6 +10,7 @@ import { toHarnessConfig } from './harness/harness-config.js';
|
|
|
10
10
|
import { DefaultAgent } from './agent.js';
|
|
11
11
|
import { AgentSDKError, AgentSDKErrorType } from './errors.js';
|
|
12
12
|
import { TelemetryRouter } from './internal/telemetry-router.js';
|
|
13
|
+
import { WireCommunicationRouter } from './internal/wire-communication-router.js';
|
|
13
14
|
import { AgentIdentityStore } from './internal/agent-identity-store.js';
|
|
14
15
|
import { DefaultAgentConnectivityResolver } from './agent-connectivity-resolver.js';
|
|
15
16
|
/**
|
|
@@ -23,6 +24,7 @@ import { DefaultAgentConnectivityResolver } from './agent-connectivity-resolver.
|
|
|
23
24
|
*/
|
|
24
25
|
export class DefaultAgentManager {
|
|
25
26
|
harness;
|
|
27
|
+
harnessSupportedProviderHints;
|
|
26
28
|
agentIdGenerator;
|
|
27
29
|
agentConnectivityResolver;
|
|
28
30
|
hooksForAgent;
|
|
@@ -32,11 +34,14 @@ export class DefaultAgentManager {
|
|
|
32
34
|
restoreFailures = [];
|
|
33
35
|
telemetryBus = new EventBus();
|
|
34
36
|
logBus;
|
|
37
|
+
wireBus = new EventBus();
|
|
35
38
|
router;
|
|
39
|
+
wireRouter;
|
|
36
40
|
unroutedUnsubs;
|
|
37
41
|
disposed = false;
|
|
38
|
-
constructor(harness, agentConnectivityResolver, hooksForAgent, identityStore, agentIdGenerator, clock, logBus) {
|
|
42
|
+
constructor(harness, harnessSupportedProviderHints, agentConnectivityResolver, hooksForAgent, identityStore, agentIdGenerator, clock, logBus) {
|
|
39
43
|
this.harness = harness;
|
|
44
|
+
this.harnessSupportedProviderHints = harnessSupportedProviderHints;
|
|
40
45
|
this.agentConnectivityResolver = agentConnectivityResolver;
|
|
41
46
|
this.hooksForAgent = hooksForAgent;
|
|
42
47
|
this.identityStore = identityStore;
|
|
@@ -44,9 +49,15 @@ export class DefaultAgentManager {
|
|
|
44
49
|
this.clock = clock;
|
|
45
50
|
this.logBus = logBus;
|
|
46
51
|
this.router = new TelemetryRouter(harness);
|
|
52
|
+
this.wireRouter = new WireCommunicationRouter(harness);
|
|
47
53
|
this.unroutedUnsubs = [
|
|
48
54
|
this.router.unrouted.telemetry.forwardTo(this.telemetryBus),
|
|
49
55
|
this.router.unrouted.log.forwardTo(this.logBus),
|
|
56
|
+
// Wire-communication uses `forwardWhileSubscribed` — when no consumer subscribes to
|
|
57
|
+
// `manager.onWireCommunication`, the link stays cold all the way back to the harness bus.
|
|
58
|
+
// The Claude harness reads `wireBus.listenerCount > 0` before each subprocess spawn and
|
|
59
|
+
// skips the (expensive) `ANTHROPIC_LOG=debug` env when nobody is listening.
|
|
60
|
+
this.wireRouter.unrouted.wire.forwardWhileSubscribed(this.wireBus),
|
|
50
61
|
];
|
|
51
62
|
}
|
|
52
63
|
/**
|
|
@@ -59,9 +70,9 @@ export class DefaultAgentManager {
|
|
|
59
70
|
* is private, so this is the only way to obtain an instance, but
|
|
60
71
|
* consumers should always go through {@link createAgentManager}.
|
|
61
72
|
*/
|
|
62
|
-
static async __build(harness, agentConnectivityResolver, hooksForAgent, storageRootFolder, agentIdGenerator, clock, logBus) {
|
|
73
|
+
static async __build(harness, harnessSupportedProviderHints, agentConnectivityResolver, hooksForAgent, storageRootFolder, agentIdGenerator, clock, logBus) {
|
|
63
74
|
const identityStore = new AgentIdentityStore(storageRootFolder, harness.harnessId, logBus);
|
|
64
|
-
const manager = new DefaultAgentManager(harness, agentConnectivityResolver, hooksForAgent, identityStore, agentIdGenerator, clock, logBus);
|
|
75
|
+
const manager = new DefaultAgentManager(harness, harnessSupportedProviderHints, agentConnectivityResolver, hooksForAgent, identityStore, agentIdGenerator, clock, logBus);
|
|
65
76
|
await manager.init();
|
|
66
77
|
return manager;
|
|
67
78
|
}
|
|
@@ -103,8 +114,10 @@ export class DefaultAgentManager {
|
|
|
103
114
|
for (const unsub of this.unroutedUnsubs)
|
|
104
115
|
unsub();
|
|
105
116
|
this.router.dispose();
|
|
117
|
+
this.wireRouter.dispose();
|
|
106
118
|
this.telemetryBus.dispose();
|
|
107
119
|
this.logBus.dispose();
|
|
120
|
+
this.wireBus.dispose();
|
|
108
121
|
this.disposed = true;
|
|
109
122
|
}
|
|
110
123
|
async createAgent(projectRoot, config = {}, options) {
|
|
@@ -157,17 +170,31 @@ export class DefaultAgentManager {
|
|
|
157
170
|
throw new Error(`projectRoot is not a directory: "${projectRoot}"`);
|
|
158
171
|
}
|
|
159
172
|
const runtime = await this.agentConnectivityResolver.resolve(projectRoot, config);
|
|
173
|
+
// G8 validation — fail before any harness work runs. The harness's static
|
|
174
|
+
// `supportedProviderHints` is the source of truth; the resolver picks one of
|
|
175
|
+
// them. If they disagree, the consumer wired the wrong harness for the model
|
|
176
|
+
// (e.g. a Claude harness pointed at GPT-5).
|
|
177
|
+
const providerHint = runtime.modelConnectivityInfo.providerHint;
|
|
178
|
+
if (!this.harnessSupportedProviderHints.includes(providerHint)) {
|
|
179
|
+
throw new AgentSDKError(`Harness "${this.harness.harnessId}" does not support providerHint "${providerHint}". Supported: ${this.harnessSupportedProviderHints.join(', ')}.`, AgentSDKErrorType.MODEL_NOT_SUPPORTED_BY_HARNESS);
|
|
180
|
+
}
|
|
160
181
|
const hooks = this.hooksForAgent?.(agentId, config) ?? {};
|
|
161
|
-
await this.harness.createAgent(agentId, projectRoot, runtime.
|
|
182
|
+
await this.harness.createAgent(agentId, projectRoot, runtime.modelConnectivityInfo, toHarnessConfig(config, runtime.orgJwt), { ...(options.abortSignal !== undefined ? { abortSignal: options.abortSignal } : {}), hooks });
|
|
162
183
|
const agentSlice = this.router.registerAgent(agentId);
|
|
163
|
-
|
|
184
|
+
// Forward-compat: register the agent against the wire-communication router
|
|
185
|
+
// too. Today every WireCommunicationEvent lands on the unrouted slice (no
|
|
186
|
+
// routing fields on the event shape), but the per-agent slice is allocated
|
|
187
|
+
// symmetrically with the telemetry router so the wiring is in place if/when
|
|
188
|
+
// the event shape grows an agentId field.
|
|
189
|
+
this.wireRouter.registerAgent(agentId);
|
|
190
|
+
const agent = new DefaultAgent(this.harness, agentId, projectRoot, config, runtime.modelConnectivityInfo, runtime.orgConnection, runtime.orgJwt, this.agentConnectivityResolver, this.harnessSupportedProviderHints, this.hooksForAgent, this.identityStore, this.router, agentSlice, { telemetry: this.telemetryBus, log: this.logBus }, this.clock, this.agentIdGenerator);
|
|
164
191
|
this.agents.set(agentId, agent);
|
|
165
192
|
this.telemetryBus.emit({
|
|
166
193
|
type: 'agent-created',
|
|
167
194
|
timestamp: this.clock.now(),
|
|
168
195
|
agentId,
|
|
169
196
|
projectRoot,
|
|
170
|
-
modelName: runtime.
|
|
197
|
+
modelName: runtime.modelConnectivityInfo.model.name,
|
|
171
198
|
});
|
|
172
199
|
if (options.rehydrateThreads) {
|
|
173
200
|
// If thread enumeration or session attachment fails, the restore is a full failure
|
|
@@ -195,6 +222,7 @@ export class DefaultAgentManager {
|
|
|
195
222
|
// Swallow secondary failure; the original error (passed to caller) is what matters.
|
|
196
223
|
}
|
|
197
224
|
this.router.unregisterAgent(agentId);
|
|
225
|
+
this.wireRouter.unregisterAgent(agentId);
|
|
198
226
|
this.agents.delete(agentId);
|
|
199
227
|
}
|
|
200
228
|
getAgentIds() {
|
|
@@ -219,6 +247,7 @@ export class DefaultAgentManager {
|
|
|
219
247
|
if (agent) {
|
|
220
248
|
await agent.destroy();
|
|
221
249
|
this.router.unregisterAgent(agentId);
|
|
250
|
+
this.wireRouter.unregisterAgent(agentId);
|
|
222
251
|
this.agents.delete(agentId);
|
|
223
252
|
}
|
|
224
253
|
if (failureIndex !== -1) {
|
|
@@ -234,6 +263,10 @@ export class DefaultAgentManager {
|
|
|
234
263
|
this.assertNotDisposed();
|
|
235
264
|
return this.logBus.on(callback);
|
|
236
265
|
}
|
|
266
|
+
onWireCommunication(callback) {
|
|
267
|
+
this.assertNotDisposed();
|
|
268
|
+
return this.wireBus.on(callback);
|
|
269
|
+
}
|
|
237
270
|
getRestoreFailures() {
|
|
238
271
|
this.assertNotDisposed();
|
|
239
272
|
return [...this.restoreFailures];
|
|
@@ -318,7 +351,7 @@ export async function createAgentManager(storageRootFolder, harnessFactory, opti
|
|
|
318
351
|
`Update the SDK or harness package.`, AgentSDKErrorType.INCOMPATIBLE_HARNESS);
|
|
319
352
|
}
|
|
320
353
|
const agentConnectivityResolver = options?.connectivityResolver ?? new DefaultAgentConnectivityResolver();
|
|
321
|
-
return DefaultAgentManager.__build(harness, agentConnectivityResolver, options?.hooksForAgent, storageRootFolder, new UUIDGenerator(), new RealClock(), new LogBus());
|
|
354
|
+
return DefaultAgentManager.__build(harness, harnessFactory.supportedProviderHints, agentConnectivityResolver, options?.hooksForAgent, storageRootFolder, new UUIDGenerator(), new RealClock(), new LogBus());
|
|
322
355
|
}
|
|
323
356
|
function isSupportedProtocolVersion(version) {
|
|
324
357
|
return (typeof version === 'number' &&
|
package/dist/agent.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { type Clock, LogBus, type LogRecord, type OrgConnection, type UniqueIDGenerator, type Unsubscribe } from '@salesforce/agentic-common';
|
|
1
|
+
import { type Clock, type JSONWebToken, LogBus, type LogRecord, type OrgConnection, type UniqueIDGenerator, type Unsubscribe } from '@salesforce/agentic-common';
|
|
2
2
|
import type { AgentHarness } from './harness/agent-harness.js';
|
|
3
3
|
import { type AgentConfig } from './harness/harness-config.js';
|
|
4
4
|
import { type ChatSession } from './chat-session.js';
|
|
5
5
|
import type { McpServerInfo } from './mcp-config.js';
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
6
|
+
import type { AgentConnectivityResolver } from './agent-connectivity-resolver.js';
|
|
7
|
+
import type { ModelConnectivityInfo, ProviderHint } from './types/model-connectivity-info.js';
|
|
8
8
|
import type { AgentIdentityStore } from './internal/agent-identity-store.js';
|
|
9
9
|
import type { TelemetryRouter, TelemetrySlice } from './internal/telemetry-router.js';
|
|
10
10
|
import type { HooksForAgent } from './types/redaction.js';
|
|
@@ -32,8 +32,12 @@ export interface Agent {
|
|
|
32
32
|
getId(): string;
|
|
33
33
|
/** Returns the project root folder this agent operates within. */
|
|
34
34
|
getProjectRoot(): string;
|
|
35
|
-
/**
|
|
36
|
-
|
|
35
|
+
/**
|
|
36
|
+
* Returns the authenticated org connection for the org this agent runs under, or
|
|
37
|
+
* `undefined` if the connectivity resolver did not produce one (non-Salesforce
|
|
38
|
+
* hosts; api-key resolvers without an `orgAlias`).
|
|
39
|
+
*/
|
|
40
|
+
getOrgConnection(): OrgConnection | undefined;
|
|
37
41
|
/** Returns the current agent configuration. */
|
|
38
42
|
getAgentConfig(): AgentConfig;
|
|
39
43
|
/**
|
|
@@ -73,10 +77,25 @@ export interface Agent {
|
|
|
73
77
|
* to the live agent (e.g., new instructions, tools, model).
|
|
74
78
|
*
|
|
75
79
|
* @param config - Partial configuration to merge with the current config.
|
|
76
|
-
* @param options - Optional execution options
|
|
80
|
+
* @param options - Optional execution options.
|
|
81
|
+
* - `abortSignal` — caller-side cancellation; threaded through to the
|
|
82
|
+
* harness's `updateAgent` call.
|
|
83
|
+
* - `forceResolve` — when `true`, re-run the connectivity resolver even
|
|
84
|
+
* if neither `orgAlias` nor `modelId` is in the partial. Useful when
|
|
85
|
+
* a consumer-side state change the resolver reads (e.g. a BYOK
|
|
86
|
+
* toggle, a feature-id flip, a rate-limit gate state) needs to land
|
|
87
|
+
* on the next outbound call without an `orgAlias` / `modelId` flip
|
|
88
|
+
* on the SDK surface. Without this flag, partials that don't touch
|
|
89
|
+
* either field skip the resolver to avoid unnecessary work. Note
|
|
90
|
+
* that **only** `orgAlias` and `modelId` automatically trigger
|
|
91
|
+
* re-resolution when present as own properties of the partial; all
|
|
92
|
+
* other `AgentConfig` fields (`instructions`, `tools`, `mcpServers`,
|
|
93
|
+
* `rules`, `skills`, `name`, `description`, etc.) flow to the
|
|
94
|
+
* harness via `updateAgent` without re-running the resolver.
|
|
77
95
|
*/
|
|
78
96
|
updateAgentConfig(config?: AgentConfig, options?: {
|
|
79
97
|
abortSignal?: AbortSignal;
|
|
98
|
+
forceResolve?: boolean;
|
|
80
99
|
}): Promise<void>;
|
|
81
100
|
/**
|
|
82
101
|
* Create a new chat session (conversation thread) for this agent.
|
|
@@ -134,10 +153,11 @@ export declare class DefaultAgent implements Agent {
|
|
|
134
153
|
private readonly agentId;
|
|
135
154
|
private readonly projectRoot;
|
|
136
155
|
private config;
|
|
137
|
-
private
|
|
156
|
+
private modelConnectivityInfo;
|
|
138
157
|
private orgConnection;
|
|
139
158
|
private orgJwt;
|
|
140
159
|
private readonly agentConnectivityResolver;
|
|
160
|
+
private readonly harnessSupportedProviderHints;
|
|
141
161
|
private readonly hooksForAgent;
|
|
142
162
|
private readonly identityStore;
|
|
143
163
|
private readonly sessions;
|
|
@@ -155,7 +175,9 @@ export declare class DefaultAgent implements Agent {
|
|
|
155
175
|
* @param agentId - Unique identifier for this agent.
|
|
156
176
|
* @param projectRoot - Project folder this agent is allowed to operate within.
|
|
157
177
|
* @param config - Initial agent configuration (instructions, model, tools, etc.).
|
|
158
|
-
* @param
|
|
178
|
+
* @param modelConnectivityInfo - Connectivity bag (model, baseUrl, nativeModelId,
|
|
179
|
+
* providerHint, getHeaders) the harness uses to talk to the LLM. Replaced
|
|
180
|
+
* on every `updateAgentConfig` re-resolve.
|
|
159
181
|
* @param orgConnection - Authenticated org connection carrying identity and env inference.
|
|
160
182
|
* @param orgJwt - Self-refreshing JWT for the resolved org (used for MCP auth injection).
|
|
161
183
|
* @param agentConnectivityResolver - Used to re-resolve org connectivity when the org or model changes.
|
|
@@ -169,7 +191,7 @@ export declare class DefaultAgent implements Agent {
|
|
|
169
191
|
* @param inbound - Router slice delivering harness events routed to this agent (non-session-scoped).
|
|
170
192
|
* @param parent - Manager's bus pair; this agent forwards its events upward into them.
|
|
171
193
|
*/
|
|
172
|
-
constructor(harness: AgentHarness, agentId: string, projectRoot: string, config: AgentConfig,
|
|
194
|
+
constructor(harness: AgentHarness, agentId: string, projectRoot: string, config: AgentConfig, modelConnectivityInfo: ModelConnectivityInfo, orgConnection: OrgConnection | undefined, orgJwt: JSONWebToken | undefined, agentConnectivityResolver: AgentConnectivityResolver, harnessSupportedProviderHints: readonly ProviderHint[], hooksForAgent: HooksForAgent | undefined, identityStore: AgentIdentityStore, router: TelemetryRouter, inbound: TelemetrySlice, parent: AgentParentBuses, clock?: Clock, idGenerator?: UniqueIDGenerator);
|
|
173
195
|
/**
|
|
174
196
|
* @requirements
|
|
175
197
|
* - MUST return the agent's ID.
|
|
@@ -177,7 +199,7 @@ export declare class DefaultAgent implements Agent {
|
|
|
177
199
|
getId(): string;
|
|
178
200
|
/** Returns the project root folder this agent operates within. */
|
|
179
201
|
getProjectRoot(): string;
|
|
180
|
-
getOrgConnection(): OrgConnection;
|
|
202
|
+
getOrgConnection(): OrgConnection | undefined;
|
|
181
203
|
/**
|
|
182
204
|
* @requirements
|
|
183
205
|
* - MUST return a shallow copy of the internal `config` object to prevent external mutation of the agent's state.
|
|
@@ -203,6 +225,7 @@ export declare class DefaultAgent implements Agent {
|
|
|
203
225
|
*/
|
|
204
226
|
updateAgentConfig(config?: AgentConfig, options?: {
|
|
205
227
|
abortSignal?: AbortSignal;
|
|
228
|
+
forceResolve?: boolean;
|
|
206
229
|
}): Promise<void>;
|
|
207
230
|
/**
|
|
208
231
|
* @requirements
|