@salesforce/sfdx-agent-sdk 0.21.0 → 0.22.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 +10 -0
  2. package/README.md +12 -11
  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
@@ -0,0 +1,124 @@
1
+ import type { UsageMetadata } from './usage.js';
2
+ /**
3
+ * Wire-level communication events, emitted by the harness layer so consumers
4
+ * see the same shape regardless of harness. The union has three members today:
5
+ * - `LlmRequestEvent` (`type: 'llm-request'`) — one outbound HTTP request to
6
+ * the LLM provider.
7
+ * - `LlmResponseEvent` (`type: 'llm-response'`) — the matching inbound
8
+ * response (or terminal failure).
9
+ * - `WireMonitoringNotSupportedEvent` (`type: 'wire-monitoring-not-supported'`)
10
+ * — emitted by harnesses whose runtime doesn't expose a programmatic seam
11
+ * for per-call wire monitoring (e.g. the Claude harness, where the
12
+ * subprocess owns the HTTP traffic). The event carries a free-form
13
+ * `message` explaining the situation and (when applicable) where the
14
+ * underlying data was written instead.
15
+ *
16
+ * **Privacy contract.** Wire-communication events may contain user prompts,
17
+ * tool arguments, model output, and other PII. They are opt-in (subscribed
18
+ * to via `AgentManager.onWireCommunication`), not flowed to log files by
19
+ * default, and intended for diagnostic / debugging use rather than
20
+ * production observability. Consumers must apply their own redaction before
21
+ * persisting these events.
22
+ *
23
+ * **OTel disambiguation.** Despite the related-sounding name, this is NOT a
24
+ * distributed trace (a tree of spans across services produced by
25
+ * OpenTelemetry / Jaeger / Zipkin / Honeycomb). The `xClientTraceId`
26
+ * correlation header is a label for matching gateway-side logs, not a span.
27
+ *
28
+ * **Cross-harness fidelity.** Differs by harness:
29
+ * - Mastra (in-process) emits one `LlmRequestEvent` + `LlmResponseEvent`
30
+ * pair per outbound HTTP call, with the full request body captured.
31
+ * - The Claude harness can't observe per-call HTTP traffic — the Claude
32
+ * Agent SDK runs the model client in a subprocess with no programmatic
33
+ * interception seam. Instead, when subscribers exist at stream-start, the
34
+ * Claude harness writes the subprocess's debug log to a file under the
35
+ * SDK storage root and emits a single `WireMonitoringNotSupportedEvent`
36
+ * per stream, with the file path embedded in `message`.
37
+ *
38
+ * **Subscriber-presence gating.** The wire-communication event chain
39
+ * (`harness.wireBus → router slice → manager.wireBus`) uses
40
+ * `EventBus.forwardWhileSubscribed`, so a harness with zero downstream
41
+ * consumers pays zero cost — the Claude harness in particular skips writing
42
+ * the debug log and skips emitting the `WireMonitoringNotSupportedEvent`
43
+ * when nobody's listening. Consumers must subscribe via
44
+ * `AgentManager.onWireCommunication` BEFORE the `chat()` they want to
45
+ * observe; subscribing mid-stream misses the in-flight subprocess on Claude.
46
+ */
47
+ export type LlmRequestEvent = {
48
+ /** Direction discriminator. */
49
+ type: 'llm-request';
50
+ /** Wall-clock timestamp when the request was emitted. */
51
+ timestamp: Date;
52
+ /** Fully-qualified URL the harness made the request to. */
53
+ url: string;
54
+ /** HTTP method (`POST`, `GET`, …). */
55
+ method: string;
56
+ /** Native model id sent on the wire (per `ModelConnectivityInfo.nativeModelId`). */
57
+ model: string;
58
+ /** Request body. Best-effort: partial / absent on harnesses that can't observe it. */
59
+ body?: Record<string, unknown>;
60
+ };
61
+ export type LlmResponseEvent = {
62
+ /** Direction discriminator. */
63
+ type: 'llm-response';
64
+ /** Wall-clock timestamp when the response (or terminal failure) was emitted. */
65
+ timestamp: Date;
66
+ /** Native model id from the matching request. */
67
+ model: string;
68
+ /** HTTP status code; `0` for harness-internal terminal failures with no transport response. */
69
+ status: number;
70
+ /** Error captured from a transport / parse failure, if any. */
71
+ error?: Error;
72
+ /**
73
+ * Time from request emit to first byte of response body, in ms.
74
+ *
75
+ * Best-effort and harness-dependent: only populated by harnesses that observe
76
+ * chunk timing on the streaming body. Today the Mastra fetch wrapper emits the
77
+ * `response` event when `globalThis.fetch` resolves (before any chunk has been
78
+ * consumed), so this field is typically `undefined` on Mastra. Consumers should
79
+ * treat the field as advisory and tolerate `undefined`.
80
+ */
81
+ timeToFirstTokenMs?: number;
82
+ /** Per-call token usage, if reported by the upstream. */
83
+ usage?: UsageMetadata;
84
+ /** Wall-clock duration from request emit to terminal event. */
85
+ totalDurationMs?: number;
86
+ /** Best-effort extracted response text (may be omitted by the harness). */
87
+ responseText?: string;
88
+ /** `x-client-trace-id` header value the SDK / harness attached to the request. */
89
+ xClientTraceId?: string;
90
+ };
91
+ /**
92
+ * Emitted by harnesses whose runtime doesn't expose a programmatic seam for
93
+ * per-call wire monitoring. The event signals to consumers that
94
+ * `LlmRequestEvent` / `LlmResponseEvent` won't be produced for this stream
95
+ * and, in `message`, points at whatever workaround the harness can offer
96
+ * (typically a debug log file path).
97
+ *
98
+ * The Claude harness emits this once per `stream()` call when subscribers
99
+ * exist at stream-start. The Mastra harness never emits it; it has the
100
+ * fetch-wrapper seam and produces structured request/response events
101
+ * directly.
102
+ */
103
+ export type WireMonitoringNotSupportedEvent = {
104
+ /** Direction discriminator. */
105
+ type: 'wire-monitoring-not-supported';
106
+ /** Wall-clock timestamp when the event was emitted (typically end-of-stream). */
107
+ timestamp: Date;
108
+ /** Identifier of the harness emitting the event (e.g. `'claude'`). */
109
+ harnessId: string;
110
+ /**
111
+ * Free-form, human-readable explanation of why direct wire monitoring
112
+ * isn't available for this stream and where (if anywhere) the underlying
113
+ * data has been written instead. Treat as display text, not parseable
114
+ * structured data — harnesses may include file paths, URLs, or caveats
115
+ * inline. May contain newlines.
116
+ */
117
+ message: string;
118
+ };
119
+ /**
120
+ * Union over every wire-communication event the harness layer can emit.
121
+ * Discriminate on the `type` field.
122
+ */
123
+ export type WireCommunicationEvent = LlmRequestEvent | LlmResponseEvent | WireMonitoringNotSupportedEvent;
124
+ export type WireCommunicationEventCallback = (event: WireCommunicationEvent) => void;
@@ -0,0 +1,6 @@
1
+ /*
2
+ * Copyright 2026, Salesforce, Inc. All rights reserved.
3
+ * See LICENSE.txt for license terms.
4
+ */
5
+ export {};
6
+ //# sourceMappingURL=wire-communication-event.js.map
@@ -0,0 +1,39 @@
1
+ import type { Unsubscribe } from '@salesforce/agentic-common';
2
+ import type { WireCommunicationEvent } from './types/wire-communication-event.js';
3
+ /**
4
+ * Source the writer subscribes to. Anything exposing `onWireCommunication(callback)`
5
+ * works — typically an {@link AgentManager}, but tests may construct a smaller
6
+ * surface.
7
+ */
8
+ export type WireCommunicationEmitter = {
9
+ onWireCommunication(callback: (event: WireCommunicationEvent) => void): Unsubscribe;
10
+ };
11
+ export type WireCommunicationFileWriterOptions = {
12
+ /** Absolute (or cwd-relative) path the writer appends Markdown to. */
13
+ filePath: string;
14
+ /**
15
+ * Optional callback invoked when an `appendFile` rejection is observed
16
+ * (disk full, parent directory unwritable, etc.). Without this, write
17
+ * failures are routed to `console.error` so a debugging session can't
18
+ * silently lose events — the writer is itself a debugging aid, so a
19
+ * silently-empty file would be the worst possible failure mode.
20
+ */
21
+ onError?: (error: Error, event: WireCommunicationEvent) => void;
22
+ };
23
+ /**
24
+ * Subscribes to a {@link WireCommunicationEmitter} (typically an
25
+ * `AgentManager`) and writes each {@link WireCommunicationEvent} to a
26
+ * Markdown file. Used as a debugging aid — consumers can attach one with a
27
+ * single line and inspect the resulting file to see request / response pairs
28
+ * in chronological order.
29
+ *
30
+ * The output may contain user prompts, tool arguments, and model output —
31
+ * apply your own redaction before sharing the file.
32
+ */
33
+ export declare class WireCommunicationFileWriter {
34
+ private options;
35
+ private unsubscribe;
36
+ constructor(emitter: WireCommunicationEmitter, options?: Partial<WireCommunicationFileWriterOptions>);
37
+ detach(): void;
38
+ private handleEvent;
39
+ }
@@ -0,0 +1,142 @@
1
+ /*
2
+ * Copyright 2026, Salesforce, Inc. All rights reserved.
3
+ * See LICENSE.txt for license terms.
4
+ */
5
+ import { appendFile } from 'node:fs/promises';
6
+ import { resolve } from 'node:path';
7
+ const DefaultWireCommunicationFileWriterOptions = {
8
+ filePath: resolve(process.cwd(), 'wire-communication.md'),
9
+ };
10
+ function validateOptions(options) {
11
+ if (typeof options.filePath !== 'string' || options.filePath.trim() === '') {
12
+ throw new Error('WireCommunicationFileWriterOptions.filePath must be a non-empty string');
13
+ }
14
+ return options;
15
+ }
16
+ /**
17
+ * Subscribes to a {@link WireCommunicationEmitter} (typically an
18
+ * `AgentManager`) and writes each {@link WireCommunicationEvent} to a
19
+ * Markdown file. Used as a debugging aid — consumers can attach one with a
20
+ * single line and inspect the resulting file to see request / response pairs
21
+ * in chronological order.
22
+ *
23
+ * The output may contain user prompts, tool arguments, and model output —
24
+ * apply your own redaction before sharing the file.
25
+ */
26
+ export class WireCommunicationFileWriter {
27
+ options;
28
+ unsubscribe;
29
+ constructor(emitter, options = {}) {
30
+ const resolvedOptions = {
31
+ ...DefaultWireCommunicationFileWriterOptions,
32
+ ...options,
33
+ };
34
+ this.options = validateOptions(resolvedOptions);
35
+ this.unsubscribe = emitter.onWireCommunication((event) => {
36
+ void this.handleEvent(event);
37
+ });
38
+ }
39
+ detach() {
40
+ this.unsubscribe?.();
41
+ this.unsubscribe = undefined;
42
+ }
43
+ async handleEvent(event) {
44
+ const content = formatAsMarkdown(event);
45
+ try {
46
+ await appendFile(this.options.filePath, content);
47
+ }
48
+ catch (err) {
49
+ const error = err instanceof Error ? err : new Error(String(err));
50
+ if (this.options.onError) {
51
+ try {
52
+ this.options.onError(error, event);
53
+ }
54
+ catch {
55
+ // Swallow errors from a misbehaving consumer callback;
56
+ // the writer must not throw out of the event listener.
57
+ }
58
+ }
59
+ else {
60
+ console.error(`[WireCommunicationFileWriter] Failed to append to "${this.options.filePath}":`, error);
61
+ }
62
+ }
63
+ }
64
+ }
65
+ function formatAsMarkdown(event) {
66
+ switch (event.type) {
67
+ case 'llm-request':
68
+ return formatRequestMarkdown(event);
69
+ case 'llm-response':
70
+ return formatResponseMarkdown(event);
71
+ case 'wire-monitoring-not-supported':
72
+ return formatWireMonitoringNotSupportedMarkdown(event);
73
+ }
74
+ }
75
+ function formatRequestMarkdown(event) {
76
+ const dateTime = event.timestamp.toISOString();
77
+ const body = event.body;
78
+ const lines = [
79
+ `### ${dateTime} [${event.type}]`,
80
+ ``,
81
+ `**Url**: ${event.method} ${event.url}`,
82
+ ``,
83
+ `**Model**: ${event.model}`,
84
+ ``,
85
+ ];
86
+ if (body !== undefined) {
87
+ lines.push(`**Body**:`, '```json', JSON.stringify(body, null, 2), '```', ``);
88
+ }
89
+ lines.push(``);
90
+ return lines.join('\n');
91
+ }
92
+ function formatResponseMarkdown(event) {
93
+ // Optional fields are emitted only when populated. Mastra's fetch wrapper
94
+ // populates `status`, `totalDurationMs`, and (when the gateway returns it)
95
+ // `xClientTraceId`; future harnesses may populate `usage`, `responseText`,
96
+ // and `timeToFirstTokenMs`. Skipping the unset fields keeps the markdown
97
+ // signal-dense rather than padding every section with `N/A` lines.
98
+ const dateTime = event.timestamp.toISOString();
99
+ const lines = [
100
+ `### ${dateTime} [${event.type}]`,
101
+ ``,
102
+ `**Model**: ${event.model}`,
103
+ ``,
104
+ `**Status**: ${event.status}`,
105
+ ``,
106
+ ];
107
+ if (event.xClientTraceId !== undefined) {
108
+ lines.push(`**x-client-trace-id**: ${event.xClientTraceId}`, ``);
109
+ }
110
+ if (event.totalDurationMs !== undefined) {
111
+ lines.push(`**Total duration**: ${event.totalDurationMs}`, ``);
112
+ }
113
+ if (event.timeToFirstTokenMs !== undefined) {
114
+ lines.push(`**Time to first token**: ${event.timeToFirstTokenMs}`, ``);
115
+ }
116
+ if (event.error !== undefined) {
117
+ lines.push(`**Error**: ${event.error.message}`, ``);
118
+ }
119
+ if (event.usage !== undefined) {
120
+ if (event.usage.inputTokens !== undefined) {
121
+ lines.push(`**Input tokens**: ${event.usage.inputTokens}`, ``);
122
+ }
123
+ if (event.usage.outputTokens !== undefined) {
124
+ lines.push(`**Output tokens**: ${event.usage.outputTokens}`, ``);
125
+ }
126
+ if (event.usage.totalTokens !== undefined) {
127
+ lines.push(`**Total tokens**: ${event.usage.totalTokens}`, ``);
128
+ }
129
+ if (event.usage.reasoningTokens !== undefined) {
130
+ lines.push(`**Reasoning tokens**: ${event.usage.reasoningTokens}`, ``);
131
+ }
132
+ }
133
+ if (event.responseText !== undefined) {
134
+ lines.push(`**Response**:`, event.responseText, ``);
135
+ }
136
+ return lines.join('\n');
137
+ }
138
+ function formatWireMonitoringNotSupportedMarkdown(event) {
139
+ const dateTime = event.timestamp.toISOString();
140
+ return [`### ${dateTime} [${event.type}]`, ``, `**Harness**: ${event.harnessId}`, ``, event.message, ``, ``].join('\n');
141
+ }
142
+ //# sourceMappingURL=wire-communication-file-writer.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/sfdx-agent-sdk",
3
- "version": "0.21.0",
3
+ "version": "0.22.0",
4
4
  "description": "Harness-agnostic agentic infrastructure for Salesforce developer experience tooling",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -40,17 +40,16 @@
40
40
  "LICENSE.txt"
41
41
  ],
42
42
  "dependencies": {
43
- "@salesforce/agentic-common": "0.10.0",
44
- "@salesforce/llm-gateway-sdk": "0.14.0"
43
+ "@salesforce/agentic-common": "0.11.0"
45
44
  },
46
45
  "devDependencies": {
47
46
  "@eslint/js": "^10.0.1",
48
- "@salesforce/sfdx-agent-harness-claude": "0.17.0",
49
- "@salesforce/sfdx-agent-harness-mastra": "0.20.0",
50
- "@types/node": "^22.19.20",
47
+ "@salesforce/sfdx-agent-harness-claude": "0.18.0",
48
+ "@salesforce/sfdx-agent-harness-mastra": "0.21.0",
49
+ "@types/node": "^22.19.21",
51
50
  "@vitest/coverage-istanbul": "^4.1.8",
52
- "@vitest/eslint-plugin": "^1.6.19",
53
- "eslint": "^10.4.1",
51
+ "@vitest/eslint-plugin": "^1.6.20",
52
+ "eslint": "^10.5.0",
54
53
  "eslint-config-prettier": "^10.1.8",
55
54
  "eslint-import-resolver-typescript": "^4.4.5",
56
55
  "eslint-plugin-import": "^2.32.0",