@salesforce/sfdx-agent-harness-mastra 0.31.0 → 0.32.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 +18 -0
- package/README.md +15 -21
- package/dist/mastra-approval-coordinator.d.ts +11 -35
- package/dist/mastra-approval-coordinator.js +42 -56
- package/dist/mastra-harness-factory.d.ts +1 -1
- package/dist/mastra-harness.d.ts +16 -40
- package/dist/mastra-harness.js +31 -72
- package/dist/mastra-tool-mapper.js +4 -4
- package/dist/pass-through-anthropic.js +7 -1
- package/package.json +15 -13
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,24 @@
|
|
|
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.32.0] - 2026-07-14
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
- **agent-sdk,harness-mastra,harness-claude**: cross-harness conformance test suite @W-23372341@ ([#663](https://github.com/forcedotcom/agentic-dx/pull/663))
|
|
10
|
+
- **sdk,harness-mastra,harness-claude**: delete deprecated tool-approval surfaces @W-23090034 ([#637](https://github.com/forcedotcom/agentic-dx/pull/637))
|
|
11
|
+
|
|
12
|
+
### Fixes
|
|
13
|
+
- **harness-conformance**: static version instead of -dev.N so release check passes @W-23446682@ ([#670](https://github.com/forcedotcom/agentic-dx/pull/670))
|
|
14
|
+
- **harness-mastra**: thread model maxOutputTokens instead of flooring max_tokens at 4096 @W-23413250@ ([#656](https://github.com/forcedotcom/agentic-dx/pull/656))
|
|
15
|
+
- **harness-mastra**: synthesize deny tool-result for @mastra/core 1.49 ([#653](https://github.com/forcedotcom/agentic-dx/pull/653))
|
|
16
|
+
|
|
17
|
+
### Chores
|
|
18
|
+
- **deps-dev**: bump typescript-eslint from 8.62.1 to 8.63.0 in the dev-dependencies group across 1 directory ([#665](https://github.com/forcedotcom/agentic-dx/pull/665))
|
|
19
|
+
- **deps-dev**: bump the eslint group across 1 directory with 2 updates ([#659](https://github.com/forcedotcom/agentic-dx/pull/659))
|
|
20
|
+
- **deps**: bump the mastra group across 1 directory with 4 updates ([#661](https://github.com/forcedotcom/agentic-dx/pull/661))
|
|
21
|
+
- **deps-dev**: bump @vitest/eslint-plugin from 1.6.22 to 1.6.23 in the vitest group ([#660](https://github.com/forcedotcom/agentic-dx/pull/660))
|
|
22
|
+
- **deps-dev**: bump the vitest group across 1 directory with 3 updates ([#655](https://github.com/forcedotcom/agentic-dx/pull/655))
|
|
23
|
+
|
|
6
24
|
## [0.31.0] - 2026-07-08
|
|
7
25
|
|
|
8
26
|
### Fixes
|
package/README.md
CHANGED
|
@@ -44,12 +44,12 @@ Linux and macOS release the handle eagerly; the retry options are no-ops on thos
|
|
|
44
44
|
|
|
45
45
|
### Core
|
|
46
46
|
|
|
47
|
-
| Export | Kind | Description
|
|
48
|
-
| ------------------------------- | --------- |
|
|
49
|
-
| `MastraHarnessFactory` | class | `HarnessFactory` that produces a Mastra-backed `AgentHarness` bound to a storage root.
|
|
50
|
-
| `MastraHarnessFactoryConfig` | interface | Configuration for `MastraHarnessFactory` (`toolApprovalTimeoutMs?`).
|
|
47
|
+
| Export | Kind | Description |
|
|
48
|
+
| ------------------------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
49
|
+
| `MastraHarnessFactory` | class | `HarnessFactory` that produces a Mastra-backed `AgentHarness` bound to a storage root. |
|
|
50
|
+
| `MastraHarnessFactoryConfig` | interface | Configuration for `MastraHarnessFactory` (`toolApprovalTimeoutMs?`). |
|
|
51
51
|
| `MASTRA_BUILT_IN_TOOL_POLICIES` | const | Frozen `ReadonlyArray<ToolPolicyRule>` the harness feeds the SDK's `resolveToolApprovalPolicy` as the `tiers.harness` slice — `allow` rules for Mastra's built-in `updateWorkingMemory` / `skill` / `skill_search` / `skill_read` tools. |
|
|
52
|
-
| `MASTRA_BUILT_IN_TOOL_NAMES` | const | The four built-in tool leaf names `MASTRA_BUILT_IN_TOOL_POLICIES` covers.
|
|
52
|
+
| `MASTRA_BUILT_IN_TOOL_NAMES` | const | The four built-in tool leaf names `MASTRA_BUILT_IN_TOOL_POLICIES` covers. |
|
|
53
53
|
|
|
54
54
|
Connectivity (model + provider auth) is supplied by the SDK's `AgentConnectivityResolver`. Use the bundled
|
|
55
55
|
`DefaultAgentConnectivityResolver` for the standard Salesforce LLM Gateway path, or `ApiKeyConnectivityResolver` for
|
|
@@ -58,11 +58,10 @@ on `ModelConnectivityInfo.providerHint` internally to pick the right pass-throug
|
|
|
58
58
|
|
|
59
59
|
#### HTTPS proxy routing
|
|
60
60
|
|
|
61
|
-
The harness honors `HTTPS_PROXY` / `HTTP_PROXY` / `NO_PROXY` (either casing) via
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
overhead).
|
|
61
|
+
The harness honors `HTTPS_PROXY` / `HTTP_PROXY` / `NO_PROXY` (either casing) via `@salesforce/agentic-common`'s
|
|
62
|
+
`resolveProxyDispatcher()`. When either proxy var is set at factory `create()` time, the harness wraps every in-process
|
|
63
|
+
`fetch` call site (LLM gateway language-model builders + MCP remote transports) with an undici-backed routing function —
|
|
64
|
+
no `globalThis` mutation. When no proxy env is set, `fetch` runs unwrapped (zero overhead).
|
|
66
65
|
|
|
67
66
|
**Snapshot timing.** `HTTPS_PROXY` / `HTTP_PROXY` are captured at `create()` time and pinned for the harness's lifetime;
|
|
68
67
|
`NO_PROXY` is re-evaluated per request. Set the proxy env vars before constructing the factory's first harness.
|
|
@@ -204,19 +203,14 @@ harness resolves a decision and acts on it:
|
|
|
204
203
|
`tool-approval-request` surfaces.
|
|
205
204
|
- **`require-approval`** — a `tool-approval-request` is emitted and the workflow suspends until the consumer settles.
|
|
206
205
|
|
|
207
|
-
Gating is **opt-in**: it engages only when the agent's config sets `toolPolicies` or `defaultToolDecision
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
`submitToolResult`. Set `StreamOptions.batchApprovals: true` to surface parallel `require-approval` requests on one
|
|
206
|
+
Gating is **opt-in**: it engages only when the agent's config sets `toolPolicies` or `defaultToolDecision`. With neither
|
|
207
|
+
configured, tools run without gating (no suspension), matching the SDK's "no policy ⇒ no gating" default. The harness
|
|
208
|
+
adds `MASTRA_BUILT_IN_TOOL_POLICIES` (Mastra's `updateWorkingMemory` / `skill` / `skill_search` / `skill_read`
|
|
209
|
+
built-ins) and the SDK's `skill_bridge` meta-tool rules as `allow` defaults, so capability-discovery never prompts.
|
|
210
|
+
Consumer-executed tools (`AgentConfig.tools`) always bypass the gate — their execution is the consumer's responsibility
|
|
211
|
+
via `submitToolResult`. Set `StreamOptions.batchApprovals: true` to surface parallel `require-approval` requests on one
|
|
214
212
|
stream for a batch-approval UI.
|
|
215
213
|
|
|
216
|
-
> `StreamOptions.requireToolApproval` is **deprecated** — superseded by per-tool `AgentConfig.toolPolicies`. It still
|
|
217
|
-
> works for one release: a truthy value engages all-or-nothing gating (every non-consumer tool requires approval), and
|
|
218
|
-
> `'batch'` maps onto `batchApprovals`.
|
|
219
|
-
|
|
220
214
|
### Tool approval timeout
|
|
221
215
|
|
|
222
216
|
When the approval gate is engaged and a tool resolves to `require-approval`, the harness suspends the workflow on each
|
|
@@ -50,10 +50,10 @@ export interface MastraApprovalCoordinatorOptions {
|
|
|
50
50
|
* (front-loaded off `tool-call` when `batchApprovals`, else on the
|
|
51
51
|
* natural `tool-call-approval` chunk).
|
|
52
52
|
*
|
|
53
|
-
* `undefined` means gating is **off** (the consumer configured no
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
* never consulted —
|
|
53
|
+
* `undefined` means gating is **off** (the consumer configured no
|
|
54
|
+
* `toolPolicies` / `defaultToolDecision`): the harness does not pass
|
|
55
|
+
* `requireToolApproval` to Mastra, Mastra suspends nothing, and this
|
|
56
|
+
* callback is never consulted — the SDK's no-gating behavior.
|
|
57
57
|
*
|
|
58
58
|
* The coordinator builds the {@link ToolInvocation} it passes here from the
|
|
59
59
|
* same identity sources it uses for event enrichment
|
|
@@ -74,8 +74,7 @@ export interface MastraApprovalCoordinatorOptions {
|
|
|
74
74
|
* iterates to natural park). When `false`, each `require-approval` surfaces
|
|
75
75
|
* on its natural `tool-call-approval` suspension chunk — one at a time.
|
|
76
76
|
*
|
|
77
|
-
*
|
|
78
|
-
* `StreamOptions.requireToolApproval` enum (#447). No effect when
|
|
77
|
+
* Read from `StreamOptions.batchApprovals` (#447). No effect when
|
|
79
78
|
* {@link lookupPolicy} is `undefined` (gating off) or when no tool resolves
|
|
80
79
|
* to `require-approval`.
|
|
81
80
|
*/
|
|
@@ -123,7 +122,8 @@ export interface MastraApprovalCoordinatorOptions {
|
|
|
123
122
|
* Responsibilities:
|
|
124
123
|
* - Policy-driven approval gating (Phase 2a): for each non-consumer tool,
|
|
125
124
|
* consult `lookupPolicy` — `allow` silently approves, `deny` silently
|
|
126
|
-
* declines (
|
|
125
|
+
* declines (the coordinator synthesizes a `tool-result(isError=true)` and
|
|
126
|
+
* drives `agent.declineToolCall` so the model sees the failed call),
|
|
127
127
|
* `require-approval` emits a `tool-approval-request` and drives
|
|
128
128
|
* `agent.approveToolCall(...)` / `declineToolCall(...)` when the consumer
|
|
129
129
|
* settles. `batchApprovals` controls whether parallel `require-approval`
|
|
@@ -166,17 +166,6 @@ export declare class MastraApprovalCoordinator {
|
|
|
166
166
|
private readonly initial;
|
|
167
167
|
/** Buffered consumer decisions keyed by `toolCallId`. */
|
|
168
168
|
private readonly decisions;
|
|
169
|
-
/**
|
|
170
|
-
* ToolCallIds the policy resolver decided `'deny'` (Phase 2a). The pump
|
|
171
|
-
* drives `agent.declineToolCall` for these, and Mastra emits a native
|
|
172
|
-
* `tool-result` on the resumed stream with `isError: false` + the text
|
|
173
|
-
* "Tool call was not approved by the user". The SDK `Decision` contract
|
|
174
|
-
* requires a denied tool to surface as `tool-result(isError=true)`, so the
|
|
175
|
-
* coordinator re-stamps `isError: true` on the matching result as it routes
|
|
176
|
-
* (see {@link routeEnriched}). Tracked separately from consumer-driven
|
|
177
|
-
* declines, which keep Mastra's native shape.
|
|
178
|
-
*/
|
|
179
|
-
private readonly policyDeniedIds;
|
|
180
169
|
/** ToolCallIds for which a `tool-approval-request` ChatEvent has been emitted. */
|
|
181
170
|
private readonly emittedApprovals;
|
|
182
171
|
/** ToolCallIds the consumer has settled. Rejects double-settle. */
|
|
@@ -407,18 +396,6 @@ export declare class MastraApprovalCoordinator {
|
|
|
407
396
|
*/
|
|
408
397
|
private routeEnriched;
|
|
409
398
|
private maybeEnrich;
|
|
410
|
-
/**
|
|
411
|
-
* Re-stamp `isError: true` on the `tool-result` for a policy-denied tool
|
|
412
|
-
* call (Phase 2a). When the resolver returns `'deny'`, the coordinator
|
|
413
|
-
* drives `agent.declineToolCall`; Mastra emits a native `tool-result` with
|
|
414
|
-
* `isError: false` and the text "Tool call was not approved by the user".
|
|
415
|
-
* The SDK `Decision` contract requires a denied tool to surface as
|
|
416
|
-
* `tool-result(isError=true)` so consumers and the model treat it as a
|
|
417
|
-
* failed call, not a successful one — so we flip the flag exactly once, on
|
|
418
|
-
* the matching result. Consumer-driven declines (not policy denials) are not
|
|
419
|
-
* in `policyDeniedIds` and keep Mastra's native shape.
|
|
420
|
-
*/
|
|
421
|
-
private policyDenyStamp;
|
|
422
399
|
/**
|
|
423
400
|
* Build the {@link ToolInvocation} the policy resolver matches against, and
|
|
424
401
|
* the single source of the `serverName` / `annotations` identity stamped on
|
|
@@ -450,11 +427,10 @@ export declare class MastraApprovalCoordinator {
|
|
|
450
427
|
* Resolve the approval decision for a non-consumer tool via the harness-
|
|
451
428
|
* supplied {@link MastraApprovalCoordinatorOptions.lookupPolicy} callback.
|
|
452
429
|
* Falls back to `'require-approval'` when no policy callback is wired — a
|
|
453
|
-
* defensive default so a future caller that
|
|
454
|
-
*
|
|
455
|
-
*
|
|
456
|
-
*
|
|
457
|
-
* tools.
|
|
430
|
+
* defensive default so a future caller that activates gating without a
|
|
431
|
+
* resolver surfaces the request rather than silently allowing. In practice
|
|
432
|
+
* this fallback is unreachable: gating is active iff `lookupPolicy` is
|
|
433
|
+
* defined, and only then does Mastra suspend non-consumer tools.
|
|
458
434
|
*/
|
|
459
435
|
private policyFor;
|
|
460
436
|
private emitApprovalRequest;
|
|
@@ -22,6 +22,13 @@ 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
32
|
/**
|
|
26
33
|
* Per-`(agentId, threadId)` coordinator that subsumes the pre-#529
|
|
27
34
|
* `MastraStreamBroker` + `activeRuns` pair into one object owning a single
|
|
@@ -30,7 +37,8 @@ function pickIdWithDecision(pendingIds, decisions) {
|
|
|
30
37
|
* Responsibilities:
|
|
31
38
|
* - Policy-driven approval gating (Phase 2a): for each non-consumer tool,
|
|
32
39
|
* consult `lookupPolicy` — `allow` silently approves, `deny` silently
|
|
33
|
-
* declines (
|
|
40
|
+
* declines (the coordinator synthesizes a `tool-result(isError=true)` and
|
|
41
|
+
* drives `agent.declineToolCall` so the model sees the failed call),
|
|
34
42
|
* `require-approval` emits a `tool-approval-request` and drives
|
|
35
43
|
* `agent.approveToolCall(...)` / `declineToolCall(...)` when the consumer
|
|
36
44
|
* settles. `batchApprovals` controls whether parallel `require-approval`
|
|
@@ -73,17 +81,6 @@ export class MastraApprovalCoordinator {
|
|
|
73
81
|
initial = new GenSink();
|
|
74
82
|
/** Buffered consumer decisions keyed by `toolCallId`. */
|
|
75
83
|
decisions = new Map();
|
|
76
|
-
/**
|
|
77
|
-
* ToolCallIds the policy resolver decided `'deny'` (Phase 2a). The pump
|
|
78
|
-
* drives `agent.declineToolCall` for these, and Mastra emits a native
|
|
79
|
-
* `tool-result` on the resumed stream with `isError: false` + the text
|
|
80
|
-
* "Tool call was not approved by the user". The SDK `Decision` contract
|
|
81
|
-
* requires a denied tool to surface as `tool-result(isError=true)`, so the
|
|
82
|
-
* coordinator re-stamps `isError: true` on the matching result as it routes
|
|
83
|
-
* (see {@link routeEnriched}). Tracked separately from consumer-driven
|
|
84
|
-
* declines, which keep Mastra's native shape.
|
|
85
|
-
*/
|
|
86
|
-
policyDeniedIds = new Set();
|
|
87
84
|
/** ToolCallIds for which a `tool-approval-request` ChatEvent has been emitted. */
|
|
88
85
|
emittedApprovals = new Set();
|
|
89
86
|
/** ToolCallIds the consumer has settled. Rejects double-settle. */
|
|
@@ -342,10 +339,6 @@ export class MastraApprovalCoordinator {
|
|
|
342
339
|
this.initial.push({ type: 'finish', finishReason: 'error' });
|
|
343
340
|
this.initial.end();
|
|
344
341
|
this.pendingSuspendIds.clear();
|
|
345
|
-
// The turn is over; any deny re-stamp that never landed (the declined
|
|
346
|
-
// tool-result will never arrive now) is moot. Clear so the set doesn't
|
|
347
|
-
// outlive the turn.
|
|
348
|
-
this.policyDeniedIds.clear();
|
|
349
342
|
this.runTeardownCallbacks();
|
|
350
343
|
}
|
|
351
344
|
// ───────────────────────── settle methods ───────────────────────────────
|
|
@@ -592,9 +585,9 @@ export class MastraApprovalCoordinator {
|
|
|
592
585
|
this.pendingSuspendIds.add(tc.toolCallId);
|
|
593
586
|
sawSuspension = true;
|
|
594
587
|
if (this.consumerToolNames.has(tc.toolName)) {
|
|
595
|
-
// Consumer tools bypass the gate
|
|
596
|
-
//
|
|
597
|
-
//
|
|
588
|
+
// Consumer tools bypass the approval gate by
|
|
589
|
+
// construction. Auto-approve silently — no ChatEvent
|
|
590
|
+
// surfaces, and the
|
|
598
591
|
// pump loop drives `agent.approveToolCall` when it
|
|
599
592
|
// picks up the buffered decision. After Mastra
|
|
600
593
|
// resumes, the bridge `execute` parker runs and
|
|
@@ -616,19 +609,36 @@ export class MastraApprovalCoordinator {
|
|
|
616
609
|
}
|
|
617
610
|
else if (decision === 'deny') {
|
|
618
611
|
// Silent decline — no `tool-approval-request` surfaces.
|
|
619
|
-
// The pump drives `agent.declineToolCall
|
|
620
|
-
//
|
|
621
|
-
// `
|
|
622
|
-
// user")
|
|
623
|
-
//
|
|
624
|
-
//
|
|
625
|
-
//
|
|
612
|
+
// The pump drives `agent.declineToolCall` so Mastra folds
|
|
613
|
+
// the denial into the model's context (persisted as the
|
|
614
|
+
// `role: 'tool'` message "Tool call was not approved by the
|
|
615
|
+
// user"), letting the model recover on the resume stream.
|
|
616
|
+
//
|
|
617
|
+
// We synthesize the consumer-facing `tool-result(isError=true)`
|
|
618
|
+
// ourselves rather than relying on Mastra to emit one:
|
|
619
|
+
// @mastra/core 1.49 no longer emits a standalone
|
|
620
|
+
// `tool-result` chunk on the decline-resume `fullStream`
|
|
621
|
+
// (the denial text lives only inside the persisted
|
|
622
|
+
// `messages.all` payload embedded in `step-finish`), so the
|
|
623
|
+
// old "re-stamp Mastra's native result" path never fired and
|
|
624
|
+
// the SDK `Decision` contract "a denied tool surfaces as
|
|
625
|
+
// `tool-result(isError=true)`" was silently broken. Routing
|
|
626
|
+
// it here — immediately after the already-routed `tool-call`
|
|
627
|
+
// and before the decline resume — keeps the natural
|
|
628
|
+
// tool-call → tool-result ordering the SDK telemetry deriver
|
|
629
|
+
// matches on. The un-emitted approval is marked
|
|
626
630
|
// coordinator-settled so a (spurious) consumer
|
|
627
631
|
// `approveToolCall` for this id is a no-op rather than a
|
|
628
632
|
// TOOL_CALL_NOT_FOUND throw.
|
|
629
633
|
this.settledByCoordinator.add(tc.toolCallId);
|
|
630
|
-
this.policyDeniedIds.add(tc.toolCallId);
|
|
631
634
|
this.decisions.set(tc.toolCallId, { kind: 'decline' });
|
|
635
|
+
this.routeEnriched({
|
|
636
|
+
type: 'tool-result',
|
|
637
|
+
toolCallId: tc.toolCallId,
|
|
638
|
+
toolName: tc.toolName,
|
|
639
|
+
result: POLICY_DENIED_RESULT_TEXT,
|
|
640
|
+
isError: true,
|
|
641
|
+
});
|
|
632
642
|
}
|
|
633
643
|
else if (!this.batchApprovals && !this.emittedApprovals.has(tc.toolCallId)) {
|
|
634
644
|
// require-approval, serial: emit on the natural chunk
|
|
@@ -727,7 +737,7 @@ export class MastraApprovalCoordinator {
|
|
|
727
737
|
routeEnriched(event) {
|
|
728
738
|
if (this.aborted || this.disposed)
|
|
729
739
|
return;
|
|
730
|
-
const enriched = this.
|
|
740
|
+
const enriched = this.maybeEnrich(event);
|
|
731
741
|
// Issue #591: track in-flight MCP tool call so progress notifications
|
|
732
742
|
// arriving via `progress.onUpdate(...)` can be attributed to the
|
|
733
743
|
// right `toolCallId`. Set on a genuinely MCP-discovered tool-call;
|
|
@@ -782,29 +792,6 @@ export class MastraApprovalCoordinator {
|
|
|
782
792
|
const out = { ...event, ...fields };
|
|
783
793
|
return out;
|
|
784
794
|
}
|
|
785
|
-
/**
|
|
786
|
-
* Re-stamp `isError: true` on the `tool-result` for a policy-denied tool
|
|
787
|
-
* call (Phase 2a). When the resolver returns `'deny'`, the coordinator
|
|
788
|
-
* drives `agent.declineToolCall`; Mastra emits a native `tool-result` with
|
|
789
|
-
* `isError: false` and the text "Tool call was not approved by the user".
|
|
790
|
-
* The SDK `Decision` contract requires a denied tool to surface as
|
|
791
|
-
* `tool-result(isError=true)` so consumers and the model treat it as a
|
|
792
|
-
* failed call, not a successful one — so we flip the flag exactly once, on
|
|
793
|
-
* the matching result. Consumer-driven declines (not policy denials) are not
|
|
794
|
-
* in `policyDeniedIds` and keep Mastra's native shape.
|
|
795
|
-
*/
|
|
796
|
-
policyDenyStamp(event) {
|
|
797
|
-
if (event.type !== 'tool-result' || !this.policyDeniedIds.has(event.toolCallId)) {
|
|
798
|
-
return event;
|
|
799
|
-
}
|
|
800
|
-
// One-shot: clear so a later same-id result (shouldn't happen, but be
|
|
801
|
-
// safe) isn't mislabeled, and the set doesn't grow unbounded.
|
|
802
|
-
this.policyDeniedIds.delete(event.toolCallId);
|
|
803
|
-
if (event.isError)
|
|
804
|
-
return event;
|
|
805
|
-
const out = { ...event, isError: true };
|
|
806
|
-
return out;
|
|
807
|
-
}
|
|
808
795
|
/**
|
|
809
796
|
* Build the {@link ToolInvocation} the policy resolver matches against, and
|
|
810
797
|
* the single source of the `serverName` / `annotations` identity stamped on
|
|
@@ -852,11 +839,10 @@ export class MastraApprovalCoordinator {
|
|
|
852
839
|
* Resolve the approval decision for a non-consumer tool via the harness-
|
|
853
840
|
* supplied {@link MastraApprovalCoordinatorOptions.lookupPolicy} callback.
|
|
854
841
|
* Falls back to `'require-approval'` when no policy callback is wired — a
|
|
855
|
-
* defensive default so a future caller that
|
|
856
|
-
*
|
|
857
|
-
*
|
|
858
|
-
*
|
|
859
|
-
* tools.
|
|
842
|
+
* defensive default so a future caller that activates gating without a
|
|
843
|
+
* resolver surfaces the request rather than silently allowing. In practice
|
|
844
|
+
* this fallback is unreachable: gating is active iff `lookupPolicy` is
|
|
845
|
+
* defined, and only then does Mastra suspend non-consumer tools.
|
|
860
846
|
*/
|
|
861
847
|
policyFor(toolName) {
|
|
862
848
|
if (!this.lookupPolicy)
|
|
@@ -2,7 +2,7 @@ import type { HarnessFactory, ProviderHint } from '@salesforce/sfdx-agent-sdk';
|
|
|
2
2
|
import { type MastraAgentHarness } from './mastra-harness.js';
|
|
3
3
|
export interface MastraHarnessFactoryConfig {
|
|
4
4
|
/**
|
|
5
|
-
* How long to wait for a
|
|
5
|
+
* How long to wait for a consumer to call
|
|
6
6
|
* `approveToolCall` / `declineToolCall` before auto-denying via
|
|
7
7
|
* `agent.abortRunStream(...)` and yielding a synthetic `error`
|
|
8
8
|
* (`code: 'tool-approval-timeout'`) + `finish` (`finishReason: 'error'`)
|
package/dist/mastra-harness.d.ts
CHANGED
|
@@ -122,9 +122,8 @@ interface MastraAgentState {
|
|
|
122
122
|
coordinators: Map<string, MastraApprovalCoordinator>;
|
|
123
123
|
/**
|
|
124
124
|
* Names of consumer-executed tools registered via `AgentConfig.tools`.
|
|
125
|
-
* The coordinator uses this set to bypass the approval gate for them
|
|
126
|
-
*
|
|
127
|
-
* suspends on a `tool-call-approval` for a name in this set the
|
|
125
|
+
* The coordinator uses this set to bypass the approval gate for them —
|
|
126
|
+
* when Mastra suspends on a `tool-call-approval` for a name in this set the
|
|
128
127
|
* coordinator silently calls `agent.approveToolCall(...)` and never
|
|
129
128
|
* surfaces a `tool-approval-request` ChatEvent.
|
|
130
129
|
*/
|
|
@@ -424,31 +423,16 @@ export declare class MastraHarness implements MastraAgentHarness {
|
|
|
424
423
|
* during pending approval (#409)".
|
|
425
424
|
*/
|
|
426
425
|
/**
|
|
427
|
-
*
|
|
428
|
-
*
|
|
426
|
+
* Decide whether the approval gate engages this turn and, if so, build the
|
|
427
|
+
* per-tool policy resolver the coordinator consults.
|
|
429
428
|
*
|
|
430
|
-
* Gating is **opt-in**. It engages when the consumer set
|
|
431
|
-
* - `config.toolPolicies` (non-empty),
|
|
432
|
-
* - `config.defaultToolDecision
|
|
433
|
-
* - the deprecated `StreamOptions.requireToolApproval` (any non-`false`
|
|
434
|
-
* value — `deprecatedMode` is `'serial'`/`'batch'`/`undefined`), or
|
|
435
|
-
* - `StreamOptions.batchApprovals: true`.
|
|
429
|
+
* Gating is **opt-in**. It engages when the consumer set either:
|
|
430
|
+
* - `config.toolPolicies` (non-empty), or
|
|
431
|
+
* - `config.defaultToolDecision`.
|
|
436
432
|
*
|
|
437
|
-
* When
|
|
433
|
+
* When neither is set, returns `{ lookupPolicy: undefined }` — the caller
|
|
438
434
|
* omits `requireToolApproval` from Mastra's `stream()` and tools run free
|
|
439
|
-
* (
|
|
440
|
-
*
|
|
441
|
-
* **Deprecation bridge.** When the gate engages *only* because the deprecated
|
|
442
|
-
* `requireToolApproval` was truthy (no `toolPolicies`, no
|
|
443
|
-
* `defaultToolDecision`), the effective default must be `'require-approval'`
|
|
444
|
-
* — legacy all-or-none — not the resolver's `'allow'` default, or the old
|
|
445
|
-
* behavior would silently flip to "auto-approve everything." We apply this
|
|
446
|
-
* by resolving against a shallow-overridden config carrying
|
|
447
|
-
* `defaultToolDecision: 'require-approval'`. Built-in allow-rules
|
|
448
|
-
* (skill_bridge meta-tools, the Mastra built-ins) and any consumer
|
|
449
|
-
* `toolPolicies` still win over the default via within-tier last-wins /
|
|
450
|
-
* cross-tier deny-wins, so the capability-discovery meta-tools stay
|
|
451
|
-
* auto-approved even under legacy gating.
|
|
435
|
+
* (the SDK's "no policy ⇒ no gating" back-compat invariant).
|
|
452
436
|
*
|
|
453
437
|
* **Live config read (#638).** `lookupPolicy` reads `agentState.config` on
|
|
454
438
|
* every call rather than closing over a snapshot taken at stream start. A
|
|
@@ -459,22 +443,14 @@ export declare class MastraHarness implements MastraAgentHarness {
|
|
|
459
443
|
* that persisted the rule. Reading live makes the remembered decision
|
|
460
444
|
* auto-resolve subsequent matching tool calls within the same turn.
|
|
461
445
|
*
|
|
462
|
-
*
|
|
463
|
-
*
|
|
464
|
-
*
|
|
465
|
-
*
|
|
466
|
-
* mid-turn. The closure is built (and returned) iff gating engaged now.
|
|
467
|
-
* - `applyLegacyBridge` (`!hasPolicy && legacyGating`) — whether the
|
|
468
|
-
* deprecation bridge injects `defaultToolDecision: 'require-approval'`.
|
|
469
|
-
* If this were recomputed live, the FIRST remembered rule would flip
|
|
470
|
-
* `hasPolicy` false→true, turn the bridge OFF mid-turn, and silently drop
|
|
471
|
-
* the effective default back to the resolver's `'allow'` — auto-approving
|
|
472
|
-
* every remaining legacy-gated tool. Freezing the boolean and applying it
|
|
473
|
-
* to the live config preserves legacy all-or-none semantics while still
|
|
474
|
-
* honoring any rule the turn appends.
|
|
446
|
+
* `gatingActive` stays **frozen at stream start** — the turn either told
|
|
447
|
+
* Mastra to suspend tools (`requireToolApproval: true`) or it didn't; gating
|
|
448
|
+
* cannot begin mid-turn. The closure is built (and returned) iff gating
|
|
449
|
+
* engaged now.
|
|
475
450
|
*
|
|
476
|
-
* `batchApprovals` is the serial-vs-batch emission axis
|
|
477
|
-
* `StreamOptions.batchApprovals
|
|
451
|
+
* `batchApprovals` is the serial-vs-batch emission axis, read straight off
|
|
452
|
+
* `StreamOptions.batchApprovals`. It has no effect when the gate is inactive
|
|
453
|
+
* (nothing resolves to `'require-approval'`, so there is nothing to batch).
|
|
478
454
|
*/
|
|
479
455
|
private buildPolicyGate;
|
|
480
456
|
/**
|
package/dist/mastra-harness.js
CHANGED
|
@@ -11,7 +11,7 @@ import { SkillSearchProcessor, ToolSearchProcessor } from '@mastra/core/processo
|
|
|
11
11
|
import { MASTRA_RESOURCE_ID_KEY, MASTRA_THREAD_ID_KEY, RequestContext } from '@mastra/core/request-context';
|
|
12
12
|
import { LibSQLStore } from '@mastra/libsql';
|
|
13
13
|
import { MCPClient } from '@mastra/mcp';
|
|
14
|
-
import { AgentSDKError, AgentSDKErrorType, DEFAULT_MAX_STEPS, McpServerStatus,
|
|
14
|
+
import { AgentSDKError, AgentSDKErrorType, DEFAULT_MAX_STEPS, McpServerStatus, resolveToolApprovalPolicy, } from '@salesforce/sfdx-agent-sdk';
|
|
15
15
|
import { HarnessBusOwner, lowerStreamInput, mcpServerConfigEqual, mergeToolResultsIntoAssistant, splitToolResultsIntoToolMessages, } from '@salesforce/sfdx-agent-sdk/harness';
|
|
16
16
|
import { MastraApprovalCoordinator } from './mastra-approval-coordinator.js';
|
|
17
17
|
import { MASTRA_BUILT_IN_TOOL_POLICIES } from './mastra-built-in-policies.js';
|
|
@@ -1339,31 +1339,16 @@ export class MastraHarness {
|
|
|
1339
1339
|
* during pending approval (#409)".
|
|
1340
1340
|
*/
|
|
1341
1341
|
/**
|
|
1342
|
-
*
|
|
1343
|
-
*
|
|
1342
|
+
* Decide whether the approval gate engages this turn and, if so, build the
|
|
1343
|
+
* per-tool policy resolver the coordinator consults.
|
|
1344
1344
|
*
|
|
1345
|
-
* Gating is **opt-in**. It engages when the consumer set
|
|
1346
|
-
* - `config.toolPolicies` (non-empty),
|
|
1347
|
-
* - `config.defaultToolDecision
|
|
1348
|
-
* - the deprecated `StreamOptions.requireToolApproval` (any non-`false`
|
|
1349
|
-
* value — `deprecatedMode` is `'serial'`/`'batch'`/`undefined`), or
|
|
1350
|
-
* - `StreamOptions.batchApprovals: true`.
|
|
1345
|
+
* Gating is **opt-in**. It engages when the consumer set either:
|
|
1346
|
+
* - `config.toolPolicies` (non-empty), or
|
|
1347
|
+
* - `config.defaultToolDecision`.
|
|
1351
1348
|
*
|
|
1352
|
-
* When
|
|
1349
|
+
* When neither is set, returns `{ lookupPolicy: undefined }` — the caller
|
|
1353
1350
|
* omits `requireToolApproval` from Mastra's `stream()` and tools run free
|
|
1354
|
-
* (
|
|
1355
|
-
*
|
|
1356
|
-
* **Deprecation bridge.** When the gate engages *only* because the deprecated
|
|
1357
|
-
* `requireToolApproval` was truthy (no `toolPolicies`, no
|
|
1358
|
-
* `defaultToolDecision`), the effective default must be `'require-approval'`
|
|
1359
|
-
* — legacy all-or-none — not the resolver's `'allow'` default, or the old
|
|
1360
|
-
* behavior would silently flip to "auto-approve everything." We apply this
|
|
1361
|
-
* by resolving against a shallow-overridden config carrying
|
|
1362
|
-
* `defaultToolDecision: 'require-approval'`. Built-in allow-rules
|
|
1363
|
-
* (skill_bridge meta-tools, the Mastra built-ins) and any consumer
|
|
1364
|
-
* `toolPolicies` still win over the default via within-tier last-wins /
|
|
1365
|
-
* cross-tier deny-wins, so the capability-discovery meta-tools stay
|
|
1366
|
-
* auto-approved even under legacy gating.
|
|
1351
|
+
* (the SDK's "no policy ⇒ no gating" back-compat invariant).
|
|
1367
1352
|
*
|
|
1368
1353
|
* **Live config read (#638).** `lookupPolicy` reads `agentState.config` on
|
|
1369
1354
|
* every call rather than closing over a snapshot taken at stream start. A
|
|
@@ -1374,47 +1359,27 @@ export class MastraHarness {
|
|
|
1374
1359
|
* that persisted the rule. Reading live makes the remembered decision
|
|
1375
1360
|
* auto-resolve subsequent matching tool calls within the same turn.
|
|
1376
1361
|
*
|
|
1377
|
-
*
|
|
1378
|
-
*
|
|
1379
|
-
*
|
|
1380
|
-
*
|
|
1381
|
-
* mid-turn. The closure is built (and returned) iff gating engaged now.
|
|
1382
|
-
* - `applyLegacyBridge` (`!hasPolicy && legacyGating`) — whether the
|
|
1383
|
-
* deprecation bridge injects `defaultToolDecision: 'require-approval'`.
|
|
1384
|
-
* If this were recomputed live, the FIRST remembered rule would flip
|
|
1385
|
-
* `hasPolicy` false→true, turn the bridge OFF mid-turn, and silently drop
|
|
1386
|
-
* the effective default back to the resolver's `'allow'` — auto-approving
|
|
1387
|
-
* every remaining legacy-gated tool. Freezing the boolean and applying it
|
|
1388
|
-
* to the live config preserves legacy all-or-none semantics while still
|
|
1389
|
-
* honoring any rule the turn appends.
|
|
1362
|
+
* `gatingActive` stays **frozen at stream start** — the turn either told
|
|
1363
|
+
* Mastra to suspend tools (`requireToolApproval: true`) or it didn't; gating
|
|
1364
|
+
* cannot begin mid-turn. The closure is built (and returned) iff gating
|
|
1365
|
+
* engaged now.
|
|
1390
1366
|
*
|
|
1391
|
-
* `batchApprovals` is the serial-vs-batch emission axis
|
|
1392
|
-
* `StreamOptions.batchApprovals
|
|
1367
|
+
* `batchApprovals` is the serial-vs-batch emission axis, read straight off
|
|
1368
|
+
* `StreamOptions.batchApprovals`. It has no effect when the gate is inactive
|
|
1369
|
+
* (nothing resolves to `'require-approval'`, so there is nothing to batch).
|
|
1393
1370
|
*/
|
|
1394
|
-
buildPolicyGate(agentState, options
|
|
1371
|
+
buildPolicyGate(agentState, options) {
|
|
1395
1372
|
const config = agentState.config;
|
|
1396
|
-
const
|
|
1397
|
-
const legacyGating = deprecatedMode !== undefined;
|
|
1398
|
-
const batchApprovals = options?.batchApprovals === true || deprecatedMode === 'batch';
|
|
1399
|
-
const gatingActive = hasPolicy || legacyGating || batchApprovals;
|
|
1373
|
+
const gatingActive = (config.toolPolicies?.length ?? 0) > 0 || config.defaultToolDecision !== undefined;
|
|
1400
1374
|
if (!gatingActive) {
|
|
1401
1375
|
return { batchApprovals: false };
|
|
1402
1376
|
}
|
|
1403
|
-
|
|
1404
|
-
//
|
|
1405
|
-
//
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
// remembered rule) is honored for the rest of this turn.
|
|
1410
|
-
const liveConfig = agentState.config;
|
|
1411
|
-
const resolverConfig = applyLegacyBridge
|
|
1412
|
-
? { ...liveConfig, defaultToolDecision: 'require-approval' }
|
|
1413
|
-
: liveConfig;
|
|
1414
|
-
return resolveToolApprovalPolicy(invocation, resolverConfig, { harness: MASTRA_BUILT_IN_TOOL_POLICIES })
|
|
1415
|
-
.decision;
|
|
1416
|
-
};
|
|
1417
|
-
return { lookupPolicy, batchApprovals };
|
|
1377
|
+
const lookupPolicy = (invocation) =>
|
|
1378
|
+
// Read the config live so a mid-turn `updateAgentConfig` (e.g. a
|
|
1379
|
+
// remembered rule) is honored for the rest of this turn (#638).
|
|
1380
|
+
resolveToolApprovalPolicy(invocation, agentState.config, { harness: MASTRA_BUILT_IN_TOOL_POLICIES })
|
|
1381
|
+
.decision;
|
|
1382
|
+
return { lookupPolicy, batchApprovals: options?.batchApprovals === true };
|
|
1418
1383
|
}
|
|
1419
1384
|
/**
|
|
1420
1385
|
* Maps an SDK `MessagePart[]` into the single AI SDK v5 user `ModelMessage` that
|
|
@@ -1440,20 +1405,14 @@ export class MastraHarness {
|
|
|
1440
1405
|
}
|
|
1441
1406
|
async stream(agentId, threadId, message, options) {
|
|
1442
1407
|
const agentState = this.getAgentStateOrThrow(agentId);
|
|
1443
|
-
//
|
|
1444
|
-
//
|
|
1445
|
-
//
|
|
1446
|
-
// `
|
|
1447
|
-
//
|
|
1448
|
-
//
|
|
1449
|
-
//
|
|
1450
|
-
|
|
1451
|
-
// `batchApprovals`. With none set, behavior is unchanged from today —
|
|
1452
|
-
// no suspension, no resolver, tools run free (the SDK's "no policy ⇒ no
|
|
1453
|
-
// gating" back-compat invariant), avoiding a suspend/resume round-trip
|
|
1454
|
-
// per tool call.
|
|
1455
|
-
const deprecatedMode = resolveToolApprovalMode(options?.requireToolApproval);
|
|
1456
|
-
const { lookupPolicy, batchApprovals } = this.buildPolicyGate(agentState, options, deprecatedMode);
|
|
1408
|
+
// Resolve whether the approval gate engages this turn, and if so, build
|
|
1409
|
+
// the per-tool policy resolver the coordinator consults. The gate is
|
|
1410
|
+
// **opt-in**: it engages only when the consumer configured
|
|
1411
|
+
// `toolPolicies` or `defaultToolDecision`. With neither set, behavior is
|
|
1412
|
+
// unchanged — no suspension, no resolver, tools run free (the SDK's "no
|
|
1413
|
+
// policy ⇒ no gating" back-compat invariant), avoiding a suspend/resume
|
|
1414
|
+
// round-trip per tool call.
|
|
1415
|
+
const { lookupPolicy, batchApprovals } = this.buildPolicyGate(agentState, options);
|
|
1457
1416
|
const gatingActive = lookupPolicy !== undefined;
|
|
1458
1417
|
// Dispose any coordinator the prior turn on this thread allocated.
|
|
1459
1418
|
// Without this a stale coordinator from the prior turn would still
|
|
@@ -39,10 +39,10 @@ export function mapToolDefinitionsToMastraTools(tools, parkerLookup) {
|
|
|
39
39
|
description: tool.description ?? '',
|
|
40
40
|
inputSchema: ensureJsonSchemaMarker(tool.inputSchema),
|
|
41
41
|
// Issue #529 step 2c (Path A) — consumer tools bypass the
|
|
42
|
-
// approval gate
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
//
|
|
42
|
+
// approval gate by construction. With `requireApproval: false`,
|
|
43
|
+
// Mastra parks the agentic run on the parker Promise without
|
|
44
|
+
// suspending the workflow. The coordinator handles the parker
|
|
45
|
+
// lifecycle.
|
|
46
46
|
//
|
|
47
47
|
// Phase 2a dual-enforcement contract: this `requireApproval: false`
|
|
48
48
|
// is the PRIMARY, structural gate for consumer tools — it is
|
|
@@ -143,7 +143,13 @@ async function buildAnthropicRequest(ctx, options) {
|
|
|
143
143
|
const tools = translateToolsToAnthropic(options);
|
|
144
144
|
const commonParams = {
|
|
145
145
|
model: info.nativeModelId,
|
|
146
|
-
|
|
146
|
+
// Precedence: an explicit per-request `maxOutputTokens` wins; otherwise
|
|
147
|
+
// thread the bound model's published cap (`info.model.maxOutputTokens`)
|
|
148
|
+
// so large completions aren't silently truncated at the 4096 floor
|
|
149
|
+
// (W-23413250). `DEFAULT_MAX_TOKENS` is only the last-resort fallback for
|
|
150
|
+
// a model shape that somehow lacks the cap — Bedrock requires the field,
|
|
151
|
+
// so this path must always yield a number.
|
|
152
|
+
max_tokens: options.maxOutputTokens ?? info.model.maxOutputTokens ?? DEFAULT_MAX_TOKENS,
|
|
147
153
|
messages,
|
|
148
154
|
};
|
|
149
155
|
if (system !== undefined)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salesforce/sfdx-agent-harness-mastra",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.32.0",
|
|
4
4
|
"description": "Mastra-backed AgentHarness implementation for @salesforce/sfdx-agent-sdk",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
"lint": "eslint .",
|
|
18
18
|
"lint:fix": "eslint . --fix",
|
|
19
19
|
"showcoverage": "open ./coverage/lcov-report/index.html",
|
|
20
|
-
"test": "tsc --build ./test/tsconfig.json && vitest run --coverage"
|
|
20
|
+
"test": "tsc --build ./test/tsconfig.json && vitest run --coverage",
|
|
21
|
+
"test:conformance": "tsc --build ./test/tsconfig.json && vitest run test/conformance"
|
|
21
22
|
},
|
|
22
23
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
23
24
|
"files": [
|
|
@@ -31,35 +32,36 @@
|
|
|
31
32
|
"@ai-sdk/provider": "^3.0.10",
|
|
32
33
|
"@anthropic-ai/bedrock-sdk": "^0.32.0",
|
|
33
34
|
"@anthropic-ai/sdk": "^0.106.0",
|
|
34
|
-
"@mastra/core": "^1.
|
|
35
|
-
"@mastra/libsql": "^1.15.
|
|
36
|
-
"@mastra/mcp": "^1.13.
|
|
37
|
-
"@mastra/memory": "^1.22.
|
|
35
|
+
"@mastra/core": "^1.50.1",
|
|
36
|
+
"@mastra/libsql": "^1.15.1",
|
|
37
|
+
"@mastra/mcp": "^1.13.1",
|
|
38
|
+
"@mastra/memory": "^1.22.2",
|
|
38
39
|
"@mastra/schema-compat": "^1.2.11",
|
|
39
40
|
"@salesforce/agentic-common": "0.13.0",
|
|
40
41
|
"openai": "^6.0.0"
|
|
41
42
|
},
|
|
42
43
|
"peerDependencies": {
|
|
43
|
-
"@salesforce/sfdx-agent-sdk": "0.
|
|
44
|
+
"@salesforce/sfdx-agent-sdk": "0.33.0"
|
|
44
45
|
},
|
|
45
46
|
"devDependencies": {
|
|
46
47
|
"@eslint/js": "^10.0.1",
|
|
47
|
-
"@salesforce/
|
|
48
|
+
"@salesforce/harness-conformance": "0.0.1",
|
|
49
|
+
"@salesforce/sfdx-agent-sdk": "0.33.0",
|
|
48
50
|
"@types/node": "^22.20.0",
|
|
49
|
-
"@vitest/coverage-istanbul": "^4.1.
|
|
50
|
-
"@vitest/eslint-plugin": "^1.6.
|
|
51
|
-
"eslint": "^10.
|
|
51
|
+
"@vitest/coverage-istanbul": "^4.1.10",
|
|
52
|
+
"@vitest/eslint-plugin": "^1.6.23",
|
|
53
|
+
"eslint": "^10.7.0",
|
|
52
54
|
"eslint-config-prettier": "^10.1.8",
|
|
53
55
|
"eslint-import-resolver-typescript": "^4.4.5",
|
|
54
56
|
"eslint-plugin-import": "^2.32.0",
|
|
55
|
-
"eslint-plugin-n": "^18.2.
|
|
57
|
+
"eslint-plugin-n": "^18.2.2",
|
|
56
58
|
"globals": "^17.6.0",
|
|
57
59
|
"lint-staged": "^17.0.7",
|
|
58
60
|
"prettier": "^3.9.4",
|
|
59
61
|
"rimraf": "^6.1.3",
|
|
60
62
|
"tsx": "^4.23.0",
|
|
61
63
|
"typescript": "^6.0.3",
|
|
62
|
-
"typescript-eslint": "^8.
|
|
64
|
+
"typescript-eslint": "^8.63.0",
|
|
63
65
|
"vitest": "^4.1.8"
|
|
64
66
|
},
|
|
65
67
|
"engines": {
|