@salesforce/sfdx-agent-sdk 0.24.0 → 0.26.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.
@@ -1,5 +1,6 @@
1
1
  import type { EventBus } from '@salesforce/agentic-common';
2
2
  import type { McpServerErrorDetail, McpServerStatus, McpToolAnnotations } from '../mcp-config.js';
3
+ import type { Decision, ToolPolicyRule } from './tools.js';
3
4
  import type { UsageMetadata } from './usage.js';
4
5
  /**
5
6
  * Telemetry events emitted by the Agent SDK.
@@ -85,6 +86,46 @@ export type ToolApprovalResolvedEvent = Base<'tool-approval-resolved'> & {
85
86
  threadId: string;
86
87
  toolCallId: string;
87
88
  approved: boolean;
89
+ /**
90
+ * `true` when the settle's `remember` flag triggered an
91
+ * `updateAgentConfig` call that appended a `'remember'` policy rule.
92
+ * Absent / `false` when the settle did not persist a rule.
93
+ */
94
+ policyWritten?: boolean;
95
+ };
96
+ /**
97
+ * Emitted by a harness on every harness-executed tool call, after
98
+ * `resolveToolApprovalPolicy` decides and before any gate work happens.
99
+ * Lets telemetry sinks answer "why did this tool prompt / not prompt / get
100
+ * denied?" by reading the rule that fired.
101
+ *
102
+ * Emitted by the harness layer (Phase 2), not the SDK — the SDK exports the
103
+ * variant so the public `TelemetryEvent` union is complete before the harness
104
+ * PRs land.
105
+ */
106
+ export type ToolApprovalPolicyResolvedEvent = Base<'tool-approval-policy-resolved'> & {
107
+ agentId: string;
108
+ threadId: string;
109
+ toolCallId: string;
110
+ toolName: string;
111
+ /** Originating MCP server name, when the tool was discovered through MCP. */
112
+ serverName?: string;
113
+ /** The resolved decision. */
114
+ decision: Decision;
115
+ /**
116
+ * The rule that drove the decision under cross-tier-deny-wins /
117
+ * within-tier-last-wins precedence (the last `'deny'` when denied, else the
118
+ * last matching non-deny rule). `undefined` when no rule matched and the
119
+ * resolver fell back to `defaultToolDecision ?? 'allow'`; the harness emits
120
+ * a `LogBus.warn` in that case.
121
+ */
122
+ matchedRule?: ToolPolicyRule;
123
+ /**
124
+ * Every matching rule in resolved-list order. Optional debug verbosity for
125
+ * sinks that surface "remembered allow overridden by built-in deny"
126
+ * scenarios; most consumers use {@link matchedRule}.
127
+ */
128
+ allMatchedRules?: ToolPolicyRule[];
88
129
  };
89
130
  export type McpServerDiscoveryStartedEvent = Base<'mcp-server-discovery-started'> & {
90
131
  agentId: string;
@@ -137,7 +178,7 @@ export type McpServerStatusChangedEvent = Base<'mcp-server-status-changed'> & {
137
178
  /** Populated when `nextStatus === 'error'`. */
138
179
  error?: string;
139
180
  };
140
- export type TelemetryEvent = AgentCreatedEvent | AgentDestroyedEvent | SessionCreatedEvent | SessionDestroyedEvent | ChatStreamStartedEvent | ChatStreamCompletedEvent | ChatStreamErrorEvent | ToolExecutionStartedEvent | ToolExecutionCompletedEvent | ToolApprovalRequestedEvent | ToolApprovalResolvedEvent | McpServerDiscoveryStartedEvent | McpServerDiscoveryCompletedEvent | McpServerDiscoveryFailedEvent | McpServerStatusChangedEvent;
181
+ export type TelemetryEvent = AgentCreatedEvent | AgentDestroyedEvent | SessionCreatedEvent | SessionDestroyedEvent | ChatStreamStartedEvent | ChatStreamCompletedEvent | ChatStreamErrorEvent | ToolExecutionStartedEvent | ToolExecutionCompletedEvent | ToolApprovalRequestedEvent | ToolApprovalResolvedEvent | ToolApprovalPolicyResolvedEvent | McpServerDiscoveryStartedEvent | McpServerDiscoveryCompletedEvent | McpServerDiscoveryFailedEvent | McpServerStatusChangedEvent;
141
182
  export type TelemetryEventCallback = (event: TelemetryEvent) => void;
142
183
  export type TelemetryBus = EventBus<TelemetryEvent>;
143
184
  export {};
@@ -46,6 +46,89 @@ export type ToolDefinition = {
46
46
  */
47
47
  annotations?: McpToolAnnotations;
48
48
  };
49
+ /**
50
+ * The policy decision for a single tool invocation.
51
+ *
52
+ * - `'allow'` — the harness executes the tool without pausing.
53
+ * - `'deny'` — the harness refuses the call and synthesizes a
54
+ * `tool-result(isError=true)` so the model can recover; no
55
+ * `tool-approval-request` surfaces.
56
+ * - `'require-approval'` — the harness emits a `tool-approval-request`
57
+ * and suspends until the consumer settles via `approveToolCall` /
58
+ * `declineToolCall`.
59
+ */
60
+ export type Decision = 'allow' | 'deny' | 'require-approval';
61
+ /**
62
+ * Structured matcher selecting which tool invocations a {@link ToolPolicyRule}
63
+ * applies to.
64
+ *
65
+ * The three shapes cover every tool category a harness gates:
66
+ *
67
+ * - `'builtin'` — harness-built-in tools (e.g. Claude's `Bash` / `Edit`;
68
+ * Mastra's `updateWorkingMemory`). Names are **harness-specific**, so a
69
+ * `'builtin'` rule applies asymmetrically across harnesses — `Bash` matches
70
+ * on Claude and is a no-op on Mastra. A `'builtin'` matcher matches only when
71
+ * the invocation has no `serverName` (MCP tools carry one).
72
+ * - `'mcp'` — MCP-discovered tools. Both `serverName` and `toolName` are
73
+ * optional: omitting `toolName` matches every tool from the given server;
74
+ * omitting `serverName` matches every tool from every MCP server; omitting
75
+ * both matches every MCP tool (a server-agnostic catch-all). MCP rules are
76
+ * **portable** — the same rule fires on both harnesses for the same tool.
77
+ * - `'mcp-annotation'` — matches any MCP tool whose discovered
78
+ * {@link McpToolAnnotations} carry the specified hint(s). Every field set on
79
+ * the matcher must equal the corresponding field on the discovered
80
+ * annotation (logical AND across set fields). A tool with no annotations does
81
+ * **not** match — the resolver falls through. This is fail-open by default;
82
+ * tenants needing fail-closed for un-annotated MCP servers set
83
+ * {@link AgentConfig.defaultToolDecision} to `'require-approval'`.
84
+ *
85
+ * There is intentionally no `consumer` matcher. Consumer-declared tools (those
86
+ * in {@link AgentConfig.tools} without an `execute`) are gated structurally at
87
+ * the harness boundary and never reach the resolver, so a matcher variant for
88
+ * them would be API surface for an unreachable case.
89
+ */
90
+ export type ToolMatcher = {
91
+ type: 'builtin';
92
+ name: string;
93
+ } | {
94
+ type: 'mcp';
95
+ serverName?: string;
96
+ toolName?: string;
97
+ } | {
98
+ type: 'mcp-annotation';
99
+ readOnlyHint?: boolean;
100
+ destructiveHint?: boolean;
101
+ };
102
+ /**
103
+ * One entry in an agent's tool-approval policy list.
104
+ *
105
+ * Resolution concatenates the SDK's `BUILT_IN_TOOL_POLICIES`, the harness's
106
+ * built-in array, any harness factory rules, and `AgentConfig.toolPolicies`,
107
+ * then applies **cross-tier deny-wins / within-tier last-wins** precedence (see
108
+ * `resolveToolApprovalPolicy`).
109
+ */
110
+ export type ToolPolicyRule = {
111
+ /** Selects which tool invocations this rule applies to. */
112
+ matcher: ToolMatcher;
113
+ /** The decision to apply when {@link matcher} matches. */
114
+ decision: Decision;
115
+ /**
116
+ * Where this rule originated. Advisory metadata only — the resolver
117
+ * **ignores** `source` when computing a decision; telemetry surfaces it so
118
+ * operators can answer "which tier fired."
119
+ *
120
+ * - `'built-in'` — shipped by the SDK's `BUILT_IN_TOOL_POLICIES` or a
121
+ * harness package's `<HARNESS>_BUILT_IN_TOOL_POLICIES` array.
122
+ * - `'agent-config'` — authored by whoever wrote the `AgentConfig`. The
123
+ * default when unset on a consumer-supplied rule.
124
+ * - `'remember'` — written by `approveToolCall(id, { remember: true })` /
125
+ * `declineToolCall(id, { remember: true })`.
126
+ *
127
+ * v1 ships these three values. A future tenant-managed-policy tier will add
128
+ * `'managed'` to this union; that widening is strictly additive.
129
+ */
130
+ source?: 'built-in' | 'agent-config' | 'remember';
131
+ };
49
132
  /**
50
133
  * Base shape for a tool call.
51
134
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/sfdx-agent-sdk",
3
- "version": "0.24.0",
3
+ "version": "0.26.0",
4
4
  "description": "Harness-agnostic agentic infrastructure for Salesforce developer experience tooling",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -40,12 +40,12 @@
40
40
  "LICENSE.txt"
41
41
  ],
42
42
  "dependencies": {
43
- "@salesforce/agentic-common": "0.12.0"
43
+ "@salesforce/agentic-common": "0.13.0"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@eslint/js": "^10.0.1",
47
- "@salesforce/sfdx-agent-harness-claude": "0.20.0",
48
- "@salesforce/sfdx-agent-harness-mastra": "0.23.0",
47
+ "@salesforce/sfdx-agent-harness-claude": "0.22.0",
48
+ "@salesforce/sfdx-agent-harness-mastra": "0.25.0",
49
49
  "@types/node": "^22.20.0",
50
50
  "@vitest/coverage-istanbul": "^4.1.8",
51
51
  "@vitest/eslint-plugin": "^1.6.20",
@@ -64,7 +64,7 @@
64
64
  "vitest": "^4.1.8"
65
65
  },
66
66
  "engines": {
67
- "node": ">=22.19.0"
67
+ "node": ">=22.22.0"
68
68
  },
69
69
  "lint-staged": {
70
70
  "*.{js,jsx,ts,tsx,json,md}": [