@salesforce/sfdx-agent-harness-mastra 0.59.0 → 0.60.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 +9 -0
- package/README.md +5 -0
- package/dist/mastra-approval-coordinator.d.ts +4 -4
- package/dist/mastra-approval-coordinator.js +15 -41
- package/dist/mastra-event-adapter.d.ts +1 -0
- package/dist/mastra-event-adapter.js +9 -0
- package/dist/mastra-harness.d.ts +2 -2
- package/dist/mastra-harness.js +5 -2
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,15 @@
|
|
|
3
3
|
All notable changes to `@salesforce/sfdx-agent-harness-mastra` are documented in this file.
|
|
4
4
|
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
5
5
|
|
|
6
|
+
## [0.60.0] - 2026-09-01
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
- **agent-sdk**: add policy-aware tool declines @W-24048333@ ([#776](https://github.com/forcedotcom/agentic-dx/pull/776))
|
|
10
|
+
|
|
11
|
+
### Chores
|
|
12
|
+
- **deps-dev**: bump typescript-eslint from 8.67.0 to 8.68.0 in the dev-dependencies group across 1 directory ([#777](https://github.com/forcedotcom/agentic-dx/pull/777))
|
|
13
|
+
- **deps-dev**: bump eslint from 10.8.1 to 10.9.1 in the eslint group ([#778](https://github.com/forcedotcom/agentic-dx/pull/778))
|
|
14
|
+
|
|
6
15
|
## [0.59.0] - 2026-08-28
|
|
7
16
|
|
|
8
17
|
### Fixes
|
package/README.md
CHANGED
|
@@ -249,6 +249,11 @@ Consumer-executed tools (`AgentConfig.tools`) always bypass the gate — their e
|
|
|
249
249
|
via `submitToolResult`. Set `StreamOptions.batchApprovals: true` to surface parallel `require-approval` requests on one
|
|
250
250
|
stream for a batch-approval UI.
|
|
251
251
|
|
|
252
|
+
With `@mastra/core` 1.62.0, an organization-policy decline is passed through `agent.declineToolCall({ reason })`. Mastra
|
|
253
|
+
emits one native `tool-output-denied` chunk; the adapter maps it to one `tool-result(isError=true)` carrying the same
|
|
254
|
+
exact message the model sees. Automatic policy denies use `Denied by tool policy.`; reasonless consumer declines retain
|
|
255
|
+
Mastra's `Tool call was not approved by the user` default.
|
|
256
|
+
|
|
252
257
|
### Tool approval timeout
|
|
253
258
|
|
|
254
259
|
When the approval gate is engaged and a tool resolves to `require-approval`, the harness suspends the workflow on each
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Agent } from '@mastra/core/agent';
|
|
2
2
|
import type { RequestContext } from '@mastra/core/request-context';
|
|
3
3
|
import type { ChunkType } from '@mastra/core/stream';
|
|
4
|
-
import { type ChatEvent, type Decision as PolicyDecision, type McpToolAnnotations, type ToolInvocation, type WireCommunicationEvent } from '@salesforce/sfdx-agent-sdk';
|
|
4
|
+
import { type ChatEvent, type Decision as PolicyDecision, type McpToolAnnotations, type ToolDeclineReason, type ToolInvocation, type WireCommunicationEvent } from '@salesforce/sfdx-agent-sdk';
|
|
5
5
|
import { GenSink } from '@salesforce/sfdx-agent-sdk/harness';
|
|
6
6
|
import type { Clock, LogBus } from '@salesforce/agentic-common';
|
|
7
7
|
/**
|
|
@@ -125,8 +125,8 @@ export interface MastraApprovalCoordinatorOptions {
|
|
|
125
125
|
* Responsibilities:
|
|
126
126
|
* - Policy-driven approval gating (Phase 2a): for each non-consumer tool,
|
|
127
127
|
* consult `lookupPolicy` — `allow` silently approves, `deny` silently
|
|
128
|
-
* declines
|
|
129
|
-
*
|
|
128
|
+
* declines through `agent.declineToolCall` so Mastra emits one native
|
|
129
|
+
* `tool-output-denied` result for the model and event adapter,
|
|
130
130
|
* `require-approval` emits a `tool-approval-request` and drives
|
|
131
131
|
* `agent.approveToolCall(...)` / `declineToolCall(...)` when the consumer
|
|
132
132
|
* settles. `batchApprovals` controls whether parallel `require-approval`
|
|
@@ -337,7 +337,7 @@ export declare class MastraApprovalCoordinator {
|
|
|
337
337
|
*/
|
|
338
338
|
private teardown;
|
|
339
339
|
approve(toolCallId: string): void;
|
|
340
|
-
decline(toolCallId: string): void;
|
|
340
|
+
decline(toolCallId: string, reason?: ToolDeclineReason): void;
|
|
341
341
|
private recordDecision;
|
|
342
342
|
/**
|
|
343
343
|
* Parker callback the consumer-tool bridge `execute` handler invokes
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* See LICENSE.txt for license terms.
|
|
4
4
|
*/
|
|
5
5
|
import { AgentSDKError, AgentSDKErrorType, } from '@salesforce/sfdx-agent-sdk';
|
|
6
|
-
import { GenSink } from '@salesforce/sfdx-agent-sdk/harness';
|
|
6
|
+
import { GenSink, resolveToolDeclineModelMessage } from '@salesforce/sfdx-agent-sdk/harness';
|
|
7
7
|
import { MastraEventAdapter } from './mastra-event-adapter.js';
|
|
8
8
|
import { SKILL_BRIDGE_SERVER_ID } from './skill-bridge.js';
|
|
9
9
|
/**
|
|
@@ -22,13 +22,7 @@ function pickIdWithDecision(pendingIds, decisions) {
|
|
|
22
22
|
}
|
|
23
23
|
return undefined;
|
|
24
24
|
}
|
|
25
|
-
|
|
26
|
-
* Result text the coordinator stamps on the synthesized `tool-result` for a
|
|
27
|
-
* policy-denied tool call. Matches the text Mastra folds into the model's
|
|
28
|
-
* context on decline ("Tool call was not approved by the user") so the
|
|
29
|
-
* consumer-facing event and the model's transcript stay consistent.
|
|
30
|
-
*/
|
|
31
|
-
const POLICY_DENIED_RESULT_TEXT = 'Tool call was not approved by the user';
|
|
25
|
+
const POLICY_DENIED_RESULT_TEXT = 'Denied by tool policy.';
|
|
32
26
|
/**
|
|
33
27
|
* Per-`(agentId, threadId)` coordinator that subsumes the pre-#529
|
|
34
28
|
* `MastraStreamBroker` + `activeRuns` pair into one object owning a single
|
|
@@ -37,8 +31,8 @@ const POLICY_DENIED_RESULT_TEXT = 'Tool call was not approved by the user';
|
|
|
37
31
|
* Responsibilities:
|
|
38
32
|
* - Policy-driven approval gating (Phase 2a): for each non-consumer tool,
|
|
39
33
|
* consult `lookupPolicy` — `allow` silently approves, `deny` silently
|
|
40
|
-
* declines
|
|
41
|
-
*
|
|
34
|
+
* declines through `agent.declineToolCall` so Mastra emits one native
|
|
35
|
+
* `tool-output-denied` result for the model and event adapter,
|
|
42
36
|
* `require-approval` emits a `tool-approval-request` and drives
|
|
43
37
|
* `agent.approveToolCall(...)` / `declineToolCall(...)` when the consumer
|
|
44
38
|
* settles. `batchApprovals` controls whether parallel `require-approval`
|
|
@@ -353,8 +347,11 @@ export class MastraApprovalCoordinator {
|
|
|
353
347
|
approve(toolCallId) {
|
|
354
348
|
this.recordDecision('approve', toolCallId, { kind: 'approve' });
|
|
355
349
|
}
|
|
356
|
-
decline(toolCallId) {
|
|
357
|
-
this.recordDecision('decline', toolCallId, {
|
|
350
|
+
decline(toolCallId, reason) {
|
|
351
|
+
this.recordDecision('decline', toolCallId, {
|
|
352
|
+
kind: 'decline',
|
|
353
|
+
...(reason ? { modelMessage: resolveToolDeclineModelMessage(reason) } : {}),
|
|
354
|
+
});
|
|
358
355
|
}
|
|
359
356
|
recordDecision(method, toolCallId, decision) {
|
|
360
357
|
// Issue #589: idempotent settle. A tool call the coordinator
|
|
@@ -618,36 +615,12 @@ export class MastraApprovalCoordinator {
|
|
|
618
615
|
this.decisions.set(tc.toolCallId, { kind: 'approve' });
|
|
619
616
|
}
|
|
620
617
|
else if (decision === 'deny') {
|
|
621
|
-
//
|
|
622
|
-
//
|
|
623
|
-
// the denial into the model's context (persisted as the
|
|
624
|
-
// `role: 'tool'` message "Tool call was not approved by the
|
|
625
|
-
// user"), letting the model recover on the resume stream.
|
|
626
|
-
//
|
|
627
|
-
// We synthesize the consumer-facing `tool-result(isError=true)`
|
|
628
|
-
// ourselves rather than relying on Mastra to emit one:
|
|
629
|
-
// @mastra/core 1.49 no longer emits a standalone
|
|
630
|
-
// `tool-result` chunk on the decline-resume `fullStream`
|
|
631
|
-
// (the denial text lives only inside the persisted
|
|
632
|
-
// `messages.all` payload embedded in `step-finish`), so the
|
|
633
|
-
// old "re-stamp Mastra's native result" path never fired and
|
|
634
|
-
// the SDK `Decision` contract "a denied tool surfaces as
|
|
635
|
-
// `tool-result(isError=true)`" was silently broken. Routing
|
|
636
|
-
// it here — immediately after the already-routed `tool-call`
|
|
637
|
-
// and before the decline resume — keeps the natural
|
|
638
|
-
// tool-call → tool-result ordering the SDK telemetry deriver
|
|
639
|
-
// matches on. The un-emitted approval is marked
|
|
640
|
-
// coordinator-settled so a (spurious) consumer
|
|
641
|
-
// `approveToolCall` for this id is a no-op rather than a
|
|
642
|
-
// TOOL_CALL_NOT_FOUND throw.
|
|
618
|
+
// Mastra emits the native `tool-output-denied` chunk
|
|
619
|
+
// carrying this same reason on the resumed stream.
|
|
643
620
|
this.settledByCoordinator.add(tc.toolCallId);
|
|
644
|
-
this.decisions.set(tc.toolCallId, {
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
toolCallId: tc.toolCallId,
|
|
648
|
-
toolName: tc.toolName,
|
|
649
|
-
result: POLICY_DENIED_RESULT_TEXT,
|
|
650
|
-
isError: true,
|
|
621
|
+
this.decisions.set(tc.toolCallId, {
|
|
622
|
+
kind: 'decline',
|
|
623
|
+
modelMessage: POLICY_DENIED_RESULT_TEXT,
|
|
651
624
|
});
|
|
652
625
|
}
|
|
653
626
|
else if (!this.batchApprovals && !this.emittedApprovals.has(tc.toolCallId)) {
|
|
@@ -710,6 +683,7 @@ export class MastraApprovalCoordinator {
|
|
|
710
683
|
toolCallId,
|
|
711
684
|
memory: this.memory,
|
|
712
685
|
requestContext: this.requestContext,
|
|
686
|
+
...(decision.modelMessage ? { reason: decision.modelMessage } : {}),
|
|
713
687
|
});
|
|
714
688
|
// Post-await guard: abort/dispose may have fired during the
|
|
715
689
|
// round-trip. Release the new run if so.
|
|
@@ -33,6 +33,7 @@ import type { ChatEvent } from '@salesforce/sfdx-agent-sdk';
|
|
|
33
33
|
* | `tool-call-approval` | `tool-approval-request` | `{ toolCall: { toolCallId, toolName, args } }` — stream suspends |
|
|
34
34
|
* | `tool-result` | `tool-result` | `{ toolCallId, toolName, result, isError: false }` |
|
|
35
35
|
* | `tool-error` | `tool-result` | `{ toolCallId, toolName, result: error, isError: true}`|
|
|
36
|
+
* | `tool-output-denied` | `tool-result` | `{ toolCallId, toolName, result: reason, isError: true}`|
|
|
36
37
|
* | `tool-output` | `tool-progress` | `{ toolCallId, toolName, output }` |
|
|
37
38
|
* | `tool-call-delta` | `tool-call-delta` | `{ toolCallId, toolName?, argsTextDelta }` |
|
|
38
39
|
* | `step-start` | `step-start` | `{ stepIndex }` from step metadata |
|
|
@@ -35,6 +35,7 @@ import { isNetworkStreamError } from './stream-errors.js';
|
|
|
35
35
|
* | `tool-call-approval` | `tool-approval-request` | `{ toolCall: { toolCallId, toolName, args } }` — stream suspends |
|
|
36
36
|
* | `tool-result` | `tool-result` | `{ toolCallId, toolName, result, isError: false }` |
|
|
37
37
|
* | `tool-error` | `tool-result` | `{ toolCallId, toolName, result: error, isError: true}`|
|
|
38
|
+
* | `tool-output-denied` | `tool-result` | `{ toolCallId, toolName, result: reason, isError: true}`|
|
|
38
39
|
* | `tool-output` | `tool-progress` | `{ toolCallId, toolName, output }` |
|
|
39
40
|
* | `tool-call-delta` | `tool-call-delta` | `{ toolCallId, toolName?, argsTextDelta }` |
|
|
40
41
|
* | `step-start` | `step-start` | `{ stepIndex }` from step metadata |
|
|
@@ -117,6 +118,14 @@ export class MastraEventAdapter {
|
|
|
117
118
|
error,
|
|
118
119
|
};
|
|
119
120
|
}
|
|
121
|
+
case 'tool-output-denied':
|
|
122
|
+
return {
|
|
123
|
+
type: 'tool-result',
|
|
124
|
+
toolCallId: chunk.payload.toolCallId,
|
|
125
|
+
toolName: chunk.payload.toolName,
|
|
126
|
+
result: chunk.payload.approval.reason ?? 'Tool call was not approved by the user',
|
|
127
|
+
isError: true,
|
|
128
|
+
};
|
|
120
129
|
case 'step-start':
|
|
121
130
|
return { type: 'step-start', stepIndex: this.stepIndex++ };
|
|
122
131
|
case 'step-finish':
|
package/dist/mastra-harness.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Agent } from '@mastra/core/agent';
|
|
2
2
|
import { SkillSearchProcessor, ToolSearchProcessor } from '@mastra/core/processors';
|
|
3
3
|
import { MCPClient } from '@mastra/mcp';
|
|
4
|
-
import { McpServerStatus, type AgentHarness, type WithAgentConfig, type ChatStreamResult, type HarnessAgentConfig, type MCPConfiguration, type McpServerErrorDetail, type McpServerInfo, type Message, type MessagePart, type McpAuthProviders, type ModelConnectivityInfo, type SessionContext, type StreamOptions, type TelemetryEventCallback, type ToolResultInfo, type WireCommunicationEventCallback } from '@salesforce/sfdx-agent-sdk';
|
|
4
|
+
import { McpServerStatus, type AgentHarness, type WithAgentConfig, type ChatStreamResult, type HarnessAgentConfig, type MCPConfiguration, type McpServerErrorDetail, type McpServerInfo, type Message, type MessagePart, type McpAuthProviders, type ModelConnectivityInfo, type SessionContext, type StreamOptions, type TelemetryEventCallback, type ToolDeclineReason, type ToolResultInfo, type WireCommunicationEventCallback } from '@salesforce/sfdx-agent-sdk';
|
|
5
5
|
import { type AgentHooks } from '@salesforce/sfdx-agent-sdk/harness';
|
|
6
6
|
import type { MastraLanguageModelBuilderContext } from './index.js';
|
|
7
7
|
import { MastraApprovalCoordinator } from './mastra-approval-coordinator.js';
|
|
@@ -563,7 +563,7 @@ export declare class MastraHarness implements MastraAgentHarness {
|
|
|
563
563
|
* except the coordinator drives `agent.declineToolCall()` instead of
|
|
564
564
|
* `agent.approveToolCall()`.
|
|
565
565
|
*/
|
|
566
|
-
declineToolCall(agentId: string, threadId: string, toolCallId: string): Promise<void>;
|
|
566
|
+
declineToolCall(agentId: string, threadId: string, toolCallId: string, reason?: ToolDeclineReason): Promise<void>;
|
|
567
567
|
private getCoordinatorOrThrow;
|
|
568
568
|
/**
|
|
569
569
|
* @requirements
|
package/dist/mastra-harness.js
CHANGED
|
@@ -1751,9 +1751,12 @@ export class MastraHarness {
|
|
|
1751
1751
|
* except the coordinator drives `agent.declineToolCall()` instead of
|
|
1752
1752
|
* `agent.approveToolCall()`.
|
|
1753
1753
|
*/
|
|
1754
|
-
async declineToolCall(agentId, threadId, toolCallId) {
|
|
1754
|
+
async declineToolCall(agentId, threadId, toolCallId, reason) {
|
|
1755
1755
|
const coordinator = this.getCoordinatorOrThrow(agentId, threadId, 'declineToolCall');
|
|
1756
|
-
|
|
1756
|
+
if (reason)
|
|
1757
|
+
coordinator.decline(toolCallId, reason);
|
|
1758
|
+
else
|
|
1759
|
+
coordinator.decline(toolCallId);
|
|
1757
1760
|
}
|
|
1758
1761
|
getCoordinatorOrThrow(agentId, threadId, method) {
|
|
1759
1762
|
const agentState = this.getAgentStateOrThrow(agentId);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salesforce/sfdx-agent-harness-mastra",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.60.0",
|
|
4
4
|
"description": "Mastra-backed AgentHarness implementation for @salesforce/sfdx-agent-sdk",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -42,16 +42,16 @@
|
|
|
42
42
|
"openai": "^7.0.0"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
|
-
"@salesforce/sfdx-agent-sdk": "0.
|
|
45
|
+
"@salesforce/sfdx-agent-sdk": "0.61.0"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@eslint/js": "^10.0.1",
|
|
49
49
|
"@salesforce/harness-conformance": "0.0.1",
|
|
50
|
-
"@salesforce/sfdx-agent-sdk": "0.
|
|
50
|
+
"@salesforce/sfdx-agent-sdk": "0.61.0",
|
|
51
51
|
"@types/node": "^22.20.1",
|
|
52
52
|
"@vitest/coverage-istanbul": "^4.1.10",
|
|
53
53
|
"@vitest/eslint-plugin": "^1.6.27",
|
|
54
|
-
"eslint": "^10.
|
|
54
|
+
"eslint": "^10.9.1",
|
|
55
55
|
"eslint-config-prettier": "^10.1.8",
|
|
56
56
|
"eslint-import-resolver-typescript": "^4.4.5",
|
|
57
57
|
"eslint-plugin-import": "^2.32.0",
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"rimraf": "^6.1.3",
|
|
63
63
|
"tsx": "^4.23.12",
|
|
64
64
|
"typescript": "^7.0.2",
|
|
65
|
-
"typescript-eslint": "^8.
|
|
65
|
+
"typescript-eslint": "^8.68.0",
|
|
66
66
|
"vitest": "^4.1.8"
|
|
67
67
|
},
|
|
68
68
|
"engines": {
|