@salesforce/sfdx-agent-sdk 0.1.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 (67) hide show
  1. package/LICENSE.txt +21 -0
  2. package/README.md +508 -0
  3. package/dist/agent-connectivity-resolver.d.ts +62 -0
  4. package/dist/agent-connectivity-resolver.js +47 -0
  5. package/dist/agent-connectivity-resolver.js.map +1 -0
  6. package/dist/agent-manager.d.ts +134 -0
  7. package/dist/agent-manager.js +266 -0
  8. package/dist/agent-manager.js.map +1 -0
  9. package/dist/agent.d.ts +218 -0
  10. package/dist/agent.js +313 -0
  11. package/dist/agent.js.map +1 -0
  12. package/dist/chat-session.d.ts +298 -0
  13. package/dist/chat-session.js +407 -0
  14. package/dist/chat-session.js.map +1 -0
  15. package/dist/errors.d.ts +12 -0
  16. package/dist/errors.js +20 -0
  17. package/dist/errors.js.map +1 -0
  18. package/dist/harness/agent-harness.d.ts +200 -0
  19. package/dist/harness/agent-harness.js +6 -0
  20. package/dist/harness/agent-harness.js.map +1 -0
  21. package/dist/harness/harness-bus-owner.d.ts +34 -0
  22. package/dist/harness/harness-bus-owner.js +78 -0
  23. package/dist/harness/harness-bus-owner.js.map +1 -0
  24. package/dist/harness/harness-config.d.ts +89 -0
  25. package/dist/harness/harness-config.js +26 -0
  26. package/dist/harness/harness-config.js.map +1 -0
  27. package/dist/harness/harness-factory.d.ts +21 -0
  28. package/dist/harness/harness-factory.js +6 -0
  29. package/dist/harness/harness-factory.js.map +1 -0
  30. package/dist/harness/index.d.ts +4 -0
  31. package/dist/harness/index.js +6 -0
  32. package/dist/harness/index.js.map +1 -0
  33. package/dist/index.d.ts +23 -0
  34. package/dist/index.js +20 -0
  35. package/dist/index.js.map +1 -0
  36. package/dist/internal/telemetry-router.d.ts +41 -0
  37. package/dist/internal/telemetry-router.js +128 -0
  38. package/dist/internal/telemetry-router.js.map +1 -0
  39. package/dist/mcp-auth.d.ts +20 -0
  40. package/dist/mcp-auth.js +40 -0
  41. package/dist/mcp-auth.js.map +1 -0
  42. package/dist/mcp-config.d.ts +52 -0
  43. package/dist/mcp-config.js +13 -0
  44. package/dist/mcp-config.js.map +1 -0
  45. package/dist/test/tsconfig.tsbuildinfo +1 -0
  46. package/dist/types/events.d.ts +151 -0
  47. package/dist/types/events.js +6 -0
  48. package/dist/types/events.js.map +1 -0
  49. package/dist/types/index.d.ts +4 -0
  50. package/dist/types/index.js +6 -0
  51. package/dist/types/index.js.map +1 -0
  52. package/dist/types/messages.d.ts +60 -0
  53. package/dist/types/messages.js +6 -0
  54. package/dist/types/messages.js.map +1 -0
  55. package/dist/types/telemetry-events.d.ts +93 -0
  56. package/dist/types/telemetry-events.js +6 -0
  57. package/dist/types/telemetry-events.js.map +1 -0
  58. package/dist/types/tools.d.ts +57 -0
  59. package/dist/types/tools.js +6 -0
  60. package/dist/types/tools.js.map +1 -0
  61. package/dist/types/usage.d.ts +31 -0
  62. package/dist/types/usage.js +6 -0
  63. package/dist/types/usage.js.map +1 -0
  64. package/dist/workspace.d.ts +15 -0
  65. package/dist/workspace.js +48 -0
  66. package/dist/workspace.js.map +1 -0
  67. package/package.json +64 -0
package/dist/agent.js ADDED
@@ -0,0 +1,313 @@
1
+ /*
2
+ * Copyright 2026, Salesforce, Inc. All rights reserved.
3
+ * See LICENSE.txt for license terms.
4
+ */
5
+ import { EventBus, LogBus, RealClock, UUIDGenerator, } from '@salesforce/agentic-common';
6
+ import { toHarnessConfig } from './harness/harness-config.js';
7
+ import { DefaultChatSession } from './chat-session.js';
8
+ import { Models } from '@salesforce/llm-gateway-sdk';
9
+ import { AgentSDKError, AgentSDKErrorType } from './errors.js';
10
+ /**
11
+ * Default implementation of {@link Agent} that delegates
12
+ * agent and thread operations to an {@link AgentHarness}.
13
+ */
14
+ export class DefaultAgent {
15
+ harness;
16
+ agentId;
17
+ projectRoot;
18
+ config;
19
+ llmGatewayClient;
20
+ orgConnection;
21
+ orgJwt;
22
+ agentConnectivityResolver;
23
+ sessions = new Map();
24
+ sessionSliceUnregisters = new Map();
25
+ router;
26
+ telemetryBus = new EventBus();
27
+ logBus = new LogBus();
28
+ inboundUnsubs;
29
+ parentUnsubs;
30
+ clock;
31
+ idGenerator;
32
+ disposed = false;
33
+ /**
34
+ * @param harness - The agent harness managing agent and thread lifecycle.
35
+ * @param agentId - Unique identifier for this agent.
36
+ * @param projectRoot - Project folder this agent is allowed to operate within.
37
+ * @param config - Initial agent configuration (instructions, model, tools, etc.).
38
+ * @param llmGatewayClient - Authenticated LLM gateway client for the resolved org.
39
+ * @param orgConnection - Authenticated org connection carrying identity and env inference.
40
+ * @param orgJwt - Self-refreshing JWT for the resolved org (used for MCP auth injection).
41
+ * @param agentConnectivityResolver - Used to re-resolve org connectivity when the org or model changes.
42
+ * @param router - Telemetry router used to obtain session slices when sessions are created.
43
+ * @param inbound - Router slice delivering harness events routed to this agent (non-session-scoped).
44
+ * @param parent - Manager's bus pair; this agent forwards its events upward into them.
45
+ */
46
+ constructor(harness, agentId, projectRoot, config, llmGatewayClient, orgConnection, orgJwt, agentConnectivityResolver, router, inbound, parent, clock = new RealClock(), idGenerator = new UUIDGenerator()) {
47
+ this.harness = harness;
48
+ this.agentId = agentId;
49
+ this.projectRoot = projectRoot;
50
+ this.config = config;
51
+ this.llmGatewayClient = llmGatewayClient;
52
+ this.orgConnection = orgConnection;
53
+ this.orgJwt = orgJwt;
54
+ this.agentConnectivityResolver = agentConnectivityResolver;
55
+ this.router = router;
56
+ this.clock = clock;
57
+ this.idGenerator = idGenerator;
58
+ this.inboundUnsubs = [inbound.telemetry.forwardTo(this.telemetryBus), inbound.log.forwardTo(this.logBus)];
59
+ this.parentUnsubs = [this.telemetryBus.forwardTo(parent.telemetry), this.logBus.forwardTo(parent.log)];
60
+ }
61
+ /**
62
+ * @requirements
63
+ * - MUST return the agent's ID.
64
+ */
65
+ getId() {
66
+ this.assertNotDisposed();
67
+ return this.agentId;
68
+ }
69
+ /** Returns the project root folder this agent operates within. */
70
+ getProjectRoot() {
71
+ this.assertNotDisposed();
72
+ return this.projectRoot;
73
+ }
74
+ getOrgConnection() {
75
+ this.assertNotDisposed();
76
+ return this.orgConnection;
77
+ }
78
+ /**
79
+ * @requirements
80
+ * - MUST return a shallow copy of the internal `config` object to prevent external mutation of the agent's state.
81
+ */
82
+ getAgentConfig() {
83
+ this.assertNotDisposed();
84
+ return { ...this.config };
85
+ }
86
+ getMcpServerInfo() {
87
+ this.assertNotDisposed();
88
+ return this.harness.getMcpServerInfo(this.agentId);
89
+ }
90
+ /**
91
+ * @requirements
92
+ * - MUST merge the provided `config` with the internal `config` object.
93
+ * - MUST guarantee that the `agentId` remains unchanged during the merge.
94
+ * - MUST destroy the existing agent in the harness by delegating to `this.harness.destroyAgent(this.getId())`.
95
+ * - 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.
97
+ */
98
+ async updateAgentConfig(config = {}, options) {
99
+ this.assertNotDisposed();
100
+ const previousConfig = { ...this.config };
101
+ const previousClient = this.llmGatewayClient;
102
+ const previousOrgJwt = this.orgJwt;
103
+ const nextConfig = { ...this.config, ...config };
104
+ const orgAliasRequested = Object.prototype.hasOwnProperty.call(config, 'orgAlias');
105
+ const previousModelName = previousClient.getModel().name;
106
+ const nextModelName = nextConfig.modelId ?? Models.getDefault().name;
107
+ let nextClient = previousClient;
108
+ let nextConnection = this.orgConnection;
109
+ let nextOrgJwt = this.orgJwt;
110
+ if (orgAliasRequested) {
111
+ const runtime = await this.agentConnectivityResolver.resolve(this.projectRoot, nextConfig);
112
+ nextClient = runtime.llmGatewayClient;
113
+ nextConnection = runtime.orgConnection;
114
+ nextOrgJwt = runtime.orgJwt;
115
+ }
116
+ else if (nextModelName !== previousModelName) {
117
+ // Keep the same authenticated client, but pin the updated model.
118
+ // (If modelId is omitted, the resolver pinned the default at creation time.)
119
+ nextClient.setModel(Models.getByName(nextModelName));
120
+ }
121
+ await this.harness.destroyAgent(this.agentId);
122
+ try {
123
+ await this.harness.createAgent(this.agentId, this.projectRoot, nextClient, toHarnessConfig(nextConfig, nextOrgJwt), options);
124
+ this.config = nextConfig;
125
+ this.llmGatewayClient = nextClient;
126
+ this.orgConnection = nextConnection;
127
+ this.orgJwt = nextOrgJwt;
128
+ // Release the old client only once the swap has succeeded. When the orgAlias is unchanged,
129
+ // nextClient === previousClient and we must NOT dispose.
130
+ if (nextClient !== previousClient) {
131
+ previousClient.dispose();
132
+ }
133
+ }
134
+ catch (error) {
135
+ // Best-effort restoration to keep wrapper and harness state aligned.
136
+ try {
137
+ // Restore client model if we mutated it in-place.
138
+ if (nextClient === previousClient) {
139
+ previousClient.setModel(Models.getByName(previousModelName));
140
+ }
141
+ await this.harness.createAgent(this.agentId, this.projectRoot, previousClient, toHarnessConfig(previousConfig, previousOrgJwt));
142
+ }
143
+ catch {
144
+ // Ignore restoration errors; rethrow the original failure.
145
+ }
146
+ // A freshly-resolved client we never installed must be released so its auth resources don't leak.
147
+ if (nextClient !== previousClient) {
148
+ try {
149
+ nextClient.dispose();
150
+ }
151
+ catch {
152
+ // Ignore; the original error is the one the caller cares about.
153
+ }
154
+ }
155
+ throw error;
156
+ }
157
+ }
158
+ /**
159
+ * @requirements
160
+ * - MUST delegate to `this.harness.createThread(this.config.agentId)` to generate a new thread ID.
161
+ * - MUST instantiate a new `DefaultChatSession` initialized with the harness, agent ID, and the new thread ID.
162
+ * - MUST store the newly created session in the internal `sessions` map, keyed by the thread ID.
163
+ * - MUST return the newly created session.
164
+ */
165
+ async createChatSession() {
166
+ this.assertNotDisposed();
167
+ const threadId = await this.harness.createThread(this.agentId);
168
+ return this.attachSession(threadId);
169
+ }
170
+ /**
171
+ * @requirements
172
+ * - MUST throw an Error if the provided `sessionId` is not found in the internal `sessions` map.
173
+ * - MUST delegate to `this.harness.destroyThread(this.config.agentId, sessionId)`.
174
+ * - MUST remove the session from the internal `sessions` map.
175
+ */
176
+ async destroyChatSession(sessionId) {
177
+ this.assertNotDisposed();
178
+ const session = this.sessions.get(sessionId);
179
+ if (!session) {
180
+ throw new AgentSDKError(`No ChatSession found with id: "${sessionId}"`, AgentSDKErrorType.CHAT_SESSION_NOT_FOUND);
181
+ }
182
+ await this.harness.destroyThread(this.agentId, sessionId);
183
+ this.detachSession(sessionId, session);
184
+ }
185
+ /**
186
+ * @requirements
187
+ * - MUST throw an Error if the provided `sessionId` is not found in the internal `sessions` map.
188
+ * - MUST return the session object associated with the given `sessionId`.
189
+ */
190
+ getChatSession(sessionId) {
191
+ this.assertNotDisposed();
192
+ const session = this.sessions.get(sessionId);
193
+ if (!session) {
194
+ throw new AgentSDKError(`No ChatSession found with id: "${sessionId}"`, AgentSDKErrorType.CHAT_SESSION_NOT_FOUND);
195
+ }
196
+ return session;
197
+ }
198
+ /**
199
+ * @requirements
200
+ * - MUST return an array of all string keys (session IDs) currently tracked in the internal `sessions` map.
201
+ */
202
+ getChatSessionIds() {
203
+ this.assertNotDisposed();
204
+ return Array.from(this.sessions.keys());
205
+ }
206
+ /**
207
+ * @requirements
208
+ * - MUST throw an Error if the provided `sourceSessionId` is not found in the internal `sessions` map.
209
+ * - MUST delegate to `this.harness.cloneThread(this.config.agentId, sourceSessionId)` to create a new thread with cloned history.
210
+ * - MUST instantiate a new `DefaultChatSession` initialized with the harness, agent ID, and the new thread ID.
211
+ * - MUST store the newly created session in the internal `sessions` map, keyed by the new thread ID.
212
+ * - MUST return the newly created session.
213
+ */
214
+ async cloneChatSession(sourceSessionId) {
215
+ this.assertNotDisposed();
216
+ if (!this.sessions.has(sourceSessionId)) {
217
+ throw new AgentSDKError(`No ChatSession found with id: "${sourceSessionId}"`, AgentSDKErrorType.CHAT_SESSION_NOT_FOUND);
218
+ }
219
+ const newThreadId = await this.harness.cloneThread(this.agentId, sourceSessionId);
220
+ return this.attachSession(newThreadId);
221
+ }
222
+ /**
223
+ * @requirements
224
+ * - MUST throw an Error if the provided `sessionId` is not found in the internal `sessions` map.
225
+ * - MUST delegate to `this.harness.compactThread(this.agentId, sessionId)` to create a new thread with a summary.
226
+ * - MUST detach the source session from this agent on success — the
227
+ * harness destroys the source thread, so leaving the wrapper attached
228
+ * would leak a router slice and a `DefaultChatSession` whose thread
229
+ * no longer exists.
230
+ * - MUST instantiate a new `DefaultChatSession` initialized with the harness, agent ID, and the new thread ID.
231
+ * - MUST store the newly created session in the internal `sessions` map, keyed by the new thread ID.
232
+ * - MUST return the newly created session.
233
+ */
234
+ async compactChatSession(sessionId) {
235
+ this.assertNotDisposed();
236
+ const sourceSession = this.sessions.get(sessionId);
237
+ if (!sourceSession) {
238
+ throw new AgentSDKError(`No ChatSession found with id: "${sessionId}"`, AgentSDKErrorType.CHAT_SESSION_NOT_FOUND);
239
+ }
240
+ const newThreadId = await this.harness.compactThread(this.agentId, sessionId);
241
+ this.detachSession(sessionId, sourceSession);
242
+ return this.attachSession(newThreadId);
243
+ }
244
+ /**
245
+ * @requirements
246
+ * - MUST iterate over all active session IDs and sequentially delegate to `this.harness.destroyThread()` for each.
247
+ * - MUST clear the internal `sessions` map.
248
+ * - MUST delegate to `this.harness.destroyAgent(this.config.agentId)` to clean up the agent's harness resources.
249
+ */
250
+ async destroy() {
251
+ if (this.disposed) {
252
+ return;
253
+ }
254
+ for (const [sessionId, session] of this.sessions) {
255
+ await this.harness.destroyThread(this.agentId, sessionId);
256
+ this.detachSession(sessionId, session);
257
+ }
258
+ this.sessions.clear();
259
+ await this.harness.destroyAgent(this.agentId);
260
+ this.llmGatewayClient.dispose();
261
+ this.telemetryBus.emit({
262
+ type: 'agent-destroyed',
263
+ timestamp: this.clock.now(),
264
+ agentId: this.agentId,
265
+ });
266
+ for (const unsub of this.inboundUnsubs)
267
+ unsub();
268
+ for (const unsub of this.parentUnsubs)
269
+ unsub();
270
+ this.telemetryBus.dispose();
271
+ this.logBus.dispose();
272
+ this.disposed = true;
273
+ }
274
+ onTelemetry(callback) {
275
+ this.assertNotDisposed();
276
+ return this.telemetryBus.on(callback);
277
+ }
278
+ onLog(callback) {
279
+ this.assertNotDisposed();
280
+ return this.logBus.on(callback);
281
+ }
282
+ attachSession(threadId) {
283
+ const slice = this.router.registerSession(threadId);
284
+ const session = new DefaultChatSession(this.harness, this.agentId, threadId, slice, {
285
+ telemetry: this.telemetryBus,
286
+ log: this.logBus,
287
+ }, this.clock, this.idGenerator);
288
+ this.sessions.set(threadId, session);
289
+ this.sessionSliceUnregisters.set(threadId, () => this.router.unregisterSession(threadId));
290
+ this.telemetryBus.emit({
291
+ type: 'session-created',
292
+ timestamp: this.clock.now(),
293
+ agentId: this.agentId,
294
+ threadId,
295
+ });
296
+ return session;
297
+ }
298
+ detachSession(threadId, session) {
299
+ session.dispose();
300
+ const unregister = this.sessionSliceUnregisters.get(threadId);
301
+ if (unregister) {
302
+ unregister();
303
+ this.sessionSliceUnregisters.delete(threadId);
304
+ }
305
+ this.sessions.delete(threadId);
306
+ }
307
+ assertNotDisposed() {
308
+ if (this.disposed) {
309
+ throw new AgentSDKError('Agent has been disposed.', AgentSDKErrorType.DISPOSED);
310
+ }
311
+ }
312
+ }
313
+ //# sourceMappingURL=agent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent.js","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,298 @@
1
+ import { type Clock, LogBus, type LogRecord, type UniqueIDGenerator, type Unsubscribe } from '@salesforce/agentic-common';
2
+ import type { AgentHarness } from './harness/agent-harness.js';
3
+ import type { StreamOptions } from './harness/harness-config.js';
4
+ import type { TelemetrySlice } from './internal/telemetry-router.js';
5
+ import type { ChatEvent, ChatStreamResult } from './types/events.js';
6
+ import type { Message } from './types/messages.js';
7
+ import type { TelemetryBus, TelemetryEventCallback } from './types/telemetry-events.js';
8
+ import type { ToolResultInfo } from './types/tools.js';
9
+ /**
10
+ * Options for a single chat interaction.
11
+ */
12
+ export type ChatOptions = StreamOptions;
13
+ /**
14
+ * Parent bus pair used to wire upward forwarding at construction time.
15
+ */
16
+ export type ChatSessionParentBuses = {
17
+ telemetry: TelemetryBus;
18
+ log: LogBus;
19
+ };
20
+ /**
21
+ * An isolated conversation thread with an agent.
22
+ *
23
+ * Each `ChatSession` maps to a single thread in the underlying harness. Sessions maintain their own message
24
+ * history but share the parent agent's tools, workspace, and model configuration.
25
+ *
26
+ * ### Failure handling for streaming methods
27
+ *
28
+ * `chat()`, `submitToolResult()`, `approveToolCall()`, and `declineToolCall()` share a single failure
29
+ * contract. Subscribers registered via {@link ChatSession.subscribe} are guaranteed to observe a
30
+ * terminal event for every turn:
31
+ *
32
+ * - **Pre-stream failure** (the harness rejects before producing a stream): subscribers receive an
33
+ * `ErrorEvent` followed by a `FinishEvent` with `finishReason: 'error'`, then the returned promise
34
+ * rejects with the original error.
35
+ * - **In-stream failure** (the stream yields an `error` event or the underlying generator throws):
36
+ * the event stream itself yields the `ErrorEvent` (synthesizing one from a thrown exception if
37
+ * needed) and, if no `FinishEvent` was already emitted, appends a synthetic
38
+ * `FinishEvent(finishReason: 'error')` at the end. Subscribers see the same sequence.
39
+ * - **Calling a disposed session** throws `AgentSDKError('DISPOSED')` synchronously without
40
+ * notifying subscribers — this is a programmer error, not an operational one.
41
+ */
42
+ export interface ChatSession {
43
+ /** Returns the unique session/thread identifier. */
44
+ getId(): string;
45
+ /**
46
+ * Send a message and stream the agent's response.
47
+ * Returns a {@link ChatStreamResult} which provides multiple ways to consume
48
+ * the stream (text-only or full events).
49
+ *
50
+ * On pre-stream failure, subscribers are notified with `ErrorEvent` + `FinishEvent` before
51
+ * the returned promise rejects. See the interface-level "Failure handling" notes for details.
52
+ *
53
+ * @param message - User message as a plain string.
54
+ * @param options - Per-call options controlling mode, tools, model, etc.
55
+ */
56
+ chat(message: string, options?: ChatOptions): Promise<ChatStreamResult>;
57
+ /**
58
+ * Feed the result of a client-side tool execution back into the conversation
59
+ * and resume stream generation.
60
+ *
61
+ * Resumes the suspended agentic loop from the most recent `chat()` call.
62
+ *
63
+ * On pre-stream failure, subscribers are notified with `ErrorEvent` + `FinishEvent` before
64
+ * the returned promise rejects. See the interface-level "Failure handling" notes for details.
65
+ *
66
+ * @param toolResult - The completed tool execution result.
67
+ */
68
+ submitToolResult(toolResult: ToolResultInfo): Promise<ChatStreamResult>;
69
+ /**
70
+ * Approve a pending tool call, allowing the harness to execute it.
71
+ * Called after receiving a `tool-approval-request` event from the stream.
72
+ *
73
+ * Returns a `ChatStreamResult` containing the continuation stream — the harness
74
+ * executes the approved tool, generates the model's follow-up response, and
75
+ * streams both the text and events back to the caller.
76
+ *
77
+ * On pre-stream failure, subscribers are notified with `ErrorEvent` + `FinishEvent` before
78
+ * the returned promise rejects. See the interface-level "Failure handling" notes for details.
79
+ *
80
+ * @param toolCallId - ID of the pending tool call to approve.
81
+ * @param options - Optional approval metadata.
82
+ * @param options.remember - When `true`, signals that the consumer wants to
83
+ * persist this approval decision (e.g., "always allow this tool").
84
+ * The harness does not implement the persistence — the consumer manages
85
+ * its own permission cache. Borrowed from OpenCode's `once/always/reject` model.
86
+ */
87
+ approveToolCall(toolCallId: string, options?: {
88
+ remember?: boolean;
89
+ }): Promise<ChatStreamResult>;
90
+ /**
91
+ * Decline a pending tool call. The stream resumes with the model
92
+ * acknowledging the decline and potentially suggesting alternatives.
93
+ *
94
+ * Returns a `ChatStreamResult` containing the continuation stream — the harness
95
+ * cancels the pending tool call, generates the model's acknowledgement response,
96
+ * and streams both the text and events back to the caller.
97
+ *
98
+ * On pre-stream failure, subscribers are notified with `ErrorEvent` + `FinishEvent` before
99
+ * the returned promise rejects. See the interface-level "Failure handling" notes for details.
100
+ *
101
+ * @param toolCallId - ID of the pending tool call to decline.
102
+ */
103
+ declineToolCall(toolCallId: string): Promise<ChatStreamResult>;
104
+ /**
105
+ * Retrieve message history for this session.
106
+ *
107
+ * @returns All messages in chronological order (ascending by creation time).
108
+ */
109
+ getMessageHistory(): Promise<Message[]>;
110
+ /** Delete all messages in this session's history. */
111
+ clearHistory(): Promise<void>;
112
+ /**
113
+ * Inject context messages into the thread without triggering an LLM response.
114
+ * Useful for seeding file contents, system instructions, or prior conversation
115
+ * state before the user's first prompt.
116
+ *
117
+ * Borrowed from OpenCode's `session.prompt({ noReply: true })` pattern.
118
+ *
119
+ * @param message - Context to inject (string shorthand or structured messages).
120
+ */
121
+ addContext(message: string | Message[]): Promise<void>;
122
+ /**
123
+ * Register a callback to receive chat events in real-time. Returns an `Unsubscribe` function
124
+ * that removes the listener. The returned function is safe to call after `dispose()` (no-op).
125
+ * Multiple listeners can be registered concurrently.
126
+ */
127
+ subscribe(callback: (event: ChatEvent) => void): Unsubscribe;
128
+ /** Subscribe to telemetry events scoped to this session. Returns an unsubscribe function. */
129
+ onTelemetry(callback: TelemetryEventCallback): Unsubscribe;
130
+ /** Subscribe to structured log records scoped to this session. Returns an unsubscribe function. */
131
+ onLog(callback: (record: LogRecord) => void): Unsubscribe;
132
+ /**
133
+ * Release session-level event resources. Idempotent. After `dispose()`, public methods throw
134
+ * `AgentSDKError('DISPOSED')`.
135
+ */
136
+ dispose(): void;
137
+ }
138
+ /**
139
+ * Default implementation of {@link ChatSession} that delegates all operations
140
+ * to an {@link AgentHarness}. The session holds its agent ID and thread ID
141
+ * and forwards calls with appropriate scoping.
142
+ */
143
+ export declare class DefaultChatSession implements ChatSession {
144
+ private readonly harness;
145
+ private readonly agentId;
146
+ private readonly threadId;
147
+ private readonly chatEventBus;
148
+ private readonly telemetryBus;
149
+ private readonly logBus;
150
+ private readonly inboundUnsubs;
151
+ private readonly parentUnsubs;
152
+ private readonly clock;
153
+ private readonly idGenerator;
154
+ private disposed;
155
+ /**
156
+ * @param harness - The agent harness managing thread and message lifecycle.
157
+ * @param agentId - ID of the agent this session belongs to.
158
+ * @param threadId - ID of the conversation thread backing this session.
159
+ * @param inbound - Router slice delivering harness events routed to this session.
160
+ * @param parent - Parent agent's buses; this session forwards its events upward into them.
161
+ * @param clock - Source of monotonic timestamps for telemetry events. Defaults to `RealClock`.
162
+ * @param idGenerator - Source of message ids for `addContext()`. Defaults to `UUIDGenerator`.
163
+ */
164
+ constructor(harness: AgentHarness, agentId: string, threadId: string, inbound: TelemetrySlice, parent: ChatSessionParentBuses, clock?: Clock, idGenerator?: UniqueIDGenerator);
165
+ getId(): string;
166
+ /**
167
+ * @requirements
168
+ * - MUST delegate to `this.harness.stream()`, passing `this.agentId` and `this.threadId`.
169
+ * - MUST pass the `message` string and `options` arguments directly to the harness.
170
+ * - MUST wrap the returned `eventStream` using `this.wrapEventStream()` so that listeners are notified.
171
+ * - MUST return an object containing the original `textStream` and the wrapped `eventStream`.
172
+ * - MUST notify listeners with `ErrorEvent` + `FinishEvent` and re-throw if the harness throws
173
+ * before returning a stream result.
174
+ */
175
+ chat(message: string, options?: ChatOptions): Promise<ChatStreamResult>;
176
+ /**
177
+ * @requirements
178
+ * - MUST delegate to `this.harness.submitToolResult()`, passing `this.agentId` and `this.threadId`.
179
+ * - MUST pass the `toolResult` argument directly to the harness.
180
+ * - MUST wrap the returned `eventStream` using `this.wrapEventStream()` so that listeners are notified.
181
+ * - MUST return an object containing the original `textStream` and the wrapped `eventStream`.
182
+ * - MUST notify listeners with `ErrorEvent` + `FinishEvent` and re-throw if the harness throws
183
+ * before returning a stream result.
184
+ */
185
+ submitToolResult(toolResult: ToolResultInfo): Promise<ChatStreamResult>;
186
+ /**
187
+ * @requirements
188
+ * - MUST yield each event from the provided `stream`.
189
+ * - MUST emit the event to the internal `chatEventBus`.
190
+ * - MUST always yield a `FinishEvent` with `finishReason: 'error'` after any error path
191
+ * (in-stream `ErrorEvent` or thrown exception) if no `FinishEvent` was already emitted.
192
+ * - MUST emit `chat-stream-completed` telemetry on natural completion (no error path).
193
+ * - MUST emit `chat-stream-error` telemetry exactly once when an error occurred (in-stream
194
+ * `ErrorEvent` or thrown exception). Mutually exclusive with `chat-stream-completed`.
195
+ * - MUST derive `tool-execution-started`, `tool-execution-completed`, and
196
+ * `tool-approval-requested` telemetry from the corresponding `ChatEvent` types so harness
197
+ * implementations don't reimplement this. `tool-execution-completed.durationMs` is measured
198
+ * between the matching `tool-call` and `tool-result`; an unmatched `tool-result` (e.g. from
199
+ * a resume flow that crosses a stream boundary) is skipped.
200
+ *
201
+ * `chat-stream-started` is emitted by the entry-point method (chat / submitToolResult /
202
+ * approveToolCall / declineToolCall) before the harness call so that pre-stream rejections
203
+ * still produce a started+error pair. `startedAt` is captured there and threaded down so
204
+ * `durationMs` measures real elapsed time on both terminal events.
205
+ */
206
+ private wrapEventStream;
207
+ /**
208
+ * @requirements
209
+ * - MUST delegate to `this.harness.approveToolCall()`, passing `this.agentId`, `this.threadId`, and `toolCallId`.
210
+ * - MUST wrap the returned `eventStream` using `this.wrapEventStream()` so that listeners are notified.
211
+ * - MUST return an object containing the original `textStream` and the wrapped `eventStream`.
212
+ * - MUST emit `tool-approval-resolved` telemetry with `approved: true` only after the harness
213
+ * call resolves. Pre-stream rejections produce `chat-stream-error` via `notifyPreStreamError`
214
+ * and intentionally skip approval-resolved emission — consumers correlate the prior
215
+ * `tool-approval-requested` by `toolCallId` and observe the failure on the chat-stream contract.
216
+ * - MUST notify listeners with `ErrorEvent` + `FinishEvent` and re-throw if the harness throws
217
+ * before returning a stream result.
218
+ * - The `options.remember` flag is consumer-only metadata — the harness does not use it.
219
+ */
220
+ approveToolCall(toolCallId: string, _options?: {
221
+ remember?: boolean;
222
+ }): Promise<ChatStreamResult>;
223
+ /**
224
+ * @requirements
225
+ * - MUST delegate to `this.harness.declineToolCall()`, passing `this.agentId`, `this.threadId`, and `toolCallId`.
226
+ * - MUST wrap the returned `eventStream` using `this.wrapEventStream()` so that listeners are notified.
227
+ * - MUST return an object containing the original `textStream` and the wrapped `eventStream`.
228
+ * - MUST emit `tool-approval-resolved` telemetry with `approved: false` only after the harness
229
+ * call resolves. Pre-stream rejections produce `chat-stream-error` via `notifyPreStreamError`
230
+ * and intentionally skip approval-resolved emission.
231
+ * - MUST notify listeners with `ErrorEvent` + `FinishEvent` and re-throw if the harness throws
232
+ * before returning a stream result.
233
+ */
234
+ declineToolCall(toolCallId: string): Promise<ChatStreamResult>;
235
+ /**
236
+ * @requirements
237
+ * - MUST delegate to `this.harness.getMessages()`, passing `this.agentId` and `this.threadId`.
238
+ * - MUST return the result directly.
239
+ */
240
+ getMessageHistory(): Promise<Message[]>;
241
+ /**
242
+ * @requirements
243
+ * - MUST delegate to `this.harness.clearMessages()`, passing `this.agentId` and `this.threadId`.
244
+ */
245
+ clearHistory(): Promise<void>;
246
+ /**
247
+ * @requirements
248
+ * - IF `message` is a `string`, it MUST be formatted into a standard `Message` object array containing exactly one message.
249
+ * - The formatted message MUST have `role: 'user'`.
250
+ * - The formatted message MUST have a newly generated `id` from the injected `idGenerator`.
251
+ * - The formatted message MUST have a `createdAt` timestamp from the injected `clock`.
252
+ * - IF `message` is already an array of `Message` objects, it MUST be used directly.
253
+ * - MUST delegate the final array of messages to `this.harness.addContext()`, passing `this.agentId` and `this.threadId`.
254
+ */
255
+ addContext(message: string | Message[]): Promise<void>;
256
+ /**
257
+ * @requirements
258
+ * - MUST register the provided `callback` on the internal `chatEventBus`.
259
+ * - MUST return an `Unsubscribe` closure that removes the callback when called.
260
+ */
261
+ subscribe(callback: (event: ChatEvent) => void): Unsubscribe;
262
+ onTelemetry(callback: TelemetryEventCallback): Unsubscribe;
263
+ onLog(callback: (record: LogRecord) => void): Unsubscribe;
264
+ dispose(): void;
265
+ private emitToolApprovalResolved;
266
+ /**
267
+ * Derives `tool-execution-*` and `tool-approval-requested` telemetry from `ChatEvent`s as
268
+ * they pass through the stream wrapper. Centralizing the derivation here keeps every harness
269
+ * implementation free of telemetry plumbing — they just yield the right `ChatEvent` shapes.
270
+ *
271
+ * `tool-execution-completed.durationMs` is measured between the matching `tool-call` and
272
+ * `tool-result` using `this.clock`. An unmatched `tool-result` (e.g. a resume flow that
273
+ * crosses a stream boundary so the original `tool-call` was on a previous stream) is skipped
274
+ * — the consumer correlates by `toolCallId` against the earlier `tool-execution-started`.
275
+ */
276
+ private deriveToolTelemetry;
277
+ /**
278
+ * Emits a `chat-stream-started` telemetry event and returns the `startedAt` timestamp the
279
+ * caller threads through to the stream wrapper / pre-stream error notifier so terminal
280
+ * events report real elapsed time.
281
+ *
282
+ * Hoisting this above the harness call keeps the `chat-stream-*` contract honest under
283
+ * pre-stream failures: every `chat-stream-error` follows a `chat-stream-started` for the
284
+ * same `(agentId, threadId)` pair.
285
+ */
286
+ private emitChatStreamStarted;
287
+ /**
288
+ * Emits an `ErrorEvent` + `FinishEvent` to all `subscribe()` listeners and a `chat-stream-error`
289
+ * telemetry event for a failure that occurred before a stream was established. The caller is
290
+ * responsible for re-throwing the original error after calling this — the method itself does
291
+ * not throw.
292
+ *
293
+ * `startedAt` is the timestamp captured by {@link emitChatStreamStarted} so `durationMs`
294
+ * measures real elapsed time even for pre-stream rejections.
295
+ */
296
+ private notifyPreStreamError;
297
+ private assertNotDisposed;
298
+ }