@salesforce/sfdx-agent-sdk 0.6.0 → 0.8.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/README.md CHANGED
@@ -180,19 +180,19 @@ Returned by `chat()`, `submitToolResult()`, `approveToolCall()`, and `declineToo
180
180
 
181
181
  Discriminated union (`event.type`) of streaming events:
182
182
 
183
- | Type | Key Fields | Description |
184
- | ----------------------- | ---------------------------------------------- | -------------------------------------------------------- |
185
- | `start` | — | Stream has begun. |
186
- | `text-delta` | `text` | Incremental response text. |
187
- | `reasoning-delta` | `text` | Chain-of-thought fragment. |
188
- | `tool-call` | `toolCallId`, `toolName`, `args` | Tool invocation. |
189
- | `tool-approval-request` | `toolCall: ToolCallInfo` | Engine requests approval before executing a tool. |
190
- | `tool-result` | `toolCallId`, `toolName`, `result`, `isError?` | Tool execution completed. |
191
- | `step-start` | `stepIndex` | New LLM invocation step began. |
192
- | `step-finish` | `stepIndex`, `finishReason`, `usage?` | Step completed with per-step token usage. |
193
- | `error` | `error`, `code?` | Mid-stream error (yielded, not thrown). |
194
- | `finish` | `finishReason`, `usage?` | Stream completed with aggregate token usage. |
195
- | `unmapped-chunk` | `chunkType`, `rawChunk` | Unrecognized harness event, preserved for observability. |
183
+ | Type | Key Fields | Description |
184
+ | ----------------------- | ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
185
+ | `start` | — | Stream has begun. |
186
+ | `text-delta` | `text` | Incremental response text. |
187
+ | `reasoning-delta` | `text` | Chain-of-thought fragment. |
188
+ | `tool-call` | `toolCallId`, `toolName`, `args`, `annotations?`, `serverName?` | Tool invocation. `annotations` is the MCP-spec hints (`readOnlyHint`, `destructiveHint`, …) when the source declared them; `serverName` is set when the tool came from an MCP server. |
189
+ | `tool-approval-request` | `toolCall: ToolCallInfo`, `annotations?`, `serverName?` | Engine requests approval before executing a tool. Same `annotations` / `serverName` semantics as `tool-call`. |
190
+ | `tool-result` | `toolCallId`, `toolName`, `result`, `isError?`, `annotations?`, `serverName?` | Tool execution completed. Same `annotations` / `serverName` semantics as `tool-call`. |
191
+ | `step-start` | `stepIndex` | New LLM invocation step began. |
192
+ | `step-finish` | `stepIndex`, `finishReason`, `usage?` | Step completed with per-step token usage. |
193
+ | `error` | `error`, `code?` | Mid-stream error (yielded, not thrown). |
194
+ | `finish` | `finishReason`, `usage?` | Stream completed with aggregate token usage. |
195
+ | `unmapped-chunk` | `chunkType`, `rawChunk` | Unrecognized harness event, preserved for observability. |
196
196
 
197
197
  ### Configuration Types
198
198
 
@@ -302,6 +302,10 @@ type ToolDefinition = {
302
302
  name: string;
303
303
  description?: string;
304
304
  inputSchema: Record<string, unknown>;
305
+ // Optional MCP-spec UI/behavioral hints. Consumer-declared tools can carry
306
+ // the same hints as MCP-discovered tools and receive matching UI treatment.
307
+ // Example: `{ name: 'read_doc', annotations: { readOnlyHint: true } }`.
308
+ annotations?: McpToolAnnotations;
305
309
  };
306
310
 
307
311
  type ToolCallInfo = {
@@ -417,6 +421,11 @@ for (const s of servers) {
417
421
 
418
422
  ### Tool Approval Flow
419
423
 
424
+ `tool-call`, `tool-result`, and `tool-approval-request` events all carry optional `annotations` and `serverName` so
425
+ consumers can branch on tool hints (e.g. auto-approve `readOnlyHint`) or group by originating MCP server without
426
+ reparsing namespaced tool names. `annotations` is `undefined` when the source did not declare them (per the MCP spec);
427
+ `serverName` is `undefined` when the tool is not from an MCP server.
428
+
420
429
  ```typescript
421
430
  const { eventStream } = await session.chat('Run the deployment', {
422
431
  requireToolApproval: true,
@@ -424,9 +433,21 @@ const { eventStream } = await session.chat('Run the deployment', {
424
433
 
425
434
  for await (const event of eventStream) {
426
435
  if (event.type === 'tool-approval-request') {
427
- const continuation = await session.approveToolCall(event.toolCall.toolCallId);
428
- for await (const e of continuation.eventStream) {
429
- // process continuation
436
+ if (event.annotations?.readOnlyHint) {
437
+ // Safe read auto-approve.
438
+ const continuation = await session.approveToolCall(event.toolCall.toolCallId);
439
+ for await (const e of continuation.eventStream) {
440
+ // process continuation
441
+ }
442
+ } else {
443
+ // Route to the user; group/label by event.serverName when set.
444
+ const approved = await promptUser(event);
445
+ const continuation = approved
446
+ ? await session.approveToolCall(event.toolCall.toolCallId)
447
+ : await session.declineToolCall(event.toolCall.toolCallId);
448
+ for await (const e of continuation.eventStream) {
449
+ // process continuation
450
+ }
430
451
  }
431
452
  }
432
453
  }
@@ -670,13 +691,17 @@ All variants share `{ type: <discriminant>, timestamp: Date }` plus the fields b
670
691
  | `chat-stream-started` | `agentId`, `threadId`, `trigger` (`'chat' \| 'submit-tool-result' \| 'approve-tool-call' \| 'decline-tool-call'`) |
671
692
  | `chat-stream-completed` | `agentId`, `threadId`, `durationMs`, `usage?` |
672
693
  | `chat-stream-error` | `agentId`, `threadId`, `durationMs`, `error` |
673
- | `tool-execution-started` | `agentId`, `threadId`, `toolCallId`, `toolName` |
674
- | `tool-execution-completed` | `agentId`, `threadId`, `toolCallId`, `toolName`, `durationMs`, `isError` |
675
- | `tool-approval-requested` | `agentId`, `threadId`, `toolCallId`, `toolName` |
694
+ | `tool-execution-started` | `agentId`, `threadId`, `toolCallId`, `toolName`, `annotations?`, `serverName?` |
695
+ | `tool-execution-completed` | `agentId`, `threadId`, `toolCallId`, `toolName`, `durationMs`, `isError`, `annotations?`, `serverName?` |
696
+ | `tool-approval-requested` | `agentId`, `threadId`, `toolCallId`, `toolName`, `annotations?`, `serverName?` |
676
697
  | `tool-approval-resolved` | `agentId`, `threadId`, `toolCallId`, `approved` |
677
698
  | `mcp-server-discovery-started` | `agentId`, `serverName` |
678
699
  | `mcp-server-discovery-completed` | `agentId`, `serverName`, `toolCount`, `durationMs` |
679
700
  | `mcp-server-discovery-failed` | `agentId`, `serverName`, `durationMs`, `error` |
701
+ | `mcp-discovery-snapshot` | `agentId`, `servers` (`McpServerInfo[]`) |
702
+
703
+ `mcp-discovery-snapshot` is emitted by harnesses whose runtime exposes aggregate MCP server status (currently only the
704
+ Claude harness, once per `init` arrival). Its `servers` payload mirrors `agent.getMcpServerInfo()`.
680
705
 
681
706
  Every chat entry point (`chat`, `submitToolResult`, `approveToolCall`, `declineToolCall`) emits exactly one
682
707
  `chat-stream-started` followed by exactly one terminal event — either `chat-stream-completed` (natural completion) or
@@ -157,7 +157,7 @@ export class DefaultAgentManager {
157
157
  const runtime = await this.agentConnectivityResolver.resolve(projectRoot, config);
158
158
  await this.harness.createAgent(agentId, projectRoot, runtime.llmGatewayClient, toHarnessConfig(config, runtime.orgJwt), options.abortSignal !== undefined ? { abortSignal: options.abortSignal } : undefined);
159
159
  const agentSlice = this.router.registerAgent(agentId);
160
- const agent = new DefaultAgent(this.harness, agentId, projectRoot, config, runtime.llmGatewayClient, runtime.orgConnection, runtime.orgJwt, this.agentConnectivityResolver, this.router, agentSlice, { telemetry: this.telemetryBus, log: this.logBus }, this.clock, this.agentIdGenerator);
160
+ const agent = new DefaultAgent(this.harness, agentId, projectRoot, config, runtime.llmGatewayClient, runtime.orgConnection, runtime.orgJwt, this.agentConnectivityResolver, this.identityStore, this.router, agentSlice, { telemetry: this.telemetryBus, log: this.logBus }, this.clock, this.agentIdGenerator);
161
161
  this.agents.set(agentId, agent);
162
162
  this.telemetryBus.emit({
163
163
  type: 'agent-created',
package/dist/agent.d.ts CHANGED
@@ -5,6 +5,7 @@ import { type ChatSession } from './chat-session.js';
5
5
  import type { McpServerInfo } from './mcp-config.js';
6
6
  import { type JSONWebToken, type LLMGatewayClient } from '@salesforce/llm-gateway-sdk';
7
7
  import type { AgentConnectivityResolver } from './agent-connectivity-resolver.js';
8
+ import type { AgentIdentityStore } from './internal/agent-identity-store.js';
8
9
  import type { TelemetryRouter, TelemetrySlice } from './internal/telemetry-router.js';
9
10
  import type { TelemetryBus, TelemetryEventCallback } from './types/telemetry-events.js';
10
11
  /**
@@ -110,6 +111,7 @@ export declare class DefaultAgent implements Agent {
110
111
  private orgConnection;
111
112
  private orgJwt;
112
113
  private readonly agentConnectivityResolver;
114
+ private readonly identityStore;
113
115
  private readonly sessions;
114
116
  private readonly sessionSliceUnregisters;
115
117
  private readonly router;
@@ -129,11 +131,13 @@ export declare class DefaultAgent implements Agent {
129
131
  * @param orgConnection - Authenticated org connection carrying identity and env inference.
130
132
  * @param orgJwt - Self-refreshing JWT for the resolved org (used for MCP auth injection).
131
133
  * @param agentConnectivityResolver - Used to re-resolve org connectivity when the org or model changes.
134
+ * @param identityStore - SDK-owned persistence for the `{ agentId, projectRoot, AgentConfig }` triple. The agent
135
+ * calls `write()` on a successful `updateAgentConfig` so disk state and in-memory state stay in lockstep.
132
136
  * @param router - Telemetry router used to obtain session slices when sessions are created.
133
137
  * @param inbound - Router slice delivering harness events routed to this agent (non-session-scoped).
134
138
  * @param parent - Manager's bus pair; this agent forwards its events upward into them.
135
139
  */
136
- constructor(harness: AgentHarness, agentId: string, projectRoot: string, config: AgentConfig, llmGatewayClient: LLMGatewayClient, orgConnection: OrgConnection, orgJwt: JSONWebToken, agentConnectivityResolver: AgentConnectivityResolver, router: TelemetryRouter, inbound: TelemetrySlice, parent: AgentParentBuses, clock?: Clock, idGenerator?: UniqueIDGenerator);
140
+ constructor(harness: AgentHarness, agentId: string, projectRoot: string, config: AgentConfig, llmGatewayClient: LLMGatewayClient, orgConnection: OrgConnection, orgJwt: JSONWebToken, agentConnectivityResolver: AgentConnectivityResolver, identityStore: AgentIdentityStore, router: TelemetryRouter, inbound: TelemetrySlice, parent: AgentParentBuses, clock?: Clock, idGenerator?: UniqueIDGenerator);
137
141
  /**
138
142
  * @requirements
139
143
  * - MUST return the agent's ID.
@@ -154,7 +158,9 @@ export declare class DefaultAgent implements Agent {
154
158
  * - MUST guarantee that the `agentId` remains unchanged during the merge.
155
159
  * - MUST destroy the existing agent in the harness by delegating to `this.harness.destroyAgent(this.getId())`.
156
160
  * - MUST recreate the agent in the harness with the newly merged configuration by delegating to `this.harness.createAgent(...)`.
157
- * - MUST preserve the previous in-memory config state if recreation fails.
161
+ * - MUST persist the merged config via `this.identityStore.write(...)` after the harness recreate succeeds and
162
+ * before the in-memory swaps, so a write failure rolls back through the same catch path as a recreate failure.
163
+ * - MUST preserve the previous in-memory config state if recreation or persistence fails.
158
164
  */
159
165
  updateAgentConfig(config?: AgentConfig, options?: {
160
166
  abortSignal?: AbortSignal;
package/dist/agent.js CHANGED
@@ -20,6 +20,7 @@ export class DefaultAgent {
20
20
  orgConnection;
21
21
  orgJwt;
22
22
  agentConnectivityResolver;
23
+ identityStore;
23
24
  sessions = new Map();
24
25
  sessionSliceUnregisters = new Map();
25
26
  router;
@@ -39,11 +40,13 @@ export class DefaultAgent {
39
40
  * @param orgConnection - Authenticated org connection carrying identity and env inference.
40
41
  * @param orgJwt - Self-refreshing JWT for the resolved org (used for MCP auth injection).
41
42
  * @param agentConnectivityResolver - Used to re-resolve org connectivity when the org or model changes.
43
+ * @param identityStore - SDK-owned persistence for the `{ agentId, projectRoot, AgentConfig }` triple. The agent
44
+ * calls `write()` on a successful `updateAgentConfig` so disk state and in-memory state stay in lockstep.
42
45
  * @param router - Telemetry router used to obtain session slices when sessions are created.
43
46
  * @param inbound - Router slice delivering harness events routed to this agent (non-session-scoped).
44
47
  * @param parent - Manager's bus pair; this agent forwards its events upward into them.
45
48
  */
46
- constructor(harness, agentId, projectRoot, config, llmGatewayClient, orgConnection, orgJwt, agentConnectivityResolver, router, inbound, parent, clock = new RealClock(), idGenerator = new UUIDGenerator()) {
49
+ constructor(harness, agentId, projectRoot, config, llmGatewayClient, orgConnection, orgJwt, agentConnectivityResolver, identityStore, router, inbound, parent, clock = new RealClock(), idGenerator = new UUIDGenerator()) {
47
50
  this.harness = harness;
48
51
  this.agentId = agentId;
49
52
  this.projectRoot = projectRoot;
@@ -52,6 +55,7 @@ export class DefaultAgent {
52
55
  this.orgConnection = orgConnection;
53
56
  this.orgJwt = orgJwt;
54
57
  this.agentConnectivityResolver = agentConnectivityResolver;
58
+ this.identityStore = identityStore;
55
59
  this.router = router;
56
60
  this.clock = clock;
57
61
  this.idGenerator = idGenerator;
@@ -93,7 +97,9 @@ export class DefaultAgent {
93
97
  * - MUST guarantee that the `agentId` remains unchanged during the merge.
94
98
  * - MUST destroy the existing agent in the harness by delegating to `this.harness.destroyAgent(this.getId())`.
95
99
  * - MUST recreate the agent in the harness with the newly merged configuration by delegating to `this.harness.createAgent(...)`.
96
- * - MUST preserve the previous in-memory config state if recreation fails.
100
+ * - MUST persist the merged config via `this.identityStore.write(...)` after the harness recreate succeeds and
101
+ * before the in-memory swaps, so a write failure rolls back through the same catch path as a recreate failure.
102
+ * - MUST preserve the previous in-memory config state if recreation or persistence fails.
97
103
  */
98
104
  async updateAgentConfig(config = {}, options) {
99
105
  this.assertNotDisposed();
@@ -121,6 +127,10 @@ export class DefaultAgent {
121
127
  await this.harness.destroyAgent(this.agentId);
122
128
  try {
123
129
  await this.harness.createAgent(this.agentId, this.projectRoot, nextClient, toHarnessConfig(nextConfig, nextOrgJwt), options);
130
+ // Persist before the in-memory swaps so a write failure flows through the same
131
+ // catch block as a recreate failure: the rollback restores the harness with
132
+ // previousConfig and disk state remains the pre-update record.
133
+ await this.identityStore.write(this.agentId, this.projectRoot, nextConfig);
124
134
  this.config = nextConfig;
125
135
  this.llmGatewayClient = nextClient;
126
136
  this.orgConnection = nextConnection;
@@ -138,6 +148,12 @@ export class DefaultAgent {
138
148
  if (nextClient === previousClient) {
139
149
  previousClient.setModel(Models.getByName(previousModelName));
140
150
  }
151
+ // Clear any nextConfig registration left behind by a successful harness recreate
152
+ // before the rollback createAgent runs. On the harness-recreate-failure path this
153
+ // is a no-op (the agent was never registered with nextConfig); on the
154
+ // identityStore.write-failure path it removes the live nextConfig so the rollback
155
+ // doesn't trip the harness's duplicate-registration guard.
156
+ await this.harness.destroyAgent(this.agentId);
141
157
  await this.harness.createAgent(this.agentId, this.projectRoot, previousClient, toHarnessConfig(previousConfig, previousOrgJwt));
142
158
  }
143
159
  catch {
@@ -326,6 +326,8 @@ export class DefaultChatSession {
326
326
  threadId: this.threadId,
327
327
  toolCallId: event.toolCallId,
328
328
  toolName: event.toolName,
329
+ ...(event.annotations ? { annotations: event.annotations } : {}),
330
+ ...(event.serverName ? { serverName: event.serverName } : {}),
329
331
  });
330
332
  }
331
333
  else if (event.type === 'tool-result') {
@@ -342,6 +344,8 @@ export class DefaultChatSession {
342
344
  toolName: event.toolName,
343
345
  durationMs: this.clock.now().getTime() - start,
344
346
  isError: event.isError === true,
347
+ ...(event.annotations ? { annotations: event.annotations } : {}),
348
+ ...(event.serverName ? { serverName: event.serverName } : {}),
345
349
  });
346
350
  }
347
351
  else if (event.type === 'tool-approval-request') {
@@ -352,6 +356,8 @@ export class DefaultChatSession {
352
356
  threadId: this.threadId,
353
357
  toolCallId: event.toolCall.toolCallId,
354
358
  toolName: event.toolCall.toolName,
359
+ ...(event.annotations ? { annotations: event.annotations } : {}),
360
+ ...(event.serverName ? { serverName: event.serverName } : {}),
355
361
  });
356
362
  }
357
363
  }
package/dist/index.d.ts CHANGED
@@ -16,7 +16,7 @@ export type { AgentHarness, HarnessFactory, WithAgentConfig, ConfigOf } from './
16
16
  export { SUPPORTED_PROTOCOL_VERSIONS } from './harness/agent-harness.js';
17
17
  export { HarnessBusOwner } from './harness/harness-bus-owner.js';
18
18
  export { AgentSDKError, AgentSDKErrorType } from './errors.js';
19
- export type { AgentCreatedEvent, AgentDestroyedEvent, ChatStreamCompletedEvent, ChatStreamErrorEvent, ChatStreamStartedEvent, ChatStreamTrigger, McpServerDiscoveryCompletedEvent, McpServerDiscoveryFailedEvent, McpServerDiscoveryStartedEvent, SessionCreatedEvent, SessionDestroyedEvent, TelemetryEvent, TelemetryEventCallback, ToolApprovalRequestedEvent, ToolApprovalResolvedEvent, ToolExecutionCompletedEvent, ToolExecutionStartedEvent, } from './types/telemetry-events.js';
19
+ export type { AgentCreatedEvent, AgentDestroyedEvent, ChatStreamCompletedEvent, ChatStreamErrorEvent, ChatStreamStartedEvent, ChatStreamTrigger, McpDiscoverySnapshotEvent, McpServerDiscoveryCompletedEvent, McpServerDiscoveryFailedEvent, McpServerDiscoveryStartedEvent, SessionCreatedEvent, SessionDestroyedEvent, TelemetryEvent, TelemetryEventCallback, ToolApprovalRequestedEvent, ToolApprovalResolvedEvent, ToolExecutionCompletedEvent, ToolExecutionStartedEvent, } from './types/telemetry-events.js';
20
20
  export type { LogLevel, LogRecord, Unsubscribe } from '@salesforce/agentic-common';
21
21
  export { resolveMcpServerHeaders } from './mcp-auth.js';
22
22
  export type { OrgConnection, OrgConnectionFactory } from '@salesforce/agentic-common';
@@ -1,3 +1,4 @@
1
+ import type { McpToolAnnotations } from '../mcp-config.js';
1
2
  import type { ToolCallInfo, ToolResultInfo } from './tools.js';
2
3
  import type { FinishReason, UsageMetadata } from './usage.js';
3
4
  /**
@@ -47,9 +48,25 @@ export type ReasoningDeltaEvent = {
47
48
  *
48
49
  * Extends {@link ToolCallInfo} with a discriminator type for use in the
49
50
  * {@link ChatEvent} discriminated union.
51
+ *
52
+ * `annotations` and `serverName` are populated by the harness when the tool is
53
+ * in an MCP server's catalog, mirroring the enrichment on
54
+ * {@link ToolApprovalRequestEvent}. They are stream-event metadata — not part
55
+ * of {@link ToolCallInfo} — so persisted message history (`MessagePart`s) does
56
+ * not carry per-stream MCP enrichment.
50
57
  */
51
58
  export type ToolCallEvent = ToolCallInfo & {
52
59
  type: 'tool-call';
60
+ /**
61
+ * Behavioral / UI-presentation hints declared for the tool. See
62
+ * {@link ToolApprovalRequestEvent.annotations}.
63
+ */
64
+ annotations?: McpToolAnnotations;
65
+ /**
66
+ * Originating MCP server name when the tool was discovered through an MCP
67
+ * server. `undefined` for consumer-declared tools or workspace tools.
68
+ */
69
+ serverName?: string;
53
70
  };
54
71
  /**
55
72
  * The harness is requesting approval before executing a tool call.
@@ -65,15 +82,42 @@ export type ToolApprovalRequestEvent = {
65
82
  type: 'tool-approval-request';
66
83
  /** The tool call awaiting approval. */
67
84
  toolCall: ToolCallInfo;
85
+ /**
86
+ * Behavioral / UI-presentation hints declared for the tool. Populated by
87
+ * the harness when the tool is in an MCP server's tool catalog and the
88
+ * server declared annotations; `undefined` when the source did not declare
89
+ * them (per the MCP spec) or the tool is not from an MCP server.
90
+ */
91
+ annotations?: McpToolAnnotations;
92
+ /**
93
+ * Originating MCP server name when the tool was discovered through an MCP
94
+ * server. `undefined` for consumer-declared tools or workspace tools that
95
+ * have no MCP origin.
96
+ */
97
+ serverName?: string;
68
98
  };
69
99
  /**
70
100
  * A real-time stream event indicating a tool execution has completed.
71
101
  *
72
102
  * Extends {@link ToolResultInfo} with a discriminator type for use in the
73
103
  * {@link ChatEvent} discriminated union.
104
+ *
105
+ * `annotations` and `serverName` are populated by the harness when the tool is
106
+ * in an MCP server's catalog, mirroring the enrichment on
107
+ * {@link ToolApprovalRequestEvent} and {@link ToolCallEvent}.
74
108
  */
75
109
  export type ToolResultEvent = ToolResultInfo & {
76
110
  type: 'tool-result';
111
+ /**
112
+ * Behavioral / UI-presentation hints declared for the tool. See
113
+ * {@link ToolApprovalRequestEvent.annotations}.
114
+ */
115
+ annotations?: McpToolAnnotations;
116
+ /**
117
+ * Originating MCP server name when the tool was discovered through an MCP
118
+ * server. `undefined` for consumer-declared tools or workspace tools.
119
+ */
120
+ serverName?: string;
77
121
  };
78
122
  /**
79
123
  * Marks the beginning of a new LLM invocation within a multi-step agentic loop.
@@ -1,4 +1,5 @@
1
1
  import type { EventBus } from '@salesforce/agentic-common';
2
+ import type { McpServerInfo, McpToolAnnotations } from '../mcp-config.js';
2
3
  import type { UsageMetadata } from './usage.js';
3
4
  /**
4
5
  * Telemetry events emitted by the Agent SDK.
@@ -50,6 +51,10 @@ export type ToolExecutionStartedEvent = Base<'tool-execution-started'> & {
50
51
  threadId: string;
51
52
  toolCallId: string;
52
53
  toolName: string;
54
+ /** Annotations declared for the tool, when available. See {@link ToolApprovalRequestEvent.annotations}. */
55
+ annotations?: McpToolAnnotations;
56
+ /** Originating MCP server name, when the tool was discovered through MCP. */
57
+ serverName?: string;
53
58
  };
54
59
  export type ToolExecutionCompletedEvent = Base<'tool-execution-completed'> & {
55
60
  agentId: string;
@@ -58,12 +63,20 @@ export type ToolExecutionCompletedEvent = Base<'tool-execution-completed'> & {
58
63
  toolName: string;
59
64
  durationMs: number;
60
65
  isError: boolean;
66
+ /** Annotations declared for the tool, when available. See {@link ToolApprovalRequestEvent.annotations}. */
67
+ annotations?: McpToolAnnotations;
68
+ /** Originating MCP server name, when the tool was discovered through MCP. */
69
+ serverName?: string;
61
70
  };
62
71
  export type ToolApprovalRequestedEvent = Base<'tool-approval-requested'> & {
63
72
  agentId: string;
64
73
  threadId: string;
65
74
  toolCallId: string;
66
75
  toolName: string;
76
+ /** Annotations declared for the tool, when available. See {@link ToolApprovalRequestEvent.annotations}. */
77
+ annotations?: McpToolAnnotations;
78
+ /** Originating MCP server name, when the tool was discovered through MCP. */
79
+ serverName?: string;
67
80
  };
68
81
  export type ToolApprovalResolvedEvent = Base<'tool-approval-resolved'> & {
69
82
  agentId: string;
@@ -87,7 +100,21 @@ export type McpServerDiscoveryFailedEvent = Base<'mcp-server-discovery-failed'>
87
100
  durationMs: number;
88
101
  error: Error;
89
102
  };
90
- export type TelemetryEvent = AgentCreatedEvent | AgentDestroyedEvent | SessionCreatedEvent | SessionDestroyedEvent | ChatStreamStartedEvent | ChatStreamCompletedEvent | ChatStreamErrorEvent | ToolExecutionStartedEvent | ToolExecutionCompletedEvent | ToolApprovalRequestedEvent | ToolApprovalResolvedEvent | McpServerDiscoveryStartedEvent | McpServerDiscoveryCompletedEvent | McpServerDiscoveryFailedEvent;
103
+ /**
104
+ * Whole-snapshot MCP discovery event emitted by harnesses whose underlying SDK
105
+ * surfaces MCP status in a single aggregate message rather than as per-server
106
+ * lifecycle events. Currently emitted by the Claude harness on each
107
+ * `SDKSystemMessage init` arrival.
108
+ *
109
+ * The `servers` payload mirrors the result of `harness.getMcpServerInfo(agentId)`
110
+ * at the moment of the snapshot, so consumers can read status + tools without
111
+ * a follow-up call.
112
+ */
113
+ export type McpDiscoverySnapshotEvent = Base<'mcp-discovery-snapshot'> & {
114
+ agentId: string;
115
+ servers: McpServerInfo[];
116
+ };
117
+ export type TelemetryEvent = AgentCreatedEvent | AgentDestroyedEvent | SessionCreatedEvent | SessionDestroyedEvent | ChatStreamStartedEvent | ChatStreamCompletedEvent | ChatStreamErrorEvent | ToolExecutionStartedEvent | ToolExecutionCompletedEvent | ToolApprovalRequestedEvent | ToolApprovalResolvedEvent | McpServerDiscoveryStartedEvent | McpServerDiscoveryCompletedEvent | McpServerDiscoveryFailedEvent | McpDiscoverySnapshotEvent;
91
118
  export type TelemetryEventCallback = (event: TelemetryEvent) => void;
92
119
  export type TelemetryBus = EventBus<TelemetryEvent>;
93
120
  export {};
@@ -1,3 +1,4 @@
1
+ import type { McpToolAnnotations } from '../mcp-config.js';
1
2
  /**
2
3
  * Declares a consumer-executed tool that an agent can invoke.
3
4
  *
@@ -24,6 +25,15 @@ export type ToolDefinition = {
24
25
  * Harnesses use this to validate arguments before execution.
25
26
  */
26
27
  inputSchema: Record<string, unknown>;
28
+ /**
29
+ * Behavioral / UI-presentation hints for this tool. Mirrors the MCP
30
+ * protocol's `Tool.annotations` shape so consumer-declared tools can carry
31
+ * the same hints (`readOnlyHint`, `destructiveHint`, `idempotentHint`,
32
+ * `openWorldHint`, `title`) as MCP-discovered tools and receive matching
33
+ * UI treatment. Absence of a field means "the author did not declare this
34
+ * hint," not "false."
35
+ */
36
+ annotations?: McpToolAnnotations;
27
37
  };
28
38
  /**
29
39
  * Base shape for a tool call.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/sfdx-agent-sdk",
3
- "version": "0.6.0",
3
+ "version": "0.8.0",
4
4
  "description": "Harness-agnostic agentic infrastructure for Salesforce developer experience tooling",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -36,11 +36,12 @@
36
36
  ],
37
37
  "dependencies": {
38
38
  "@salesforce/agentic-common": "0.4.0",
39
- "@salesforce/llm-gateway-sdk": "0.4.0"
39
+ "@salesforce/llm-gateway-sdk": "0.5.0"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@eslint/js": "^10.0.1",
43
- "@salesforce/sfdx-agent-harness-mastra": "0.6.0",
43
+ "@salesforce/sfdx-agent-harness-claude": "0.4.0",
44
+ "@salesforce/sfdx-agent-harness-mastra": "0.7.0",
44
45
  "@types/node": "^22.19.17",
45
46
  "@vitest/coverage-istanbul": "^4.1.7",
46
47
  "@vitest/eslint-plugin": "^1.6.17",