@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.
Files changed (59) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +336 -64
  3. package/dist/agent-connectivity-resolver.d.ts +47 -25
  4. package/dist/agent-connectivity-resolver.js +92 -15
  5. package/dist/agent-manager.d.ts +17 -1
  6. package/dist/agent-manager.js +40 -7
  7. package/dist/agent.d.ts +33 -10
  8. package/dist/agent.js +38 -50
  9. package/dist/api-key-connectivity-resolver.d.ts +110 -0
  10. package/dist/api-key-connectivity-resolver.js +114 -0
  11. package/dist/errors.d.ts +1 -0
  12. package/dist/errors.js +1 -0
  13. package/dist/harness/agent-harness.d.ts +27 -5
  14. package/dist/harness/harness-bus-owner.d.ts +17 -0
  15. package/dist/harness/harness-bus-owner.js +27 -0
  16. package/dist/harness/harness-config.d.ts +3 -2
  17. package/dist/harness/harness-factory.d.ts +13 -0
  18. package/dist/harness/stream-input.d.ts +9 -5
  19. package/dist/harness/stream-input.js +12 -14
  20. package/dist/index.d.ts +8 -2
  21. package/dist/index.js +4 -1
  22. package/dist/internal/wire-communication-router.d.ts +43 -0
  23. package/dist/internal/wire-communication-router.js +119 -0
  24. package/dist/mcp-auth.d.ts +1 -1
  25. package/dist/models/claude-opus-4-5.d.ts +11 -0
  26. package/dist/models/claude-opus-4-5.js +21 -0
  27. package/dist/models/claude-opus-4-6.d.ts +11 -0
  28. package/dist/models/claude-opus-4-6.js +22 -0
  29. package/dist/models/claude-opus-4-7.d.ts +11 -0
  30. package/dist/models/claude-opus-4-7.js +22 -0
  31. package/dist/models/claude-sonnet-4-5.d.ts +11 -0
  32. package/dist/models/claude-sonnet-4-5.js +23 -0
  33. package/dist/models/claude-sonnet-4-6.d.ts +11 -0
  34. package/dist/models/claude-sonnet-4-6.js +22 -0
  35. package/dist/models/create-claude-model.d.ts +54 -0
  36. package/dist/models/create-claude-model.js +62 -0
  37. package/dist/models/gpt-5-4.d.ts +11 -0
  38. package/dist/models/gpt-5-4.js +21 -0
  39. package/dist/models/gpt-5-5.d.ts +15 -0
  40. package/dist/models/gpt-5-5.js +24 -0
  41. package/dist/models/gpt-5.d.ts +11 -0
  42. package/dist/models/gpt-5.js +23 -0
  43. package/dist/models/index.d.ts +19 -0
  44. package/dist/models/index.js +49 -0
  45. package/dist/models/model.d.ts +69 -0
  46. package/dist/models/model.js +63 -0
  47. package/dist/models/multimodal.d.ts +35 -0
  48. package/dist/models/multimodal.js +78 -0
  49. package/dist/models/types.d.ts +49 -0
  50. package/dist/models/types.js +18 -0
  51. package/dist/types/index.d.ts +1 -0
  52. package/dist/types/model-connectivity-info.d.ts +87 -0
  53. package/dist/types/model-connectivity-info.js +6 -0
  54. package/dist/types/usage.d.ts +3 -3
  55. package/dist/types/wire-communication-event.d.ts +124 -0
  56. package/dist/types/wire-communication-event.js +6 -0
  57. package/dist/wire-communication-file-writer.d.ts +39 -0
  58. package/dist/wire-communication-file-writer.js +142 -0
  59. package/package.json +7 -8
package/CHANGELOG.md CHANGED
@@ -3,6 +3,24 @@
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.23.0] - 2026-06-22
7
+
8
+ ### Features
9
+ - **agentic-common,harnesses**: route HTTPS_PROXY via injectable undici Dispatcher @W-23121596 ([#610](https://github.com/forcedotcom/agentic-dx/pull/610))
10
+
11
+ ### Docs
12
+ - align SDK + agentic-common docs with current public surface (post-PR-#607) ([#608](https://github.com/forcedotcom/agentic-dx/pull/608))
13
+
14
+ ## [0.22.0] - 2026-06-19
15
+
16
+ ### Features
17
+ - **BREAKING** Major refactor to Unify Connectivity via ModelConnectivityInfo @W-22782317 ([#607](https://github.com/forcedotcom/agentic-dx/pull/607))
18
+
19
+ ### Chores
20
+ - **deps-dev**: bump @vitest/eslint-plugin from 1.6.19 to 1.6.20 in the vitest group across 1 directory ([#601](https://github.com/forcedotcom/agentic-dx/pull/601))
21
+ - **deps-dev**: bump eslint from 10.4.1 to 10.5.0 in the eslint group across 1 directory ([#600](https://github.com/forcedotcom/agentic-dx/pull/600))
22
+ - **deps-dev**: bump @types/node from 22.19.20 to 22.19.21 in the dev-dependencies group ([#599](https://github.com/forcedotcom/agentic-dx/pull/599))
23
+
6
24
  ## [0.21.0] - 2026-06-15
7
25
 
8
26
  ### Fixes
package/README.md CHANGED
@@ -102,17 +102,18 @@ through a typed `manager.extensions` slot. `createAgentManager` infers `H` from
102
102
  explicitly. The `createAgent` config parameter narrows automatically when the harness brands itself with
103
103
  `WithAgentConfig` — see "Harness Extensibility" below.
104
104
 
105
- | Property / Method | Signature | Description |
106
- | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
107
- | `extensions` | `H['extensions']` | Harness-specific extensions namespace (read-only). Re-exposes the harness's `extensions` slot typed off `H`. Per-agent accessors take the agent id as their first argument. The SDK never reads or interprets this — see "Harness Extensibility" below. |
108
- | `createAgent` | `(projectRoot: string, config?: ConfigOf<H> & { agentId?: string }, options?: { abortSignal?: AbortSignal }) => Promise<Agent<H>>` | Create and register a new agent and persist its identity triple. `projectRoot` must be an existing directory. If `agentId` is omitted a UUID is generated. The config type is inferred from the harness — see `ConfigOf<H>`. |
109
- | `getAgent` | `(agentId: string) => Agent<H>` | Retrieve a live agent by ID. Throws `AgentSDKError` (`AGENT_NOT_FOUND`) for unknown ids and for ids that are only present in `getRestoreFailures()`. |
110
- | `getAgentIds` | `() => string[]` | List all live agent IDs (successful + successfully restored). Failed-restore agents are not included — query `getRestoreFailures()` separately. |
111
- | `destroyAgent` | `(agentId: string) => Promise<void>` | Destroy an agent, remove its identity record from disk, and clear any matching `getRestoreFailures()` entry. Failed-restore-only ids are accepted (no harness call made). |
112
- | `shutdown` | `() => Promise<void>` | Destroy all live agents and shut down the harness. Identity files survive (that's the whole point) — restart `createAgentManager` over the same root to bring them back. |
113
- | `onTelemetry` | `(callback: TelemetryEventCallback) => Unsubscribe` | Subscribe to telemetry across all managed agents. |
114
- | `onLog` | `(callback: (record: LogRecord) => void) => Unsubscribe` | Subscribe to structured logs across all managed agents. Bridge this into your host logger to observe restore-failure events + soft-skip warnings. |
115
- | `getRestoreFailures` | `() => RestoreFailure[]` | Snapshot of agents the SDK could not restore on this boot. Each entry carries the persisted `{ agentId, projectRoot, config }` plus the underlying error. |
105
+ | Property / Method | Signature | Description |
106
+ | --------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
107
+ | `extensions` | `H['extensions']` | Harness-specific extensions namespace (read-only). Re-exposes the harness's `extensions` slot typed off `H`. Per-agent accessors take the agent id as their first argument. The SDK never reads or interprets this — see "Harness Extensibility" below. |
108
+ | `createAgent` | `(projectRoot: string, config?: ConfigOf<H> & { agentId?: string }, options?: { abortSignal?: AbortSignal }) => Promise<Agent<H>>` | Create and register a new agent and persist its identity triple. `projectRoot` must be an existing directory. If `agentId` is omitted a UUID is generated. The config type is inferred from the harness — see `ConfigOf<H>`. |
109
+ | `getAgent` | `(agentId: string) => Agent<H>` | Retrieve a live agent by ID. Throws `AgentSDKError` (`AGENT_NOT_FOUND`) for unknown ids and for ids that are only present in `getRestoreFailures()`. |
110
+ | `getAgentIds` | `() => string[]` | List all live agent IDs (successful + successfully restored). Failed-restore agents are not included — query `getRestoreFailures()` separately. |
111
+ | `destroyAgent` | `(agentId: string) => Promise<void>` | Destroy an agent, remove its identity record from disk, and clear any matching `getRestoreFailures()` entry. Failed-restore-only ids are accepted (no harness call made). |
112
+ | `shutdown` | `() => Promise<void>` | Destroy all live agents and shut down the harness. Identity files survive (that's the whole point) — restart `createAgentManager` over the same root to bring them back. |
113
+ | `onTelemetry` | `(callback: TelemetryEventCallback) => Unsubscribe` | Subscribe to telemetry across all managed agents. |
114
+ | `onLog` | `(callback: (record: LogRecord) => void) => Unsubscribe` | Subscribe to structured logs across all managed agents. Bridge this into your host logger to observe restore-failure events + soft-skip warnings. |
115
+ | `onWireCommunication` | `(callback: WireCommunicationEventCallback) => Unsubscribe` | Subscribe to wire-level communication events from the harness. Opt-in diagnostic channel that surfaces outbound LLM requests, responses, and harness-specific monitoring metadata. Subscriber-gated end-to-end harnesses pay no cost when nobody listens. See "Wire-Communication Events" below for the event-shape catalog and the harness-asymmetric coverage (Mastra emits per-call request/response pairs; Claude emits a per-stream pointer to a debug log file). |
116
+ | `getRestoreFailures` | `() => RestoreFailure[]` | Snapshot of agents the SDK could not restore on this boot. Each entry carries the persisted `{ agentId, projectRoot, config }` plus the underlying error. |
116
117
 
117
118
  #### `RestoreFailure`
118
119
 
@@ -135,24 +136,24 @@ A configured AI agent. Factory for chat sessions. The optional `H` type paramete
135
136
  — harness-specific features are reached through `manager.extensions`, not `agent.extensions`. The default `AgentHarness`
136
137
  keeps unparameterized call sites working.
137
138
 
138
- | Method | Signature | Description |
139
- | ---------------------- | ---------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
140
- | `getId` | `() => string` | Agent identifier. |
141
- | `getProjectRoot` | `() => string` | Absolute project path. |
142
- | `getOrgConnectionInfo` | `() => OrgConnectionInfo` | Org metadata (orgId, alias, instanceUrl, username, env). |
143
- | `getAgentConfig` | `() => AgentConfig` | Current configuration (shallow copy). |
144
- | `getMcpServerInfo` | `() => McpServerInfo[]` | MCP server status and discovered tools. |
145
- | `reconnectMcpServer` | `(serverName: string) => Promise<void>` | Recover one MCP server without recycling the agent. Semantics vary by harness — observe via `getMcpServerInfo()` and discovery telemetry. |
146
- | `updateAgentConfig` | `(config?: AgentConfig, options?: { abortSignal?: AbortSignal }) => Promise<void>` | Merge new config into the live agent. |
147
- | `createChatSession` | `() => Promise<ChatSession>` | Open a new conversation thread. |
148
- | `getChatSession` | `(sessionId: string) => ChatSession` | Retrieve a session. Throws `AgentSDKError` (`CHAT_SESSION_NOT_FOUND`). |
149
- | `getChatSessionIds` | `() => string[]` | List active session IDs. |
150
- | `destroyChatSession` | `(sessionId: string) => Promise<void>` | Destroy a session and its history. |
151
- | `cloneChatSession` | `(sourceSessionId: string) => Promise<ChatSession>` | Clone a session with its message history. |
152
- | `compactChatSession` | `(sessionId: string) => Promise<ChatSession>` | Compact a session into a summarized new session. |
153
- | `destroy` | `() => Promise<void>` | Destroy the agent and all its sessions. |
154
- | `onTelemetry` | `(callback: TelemetryEventCallback) => Unsubscribe` | Subscribe to telemetry scoped to this agent (and its sessions). |
155
- | `onLog` | `(callback: (record: LogRecord) => void) => Unsubscribe` | Subscribe to logs scoped to this agent (and its sessions). |
139
+ | Method | Signature | Description |
140
+ | -------------------- | ---------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
141
+ | `getId` | `() => string` | Agent identifier. |
142
+ | `getProjectRoot` | `() => string` | Absolute project path. |
143
+ | `getOrgConnection` | `() => OrgConnection \| undefined` | The Salesforce `OrgConnection` resolved for this agent (or `undefined` if the connectivity resolver omitted it — non-Salesforce hosts on the BYOK / api-key path return `undefined`). |
144
+ | `getAgentConfig` | `() => AgentConfig` | Current configuration (shallow copy). |
145
+ | `getMcpServerInfo` | `() => McpServerInfo[]` | MCP server status and discovered tools. |
146
+ | `reconnectMcpServer` | `(serverName: string) => Promise<void>` | Recover one MCP server without recycling the agent. Semantics vary by harness — observe via `getMcpServerInfo()` and discovery telemetry. |
147
+ | `updateAgentConfig` | `(config?: AgentConfig, options?: { abortSignal?: AbortSignal; forceResolve?: boolean }) => Promise<void>` | Merge new config into the live agent. Pass `forceResolve: true` to re-run the connectivity resolver even when the partial config doesn't include `orgAlias` or `modelId` — for consumer-side state changes (BYOK toggle, feature-id flip, rate-limit gate) the resolver reads but the SDK can't observe directly. |
148
+ | `createChatSession` | `() => Promise<ChatSession>` | Open a new conversation thread. |
149
+ | `getChatSession` | `(sessionId: string) => ChatSession` | Retrieve a session. Throws `AgentSDKError` (`CHAT_SESSION_NOT_FOUND`). |
150
+ | `getChatSessionIds` | `() => string[]` | List active session IDs. |
151
+ | `destroyChatSession` | `(sessionId: string) => Promise<void>` | Destroy a session and its history. |
152
+ | `cloneChatSession` | `(sourceSessionId: string) => Promise<ChatSession>` | Clone a session with its message history. |
153
+ | `compactChatSession` | `(sessionId: string) => Promise<ChatSession>` | Compact a session into a summarized new session. |
154
+ | `destroy` | `() => Promise<void>` | Destroy the agent and all its sessions. |
155
+ | `onTelemetry` | `(callback: TelemetryEventCallback) => Unsubscribe` | Subscribe to telemetry scoped to this agent (and its sessions). |
156
+ | `onLog` | `(callback: (record: LogRecord) => void) => Unsubscribe` | Subscribe to logs scoped to this agent (and its sessions). |
156
157
 
157
158
  ### `ChatSession`
158
159
 
@@ -211,6 +212,22 @@ Discriminated union (`event.type`) of streaming events:
211
212
  > with `chunkType` and `rawChunk` in the record's `context`. Subscribe via `manager.onLog` (or `agent.onLog` /
212
213
  > `session.onLog`) at debug level to observe these. Production consumers do not need to filter for unrecognized chunks.
213
214
 
215
+ #### Per-variant event types
216
+
217
+ Every `ChatEvent` variant is exported as a named type so consumers can write narrowed callbacks without re-declaring the
218
+ shape: `StartEvent`, `TextDeltaEvent`, `ReasoningDeltaEvent`, `ToolCallEvent`, `ToolCallDeltaEvent`,
219
+ `ToolApprovalRequestEvent`, `ToolResultEvent`, `ToolProgressEvent`, `StepStartEvent`, `StepFinishEvent`, `ErrorEvent`,
220
+ `FinishEvent`. Useful when factoring per-event handlers out of a `for await` loop:
221
+
222
+ ```typescript
223
+ import type { ToolApprovalRequestEvent } from '@salesforce/sfdx-agent-sdk';
224
+
225
+ function onApprovalRequest(event: ToolApprovalRequestEvent): Promise<boolean> {
226
+ // typed access to event.toolCall, event.annotations, event.serverName
227
+ return promptUser(event);
228
+ }
229
+ ```
230
+
214
231
  ### Configuration Types
215
232
 
216
233
  #### `AgentConfig`
@@ -218,7 +235,7 @@ Discriminated union (`event.type`) of streaming events:
218
235
  | Field | Type | Description |
219
236
  | --------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
220
237
  | `orgAlias?` | `string` | Salesforce org alias or username. Falls back to project/default org. |
221
- | `modelId?` | `ModelName \| Model` | LLM model selector. Pass a `ModelName` enum value for an in-tree model (e.g. `'llmgateway__OpenAIGPT5'`), or a pre-built `Model` instance to opt into a Bedrock-Anthropic Claude variant the SDK has not yet released — see `createClaudeModel(gatewayId, overrides)` in `@salesforce/llm-gateway-sdk`. |
238
+ | `modelId?` | `ModelName \| Model` | LLM model selector. Pass a `ModelName` enum value for an in-tree model (e.g. `'llmgateway__OpenAIGPT5'`), or a pre-built `Model` instance to opt into a Bedrock-Anthropic Claude variant the SDK has not yet released — see `createClaudeModel(gatewayId, overrides)` exported from this package. |
222
239
  | `name?` | `string` | Human-readable agent name. |
223
240
  | `description?` | `string` | Agent purpose description. |
224
241
  | `instructions?` | `string` | System instructions for the agent. |
@@ -229,10 +246,16 @@ Discriminated union (`event.type`) of streaming events:
229
246
 
230
247
  #### `StreamOptions`
231
248
 
232
- | Field | Type | Description |
233
- | ---------------------- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
234
- | `abortSignal?` | `AbortSignal` | Abort the streaming operation. |
235
- | `requireToolApproval?` | `boolean \| 'serial' \| 'batch'` | Gates native tool execution behind a `tool-approval-request` event. `true` / `'serial'` (the default) emits one approval per stream — safe for any iterator pattern. `'batch'` opts into parallel-approval UX: when the model emits parallel `tool_use` blocks, all approvals surface on the same stream so the consumer can render a batch approval card. **`'batch'` requires Pattern A iterators** (collect-all-approvals-then-settle); a `break`-on-first-approval loop will hang. See "Tool Approval Flow" below. |
249
+ | Field | Type | Description |
250
+ | ---------------------- | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
251
+ | `abortSignal?` | `AbortSignal` | Abort the streaming operation. |
252
+ | `requireToolApproval?` | `boolean \| ToolApprovalMode` | Gates native tool execution behind a `tool-approval-request` event. `true` / `'serial'` (the default) emits one approval per stream — safe for any iterator pattern. `'batch'` opts into parallel-approval UX: when the model emits parallel `tool_use` blocks, all approvals surface on the same stream so the consumer can render a batch approval card. **`'batch'` requires Pattern A iterators** (collect-all-approvals-then-settle); a `break`-on-first-approval loop will hang. See "Tool Approval Flow" below. |
253
+ | `maxSteps?` | `number` | Maximum number of LLM call steps the agent may take per `stream()` invocation. Each step is one LLM call (which may produce text, tool calls, or both). Must be `>= 1`. Defaults to `DEFAULT_MAX_STEPS` (1024) — high enough to be effectively unlimited for real tasks; the practical ceiling is the context window and cost. The constant is exported so consumers and harness authors share one source of truth. |
254
+
255
+ `ToolApprovalMode` is the exported type alias `'serial' | 'batch'` — useful when typing a settings struct that drives
256
+ `requireToolApproval`. Pair with `resolveToolApprovalMode(boolean | ToolApprovalMode | undefined)` (also exported) to
257
+ normalize consumer input the same way the SDK does internally (`undefined` / `false` → `undefined`, `true` → `'serial'`,
258
+ strings pass through, unknown strings throw).
236
259
 
237
260
  #### `MCPConfiguration`
238
261
 
@@ -280,13 +303,13 @@ only `MCPRemoteServerConfig` carries it.
280
303
 
281
304
  #### `McpServerInfo`
282
305
 
283
- | Field | Type | Description |
284
- | -------------- | ------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- |
285
- | `name` | `string` | Server identifier. |
286
- | `status` | `'connected' \| 'connecting' \| 'disabled' \| 'error' \| 'reconnecting'` | Connection state. `'reconnecting'` is reported during an `Agent.reconnectMcpServer(name)` call against a previously-`'connected'` server. |
287
- | `tools` | [`McpToolInfo[]`](#mcptoolinfo) | Discovered tools (name + metadata). |
288
- | `error?` | `string` | Sanitized human-readable error message when status is `'error'`. Stack frames and file paths are stripped at the harness boundary. |
289
- | `errorDetail?` | [`McpServerErrorDetail`](#mcpservererrordetail) | Structured failure projection for programmatic routing (category / code / retriable). Populated when status is `'error'`. |
306
+ | Field | Type | Description |
307
+ | -------------- | ----------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
308
+ | `name` | `string` | Server identifier. |
309
+ | `status` | `McpServerStatus` | Connection state — exported enum with the values `'connected' \| 'connecting' \| 'disabled' \| 'error' \| 'reconnecting'`. `'reconnecting'` is reported during an `Agent.reconnectMcpServer(name)` call against a previously-`'connected'` server. |
310
+ | `tools` | [`McpToolInfo[]`](#mcptoolinfo) | Discovered tools (name + metadata). |
311
+ | `error?` | `string` | Sanitized human-readable error message when status is `'error'`. Stack frames and file paths are stripped at the harness boundary. |
312
+ | `errorDetail?` | [`McpServerErrorDetail`](#mcpservererrordetail) | Structured failure projection for programmatic routing (category / code / retriable). Populated when status is `'error'`. |
290
313
 
291
314
  #### `McpServerErrorDetail`
292
315
 
@@ -396,6 +419,16 @@ type Message = {
396
419
 
397
420
  type MessagePart = TextPart | ReasoningPart | ToolCallPart | ToolResultPart | ImagePart | FilePart;
398
421
 
422
+ // Plain text and reasoning segments (model chain-of-thought).
423
+ type TextPart = { type: 'text'; text: string };
424
+ type ReasoningPart = { type: 'reasoning'; text: string };
425
+
426
+ // Tool invocation / result segments persisted on `Message.content`. Extend the
427
+ // `ToolCallInfo` / `ToolResultInfo` shapes with a discriminator. They appear in
428
+ // message history; they are NOT valid input on `chat()` / `addContext()`.
429
+ type ToolCallPart = ToolCallInfo & { type: 'tool-call' };
430
+ type ToolResultPart = ToolResultInfo & { type: 'tool-result' };
431
+
399
432
  // Multimodal input parts. `data` is base64-encoded bytes with no `data:` URI prefix.
400
433
  type ImagePart = { type: 'image'; mimeType: 'image/png' | 'image/jpeg'; data: string; fileName?: string };
401
434
  type FilePart = { type: 'file'; mimeType: 'application/pdf'; data: string; fileName?: string };
@@ -540,17 +573,20 @@ totals, subscribe to `chat-stream-completed` telemetry instead.
540
573
  The SDK throws `AgentSDKError` for predictable not-found and compatibility conditions. Each error has a `type` property
541
574
  from `AgentSDKErrorType`:
542
575
 
543
- | Type | Thrown By |
544
- | -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
545
- | `AGENT_NOT_FOUND` | `AgentManager.getAgent()`, `AgentManager.destroyAgent()` |
546
- | `CHAT_SESSION_NOT_FOUND` | `Agent.getChatSession()`, `Agent.destroyChatSession()`, `Agent.cloneChatSession()`, `Agent.compactChatSession()` |
547
- | `COMPACTION_FAILED` | `Agent.compactChatSession()` when the harness's underlying summarization call rejects. The original error is attached as `cause`; the source session is left intact. |
548
- | `DISPOSED` | `Agent` and `ChatSession` methods called after the owner has been destroyed |
549
- | `INCOMPATIBLE_HARNESS` | `createAgentManager()` when the factory advertises an unsupported `protocolVersion`, or the constructed harness reports a `protocolVersion` that differs from the factory's |
550
- | `INVALID_MESSAGE_CONTENT` | `ChatSession.chat()` / harness `stream()` when a message part is not valid as input (a `tool-call`/`tool-result` part, or non-base64-string file data) |
551
- | `MCP_SERVER_DISABLED` | `Agent.reconnectMcpServer()` when the named server is configured with `enabled: false` |
552
- | `MCP_SERVER_NOT_FOUND` | `Agent.reconnectMcpServer()` when the server name is not in the agent's `mcpServers` config |
553
- | `MULTIMODAL_NOT_SUPPORTED` | `ChatSession.chat()` / harness `stream()` when a file fails pre-stream capability validation (unsupported format, too large, or too many files) |
576
+ | Type | Thrown By |
577
+ | -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
578
+ | `AGENT_NOT_FOUND` | `AgentManager.getAgent()`, `AgentManager.destroyAgent()` |
579
+ | `CHAT_SESSION_NOT_FOUND` | `Agent.getChatSession()`, `Agent.destroyChatSession()`, `Agent.cloneChatSession()`, `Agent.compactChatSession()` |
580
+ | `COMPACTION_FAILED` | `Agent.compactChatSession()` when the harness's underlying summarization call rejects. The original error is attached as `cause`; the source session is left intact. |
581
+ | `DISPOSED` | `Agent` and `ChatSession` methods called after the owner has been destroyed |
582
+ | `INCOMPATIBLE_HARNESS` | `createAgentManager()` when the factory advertises an unsupported `protocolVersion`, or the constructed harness reports a `protocolVersion` that differs from the factory's |
583
+ | `INVALID_MESSAGE_CONTENT` | `ChatSession.chat()` / harness `stream()` when a message part is not valid as input (a `tool-call`/`tool-result` part, or non-base64-string file data) |
584
+ | `MCP_SERVER_DISABLED` | `Agent.reconnectMcpServer()` when the named server is configured with `enabled: false` |
585
+ | `MCP_SERVER_NOT_FOUND` | `Agent.reconnectMcpServer()` when the server name is not in the agent's `mcpServers` config |
586
+ | `MODEL_NOT_SUPPORTED_BY_HARNESS` | `AgentManager.createAgent()` / `Agent.updateAgentConfig()` (G8 pre-flight) when the resolved `ModelConnectivityInfo.providerHint` isn't in the harness's `supportedProviderHints`. Surfaces before any harness work runs (no MCP discovery, no subprocess spawn, no language-model construction) so the consumer can branch cleanly on `err.type` and recover without resource cleanup. |
587
+ | `MULTIMODAL_NOT_SUPPORTED` | `ChatSession.chat()` / harness `stream()` when a file fails pre-stream capability validation (unsupported format, too large, or too many files) |
588
+ | `NOT_SUPPORTED` | `ApiKeyConnectivityResolver.resolve()` when the consumer-supplied `getApiKey` returns an empty / nullish value. Surfaces locally so the consumer sees "the resolver returned an empty key" rather than chasing a 401 through provider logs after `Authorization: Bearer ` (no key) lands on the wire. |
589
+ | `TOOL_CALL_NOT_FOUND` | `ChatSession.approveToolCall()` / `declineToolCall()` / `submitToolResult()` when the supplied `toolCallId` doesn't match any pending tool-call request on the current session. Typically indicates a wrong-id, wrong-session, or already-settled call. Both production harnesses (Mastra, Claude) throw this from their per-turn approval coordinator. |
554
590
 
555
591
  ```typescript
556
592
  import { AgentSDKError, AgentSDKErrorType } from '@salesforce/sfdx-agent-sdk';
@@ -901,15 +937,104 @@ is what actually replaces the value the model sees.
901
937
 
902
938
  ### Connectivity Resolution
903
939
 
940
+ The connectivity surface is the seam between the SDK and whatever-talks-to-the-LLM. It resolves three things per agent:
941
+ which model the harness will use, the wire shape the harness should speak, and how to authenticate each outbound
942
+ request. A bundled default resolver covers the Salesforce-org gateway path; a bundled api-key resolver covers BYOK /
943
+ direct-provider / LLMG-Express; and consumers can implement `AgentConnectivityResolver` directly for anything else.
944
+
945
+ #### `AgentConnectivityResolver`
946
+
947
+ The contract every connectivity resolver implements. `createAgentManager` calls `resolve()` once per agent install
948
+ (`createAgent`, boot-time restore, and `Agent.updateAgentConfig` when `orgAlias` / `modelId` / `forceResolve: true` is
949
+ set), then threads the returned `ResolvedConnectivity` through to the harness.
950
+
951
+ ```typescript
952
+ interface AgentConnectivityResolver {
953
+ resolve(projectRoot: string, config: AgentConfig): Promise<ResolvedConnectivity>;
954
+ }
955
+ ```
956
+
957
+ The bundled `DefaultAgentConnectivityResolver` (used when `createAgentManager`'s `connectivityResolver` option is unset)
958
+ targets the Salesforce LLM Gateway via a self-refreshing JWT. The class is implementation detail — the public entry
959
+ point is implicit via `createAgentManager`.
960
+
904
961
  #### `ResolvedConnectivity`
905
962
 
906
- Returned by `AgentConnectivityResolver.resolve()`. Contains all resolved runtime artifacts for an agent's org.
963
+ Returned by `AgentConnectivityResolver.resolve()`. Three fields, two of them optional for non-Salesforce hosts:
964
+
965
+ | Field | Type | Description |
966
+ | ----------------------- | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
967
+ | `modelConnectivityInfo` | `ModelConnectivityInfo` | The five-field bag the harness reads to make every outbound LLM request. See below. |
968
+ | `orgConnection?` | `OrgConnection` | Authenticated Salesforce org connection carrying identity and env inference. Optional — non-Salesforce hosts (MuleSoft, Commerce Vibes) omit it. |
969
+ | `orgJwt?` | `JSONWebToken` | Self-refreshing JWT for the resolved org. Used for MCP auth injection and any other Salesforce-platform-auth need (identity, future Apex). Optional — non-Salesforce hosts and api-key BYOK hosts that don't need org auth omit it. |
970
+
971
+ #### `ModelConnectivityInfo`
907
972
 
908
- | Field | Type | Description |
909
- | ------------------ | ------------------ | ---------------------------------------------------------------------- |
910
- | `llmGatewayClient` | `LLMGatewayClient` | Pre-configured and authenticated LLM gateway client. |
911
- | `orgConnection` | `OrgConnection` | Authenticated org connection carrying identity and env inference. |
912
- | `orgJwt` | `JSONWebToken` | Self-refreshing JWT for the resolved org, used for MCP auth injection. |
973
+ The connectivity facts a harness needs to make an LLM request. Five fields. Auth-kind branching lives inside
974
+ `getHeaders()` the harness never inspects whether the auth is a JWT, an API key, or anything else.
975
+
976
+ | Field | Type | Description |
977
+ | --------------- | --------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
978
+ | `model` | `Model` | Capability descriptor used for pre-flight multimodal validation and SDK-side context-usage reporting. |
979
+ | `baseUrl` | `string` | Provider-native or gateway base URL (no trailing slash, no path beyond the API root). Harnesses concat the wire-shape-specific path. |
980
+ | `nativeModelId` | `string` | Wire id the provider SDK sets on the request body's `model` field. May differ from `model.name` (e.g. Anthropic-direct expects `claude-sonnet-4-6`; the Salesforce gateway expects `llmgateway__BedrockAnthropicClaude46Sonnet`). |
981
+ | `providerHint` | `ProviderHint` | Wire shape the harness should speak. The manager validates this against the harness's `supportedProviderHints` before any harness work begins; a harness only sees hints it recognizes. |
982
+ | `getHeaders` | `() => Promise<Record<string, string>>` | Returns the FULL header set (Authorization included) for the NEXT outbound request. **Per-call invocation contract.** Mastra harnesses MUST call this on every HTTP call (inside their fetch wrapper); Claude harnesses MUST call this once per subprocess spawn. Caching the returned map across requests is a bug. |
983
+
984
+ `getHeaders()` is allowed to throw — the harness propagates the throw as a stream-time failure, identical to any other
985
+ transport error.
986
+
987
+ #### `ProviderHint`
988
+
989
+ ```typescript
990
+ type ProviderHint =
991
+ | 'anthropic' // Anthropic Messages API direct (api.anthropic.com)
992
+ | 'bedrock-anthropic' // Anthropic via AWS Bedrock invoke (Salesforce gateway uses this shape)
993
+ | 'openai' // OpenAI Responses API direct (api.openai.com)
994
+ | 'openai-responses' // OpenAI Responses API via Salesforce gateway pass-through
995
+ | 'openai-compatible' // OpenAI-shaped /v1/chat/completions (LLMG Express, LiteLLM, vLLM, third-party gateways)
996
+ | (string & {}); // Forward-compat tail — harnesses must reject unknown hints
997
+ ```
998
+
999
+ The trailing `string & {}` allows resolvers to return a hint for a future provider without a same-day type bump in the
1000
+ SDK. Harnesses that don't recognize a hint MUST throw `AgentSDKError(MODEL_NOT_SUPPORTED_BY_HARNESS)` rather than fall
1001
+ through to a wrong-shape builder.
1002
+
1003
+ #### `ApiKeyConnectivityResolver`
1004
+
1005
+ Bundled api-key resolver covering Anthropic-direct, OpenAI-direct, and any OpenAI-compatible endpoint (LLMG Express,
1006
+ LiteLLM, vLLM, third-party gateways). Pass an instance via `createAgentManager`'s `connectivityResolver` option.
1007
+
1008
+ ```typescript
1009
+ import { ApiKeyConnectivityResolver, createAgentManager } from '@salesforce/sfdx-agent-sdk';
1010
+
1011
+ const manager = await createAgentManager('/path/to/storage', new MastraHarnessFactory(), {
1012
+ connectivityResolver: new ApiKeyConnectivityResolver({
1013
+ getApiKey: () => process.env.LLMG_EXPRESS_API_KEY!,
1014
+ baseUrl: 'https://express-internal/v1',
1015
+ providerHint: 'openai-compatible',
1016
+ }),
1017
+ });
1018
+ ```
1019
+
1020
+ ##### `ApiKeyConnectivityResolverConfig`
1021
+
1022
+ | Field | Type | Description |
1023
+ | -------------------- | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1024
+ | `getApiKey` | `() => string \| Promise<string>` | Returns the API key. Re-evaluated on every `resolve()` call, and the resulting `getHeaders()` re-invokes it on every header read so a rotating key source (env var, secret store, BYOK toggle) lands without reconstructing the resolver. |
1025
+ | `baseUrl` | `string` | Fully-qualified provider URL with API version segment (e.g. `https://api.openai.com/v1`, not `https://api.openai.com`). |
1026
+ | `providerHint` | `ProviderHint` | Wire shape. Must be one a harness in the consumer's setup advertises via `HarnessFactory.supportedProviderHints`. |
1027
+ | `nativeModelIdFor?` | `(model: Model) => string` | Optional translator from canonical `model.name` to the endpoint-native model id. Direct-provider endpoints expect their own naming (`claude-sonnet-4-6`); gateway-shaped endpoints accept the canonical `llmgateway__*` id verbatim. Defaults to `model.name`. |
1028
+ | `connectionFactory?` | `OrgConnectionFactory` | Optional. When supplied, the resolver mints an org connection + JWT alongside the api-key bag — the BYOK shape (api-key authenticates the LLM; org JWT authenticates MCP servers / identity). Without it, `orgConnection` and `orgJwt` are omitted from `ResolvedConnectivity`. |
1029
+
1030
+ The resolver throws `AgentSDKError(NOT_SUPPORTED)` when `getApiKey()` returns empty / nullish so consumers see "the
1031
+ resolver returned an empty key" rather than chasing a 401 through provider logs after `Authorization: Bearer ` (no key)
1032
+ lands on the wire.
1033
+
1034
+ `Model.customHeaders` (a vendor-label map like `{ 'anthropic-version': '2023-06-01' }`) flows through to the resolver's
1035
+ header bag. **Reserved auth headers are not overridable**: the resolver spreads `customHeaders` first and writes its own
1036
+ `Authorization` / `Content-Type` last, so a `Model` instance whose `customHeaders` includes an `Authorization` key
1037
+ silently shadows nothing — the resolver wins.
913
1038
 
914
1039
  #### `HarnessAgentConfig`
915
1040
 
@@ -931,18 +1056,51 @@ Authorization header already exists. For all other URLs, returns headers unmodif
931
1056
 
932
1057
  Returns `true` if the URL matches a Salesforce Hosted MCP Server endpoint (prod, dev, test, perf, stage).
933
1058
 
934
- ### Re-exported from `@salesforce/llm-gateway-sdk`
1059
+ ### `Workspace`
1060
+
1061
+ Convenience helper for validating and normalizing a project path before handing it to `manager.createAgent`. The
1062
+ constructor is private; build one through the static async factory:
1063
+
1064
+ ```typescript
1065
+ import { Workspace } from '@salesforce/sfdx-agent-sdk';
1066
+
1067
+ const workspace = await Workspace.create('/path/to/project');
1068
+ const agent = await manager.createAgent(workspace.projectPath, { instructions: '...' });
1069
+ ```
1070
+
1071
+ `Workspace.create(projectPath)` resolves the path to an absolute, real (symlink-followed) directory and throws if it
1072
+ doesn't exist or isn't a directory. The returned instance exposes `projectPath: string`. Pass any string to
1073
+ `manager.createAgent` directly if you don't need the validation step — `Workspace` is a convenience, not a requirement.
1074
+
1075
+ ### Model registry
1076
+
1077
+ The `Model` family ships with this package — registered Bedrock-Anthropic Claude and OpenAI GPT variants the SDK
1078
+ recognizes today, plus an escape hatch for consumers who need a Claude variant the SDK hasn't released yet.
935
1079
 
936
1080
  - `Model` — abstract base class. Returned by `Models.getByName(...)` and accepted as an `AgentConfig.modelId` value.
937
1081
  - `ModelName` — enum of in-tree model identifiers.
1082
+ - `Models` — registry: `Models.getByName(name)`, `Models.getDefault()`.
938
1083
  - `createClaudeModel(gatewayId, overrides?)` — escape-hatch factory for opting into a Bedrock-Anthropic Claude variant
939
1084
  the SDK has not released yet (`AgentConfig.modelId` accepts the returned instance directly).
940
1085
  - `ClaudeModelOverrides` — optional caps for `createClaudeModel`.
941
- - `SfApiEnv` — Salesforce API environment enum (`dev`, `perf`, `prod`, `stage`, `test`)
942
- - `inferSfApiEnv(instanceUrl, options?)` maps an instance URL to a `SfApiEnv`. Re-exported from
943
- `@salesforce/agentic-common` for consumers that need the mapping without an `OrgConnection` (e.g. building a
944
- Salesforce platform MCP URL). Defaults the unrecognized-`.pc-rnd.` fallback to `SfApiEnv.Test` (`.pc-rnd.` is an
945
- internal-only OrgFarm domain — not Prod by definition); pass `{ pcRndFallback: SfApiEnv.Prod }` to override. See the
1086
+ - `validateMultimodalFiles(files, model)` — helper harnesses use to enforce per-format / per-model multimodal caps
1087
+ pre-stream. Throws `AgentSDKError(MULTIMODAL_NOT_SUPPORTED)` on a cap-exceeding file.
1088
+ - `MultimodalFile`, `SupportedFileFormat`, `MimeType` supporting types for multimodal validation.
1089
+
1090
+ ### Re-exported from `@salesforce/agentic-common`
1091
+
1092
+ For consumers who name SDK public-type signatures without adding `@salesforce/agentic-common` as a direct dependency.
1093
+
1094
+ - `OrgConnection` / `OrgConnectionFactory` — surface on `Agent.getOrgConnection()`.
1095
+ - `JSONWebToken` / `JWTOptions` / `RequiredJWTHeaders` / `RequiredJWTPayload` — surface on `ResolvedConnectivity.orgJwt`
1096
+ and `HarnessAgentConfig.orgJwt`.
1097
+ - `LogLevel` / `LogRecord` / `Unsubscribe` — surface on `manager.onLog` / `manager.onTelemetry` /
1098
+ `manager.onWireCommunication` callbacks.
1099
+ - `SfApiEnv` — Salesforce API environment enum (`dev`, `perf`, `prod`, `stage`, `test`).
1100
+ - `inferSfApiEnv(instanceUrl, options?)` — maps an instance URL to a `SfApiEnv`. Useful for consumers that need the
1101
+ mapping without an `OrgConnection` (e.g. building a Salesforce platform MCP URL). Defaults the unrecognized-`.pc-rnd.`
1102
+ fallback to `SfApiEnv.Test` (`.pc-rnd.` is an internal-only OrgFarm domain — not Prod by definition); pass
1103
+ `{ pcRndFallback: SfApiEnv.Prod }` to override. See the
946
1104
  [`@salesforce/agentic-common` README](../agentic-common/README.md#infersfapienvinstanceurl-options-sfapienv) for the
947
1105
  full resolution order.
948
1106
 
@@ -951,6 +1109,112 @@ Returns `true` if the URL matches a Salesforce Hosted MCP Server endpoint (prod,
951
1109
  The SDK exposes typed `TelemetryEvent` / `LogRecord` streams at the manager, agent, and session scopes. See
952
1110
  [Telemetry & Logs](#telemetry--logs) below for the event catalog, emit contract, and structured-log table.
953
1111
 
1112
+ ### Wire-Communication Events
1113
+
1114
+ A separate, opt-in diagnostic channel that surfaces outbound LLM communication. Distinct from `onTelemetry` (SDK-level
1115
+ lifecycle and routing) and `onLog` (structured log records): wire-communication events carry the request URL, parsed
1116
+ body (where the harness can capture it), response status, and timing — for end-to-end debugging of gateway / provider
1117
+ behavior.
1118
+
1119
+ Subscribe via `AgentManager.onWireCommunication(callback)`:
1120
+
1121
+ ```typescript
1122
+ const unsubscribe = manager.onWireCommunication((event) => {
1123
+ switch (event.type) {
1124
+ case 'llm-request':
1125
+ // outbound HTTP request to the LLM provider
1126
+ break;
1127
+ case 'llm-response':
1128
+ // matching response (or terminal failure)
1129
+ break;
1130
+ case 'wire-monitoring-not-supported':
1131
+ // harness can't emit per-call events for this stream;
1132
+ // event.message points at the workaround (typically a debug log file path)
1133
+ break;
1134
+ }
1135
+ });
1136
+ ```
1137
+
1138
+ **Discriminated union — three variants keyed on `type`:**
1139
+
1140
+ | `type` | Fields |
1141
+ | ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1142
+ | `llm-request` | `timestamp`, `url`, `method`, `model`, `body?` |
1143
+ | `llm-response` | `timestamp`, `model`, `status`, `xClientTraceId?`, `totalDurationMs?`, `timeToFirstTokenMs?`, `usage?` (`inputTokens?`, `outputTokens?`, `totalTokens?`, `reasoningTokens?`), `responseText?`, `error?` ({ `message` }) |
1144
+ | `wire-monitoring-not-supported` | `timestamp`, `harnessId`, `message` (free-form text — typically explains why direct wire monitoring isn't available and points at a fallback log file the harness wrote) |
1145
+
1146
+ **Privacy contract.** Events MAY contain user prompts, tool arguments, and model output in the `body` field. They are
1147
+ **not** flowed to the structured-log channel by default — consumers opt in to wire-level visibility explicitly via
1148
+ `onWireCommunication`. Apply your own redaction / scrubbing before persisting or sharing. Note that request HTTP headers
1149
+ (e.g. `Authorization`) are **not** captured on the events; the harness's fetch wrapper sees them but doesn't forward
1150
+ them onto the channel.
1151
+
1152
+ **Subscriber-gated end-to-end.** The chain `harness.wireBus → router slice → manager.wireBus` uses
1153
+ `EventBus.forwardWhileSubscribed` (from `@salesforce/agentic-common`), so a manager with zero `onWireCommunication`
1154
+ listeners leaves every link cold. Harnesses can read `wireBus.listenerCount > 0` and skip the work entirely — the Claude
1155
+ harness in particular skips writing the subprocess's debug log when nobody is listening. Subscribe **before** the
1156
+ `chat()` you want to inspect: subscriptions added mid-stream miss the in-flight subprocess on Claude (the listener count
1157
+ is captured at stream-start).
1158
+
1159
+ **Cross-harness coverage.**
1160
+
1161
+ - **Mastra harness**: emits one `llm-request` + one `llm-response` per outbound HTTP call to the LLM provider (every
1162
+ provider — Bedrock-Anthropic, direct Anthropic, OpenAI Responses, OpenAI Chat Completions). The body is captured from
1163
+ the in-process fetch wrapper.
1164
+ - **Claude harness**: cannot emit per-call events because the Claude Agent SDK runs the model client in a subprocess
1165
+ with no programmatic HTTP-interception seam. Instead, when at least one consumer is subscribed at stream-start, the
1166
+ harness writes the subprocess's debug log to `${storageRoot}/claude-debug-logs/${agentId}__${threadId}.log` (one file
1167
+ per thread; turns of the same conversation accumulate, separated by greppable `# Turn started: <ISO>` banners) and
1168
+ emits a single `wire-monitoring-not-supported` event whose `message` field embeds the file path. The file is left in
1169
+ place; the consumer is responsible for retention and cleanup.
1170
+
1171
+ #### `WireCommunicationFileWriter`
1172
+
1173
+ Convenience writer that subscribes to a `WireCommunicationEmitter` (typically the `AgentManager`) and appends each event
1174
+ to a Markdown file. Used for debugging sessions where you want a chronological transcript without building one yourself.
1175
+
1176
+ ```typescript
1177
+ import { WireCommunicationFileWriter } from '@salesforce/sfdx-agent-sdk';
1178
+
1179
+ const writer = new WireCommunicationFileWriter(manager, {
1180
+ filePath: '/tmp/wire-communication.md',
1181
+ // optional — invoked on appendFile failure; defaults to console.error
1182
+ onError: (err, event) => {
1183
+ /* … */
1184
+ },
1185
+ });
1186
+
1187
+ // later
1188
+ writer.detach();
1189
+ ```
1190
+
1191
+ The output file is plain Markdown with one `### <iso-timestamp> [llm-request]`, `[llm-response]`, or
1192
+ `[wire-monitoring-not-supported]` header per event followed by the populated fields. Optional fields are emitted only
1193
+ when set — sparse responses do not pad the file with empty `**Field**: N/A` lines. On the Claude harness, the
1194
+ `wire-monitoring-not-supported` block links to the per-thread debug log file where raw wire detail can be inspected
1195
+ directly.
1196
+
1197
+ The same privacy contract applies: the resulting file may contain prompts, tool arguments, and model output. Treat it as
1198
+ sensitive and apply your own retention / scrubbing rules before sharing.
1199
+
1200
+ ##### `WireCommunicationFileWriterOptions`
1201
+
1202
+ | Field | Type | Description |
1203
+ | ---------- | ------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1204
+ | `filePath` | `string` | Absolute (or cwd-relative) path the writer appends Markdown to. Required. |
1205
+ | `onError?` | `(error: Error, event: WireCommunicationEvent) => void` | Optional callback invoked when an `appendFile` rejection is observed (disk full, parent directory unwritable, etc.). Without this, write failures are routed to `console.error` so a debugging session can't silently lose events. |
1206
+
1207
+ ##### `WireCommunicationEmitter`
1208
+
1209
+ The shape `WireCommunicationFileWriter` accepts. Anything exposing `onWireCommunication(callback)` works — typically an
1210
+ `AgentManager`, but tests may construct a smaller surface.
1211
+
1212
+ ```typescript
1213
+ type WireCommunicationEmitter = {
1214
+ onWireCommunication(callback: (event: WireCommunicationEvent) => void): Unsubscribe;
1215
+ };
1216
+ ```
1217
+
954
1218
  ### Harness Extensibility
955
1219
 
956
1220
  The `AgentHarness` contract is the SDK's lowest-common-denominator surface — every harness implements it, and the SDK
@@ -1161,6 +1425,14 @@ All variants share `{ type: <discriminant>, timestamp: Date }` plus the fields b
1161
1425
  | `mcp-server-discovery-failed` | `agentId`, `serverName`, `durationMs`, `error`, `errorDetail?` ([`McpServerErrorDetail`](#mcpservererrordetail)) |
1162
1426
  | `mcp-server-status-changed` | `agentId`, `serverName`, `previousStatus`, `nextStatus`, `error?` |
1163
1427
 
1428
+ Each variant is also exported as a named type so consumers can write narrowed handlers: `AgentCreatedEvent`,
1429
+ `AgentDestroyedEvent`, `SessionCreatedEvent`, `SessionDestroyedEvent`, `ChatStreamStartedEvent`,
1430
+ `ChatStreamCompletedEvent`, `ChatStreamErrorEvent`, `ToolExecutionStartedEvent`, `ToolExecutionCompletedEvent`,
1431
+ `ToolApprovalRequestedEvent`, `ToolApprovalResolvedEvent`, `McpServerDiscoveryStartedEvent`,
1432
+ `McpServerDiscoveryCompletedEvent`, `McpServerDiscoveryFailedEvent`, `McpServerStatusChangedEvent`. The
1433
+ `ChatStreamTrigger` union (`'chat' | 'submit-tool-result' | 'approve-tool-call' | 'decline-tool-call'`) and
1434
+ `TelemetryEventCallback` (the `onTelemetry` argument shape) are exported alongside.
1435
+
1164
1436
  `mcp-server-status-changed` fires on every per-server status transition — emitted in addition to the discovery trio so
1165
1437
  consumers can route on the `(previousStatus, nextStatus)` pair without polling `getMcpServerInfo()`. A reconnect on a
1166
1438
  previously-`connected` server emits, in order: `connected → reconnecting`, then on success `reconnecting → connected`,