@salesforce/sfdx-agent-sdk 0.4.0 → 0.6.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.
@@ -1,134 +1,202 @@
1
1
  import { type Clock, LogBus, type LogRecord, type Unsubscribe, type UniqueIDGenerator } from '@salesforce/agentic-common';
2
- import { type AgentHarness } from './harness/agent-harness.js';
2
+ import { type AgentHarness, type ConfigOf } from './harness/agent-harness.js';
3
3
  import type { HarnessFactory } from './harness/harness-factory.js';
4
4
  import { type AgentConfig } from './harness/harness-config.js';
5
5
  import { type Agent } from './agent.js';
6
6
  import type { TelemetryEventCallback } from './types/telemetry-events.js';
7
7
  import { type AgentConnectivityResolver } from './agent-connectivity-resolver.js';
8
+ /**
9
+ * Per-agent failure recorded during the boot-time restore pass.
10
+ *
11
+ * Returned in bulk by {@link AgentManager.getRestoreFailures}. Each entry
12
+ * captures the persisted identity triple plus the underlying error so the
13
+ * consumer can mark its placeholder as `error` and either DELETE or recreate
14
+ * the agent.
15
+ */
16
+ export type RestoreFailure = {
17
+ agentId: string;
18
+ projectRoot: string;
19
+ config: AgentConfig;
20
+ error: unknown;
21
+ };
8
22
  /**
9
23
  * Manages the lifecycle of {@link Agent} instances.
10
24
  *
11
- * The manager owns an {@link AgentHarness} and coordinates initialization,
12
- * agent creation, and graceful shutdown. The harness implementation is
13
- * abstracted away from downstream code (agents, sessions).
25
+ * The concrete implementation ({@link DefaultAgentManager}) is internal to
26
+ * the SDK and constructed only via {@link createAgentManager}. Consumers
27
+ * program against this interface; they never see the implementation type.
28
+ *
29
+ * ### Boot-time restore
14
30
  *
15
- * ### Persistence Responsibility
16
- * Persistence is shared between the harness and the consuming application:
17
- * - **Engine**: Handles physical data persistence (database storage of threads, messages, and memory state).
18
- * - **Application**: Handles lifecycle persistence. It manages the discovery and restoration of saved agents,
19
- * and calls `createAgent(projectRoot, { agentId: savedId, ...savedConfig })` to rebuild active agent state in memory
20
- * (like MCP connections and custom tool closures) while reconnecting to the persistent memory data.
31
+ * `createAgentManager` reads any agents the SDK persisted on a prior run
32
+ * (`${storageRootFolder}/agents/<id>.json`) and replays `installAgent` for
33
+ * each before returning. Failures land in {@link getRestoreFailures}, not
34
+ * thrown. Subscribe to {@link onLog} to bridge SDK log events into your
35
+ * host logger; restore failures are emitted at `error` level and soft skips
36
+ * (corrupt JSON, harness-id mismatch) at `warn`.
21
37
  */
22
- export declare class AgentManager {
23
- private readonly harness;
24
- private readonly agentIdGenerator;
25
- private readonly agentConnectivityResolver;
26
- private readonly clock;
27
- private readonly agents;
28
- private readonly telemetryBus;
29
- private readonly logBus;
30
- private readonly router;
31
- private readonly unroutedUnsubs;
32
- private disposed;
38
+ export interface AgentManager<H extends AgentHarness = AgentHarness> {
33
39
  /**
34
- * Creates a new {@link AgentManager}.
40
+ * Creates a new {@link Agent} with the given configuration and persists
41
+ * its identity triple `{ agentId, projectRoot, config }` so it can be
42
+ * restored on the next boot.
35
43
  *
36
- * This constructor is **exposed for testing only** so unit tests can inject a stubbed
37
- * {@link AgentHarness} and deterministic ID generator.
44
+ * The config type is inferred from the harness type `H`. Harnesses that
45
+ * declare a subtype via `__agentConfig` (e.g. `MastraAgentHarness`
46
+ * declares `MastraAgentConfig`) get their extra fields autocompleted
47
+ * without an explicit generic at the call site. Extra fields thread
48
+ * through opaquely to the harness, which narrows what it cares about.
38
49
  *
39
- * Production callers should use {@link createAgentManager} instead.
50
+ * @throws If `projectRoot` does not exist or is not a directory.
51
+ * @throws If `config.agentId` is provided and an agent with that ID is
52
+ * already registered (live or in restore-failure state).
53
+ */
54
+ createAgent(projectRoot: string, config?: ConfigOf<H> & {
55
+ agentId?: string;
56
+ }, options?: {
57
+ abortSignal?: AbortSignal;
58
+ }): Promise<Agent>;
59
+ /**
60
+ * Returns the live {@link Agent} for the given id.
40
61
  *
41
- * @example
42
- * ```ts
43
- * // Recommended for production usage (constructs the default connectivity resolver):
44
- * const manager = await createAgentManager(storageRootFolder, harnessFactory);
45
- * ```
62
+ * @throws `AGENT_NOT_FOUND` if the id is unknown or only present in
63
+ * {@link getRestoreFailures}.
46
64
  */
47
- constructor(harness: AgentHarness, agentConnectivityResolver: AgentConnectivityResolver, agentIdGenerator?: UniqueIDGenerator, clock?: Clock, logBus?: LogBus);
65
+ getAgent(agentId: string): Agent;
48
66
  /**
49
- * Shuts down the harness and destroys all managed agents.
67
+ * Returns the ids of all live agents (successfully created or
68
+ * restored). Failed-restore agents are not included — query
69
+ * {@link getRestoreFailures} separately.
70
+ */
71
+ getAgentIds(): string[];
72
+ /**
73
+ * Destroys a managed agent, removes its identity record from disk,
74
+ * and clears any matching entry in {@link getRestoreFailures}.
75
+ *
76
+ * If the id is only in `getRestoreFailures()` (no live agent), the
77
+ * harness is not consulted; the identity file is removed and the
78
+ * failure entry cleared. After this returns, the same id may be
79
+ * passed to {@link createAgent} again.
50
80
  *
51
- * @requirements
52
- * - MUST iterate through all agents in the internal `agents` map and call `agent.destroy()` on each.
53
- * - MUST clear the internal `agents` map.
54
- * - MUST delegate to `this.harness.shutdown()` to release harness-level resources.
81
+ * @throws `AGENT_NOT_FOUND` if the id is neither live nor a
82
+ * restore-failure entry.
55
83
  */
84
+ destroyAgent(agentId: string): Promise<void>;
85
+ /** Shuts down the harness and destroys all live agents. */
56
86
  shutdown(): Promise<void>;
57
87
  /**
58
- * Creates a new `Agent` with the given configuration.
88
+ * Harness-specific extensions namespace, typed off the harness subtype `H`.
59
89
  *
60
- * @param projectRoot - Absolute path to the project folder the agent can manipulate files from.
61
- * Relative paths are resolved against `process.cwd()`. The path must exist and be a directory.
62
- * @param config - Agent configuration (instructions, tools, model, etc.).
63
- * @param options - Optional execution options, including abort signals.
64
- * @returns A new `Agent` ready for chat sessions.
90
+ * For the default `AgentHarness`, this is the opaque `Record<string, unknown>`
91
+ * declared on the contract no IDE help. Consumers parameterize the manager
92
+ * over a harness-specific subtype (e.g. `MastraAgentHarness`) to get a typed
93
+ * slot like `manager.extensions.mastra.getToolSearchProcessor(agentId)`.
65
94
  *
66
- * @throws If `projectRoot` does not exist or is not a directory.
95
+ * Per-agent extension accessors take the agent id as their first argument;
96
+ * orchestrator-level accessors (e.g. workflow registries) don't.
67
97
  *
68
- * @requirements
69
- * - MUST resolve `projectRoot` to an absolute, normalized path before use.
70
- * - MUST throw an Error if the resolved `projectRoot` does not exist or is not a directory.
71
- * - MUST throw an Error if `config.agentId` is provided and an agent with that ID already exists in the internal `agents` map.
72
- * - MUST generate a unique ID via `this.agentIdGenerator` if `config.agentId` is omitted.
73
- * - MUST delegate to `this.harness.createAgent(agentId, projectRoot, llmGatewayClient, config, options)` to register the agent in the harness and allow for cancellation.
74
- * - MUST instantiate a `DefaultAgent` with the harness and the complete configuration.
75
- * - MUST store the newly created agent in the internal `agents` map, keyed by its `agentId`.
76
- * - MUST return the newly created agent.
98
+ * The SDK never reads or interprets this object; it is a passthrough surface
99
+ * owned by the harness package.
77
100
  */
78
- createAgent(projectRoot: string, config?: AgentConfig & {
101
+ readonly extensions: H['extensions'];
102
+ /** Subscribe to telemetry events across every managed agent. */
103
+ onTelemetry(callback: TelemetryEventCallback): Unsubscribe;
104
+ /** Subscribe to structured log records across every managed agent. */
105
+ onLog(callback: (record: LogRecord) => void): Unsubscribe;
106
+ /**
107
+ * Returns a snapshot of the boot-time restore failures the SDK has not
108
+ * yet been told to forget. Each entry is cleared on a successful
109
+ * {@link destroyAgent} for the same id; recreating an agent with the
110
+ * same id via {@link createAgent} also clears the entry.
111
+ */
112
+ getRestoreFailures(): RestoreFailure[];
113
+ }
114
+ /**
115
+ * Concrete implementation of {@link AgentManager}. **Not exported** from
116
+ * `src/index.ts` — public construction goes through {@link createAgentManager}.
117
+ *
118
+ * Construction is asynchronous because {@link init} reads the persistence
119
+ * directory and replays prior agents before the manager becomes useful. The
120
+ * pattern mirrors {@link Workspace.create}: a private constructor for sync
121
+ * field assignment, then a static async builder that calls `init()`.
122
+ */
123
+ export declare class DefaultAgentManager<H extends AgentHarness = AgentHarness> implements AgentManager<H> {
124
+ private readonly harness;
125
+ private readonly agentIdGenerator;
126
+ private readonly agentConnectivityResolver;
127
+ private readonly clock;
128
+ private readonly identityStore;
129
+ private readonly agents;
130
+ private restoreFailures;
131
+ private readonly telemetryBus;
132
+ private readonly logBus;
133
+ private readonly router;
134
+ private readonly unroutedUnsubs;
135
+ private disposed;
136
+ private constructor();
137
+ /**
138
+ * Module-internal builder used by {@link createAgentManager}. Mirrors
139
+ * {@link Workspace.create}: sync field assignment in the private
140
+ * constructor, then `await init()` to do async startup work
141
+ * (reading the persistence directory + replaying prior agents).
142
+ *
143
+ * The double-underscore signals "do not call directly" — the constructor
144
+ * is private, so this is the only way to obtain an instance, but
145
+ * consumers should always go through {@link createAgentManager}.
146
+ */
147
+ static __build<H extends AgentHarness>(harness: H, agentConnectivityResolver: AgentConnectivityResolver, storageRootFolder: string, agentIdGenerator: UniqueIDGenerator, clock: Clock, logBus: LogBus): Promise<DefaultAgentManager<H>>;
148
+ private init;
149
+ shutdown(): Promise<void>;
150
+ createAgent(projectRoot: string, config?: ConfigOf<H> & {
79
151
  agentId?: string;
80
152
  }, options?: {
81
153
  abortSignal?: AbortSignal;
82
154
  }): Promise<Agent>;
83
155
  /**
84
- * Returns the IDs of all currently managed agents.
156
+ * Shared install path for {@link createAgent} and the boot-time restore
157
+ * loop. Resolves connectivity, calls `harness.createAgent`, constructs
158
+ * the {@link DefaultAgent}, registers the telemetry slice, emits
159
+ * `agent-created`, and (when restoring) attaches a chat session per
160
+ * persisted thread id.
85
161
  *
86
- * @requirements
87
- * - MUST return an array of all string keys (agent IDs) currently tracked in the internal `agents` map.
162
+ * Returns {@link DefaultAgent} (concrete) rather than {@link Agent}
163
+ * because both call sites are internal widening to the interface only
164
+ * happens at the public-method return statement.
88
165
  */
166
+ private installAgent;
167
+ private rollbackInstall;
89
168
  getAgentIds(): string[];
90
- /**
91
- * Retrieves a managed agent by its ID.
92
- *
93
- * @param agentId - ID of the agent to retrieve.
94
- *
95
- * @requirements
96
- * - MUST throw an Error if the provided `agentId` is not found in the internal `agents` map.
97
- * - MUST return the `Agent` instance associated with the given `agentId`.
98
- */
99
169
  getAgent(agentId: string): Agent;
100
- /**
101
- * Destroys a managed agent by its ID, releasing all its resources.
102
- *
103
- * @param agentId - ID of the agent to destroy.
104
- *
105
- * @requirements
106
- * - MUST throw an Error if the provided `agentId` is not found in the internal `agents` map.
107
- * - MUST call `destroy()` on the target agent instance.
108
- * - MUST remove the agent from the internal `agents` map.
109
- */
110
170
  destroyAgent(agentId: string): Promise<void>;
111
- /** Subscribe to telemetry events across every managed agent. Returns an unsubscribe function. */
112
171
  onTelemetry(callback: TelemetryEventCallback): Unsubscribe;
113
- /** Subscribe to structured log records across every managed agent. Returns an unsubscribe function. */
114
172
  onLog(callback: (record: LogRecord) => void): Unsubscribe;
173
+ getRestoreFailures(): RestoreFailure[];
174
+ /**
175
+ * Re-exposes `harness.extensions` typed off `H`. Read-only; the SDK never
176
+ * leaks the harness reference itself, only its declared extensions surface.
177
+ * `assertNotDisposed` is intentionally NOT called here — the extensions
178
+ * object is reachable on the harness, and reading it after shutdown is the
179
+ * harness's prerogative to handle (its own disposal mechanics may have
180
+ * already torn down the underlying state).
181
+ */
182
+ get extensions(): H['extensions'];
115
183
  private assertNotDisposed;
116
184
  }
117
185
  /**
118
- * Creates an {@link AgentManager} using the provided harness factory.
119
- *
120
- * Use this function in production code. It validates the `storageRootFolder`,
121
- * verifies the factory advertises a supported protocol version (failing fast
122
- * before any expensive harness construction), constructs the harness
123
- * asynchronously, sanity-checks that the constructed harness honors the
124
- * factory's advertised version, and returns a ready-to-use manager.
186
+ * Public entry point. Validates the storage root, gates the harness's
187
+ * advertised protocol version, constructs the harness, and returns a
188
+ * fully-restored {@link AgentManager}. Boot-time restore runs before this
189
+ * function returns; failures are queryable via
190
+ * {@link AgentManager.getRestoreFailures}.
125
191
  *
126
- * @param storageRootFolder - Existing directory used for agent persistence.
127
- * @param harnessFactory - Factory that constructs the harness implementation.
192
+ * The optional `connectivityResolver` overrides the default
193
+ * `DefaultAgentConnectivityResolver` used by e2e tests and custom-auth
194
+ * deployments where the SDK should not run sf-CLI-based org resolution.
195
+ * Production callers leave it unset.
128
196
  *
129
197
  * @throws {AgentSDKError} `INCOMPATIBLE_HARNESS` when either the factory or
130
198
  * the constructed harness reports a `protocolVersion` outside
131
199
  * {@link SUPPORTED_PROTOCOL_VERSIONS}, or when the harness's reported
132
200
  * version disagrees with the factory's.
133
201
  */
134
- export declare function createAgentManager(storageRootFolder: string, harnessFactory: HarnessFactory): Promise<AgentManager>;
202
+ export declare function createAgentManager<H extends AgentHarness = AgentHarness>(storageRootFolder: string, harnessFactory: HarnessFactory<H>, connectivityResolver?: AgentConnectivityResolver): Promise<AgentManager<H>>;