mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-05-19 18:45:15 +02:00
hitl/generic-approval: drop client-side MCP gate, dispatch approve_always
The 'Always Allow' button is now driven entirely by the server-supplied
allowed_decisions palette. The card no longer peeks at
context.mcp_connector_id to decide whether to render the button, and no
longer fires a separate trust-tool HTTP call on click - one
{type: 'approve_always'} dispatch is enough; the agent middleware
handles the in-memory promotion and (for MCP tools) the database save
via its trusted_tool_saver callback.
Drops the dead trustMCPTool / untrustMCPTool service helpers - they had
no remaining callers after this rework. The backing HTTP routes are
kept on the server as a programmatic surface.
This commit is contained in:
parent
98b6977c68
commit
1f1b6c5425
2 changed files with 7 additions and 38 deletions
|
|
@ -2,13 +2,11 @@
|
||||||
|
|
||||||
import { CornerDownLeftIcon, Pencil } from "lucide-react";
|
import { CornerDownLeftIcon, Pencil } from "lucide-react";
|
||||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
import { toast } from "sonner";
|
|
||||||
import { TextShimmerLoader } from "@/components/prompt-kit/loader";
|
import { TextShimmerLoader } from "@/components/prompt-kit/loader";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { getToolDisplayName } from "@/contracts/enums/toolIcons";
|
import { getToolDisplayName } from "@/contracts/enums/toolIcons";
|
||||||
import { connectorsApiService } from "@/lib/apis/connectors-api.service";
|
|
||||||
import type { HitlDecision, InterruptResult, PerToolApprovalCard } from "../types";
|
import type { HitlDecision, InterruptResult, PerToolApprovalCard } from "../types";
|
||||||
import { useHitlDecision } from "../use-hitl-decision";
|
import { useHitlDecision } from "../use-hitl-decision";
|
||||||
import { useHitlPhase } from "../use-hitl-phase";
|
import { useHitlPhase } from "../use-hitl-phase";
|
||||||
|
|
@ -81,12 +79,11 @@ function GenericApprovalCardView({
|
||||||
|
|
||||||
const mcpServer = interruptData.context?.mcp_server as string | undefined;
|
const mcpServer = interruptData.context?.mcp_server as string | undefined;
|
||||||
const toolDescription = interruptData.context?.tool_description as string | undefined;
|
const toolDescription = interruptData.context?.tool_description as string | undefined;
|
||||||
const mcpConnectorId = interruptData.context?.mcp_connector_id as number | undefined;
|
|
||||||
const isMCPTool = mcpConnectorId != null;
|
|
||||||
|
|
||||||
const reviewConfig = interruptData.review_configs?.[0];
|
const reviewConfig = interruptData.review_configs?.[0];
|
||||||
const allowedDecisions = reviewConfig?.allowed_decisions ?? ["approve", "reject"];
|
const allowedDecisions = reviewConfig?.allowed_decisions ?? ["approve", "reject"];
|
||||||
const canEdit = allowedDecisions.includes("edit");
|
const canEdit = allowedDecisions.includes("edit");
|
||||||
|
const canApproveAlways = allowedDecisions.includes("approve_always");
|
||||||
|
|
||||||
const hasChanged = useMemo(() => {
|
const hasChanged = useMemo(() => {
|
||||||
return JSON.stringify(editedParams) !== JSON.stringify(args);
|
return JSON.stringify(editedParams) !== JSON.stringify(args);
|
||||||
|
|
@ -113,16 +110,11 @@ function GenericApprovalCardView({
|
||||||
editedParams,
|
editedParams,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const handleAlwaysAllow = useCallback(() => {
|
const handleApproveAlways = useCallback(() => {
|
||||||
if (phase !== "pending" || !isMCPTool) return;
|
if (phase !== "pending" || !canApproveAlways) return;
|
||||||
setProcessing();
|
setProcessing();
|
||||||
onDecision({ type: "approve" });
|
onDecision({ type: "approve_always" });
|
||||||
connectorsApiService.trustMCPTool(mcpConnectorId, toolName).catch(() => {
|
}, [phase, setProcessing, onDecision, canApproveAlways]);
|
||||||
toast.error(
|
|
||||||
"Failed to save 'Always Allow' preference. The tool will still require approval next time."
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}, [phase, setProcessing, onDecision, isMCPTool, mcpConnectorId, toolName]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handler = (e: KeyboardEvent) => {
|
const handler = (e: KeyboardEvent) => {
|
||||||
|
|
@ -214,8 +206,8 @@ function GenericApprovalCardView({
|
||||||
<CornerDownLeftIcon className="size-3 opacity-60" />
|
<CornerDownLeftIcon className="size-3 opacity-60" />
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{isMCPTool && (
|
{canApproveAlways && (
|
||||||
<Button size="sm" className="rounded-lg" onClick={handleAlwaysAllow}>
|
<Button size="sm" className="rounded-lg" onClick={handleApproveAlways}>
|
||||||
Always Allow
|
Always Allow
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -405,29 +405,6 @@ class ConnectorsApiService {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// =============================================================================
|
|
||||||
// MCP Tool Trust (Allow-List) Methods
|
|
||||||
// =============================================================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a tool to the MCP connector's "Always Allow" list.
|
|
||||||
* Subsequent calls to this tool will skip HITL approval.
|
|
||||||
*/
|
|
||||||
trustMCPTool = async (connectorId: number, toolName: string): Promise<void> => {
|
|
||||||
await baseApiService.post(`/api/v1/connectors/mcp/${connectorId}/trust-tool`, undefined, {
|
|
||||||
body: { tool_name: toolName },
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove a tool from the MCP connector's "Always Allow" list.
|
|
||||||
*/
|
|
||||||
untrustMCPTool = async (connectorId: number, toolName: string): Promise<void> => {
|
|
||||||
await baseApiService.post(`/api/v1/connectors/mcp/${connectorId}/untrust-tool`, undefined, {
|
|
||||||
body: { tool_name: toolName },
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Live stats for the Obsidian connector tile. */
|
/** Live stats for the Obsidian connector tile. */
|
||||||
getObsidianStats = async (vaultId: string): Promise<ObsidianStats> => {
|
getObsidianStats = async (vaultId: string): Promise<ObsidianStats> => {
|
||||||
return baseApiService.get<ObsidianStats>(
|
return baseApiService.get<ObsidianStats>(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue