@salesforce/sfdx-agent-sdk 0.48.0 → 0.50.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/README.md +55 -23
- package/dist/chat-session.d.ts +61 -6
- package/dist/chat-session.js +25 -3
- package/dist/harness/agent-harness.d.ts +92 -4
- package/dist/index.d.ts +3 -2
- package/dist/index.js +1 -1
- package/dist/models/index.d.ts +10 -0
- package/dist/models/index.js +13 -0
- package/dist/types/messages.d.ts +10 -3
- package/dist/types/session-context.d.ts +19 -0
- package/dist/types/session-context.js +6 -0
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,16 @@
|
|
|
3
3
|
All notable changes to `@salesforce/sfdx-agent-sdk` are documented in this file.
|
|
4
4
|
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
5
5
|
|
|
6
|
+
## [0.50.0] - 2026-08-17
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
- **agent-sdk,harness-mastra,harness-claude,harness-openai**: session-context contract + conformance gate @W-23632685@ ([#747](https://github.com/forcedotcom/agentic-dx/pull/747))
|
|
10
|
+
|
|
11
|
+
## [0.49.0] - 2026-08-14
|
|
12
|
+
|
|
13
|
+
### Fixes
|
|
14
|
+
- **agent-sdk**: export ACCEPTED_MODEL_WIRE_IDS for wire-boundary modelId validation @W-23694670@ ([#752](https://github.com/forcedotcom/agentic-dx/pull/752))
|
|
15
|
+
|
|
6
16
|
## [0.48.0] - 2026-08-10
|
|
7
17
|
|
|
8
18
|
_No changes — released alongside dependent packages._
|
package/README.md
CHANGED
|
@@ -159,22 +159,25 @@ keeps unparameterized call sites working.
|
|
|
159
159
|
|
|
160
160
|
A single conversation thread.
|
|
161
161
|
|
|
162
|
-
| Method | Signature | Description
|
|
163
|
-
| ------------------- | ------------------------------------------------------------------------- |
|
|
164
|
-
| `getId` | `() => string` | Session/thread identifier.
|
|
165
|
-
| `chat` | `(message: string, options?: ChatOptions) => Promise<ChatStreamResult>` | Send a message and stream the response. The returned `eventStream` is the single iterator for the entire chat turn.
|
|
166
|
-
| `submitToolResult` | `(toolResult: ToolResultInfo) => Promise<void>` | Return a consumer-executed tool result. Control message on the existing turn — post-resume events flow on the same stream.
|
|
167
|
-
| `approveToolCall` | `(toolCallId: string, options?: { remember?: boolean }) => Promise<void>` | Approve a pending tool call. `{ remember: true }` ("Allow always") appends an `allow` rule to `AgentConfig.toolPolicies` and persists it before settling. Control message on the existing turn.
|
|
168
|
-
| `declineToolCall` | `(toolCallId: string, options?: { remember?: boolean }) => Promise<void>` | Decline a pending tool call. `{ remember: true }` ("Deny always") appends a `deny` rule and persists it before settling. Control message on the existing turn.
|
|
169
|
-
| `getMessageHistory` | `() => Promise<Message[]>` | Retrieve all messages in chronological order.
|
|
170
|
-
| `clearHistory` | `() => Promise<void>` | Delete all messages.
|
|
171
|
-
| `getContextUsage` | `() => ContextUsage` | Snapshot of how much of the model's context window the most recent turn used.
|
|
172
|
-
| `
|
|
173
|
-
| `
|
|
174
|
-
| `
|
|
175
|
-
| `
|
|
176
|
-
| `
|
|
177
|
-
| `
|
|
162
|
+
| Method | Signature | Description |
|
|
163
|
+
| ------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
164
|
+
| `getId` | `() => string` | Session/thread identifier. |
|
|
165
|
+
| `chat` | `(message: string, options?: ChatOptions) => Promise<ChatStreamResult>` | Send a message and stream the response. The returned `eventStream` is the single iterator for the entire chat turn. |
|
|
166
|
+
| `submitToolResult` | `(toolResult: ToolResultInfo) => Promise<void>` | Return a consumer-executed tool result. Control message on the existing turn — post-resume events flow on the same stream. |
|
|
167
|
+
| `approveToolCall` | `(toolCallId: string, options?: { remember?: boolean }) => Promise<void>` | Approve a pending tool call. `{ remember: true }` ("Allow always") appends an `allow` rule to `AgentConfig.toolPolicies` and persists it before settling. Control message on the existing turn. |
|
|
168
|
+
| `declineToolCall` | `(toolCallId: string, options?: { remember?: boolean }) => Promise<void>` | Decline a pending tool call. `{ remember: true }` ("Deny always") appends a `deny` rule and persists it before settling. Control message on the existing turn. |
|
|
169
|
+
| `getMessageHistory` | `() => Promise<Message[]>` | Retrieve all messages in chronological order. |
|
|
170
|
+
| `clearHistory` | `() => Promise<void>` | Delete all messages. |
|
|
171
|
+
| `getContextUsage` | `() => ContextUsage` | Snapshot of how much of the model's context window the most recent turn used. |
|
|
172
|
+
| `addMessages` | `(message: string \| Message[]) => Promise<void>` | Append real transcript messages (`user` / `assistant` / `tool`) to the thread **without requesting an agent response** — the write-only half of a turn. The messages persist, appear in `getMessageHistory()`, and replay to the model as prior conversation on the next `chat()`. Use it to seed earlier turns (e.g. file contents as a user message) before the first live prompt; the SDK equivalent of the service's `POST /messages` with `noReply=true`. **Not** `setSessionContext`: this writes _transcript history_ (visible in `getMessageHistory`, additive); `setSessionContext` writes an _out-of-history overlay object_ (never in history, whole-object replace). `'system'` is not a valid role here — system-level state rides `setSessionContext` / `AgentConfig.instructions`. |
|
|
173
|
+
| `addContext` | `(message: string \| Message[]) => Promise<void>` | **Deprecated** — renamed to `addMessages` (identical signature/behavior); delegates to it. The old name read as a sibling of `setSessionContext`, but the two are distinct channels. Will be removed in a future release; migrate to `addMessages`. |
|
|
174
|
+
| `setSessionContext` | `(content: SessionContext) => Promise<void>` | Replace this session's session-context object in full (whole-object set, not a merge). Persisted per-thread and durable across restart; kept out of message history, so it never appears in `getMessageHistory()`. **Currently persistence-only** — the stored object is not yet rendered into the model's system-level context; per-harness rendering on subsequent turns lands in a follow-up. Delegates to `AgentHarness.setSessionContext` — see that method's JSDoc for the full delivery/durability/isolation contract. |
|
|
175
|
+
| `getSessionContext` | `() => Promise<SessionContext>` | Read this session's current session-context object. Returns `{}` (an empty object) — never `null` or `undefined` — when nothing has been set on this thread yet, so callers never need a null-check. Unrelated to `getContextUsage()`, which reports context-window token occupancy, not the seeded context object. |
|
|
176
|
+
| `subscribe` | `(callback: (event: ChatEvent) => void) => void` | Register a real-time event listener. |
|
|
177
|
+
| `unsubscribe` | `(callback: (event: ChatEvent) => void) => void` | Remove a listener. |
|
|
178
|
+
| `onTelemetry` | `(callback: TelemetryEventCallback) => Unsubscribe` | Subscribe to telemetry scoped to this session. |
|
|
179
|
+
| `onLog` | `(callback: (record: LogRecord) => void) => Unsubscribe` | Subscribe to logs scoped to this session. |
|
|
180
|
+
| `dispose` | `() => void` | Release session-level event resources. Idempotent. |
|
|
178
181
|
|
|
179
182
|
### `ChatStreamResult`
|
|
180
183
|
|
|
@@ -505,9 +508,15 @@ type ToolResultInfo = {
|
|
|
505
508
|
### Message Types
|
|
506
509
|
|
|
507
510
|
```typescript
|
|
511
|
+
// The message role, exported as a named type. `'system'` is not a member:
|
|
512
|
+
// system-level state rides `AgentConfig.instructions` and `setSessionContext`,
|
|
513
|
+
// never the transcript, so `getMessages` never returns a `system` message and
|
|
514
|
+
// one cannot be seeded via `addMessages` / `POST /messages`.
|
|
515
|
+
type MessageRole = 'user' | 'assistant' | 'tool';
|
|
516
|
+
|
|
508
517
|
type Message = {
|
|
509
518
|
id: string;
|
|
510
|
-
role:
|
|
519
|
+
role: MessageRole;
|
|
511
520
|
content: string | MessagePart[];
|
|
512
521
|
createdAt?: Date;
|
|
513
522
|
};
|
|
@@ -520,7 +529,7 @@ type ReasoningPart = { type: 'reasoning'; text: string };
|
|
|
520
529
|
|
|
521
530
|
// Tool invocation / result segments persisted on `Message.content`. Extend the
|
|
522
531
|
// `ToolCallInfo` / `ToolResultInfo` shapes with a discriminator. They appear in
|
|
523
|
-
// message history; they are NOT valid input on `chat()` / `
|
|
532
|
+
// message history; they are NOT valid input on `chat()` / `addMessages()`.
|
|
524
533
|
type ToolCallPart = ToolCallInfo & { type: 'tool-call' };
|
|
525
534
|
type ToolResultPart = ToolResultInfo & { type: 'tool-result' };
|
|
526
535
|
|
|
@@ -533,9 +542,28 @@ type FilePart = { type: 'file'; mimeType: 'application/pdf'; data: string; fileN
|
|
|
533
542
|
|
|
534
543
|
- Messages returned from `ChatSession.getMessageHistory()` always have `createdAt` populated, and the array is sorted
|
|
535
544
|
ascending by `createdAt`. Consumer code can read `msg.createdAt` directly.
|
|
536
|
-
- Consumers constructing `Message` literals for `ChatSession.
|
|
545
|
+
- Consumers constructing `Message` literals for `ChatSession.addMessages()` may omit `createdAt`; the SDK backfills the
|
|
537
546
|
current time before forwarding to the harness. Pass an explicit value to override.
|
|
538
547
|
|
|
548
|
+
### Session Context Types
|
|
549
|
+
|
|
550
|
+
The per-chat session-context channel (`ChatSession.setSessionContext` / `getSessionContext`, documented in the
|
|
551
|
+
`ChatSession` method table above) carries a JSON-serializable object, exported as two named types:
|
|
552
|
+
|
|
553
|
+
```typescript
|
|
554
|
+
// A JSON value: the transitive closure of what survives a JSON round-trip.
|
|
555
|
+
type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue };
|
|
556
|
+
|
|
557
|
+
// The session-context object: a bare JSON object at the root (no envelope).
|
|
558
|
+
type SessionContext = { [key: string]: JsonValue };
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
`setSessionContext` persists the object verbatim (so a nested / array / null-bearing object round-trips unchanged) and
|
|
562
|
+
`getSessionContext` returns `{}` — never `null` / `undefined` — when nothing was set. The channel is disjoint from
|
|
563
|
+
message history: a set object never appears in `getMessageHistory()`. Session context is the harness-agnostic channel
|
|
564
|
+
for system-level background state — which is why `MessageRole` (above) has no `'system'` member: there is no separate
|
|
565
|
+
role-capability surface to consult, because the type itself makes a `system` transcript message unconstructible.
|
|
566
|
+
|
|
539
567
|
#### Multimodal input
|
|
540
568
|
|
|
541
569
|
`ChatSession.chat()` (and the harness `stream()` it delegates to) accept either a plain string or a `MessagePart[]`. Use
|
|
@@ -570,9 +598,9 @@ await session.chat([
|
|
|
570
598
|
},
|
|
571
599
|
]);
|
|
572
600
|
|
|
573
|
-
//
|
|
574
|
-
// the SDK backfills it before forwarding to the harness.
|
|
575
|
-
await session.
|
|
601
|
+
// Seed multimodal transcript messages before a chat turn. `createdAt` is
|
|
602
|
+
// omitted — the SDK backfills it before forwarding to the harness.
|
|
603
|
+
await session.addMessages([
|
|
576
604
|
{
|
|
577
605
|
id: 'ctx-screenshot',
|
|
578
606
|
role: 'user',
|
|
@@ -1176,6 +1204,10 @@ recognizes today, plus an escape hatch for consumers who need a Claude variant t
|
|
|
1176
1204
|
- `Model` — abstract base class. Returned by `Models.getByName(...)` and accepted as an `AgentConfig.modelId` value.
|
|
1177
1205
|
- `ModelName` — enum of in-tree model identifiers.
|
|
1178
1206
|
- `Models` — registry: `Models.getByName(name)`, `Models.getDefault()`.
|
|
1207
|
+
- `ACCEPTED_MODEL_WIRE_IDS` — the `readonly string[]` union of canonical `ModelName` values and the legacy pinned
|
|
1208
|
+
`llmgateway__*` alias keys `Models.getByName(...)` remaps. Consumers that validate an inbound `modelId` (e.g. a REST
|
|
1209
|
+
body schema) should enumerate against this set, not `Object.values(ModelName)` alone, so a legacy id the resolver can
|
|
1210
|
+
heal is not rejected at the wire boundary.
|
|
1179
1211
|
- `createClaudeModel(gatewayId, overrides?)` — escape-hatch factory for opting into a Bedrock-Anthropic Claude variant
|
|
1180
1212
|
the SDK has not released yet (`AgentConfig.modelId` accepts the returned instance directly).
|
|
1181
1213
|
- `ClaudeModelOverrides` — optional caps for `createClaudeModel`.
|
|
@@ -1451,7 +1483,7 @@ This package publishes two ESM entry points:
|
|
|
1451
1483
|
| `matchesAlwaysActive` | `/harness` only | Predicate `(entries, serverName, toolName) → boolean` consulted per-tool when stamping always-load metadata or partitioning a tool-search pool. Use this instead of pattern-matching entries by hand so harness behavior stays uniform. |
|
|
1452
1484
|
| `validateAlwaysActiveEntry` | `/harness` only | Throws on a malformed entry (`{}`, both fields empty). Call once per entry at the harness boundary so a typo fails loud at config time rather than silently dropping the entry on every `stream()`. |
|
|
1453
1485
|
| `splitToolResultsIntoToolMessages` | `/harness` only | Read-side normalizer `(Message[]) → Message[]`. Hoists every completed tool call's `tool-result` part onto its own `role: 'tool'` message (leaving the `tool-call` on the assistant message), so `getMessages()` returns the canonical cross-harness layout. Idempotent; backfills a blank result `toolName` from the matching call; preserves `isError` and (in-memory) `error`. Call it at the end of `getMessages()`. (Whether `error` survives to the returned history is a harness-persistence concern — see `AgentHarness.getMessages`; `isError` always survives.) |
|
|
1454
|
-
| `mergeToolResultsIntoAssistant` | `/harness` only | Write-side inverse `(Message[]) → Message[]`. Folds each `role: 'tool'` message's result back adjacent to its `tool-call` in the preceding assistant message, so a runtime that stores a completed call as one merged object round-trips losslessly. Call it at the start of `
|
|
1486
|
+
| `mergeToolResultsIntoAssistant` | `/harness` only | Write-side inverse `(Message[]) → Message[]`. Folds each `role: 'tool'` message's result back adjacent to its `tool-call` in the preceding assistant message, so a runtime that stores a completed call as one merged object round-trips losslessly. Call it at the start of `addMessages()` before persisting. |
|
|
1455
1487
|
|
|
1456
1488
|
Minimal skeleton:
|
|
1457
1489
|
|
package/dist/chat-session.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type { StreamOptions } from './harness/harness-config.js';
|
|
|
4
4
|
import type { TelemetrySlice } from './internal/telemetry-router.js';
|
|
5
5
|
import type { ChatEvent, ChatStreamResult } from './types/events.js';
|
|
6
6
|
import type { Message, MessagePart } from './types/messages.js';
|
|
7
|
+
import type { SessionContext } from './types/session-context.js';
|
|
7
8
|
import { type TelemetryBus, type TelemetryEventCallback } from './types/telemetry-events.js';
|
|
8
9
|
import type { ToolPolicyRule, ToolResultInfo } from './types/tools.js';
|
|
9
10
|
import type { ContextUsage } from './types/usage.js';
|
|
@@ -191,15 +192,55 @@ export interface ChatSession {
|
|
|
191
192
|
*/
|
|
192
193
|
getContextUsage(): ContextUsage;
|
|
193
194
|
/**
|
|
194
|
-
*
|
|
195
|
-
*
|
|
196
|
-
*
|
|
195
|
+
* Append real conversation messages to the thread **without requesting an
|
|
196
|
+
* agent response** — the write-only half of a normal turn. The messages
|
|
197
|
+
* become part of the transcript: they are persisted as `Message`s, appear in
|
|
198
|
+
* {@link getMessageHistory}, and are replayed to the model as prior
|
|
199
|
+
* conversation on the next {@link chat}. Use it to seed a thread with earlier
|
|
200
|
+
* `user` / `assistant` / `tool` turns (e.g. file contents surfaced as a user
|
|
201
|
+
* message, or a reconstructed prior conversation) before the user's first
|
|
202
|
+
* live prompt. This is the SDK's equivalent of the service's
|
|
203
|
+
* `POST /messages` with `noReply=true`, and mirrors OpenCode's
|
|
204
|
+
* `session.prompt({ noReply: true })` pattern.
|
|
197
205
|
*
|
|
198
|
-
*
|
|
206
|
+
* NOT the same as {@link setSessionContext}. `addMessages` writes *transcript
|
|
207
|
+
* history* (visible in `getMessageHistory`, one message per call, additive).
|
|
208
|
+
* `setSessionContext` writes an *out-of-history overlay object* (never in
|
|
209
|
+
* `getMessageHistory`, whole-object replace, delivered as system-level
|
|
210
|
+
* background context each turn). Reach for `addMessages` when you want the
|
|
211
|
+
* model to see prior turns; reach for `setSessionContext` when you want to
|
|
212
|
+
* attach standing background state that should not read as a conversation
|
|
213
|
+
* message. `'system'` is not a valid role here (see {@link MessageRole}) —
|
|
214
|
+
* system-level state belongs on `setSessionContext` or `AgentConfig.instructions`.
|
|
199
215
|
*
|
|
200
|
-
* @param message -
|
|
216
|
+
* @param message - Messages to append (string shorthand for a single `user`
|
|
217
|
+
* message, or structured `Message[]`).
|
|
218
|
+
*/
|
|
219
|
+
addMessages(message: string | Message[]): Promise<void>;
|
|
220
|
+
/**
|
|
221
|
+
* @deprecated Renamed to {@link addMessages} (identical signature and
|
|
222
|
+
* behavior). The old name read as a sibling of {@link setSessionContext}, but
|
|
223
|
+
* the two are different channels — `addMessages` appends transcript history,
|
|
224
|
+
* `setSessionContext` sets an out-of-history overlay. `addContext` will be
|
|
225
|
+
* removed in a future release; migrate to `addMessages`.
|
|
226
|
+
*
|
|
227
|
+
* @param message - Messages to append (string shorthand or structured `Message[]`).
|
|
201
228
|
*/
|
|
202
229
|
addContext(message: string | Message[]): Promise<void>;
|
|
230
|
+
/**
|
|
231
|
+
* Replace this session's context object in full. Delegates to
|
|
232
|
+
* {@link AgentHarness.setSessionContext}; see that method's JSDoc for the
|
|
233
|
+
* full delivery/durability/isolation contract.
|
|
234
|
+
*
|
|
235
|
+
* @param content - The full replacement session-context object.
|
|
236
|
+
*/
|
|
237
|
+
setSessionContext(content: SessionContext): Promise<void>;
|
|
238
|
+
/**
|
|
239
|
+
* Read this session's current context object. `{}` if never set.
|
|
240
|
+
*
|
|
241
|
+
* @returns The last object passed to {@link setSessionContext}.
|
|
242
|
+
*/
|
|
243
|
+
getSessionContext(): Promise<SessionContext>;
|
|
203
244
|
/**
|
|
204
245
|
* Register a callback to receive chat events in real-time. Returns an `Unsubscribe` function
|
|
205
246
|
* that removes the listener. The returned function is safe to call after `dispose()` (no-op).
|
|
@@ -419,9 +460,23 @@ export declare class DefaultChatSession implements ChatSession {
|
|
|
419
460
|
* - The formatted message MUST have a newly generated `id` from the injected `idGenerator`.
|
|
420
461
|
* - The formatted message MUST have a `createdAt` timestamp from the injected `clock`.
|
|
421
462
|
* - IF `message` is already an array of `Message` objects, it MUST be used directly.
|
|
422
|
-
* - MUST delegate the final array of messages to `this.harness.
|
|
463
|
+
* - MUST delegate the final array of messages to `this.harness.addMessages()`, passing `this.agentId` and `this.threadId`.
|
|
423
464
|
*/
|
|
465
|
+
addMessages(message: string | Message[]): Promise<void>;
|
|
466
|
+
/** @deprecated Renamed to {@link addMessages}; this delegates verbatim. */
|
|
424
467
|
addContext(message: string | Message[]): Promise<void>;
|
|
468
|
+
/**
|
|
469
|
+
* @requirements
|
|
470
|
+
* - MUST delegate to `this.harness.setSessionContext()`, passing `this.agentId`, `this.threadId`,
|
|
471
|
+
* and the `content` object unchanged (whole-object set, no merge).
|
|
472
|
+
*/
|
|
473
|
+
setSessionContext(content: SessionContext): Promise<void>;
|
|
474
|
+
/**
|
|
475
|
+
* @requirements
|
|
476
|
+
* - MUST delegate to `this.harness.getSessionContext()`, passing `this.agentId` and `this.threadId`.
|
|
477
|
+
* - MUST return the result directly (`{}` when nothing has been set on this thread).
|
|
478
|
+
*/
|
|
479
|
+
getSessionContext(): Promise<SessionContext>;
|
|
425
480
|
/**
|
|
426
481
|
* @requirements
|
|
427
482
|
* - MUST register the provided `callback` on the internal `chatEventBus`.
|
package/dist/chat-session.js
CHANGED
|
@@ -401,9 +401,9 @@ export class DefaultChatSession {
|
|
|
401
401
|
* - The formatted message MUST have a newly generated `id` from the injected `idGenerator`.
|
|
402
402
|
* - The formatted message MUST have a `createdAt` timestamp from the injected `clock`.
|
|
403
403
|
* - IF `message` is already an array of `Message` objects, it MUST be used directly.
|
|
404
|
-
* - MUST delegate the final array of messages to `this.harness.
|
|
404
|
+
* - MUST delegate the final array of messages to `this.harness.addMessages()`, passing `this.agentId` and `this.threadId`.
|
|
405
405
|
*/
|
|
406
|
-
async
|
|
406
|
+
async addMessages(message) {
|
|
407
407
|
this.assertNotDisposed();
|
|
408
408
|
const messages = typeof message === 'string'
|
|
409
409
|
? [
|
|
@@ -423,7 +423,29 @@ export class DefaultChatSession {
|
|
|
423
423
|
// same helper at their own `addContext` boundary so a
|
|
424
424
|
// direct `harness.addContext` call gets the same shape.
|
|
425
425
|
backfillCreatedAt(message, this.clock);
|
|
426
|
-
await this.harness.
|
|
426
|
+
await this.harness.addMessages(this.agentId, this.threadId, messages);
|
|
427
|
+
}
|
|
428
|
+
/** @deprecated Renamed to {@link addMessages}; this delegates verbatim. */
|
|
429
|
+
async addContext(message) {
|
|
430
|
+
await this.addMessages(message);
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* @requirements
|
|
434
|
+
* - MUST delegate to `this.harness.setSessionContext()`, passing `this.agentId`, `this.threadId`,
|
|
435
|
+
* and the `content` object unchanged (whole-object set, no merge).
|
|
436
|
+
*/
|
|
437
|
+
async setSessionContext(content) {
|
|
438
|
+
this.assertNotDisposed();
|
|
439
|
+
await this.harness.setSessionContext(this.agentId, this.threadId, content);
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* @requirements
|
|
443
|
+
* - MUST delegate to `this.harness.getSessionContext()`, passing `this.agentId` and `this.threadId`.
|
|
444
|
+
* - MUST return the result directly (`{}` when nothing has been set on this thread).
|
|
445
|
+
*/
|
|
446
|
+
async getSessionContext() {
|
|
447
|
+
this.assertNotDisposed();
|
|
448
|
+
return this.harness.getSessionContext(this.agentId, this.threadId);
|
|
427
449
|
}
|
|
428
450
|
/**
|
|
429
451
|
* @requirements
|
|
@@ -8,6 +8,7 @@ import type { AgentHooks } from '../types/redaction.js';
|
|
|
8
8
|
import type { WireCommunicationEventCallback } from '../types/wire-communication-event.js';
|
|
9
9
|
import type { AgentConfig, HarnessAgentConfig, StreamOptions } from './harness-config.js';
|
|
10
10
|
import type { ModelConnectivityInfo } from '../types/model-connectivity-info.js';
|
|
11
|
+
import type { SessionContext } from '../types/session-context.js';
|
|
11
12
|
export declare const SUPPORTED_PROTOCOL_VERSIONS: readonly [1];
|
|
12
13
|
/**
|
|
13
14
|
* Opt-in helper that brands a harness type with the {@link AgentConfig}
|
|
@@ -453,12 +454,99 @@ export interface AgentHarness {
|
|
|
453
454
|
*/
|
|
454
455
|
clearMessages(agentId: string, threadId: string): Promise<void>;
|
|
455
456
|
/**
|
|
456
|
-
*
|
|
457
|
-
*
|
|
457
|
+
* Append real transcript messages to a thread **without triggering an LLM
|
|
458
|
+
* response** — the write-only half of a turn. The messages join the thread's
|
|
459
|
+
* message history: they persist, surface in {@link getMessages} (in canonical
|
|
460
|
+
* layout, ascending by `createdAt`), and replay to the model as prior
|
|
461
|
+
* conversation on the next {@link stream}. Used to seed a thread with earlier
|
|
462
|
+
* `user` / `assistant` / `tool` turns (e.g. file contents as a user message,
|
|
463
|
+
* or a reconstructed conversation) before the first live prompt.
|
|
464
|
+
*
|
|
465
|
+
* Distinct from {@link setSessionContext}, which writes an out-of-history
|
|
466
|
+
* overlay object that never appears in {@link getMessages}. `addMessages`
|
|
467
|
+
* writes transcript history; `setSessionContext` writes standing background
|
|
468
|
+
* state. `'system'` is not a valid role on the messages here (see
|
|
469
|
+
* {@link MessageRole}); system-level state rides `setSessionContext` or
|
|
470
|
+
* `AgentConfig.instructions`, never the transcript.
|
|
458
471
|
*
|
|
459
472
|
* @param agentId - ID of the agent.
|
|
460
473
|
* @param threadId - ID of the conversation thread.
|
|
461
|
-
* @param messages -
|
|
474
|
+
* @param messages - Transcript messages to append to the thread history.
|
|
462
475
|
*/
|
|
463
|
-
|
|
476
|
+
addMessages(agentId: string, threadId: string, messages: Message[]): Promise<void>;
|
|
477
|
+
/**
|
|
478
|
+
* @deprecated Renamed to {@link addMessages} (identical signature/behavior).
|
|
479
|
+
* The name collided semantically with {@link setSessionContext}; the two are
|
|
480
|
+
* distinct channels (transcript history vs. out-of-history overlay). Optional
|
|
481
|
+
* during the deprecation window: harnesses keep it as a thin delegator to
|
|
482
|
+
* {@link addMessages} so existing direct callers keep working, and it will be
|
|
483
|
+
* removed in a follow-up. New harnesses need only implement `addMessages`.
|
|
484
|
+
*
|
|
485
|
+
* @param agentId - ID of the agent.
|
|
486
|
+
* @param threadId - ID of the conversation thread.
|
|
487
|
+
* @param messages - Transcript messages to append to the thread history.
|
|
488
|
+
*/
|
|
489
|
+
addContext?(agentId: string, threadId: string, messages: Message[]): Promise<void>;
|
|
490
|
+
/**
|
|
491
|
+
* Replace this thread's session-context object in full (whole-object set,
|
|
492
|
+
* not a merge — merge/delete-key semantics are computed by the caller,
|
|
493
|
+
* typically the service layer's read-merge-write over {@link getSessionContext}).
|
|
494
|
+
*
|
|
495
|
+
* The full contract lands in two stages. Persistence is implemented on every
|
|
496
|
+
* harness in the contract-and-persistence milestone (W-23632685, this PR);
|
|
497
|
+
* model delivery and `compactThread` carry-forward are implemented per
|
|
498
|
+
* harness in a follow-up PR (Mastra W-23632686, Claude W-23632691, OpenAI
|
|
499
|
+
* W-23632694) and are pinned by the seam-gated `runSessionContextConformance`
|
|
500
|
+
* assertions, which register only once a harness supplies its
|
|
501
|
+
* render/carry-forward adapter. The staged bullets below state the contract
|
|
502
|
+
* each harness converges on — not behavior guaranteed live before that
|
|
503
|
+
* harness's follow-up PR lands.
|
|
504
|
+
*
|
|
505
|
+
* Live on every harness this milestone:
|
|
506
|
+
* - Persists per-thread, durably (survives harness restart against the same
|
|
507
|
+
* `storageRootFolder`), with no LLM call.
|
|
508
|
+
* - Does NOT appear in {@link getMessages} output — this channel is disjoint
|
|
509
|
+
* from message history.
|
|
510
|
+
* - Kept separate from the agent's `instructions` — the stored object is an
|
|
511
|
+
* overlay for the turn's system context (once delivery lands), never a
|
|
512
|
+
* mutation of `AgentConfig.instructions` or any shared, cross-thread state.
|
|
513
|
+
* - {@link getSessionContext} returns ONLY the consumer object, and this
|
|
514
|
+
* whole-object set replaces ONLY the consumer's session context. It stays
|
|
515
|
+
* separate from the SDK-owned carried-forward compaction summary (the
|
|
516
|
+
* `previousConversationContext` out-of-history channel that lands later with
|
|
517
|
+
* the History-Continuity subsystem, W-23799514) and MUST NOT clobber it —
|
|
518
|
+
* the two out-of-history channels never share storage. That summary channel
|
|
519
|
+
* does not exist yet, so the "must not clobber" half is pinned by a
|
|
520
|
+
* seam-gated assertion that goes live alongside it.
|
|
521
|
+
*
|
|
522
|
+
* Staged per harness (follow-up PR above; conformance-pinned):
|
|
523
|
+
* - Delivered to the model as system-level context on every subsequent turn
|
|
524
|
+
* on this thread, rendered deterministically (stable key order) so two
|
|
525
|
+
* calls with the same object produce byte-identical rendered text.
|
|
526
|
+
* - Carries forward to the new thread on {@link compactThread}.
|
|
527
|
+
* `compactThread` ROTATES to a new thread (summarize → seed the new
|
|
528
|
+
* thread → best-effort destroy the source), so the source thread does not
|
|
529
|
+
* survive the call. Carry-forward therefore means the object is COPIED
|
|
530
|
+
* from the source onto the new thread DURING compaction, before the
|
|
531
|
+
* source is retired — reading the returned thread yields the same object
|
|
532
|
+
* the source carried.
|
|
533
|
+
*
|
|
534
|
+
* @param agentId - ID of the agent.
|
|
535
|
+
* @param threadId - ID of the conversation thread.
|
|
536
|
+
* @param content - The full replacement session-context object.
|
|
537
|
+
*/
|
|
538
|
+
setSessionContext(agentId: string, threadId: string, content: SessionContext): Promise<void>;
|
|
539
|
+
/**
|
|
540
|
+
* Read this thread's current session-context object.
|
|
541
|
+
*
|
|
542
|
+
* Returns `{}` — never `null` or `undefined` — when nothing has been set on
|
|
543
|
+
* this thread yet. An empty and a never-set context are behaviorally
|
|
544
|
+
* identical (both contribute nothing to the rendered prompt), so callers
|
|
545
|
+
* never need a null-check.
|
|
546
|
+
*
|
|
547
|
+
* @param agentId - ID of the agent.
|
|
548
|
+
* @param threadId - ID of the conversation thread.
|
|
549
|
+
* @returns The last object passed to {@link setSessionContext}, or `{}`.
|
|
550
|
+
*/
|
|
551
|
+
getSessionContext(agentId: string, threadId: string): Promise<SessionContext>;
|
|
464
552
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
export type { Message, MessagePart, ImagePart, FilePart } from './types/messages.js';
|
|
1
|
+
export type { Message, MessagePart, MessageRole, ImagePart, FilePart } from './types/messages.js';
|
|
2
|
+
export type { JsonValue, SessionContext } from './types/session-context.js';
|
|
2
3
|
export type { ChatEvent, StartEvent, TextDeltaEvent, ReasoningDeltaEvent, ToolCallEvent, ToolCallDeltaEvent, ToolApprovalRequestEvent, ToolResultEvent, ToolProgressEvent, StepStartEvent, StepFinishEvent, ErrorEvent, FinishEvent, ChatStreamResult, } from './types/events.js';
|
|
3
4
|
export type { Decision, ToolDefinition, ToolCallInfo, ToolMatcher, ToolPolicyRule, ToolResultInfo, } from './types/tools.js';
|
|
4
5
|
export { BUILT_IN_TOOL_POLICIES, SKILL_BRIDGE_SERVER_ID, definePolicy, matcherMatches, resolveToolApprovalPolicy, } from './policy-resolver.js';
|
|
@@ -9,7 +10,7 @@ export type { AgentConfig, HarnessAgentConfig, StreamOptions } from './harness/h
|
|
|
9
10
|
export { DEFAULT_MAX_STEPS } from './harness/harness-config.js';
|
|
10
11
|
export type { MCPConfiguration, MCPServerConfig, MCPStdioServerConfig, MCPRemoteServerConfig, McpServerInfo, McpServerErrorCategory, McpServerErrorDetail, McpToolInfo, McpToolAnnotations, } from './mcp-config.js';
|
|
11
12
|
export { McpServerStatus, mcpServerConfigEqual } from './mcp-config.js';
|
|
12
|
-
export { Model, ModelName, createClaudeModel, Models, validateMultimodalFiles } from './models/index.js';
|
|
13
|
+
export { Model, ModelName, createClaudeModel, Models, validateMultimodalFiles, ACCEPTED_MODEL_WIRE_IDS, } from './models/index.js';
|
|
13
14
|
export type { ClaudeModelOverrides, MultimodalFile, SupportedFileFormat, ThinkingMode } from './models/index.js';
|
|
14
15
|
export { MimeType } from './models/index.js';
|
|
15
16
|
export { inferSfApiEnv, SfApiEnv } from '@salesforce/agentic-common';
|
package/dist/index.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
export { BUILT_IN_TOOL_POLICIES, SKILL_BRIDGE_SERVER_ID, definePolicy, matcherMatches, resolveToolApprovalPolicy, } from './policy-resolver.js';
|
|
11
11
|
export { DEFAULT_MAX_STEPS } from './harness/harness-config.js';
|
|
12
12
|
export { McpServerStatus, mcpServerConfigEqual } from './mcp-config.js';
|
|
13
|
-
export { Model, ModelName, createClaudeModel, Models, validateMultimodalFiles } from './models/index.js';
|
|
13
|
+
export { Model, ModelName, createClaudeModel, Models, validateMultimodalFiles, ACCEPTED_MODEL_WIRE_IDS, } from './models/index.js';
|
|
14
14
|
export { MimeType } from './models/index.js';
|
|
15
15
|
export { inferSfApiEnv, SfApiEnv } from '@salesforce/agentic-common';
|
|
16
16
|
// ── Agent Layer ─────────────────────────────────────────────────────
|
package/dist/models/index.d.ts
CHANGED
|
@@ -25,6 +25,16 @@ export { createClaudeModel, type ClaudeModelOverrides } from './create-claude-mo
|
|
|
25
25
|
* (`sfdc_ai__DefaultGPT5` etc.) are already live `ModelName` values.
|
|
26
26
|
*/
|
|
27
27
|
export declare const LEGACY_MODEL_ID_ALIASES: Readonly<Record<string, ModelName>>;
|
|
28
|
+
/**
|
|
29
|
+
* Every model id accepted on an inbound request: the canonical {@link ModelName}
|
|
30
|
+
* values plus the legacy `llmgateway__*` alias keys {@link getByName} remaps.
|
|
31
|
+
*
|
|
32
|
+
* Consumers validating an inbound `modelId` should enumerate against this set, not
|
|
33
|
+
* `Object.values(ModelName)` alone — otherwise a legacy id is rejected at the wire
|
|
34
|
+
* boundary before the resolver that would heal it is reached. Deriving both this
|
|
35
|
+
* set and the resolver from {@link LEGACY_MODEL_ID_ALIASES} keeps them from drifting.
|
|
36
|
+
*/
|
|
37
|
+
export declare const ACCEPTED_MODEL_WIRE_IDS: readonly string[];
|
|
28
38
|
export declare function getDefault(): Model;
|
|
29
39
|
export declare function getByName(modelName: ModelName): Model;
|
|
30
40
|
export declare const Models: {
|
package/dist/models/index.js
CHANGED
|
@@ -51,6 +51,19 @@ export const LEGACY_MODEL_ID_ALIASES = {
|
|
|
51
51
|
llmgateway__BedrockAnthropicClaude47Opus: ModelName.CLAUDE_OPUS_4_7,
|
|
52
52
|
llmgateway__BedrockAnthropicClaude48Opus: ModelName.CLAUDE_OPUS_4_8,
|
|
53
53
|
};
|
|
54
|
+
/**
|
|
55
|
+
* Every model id accepted on an inbound request: the canonical {@link ModelName}
|
|
56
|
+
* values plus the legacy `llmgateway__*` alias keys {@link getByName} remaps.
|
|
57
|
+
*
|
|
58
|
+
* Consumers validating an inbound `modelId` should enumerate against this set, not
|
|
59
|
+
* `Object.values(ModelName)` alone — otherwise a legacy id is rejected at the wire
|
|
60
|
+
* boundary before the resolver that would heal it is reached. Deriving both this
|
|
61
|
+
* set and the resolver from {@link LEGACY_MODEL_ID_ALIASES} keeps them from drifting.
|
|
62
|
+
*/
|
|
63
|
+
export const ACCEPTED_MODEL_WIRE_IDS = Object.freeze([
|
|
64
|
+
...Object.values(ModelName),
|
|
65
|
+
...Object.keys(LEGACY_MODEL_ID_ALIASES),
|
|
66
|
+
]);
|
|
54
67
|
export function getDefault() {
|
|
55
68
|
return new ClaudeSonnet46();
|
|
56
69
|
}
|
package/dist/types/messages.d.ts
CHANGED
|
@@ -2,12 +2,19 @@ import type { ToolCallInfo, ToolResultInfo } from './tools.js';
|
|
|
2
2
|
/**
|
|
3
3
|
* Role of a message in a conversation. Aligned with AI SDK `ModelMessage` roles.
|
|
4
4
|
*
|
|
5
|
-
* - `system` -- Instructions to the model (not visible to end users)
|
|
6
5
|
* - `user` -- Messages from the human user
|
|
7
6
|
* - `assistant` -- Messages generated by the model
|
|
8
7
|
* - `tool` -- Results from tool executions
|
|
8
|
+
*
|
|
9
|
+
* `'system'` is deliberately NOT a member. System-level state has two dedicated,
|
|
10
|
+
* harness-agnostic channels — static agent instructions (`AgentConfig.instructions`)
|
|
11
|
+
* and per-thread session context (`setSessionContext`) — neither of which flows
|
|
12
|
+
* through the `Message` transcript. `getMessages` never emits a `system` message,
|
|
13
|
+
* and a consumer can never construct one to seed via `addContext` / `POST /messages`.
|
|
14
|
+
* A harness may widen this role set internally for its own runtime, but MUST NOT
|
|
15
|
+
* surface a `system`-role message back across the SDK boundary.
|
|
9
16
|
*/
|
|
10
|
-
export type MessageRole = '
|
|
17
|
+
export type MessageRole = 'user' | 'assistant' | 'tool';
|
|
11
18
|
/**
|
|
12
19
|
* A single message in a conversation thread.
|
|
13
20
|
*
|
|
@@ -27,7 +34,7 @@ export type Message = {
|
|
|
27
34
|
/**
|
|
28
35
|
* Timestamp of when the message was created. **Always populated** on
|
|
29
36
|
* messages returned from `ChatSession.getMessageHistory()`. **Optional on
|
|
30
|
-
* write** — consumers constructing `Message` for `ChatSession.
|
|
37
|
+
* write** — consumers constructing `Message` for `ChatSession.addMessages()`
|
|
31
38
|
* may omit it; the SDK backfills the current time before forwarding to
|
|
32
39
|
* the harness, so the on-read contract still holds.
|
|
33
40
|
*
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A JSON-serializable value. Excludes `undefined`, functions, `Date`, class
|
|
3
|
+
* instances, and anything else that doesn't survive `JSON.stringify` /
|
|
4
|
+
* `JSON.parse` unchanged.
|
|
5
|
+
*/
|
|
6
|
+
export type JsonValue = string | number | boolean | null | JsonValue[] | {
|
|
7
|
+
[key: string]: JsonValue;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* A per-thread session-context object: arbitrary consumer-defined keys mapped
|
|
11
|
+
* to JSON-serializable values, delivered to the model as system-level context
|
|
12
|
+
* on every turn and kept durable across harness restart.
|
|
13
|
+
*
|
|
14
|
+
* See {@link AgentHarness.setSessionContext} / {@link AgentHarness.getSessionContext}
|
|
15
|
+
* for the full contract every harness implementation upholds.
|
|
16
|
+
*/
|
|
17
|
+
export type SessionContext = {
|
|
18
|
+
[key: string]: JsonValue;
|
|
19
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salesforce/sfdx-agent-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.50.0",
|
|
4
4
|
"description": "Harness-agnostic agentic infrastructure for Salesforce developer experience tooling",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -47,9 +47,9 @@
|
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"@eslint/js": "^10.0.1",
|
|
50
|
-
"@salesforce/sfdx-agent-harness-claude": "0.
|
|
51
|
-
"@salesforce/sfdx-agent-harness-mastra": "0.
|
|
52
|
-
"@salesforce/sfdx-agent-harness-openai": "0.
|
|
50
|
+
"@salesforce/sfdx-agent-harness-claude": "0.46.0",
|
|
51
|
+
"@salesforce/sfdx-agent-harness-mastra": "0.49.0",
|
|
52
|
+
"@salesforce/sfdx-agent-harness-openai": "0.15.0",
|
|
53
53
|
"@types/node": "^22.20.1",
|
|
54
54
|
"@vitest/coverage-istanbul": "^4.1.10",
|
|
55
55
|
"@vitest/eslint-plugin": "^1.6.26",
|