@salesforce/sfdx-agent-sdk 0.56.0 → 0.58.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,16 @@
3
3
  All notable changes to `@salesforce/sfdx-agent-sdk` are documented in this file.
4
4
  Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
5
5
 
6
+ ## [0.58.0] - 2026-08-28
7
+
8
+ ### Fixes
9
+ - **harness-mastra,agent-sdk**: resilient thread enumeration + exception-safe destroyChatSession @W-24014120@ ([#774](https://github.com/forcedotcom/agentic-dx/pull/774))
10
+
11
+ ## [0.57.0] - 2026-08-27
12
+
13
+ ### Features
14
+ - **agent-sdk**: let createAgentManager observe boot-restore logs via onBootLog @W-23992664@ ([#770](https://github.com/forcedotcom/agentic-dx/pull/770))
15
+
6
16
  ## [0.56.0] - 2026-08-27
7
17
 
8
18
  ### Features
@@ -213,7 +213,7 @@ export declare class DefaultAgentManager<H extends AgentHarness = AgentHarness>
213
213
  * is private, so this is the only way to obtain an instance, but
214
214
  * consumers should always go through {@link createAgentManager}.
215
215
  */
216
- static __build<H extends AgentHarness>(harness: H, harnessSupportedProviderHints: readonly ProviderHint[], agentConnectivityResolver: AgentConnectivityResolver, resolvers: AgentRuntimeResolvers, storageRootFolder: string, agentIdGenerator: UniqueIDGenerator, clock: Clock, logBus: LogBus): Promise<DefaultAgentManager<H>>;
216
+ static __build<H extends AgentHarness>(harness: H, harnessSupportedProviderHints: readonly ProviderHint[], agentConnectivityResolver: AgentConnectivityResolver, resolvers: AgentRuntimeResolvers, storageRootFolder: string, agentIdGenerator: UniqueIDGenerator, clock: Clock, logBus: LogBus, onBootLog?: (record: LogRecord) => void): Promise<DefaultAgentManager<H>>;
217
217
  private init;
218
218
  shutdown(): Promise<void>;
219
219
  createAgent(projectRoot: string, config?: ConfigOf<H> & {
@@ -275,6 +275,10 @@ export declare class DefaultAgentManager<H extends AgentHarness = AgentHarness>
275
275
  * resolved bag threads through to `AgentHarness.createAgent`'s
276
276
  * `options.hooks` and reaches the harness's native seam (Claude
277
277
  * `PostToolUse`, Mastra `processInputStep`).
278
+ * - `onBootLog` — observes logs emitted *during construction only* (boot-time
279
+ * restore failures + identity-store warnings), then detaches. It exists
280
+ * because {@link AgentManager.onLog} can't be attached until this function
281
+ * returns — too late for those. See the `onBootLog` option doc below.
278
282
  *
279
283
  * @throws {AgentSDKError} `INCOMPATIBLE_HARNESS` when either the factory or
280
284
  * the constructed harness reports a `protocolVersion` outside
@@ -291,4 +295,24 @@ export declare function createAgentManager<H extends AgentHarness = AgentHarness
291
295
  * third-party remote MCP servers.
292
296
  */
293
297
  mcpAuthProviderResolver?: McpAuthProviderResolver;
298
+ /**
299
+ * Observes log records emitted *during construction* — and only then.
300
+ * Boot-time restore runs inside this function (before it returns) and
301
+ * emits an `agent restore failed` record for every persisted agent that
302
+ * fails to replay, plus `AgentIdentityStore` warnings (corrupt record
303
+ * JSON, harness-id mismatch). Those are dropped today:
304
+ * {@link AgentManager.onLog} can only be attached *after* this function
305
+ * returns, and `LogBus` has no replay, so a late subscriber never sees
306
+ * the construction-time records.
307
+ *
308
+ * This callback is subscribed before the restore pass and **detached as
309
+ * soon as construction finishes**, so it observes exactly the pre-return
310
+ * window and never overlaps {@link AgentManager.onLog}. A host that
311
+ * bridges SDK logs to its own logger wires `onBootLog` for the boot
312
+ * window and `manager.onLog` for the runtime window — two disjoint
313
+ * sources into the same sink, no double-logging. Unlike
314
+ * `manager.onLog`, this returns no `Unsubscribe`; its lifetime is fixed
315
+ * to construction.
316
+ */
317
+ onBootLog?: (record: LogRecord) => void;
294
318
  }): Promise<AgentManager<H>>;
@@ -76,11 +76,25 @@ export class DefaultAgentManager {
76
76
  * is private, so this is the only way to obtain an instance, but
77
77
  * consumers should always go through {@link createAgentManager}.
78
78
  */
79
- static async __build(harness, harnessSupportedProviderHints, agentConnectivityResolver, resolvers, storageRootFolder, agentIdGenerator, clock, logBus) {
80
- const identityStore = new AgentIdentityStore(storageRootFolder, harness.harnessId, logBus);
81
- const manager = new DefaultAgentManager(harness, harnessSupportedProviderHints, agentConnectivityResolver, resolvers, identityStore, agentIdGenerator, clock, logBus);
82
- await manager.init();
83
- return manager;
79
+ static async __build(harness, harnessSupportedProviderHints, agentConnectivityResolver, resolvers, storageRootFolder, agentIdGenerator, clock, logBus, onBootLog) {
80
+ // Subscribe `onBootLog` FIRST before constructing anything that takes `logBus` — so it
81
+ // observes the ENTIRE construction window (identity-store reads + boot-time restore inside
82
+ // `init()`), then detach in `finally`. Keep it at the top: a caller can't reach
83
+ // `manager.onLog()` until this method returns, by which point construction has finished
84
+ // emitting and `LogBus` has no replay — so those records would otherwise be lost. Because
85
+ // the window closes exactly when construction does, `onBootLog` never overlaps a later
86
+ // `manager.onLog()`. (Nothing emits on `logBus` from a constructor today, but subscribing
87
+ // first means it stays correct if that ever changes.)
88
+ const unsubscribeBootLog = onBootLog ? logBus.on(onBootLog) : undefined;
89
+ try {
90
+ const identityStore = new AgentIdentityStore(storageRootFolder, harness.harnessId, logBus);
91
+ const manager = new DefaultAgentManager(harness, harnessSupportedProviderHints, agentConnectivityResolver, resolvers, identityStore, agentIdGenerator, clock, logBus);
92
+ await manager.init();
93
+ return manager;
94
+ }
95
+ finally {
96
+ unsubscribeBootLog?.();
97
+ }
84
98
  }
85
99
  async init() {
86
100
  const records = await this.identityStore.list();
@@ -386,6 +400,10 @@ export class DefaultAgentManager {
386
400
  * resolved bag threads through to `AgentHarness.createAgent`'s
387
401
  * `options.hooks` and reaches the harness's native seam (Claude
388
402
  * `PostToolUse`, Mastra `processInputStep`).
403
+ * - `onBootLog` — observes logs emitted *during construction only* (boot-time
404
+ * restore failures + identity-store warnings), then detaches. It exists
405
+ * because {@link AgentManager.onLog} can't be attached until this function
406
+ * returns — too late for those. See the `onBootLog` option doc below.
389
407
  *
390
408
  * @throws {AgentSDKError} `INCOMPATIBLE_HARNESS` when either the factory or
391
409
  * the constructed harness reports a `protocolVersion` outside
@@ -428,9 +446,9 @@ export async function createAgentManager(storageRootFolder, harnessFactory, opti
428
446
  }
429
447
  const agentConnectivityResolver = options?.connectivityResolver ?? new DefaultAgentConnectivityResolver();
430
448
  const clock = new RealClock();
431
- return DefaultAgentManager.__build(harness, harnessFactory.supportedProviderHints, agentConnectivityResolver, { hooksForAgent: options?.hooksForAgent, mcpAuthProviderResolver: options?.mcpAuthProviderResolver }, storageRootFolder, new UUIDGenerator(), clock,
432
449
  // The manager's root log bus shares the manager clock and self-reads the process environment context.
433
- new LogBus(clock));
450
+ const logBus = new LogBus(clock);
451
+ return DefaultAgentManager.__build(harness, harnessFactory.supportedProviderHints, agentConnectivityResolver, { hooksForAgent: options?.hooksForAgent, mcpAuthProviderResolver: options?.mcpAuthProviderResolver }, storageRootFolder, new UUIDGenerator(), clock, logBus, options?.onBootLog);
434
452
  }
435
453
  function isSupportedProtocolVersion(version) {
436
454
  return (typeof version === 'number' &&
package/dist/agent.js CHANGED
@@ -283,8 +283,17 @@ export class DefaultAgent {
283
283
  if (!session) {
284
284
  throw new AgentSDKError(`No ChatSession found with id: "${sessionId}"`, AgentSDKErrorType.CHAT_SESSION_NOT_FOUND);
285
285
  }
286
- await this.harness.destroyThread(this.agentId, sessionId);
287
- this.detachSession(sessionId, session);
286
+ try {
287
+ await this.harness.destroyThread(this.agentId, sessionId);
288
+ }
289
+ finally {
290
+ // Detach the in-memory session even if the harness threw (disk error,
291
+ // corrupt/unreachable backing row), so a thread that can't be torn down
292
+ // can't leave an undeletable chat attached. The harness failure still
293
+ // propagates — best-effort cleanup, not silently swallowed
294
+ // (spike agent-offline-failure-modes #11, W-24014120).
295
+ this.detachSession(sessionId, session);
296
+ }
288
297
  }
289
298
  /**
290
299
  * @requirements
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/sfdx-agent-sdk",
3
- "version": "0.56.0",
3
+ "version": "0.58.0",
4
4
  "description": "Harness-agnostic agentic infrastructure for Salesforce developer experience tooling",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -47,9 +47,9 @@
47
47
  },
48
48
  "devDependencies": {
49
49
  "@eslint/js": "^10.0.1",
50
- "@salesforce/sfdx-agent-harness-claude": "0.52.0",
51
- "@salesforce/sfdx-agent-harness-mastra": "0.55.0",
52
- "@salesforce/sfdx-agent-harness-openai": "0.21.0",
50
+ "@salesforce/sfdx-agent-harness-claude": "0.54.0",
51
+ "@salesforce/sfdx-agent-harness-mastra": "0.57.0",
52
+ "@salesforce/sfdx-agent-harness-openai": "0.23.0",
53
53
  "@types/node": "^22.20.1",
54
54
  "@vitest/coverage-istanbul": "^4.1.10",
55
55
  "@vitest/eslint-plugin": "^1.6.27",