@salesforce/sfdx-agent-sdk 0.86.0 → 0.87.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,11 @@
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.87.0] - 2026-09-17
7
+
8
+ ### Fixes
9
+ - **agent-sdk,harness-mastra**: classify aborted turns as finishReason 'cancelled' at the pre-stream sites @W-23749670@ ([#830](https://github.com/forcedotcom/agentic-dx/pull/830))
10
+
6
11
  ## [0.86.0] - 2026-09-16
7
12
 
8
13
  ### Features
@@ -673,14 +673,11 @@ export declare class DefaultChatSession implements ChatSession {
673
673
  * `abortSignal` is `chat()`'s composed signal — `cancelTurn()`'s own per-turn controller
674
674
  * folded together with any caller-supplied `options.abortSignal` — checked before starting
675
675
  * the occupancy read and again before calling `compactThread`, so an abort from EITHER source
676
- * that lands while this is running skips (rather than starts) compaction. This narrows, but
677
- * does not close, the window an abort can block on: `AgentHarness.compactThread` takes no
678
- * `AbortSignal`, so a `compactThread` call already in flight when the signal aborts cannot be
679
- * interrupted the caller blocks for the remaining compaction latency, and if
680
- * `harness.stream()` subsequently runs against an already-aborted signal, the turn can end via
681
- * `notifyPreStreamError` with `finishReason: 'error'` rather than `'cancelled'`. See
682
- * `cancelTurn()`'s Critical Invariant below for the full contract and why this residual gap is
683
- * accepted for now.
676
+ * that lands while this is running skips (rather than starts) compaction. An abort that instead
677
+ * lands DURING an in-flight `compactThread` can't preempt it (`compactThread` takes no
678
+ * `AbortSignal` by design); the post-compaction `stream()` then rejects with an `AbortError`
679
+ * that `notifyPreStreamError` classifies to `finishReason: 'cancelled'`. See `cancelTurn()`'s
680
+ * Critical Invariant in ARCHITECTURE.md for the full rationale.
684
681
  */
685
682
  private maybeAutoCompact;
686
683
  /**
@@ -810,6 +807,13 @@ export declare class DefaultChatSession implements ChatSession {
810
807
  * same `(agentId, threadId)` pair.
811
808
  */
812
809
  private emitChatStreamStarted;
810
+ /**
811
+ * Terminal {@link FinishReason} for a failure: `'cancelled'` when {@link isAbortError} (a user
812
+ * cancel), else `'error'`. See the `cancelTurn` Critical Invariant in ARCHITECTURE.md.
813
+ */
814
+ private finishReasonForError;
815
+ /** Terminal `ErrorEvent` for a failure — abort → `code: 'cancelled'`, matching {@link finishReasonForError}. */
816
+ private errorEventFor;
813
817
  /**
814
818
  * Emits an `ErrorEvent` + `FinishEvent` to all `subscribe()` listeners and a `chat-stream-error`
815
819
  * telemetry event for a failure that occurred before a stream was established. The caller is
@@ -2,7 +2,7 @@
2
2
  * Copyright 2026, Salesforce, Inc. All rights reserved.
3
3
  * See LICENSE.txt for license terms.
4
4
  */
5
- import { backfillCreatedAt, EventBus, LogBus, RealClock, UUIDGenerator, } from '@salesforce/agentic-common';
5
+ import { backfillCreatedAt, EventBus, isAbortError, LogBus, RealClock, UUIDGenerator, } from '@salesforce/agentic-common';
6
6
  import { resolveAutoCompactionThreshold } from './harness/harness-config.js';
7
7
  import { resolveToolDeclineModelMessage } from './harness/tool-decline.js';
8
8
  import { AgentSDKError, AgentSDKErrorType } from './errors.js';
@@ -382,7 +382,7 @@ export class DefaultChatSession {
382
382
  }
383
383
  catch (err) {
384
384
  const error = err instanceof Error ? err : new Error(String(err));
385
- const errorEvent = { type: 'error', error };
385
+ const errorEvent = this.errorEventFor(error);
386
386
  this.chatEventBus.emit(errorEvent);
387
387
  yield errorEvent;
388
388
  lastError = error;
@@ -405,7 +405,7 @@ export class DefaultChatSession {
405
405
  this.turnActive = false;
406
406
  }
407
407
  if (!sawFinish && lastError !== undefined) {
408
- const finishEvent = { type: 'finish', finishReason: 'error' };
408
+ const finishEvent = { type: 'finish', finishReason: this.finishReasonForError(lastError) };
409
409
  this.chatEventBus.emit(finishEvent);
410
410
  yield finishEvent;
411
411
  // Match the natural-finish branch: every terminal `finish` clears the
@@ -731,14 +731,11 @@ export class DefaultChatSession {
731
731
  * `abortSignal` is `chat()`'s composed signal — `cancelTurn()`'s own per-turn controller
732
732
  * folded together with any caller-supplied `options.abortSignal` — checked before starting
733
733
  * the occupancy read and again before calling `compactThread`, so an abort from EITHER source
734
- * that lands while this is running skips (rather than starts) compaction. This narrows, but
735
- * does not close, the window an abort can block on: `AgentHarness.compactThread` takes no
736
- * `AbortSignal`, so a `compactThread` call already in flight when the signal aborts cannot be
737
- * interrupted the caller blocks for the remaining compaction latency, and if
738
- * `harness.stream()` subsequently runs against an already-aborted signal, the turn can end via
739
- * `notifyPreStreamError` with `finishReason: 'error'` rather than `'cancelled'`. See
740
- * `cancelTurn()`'s Critical Invariant below for the full contract and why this residual gap is
741
- * accepted for now.
734
+ * that lands while this is running skips (rather than starts) compaction. An abort that instead
735
+ * lands DURING an in-flight `compactThread` can't preempt it (`compactThread` takes no
736
+ * `AbortSignal` by design); the post-compaction `stream()` then rejects with an `AbortError`
737
+ * that `notifyPreStreamError` classifies to `finishReason: 'cancelled'`. See `cancelTurn()`'s
738
+ * Critical Invariant in ARCHITECTURE.md for the full rationale.
742
739
  */
743
740
  async maybeAutoCompact(abortSignal) {
744
741
  if (this.harness.hasNativeAutoCompaction || abortSignal.aborted)
@@ -1132,6 +1129,17 @@ export class DefaultChatSession {
1132
1129
  this.telemetryBus.emitTelemetry({ type: 'chat-stream-started', agentId: this.agentId, threadId: this.threadId, trigger }, startedAt);
1133
1130
  return startedAt;
1134
1131
  }
1132
+ /**
1133
+ * Terminal {@link FinishReason} for a failure: `'cancelled'` when {@link isAbortError} (a user
1134
+ * cancel), else `'error'`. See the `cancelTurn` Critical Invariant in ARCHITECTURE.md.
1135
+ */
1136
+ finishReasonForError(err) {
1137
+ return isAbortError(err) ? 'cancelled' : 'error';
1138
+ }
1139
+ /** Terminal `ErrorEvent` for a failure — abort → `code: 'cancelled'`, matching {@link finishReasonForError}. */
1140
+ errorEventFor(error) {
1141
+ return isAbortError(error) ? { type: 'error', error, code: 'cancelled' } : { type: 'error', error };
1142
+ }
1135
1143
  /**
1136
1144
  * Emits an `ErrorEvent` + `FinishEvent` to all `subscribe()` listeners and a `chat-stream-error`
1137
1145
  * telemetry event for a failure that occurred before a stream was established. The caller is
@@ -1146,8 +1154,8 @@ export class DefaultChatSession {
1146
1154
  */
1147
1155
  notifyPreStreamError(err, startedAt, threadId) {
1148
1156
  const error = err instanceof Error ? err : new Error(String(err));
1149
- this.chatEventBus.emit({ type: 'error', error });
1150
- this.chatEventBus.emit({ type: 'finish', finishReason: 'error' });
1157
+ this.chatEventBus.emit(this.errorEventFor(error));
1158
+ this.chatEventBus.emit({ type: 'finish', finishReason: this.finishReasonForError(error) });
1151
1159
  const finishedAt = this.clock.now();
1152
1160
  const durationMs = finishedAt.getTime() - startedAt.getTime();
1153
1161
  // Tier-1 pair: share one tick across the telemetry event and its colocated log sibling.
@@ -1178,8 +1186,8 @@ export class DefaultChatSession {
1178
1186
  */
1179
1187
  notifySettleRejection(err) {
1180
1188
  const error = err instanceof Error ? err : new Error(String(err));
1181
- this.chatEventBus.emit({ type: 'error', error });
1182
- this.chatEventBus.emit({ type: 'finish', finishReason: 'error' });
1189
+ this.chatEventBus.emit(this.errorEventFor(error));
1190
+ this.chatEventBus.emit({ type: 'finish', finishReason: this.finishReasonForError(error) });
1183
1191
  }
1184
1192
  assertNotDisposed() {
1185
1193
  if (this.disposed) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/sfdx-agent-sdk",
3
- "version": "0.86.0",
3
+ "version": "0.87.0",
4
4
  "description": "Harness-agnostic agentic infrastructure for Salesforce developer experience tooling",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -48,9 +48,9 @@
48
48
  },
49
49
  "devDependencies": {
50
50
  "@eslint/js": "^10.0.1",
51
- "@salesforce/sfdx-agent-harness-claude": "0.82.0",
52
- "@salesforce/sfdx-agent-harness-mastra": "0.85.0",
53
- "@salesforce/sfdx-agent-harness-openai": "0.51.0",
51
+ "@salesforce/sfdx-agent-harness-claude": "0.83.0",
52
+ "@salesforce/sfdx-agent-harness-mastra": "0.86.0",
53
+ "@salesforce/sfdx-agent-harness-openai": "0.52.0",
54
54
  "@types/node": "^22.20.1",
55
55
  "@vitest/coverage-istanbul": "^4.1.11",
56
56
  "@vitest/eslint-plugin": "^1.6.27",