mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-08-09 20:50:58 +00:00
feat(agent-runner): enforce 4.x host-owned execution
This commit is contained in:
@@ -106,6 +106,25 @@ function getBoundPluginId(plugin: BoundPlugin) {
|
||||
return `${plugin.author || ''}/${plugin.name}`;
|
||||
}
|
||||
|
||||
function getGlobalExtensions(
|
||||
plugins: AvailablePlugin[],
|
||||
mcpServers: MCPServer[],
|
||||
): PipelineExtensions {
|
||||
return {
|
||||
enable_all_plugins: true,
|
||||
enable_all_mcp_servers: true,
|
||||
enable_all_skills: true,
|
||||
mcp_resource_agent_read_enabled: true,
|
||||
bound_plugins: [],
|
||||
available_plugins: plugins,
|
||||
bound_mcp_servers: [],
|
||||
available_mcp_servers: mcpServers,
|
||||
bound_mcp_resources: [],
|
||||
bound_skills: [],
|
||||
available_skills: [],
|
||||
};
|
||||
}
|
||||
|
||||
function InfoTooltip({ label }: { label: string }) {
|
||||
return (
|
||||
<Tooltip>
|
||||
@@ -348,19 +367,60 @@ export default function ToolResourceSelectors({
|
||||
const [tempSelectedKBIds, setTempSelectedKBIds] = useState<string[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
setTools([]);
|
||||
setKnowledgeBases([]);
|
||||
setExtensions(null);
|
||||
|
||||
if (mode !== 'resources') {
|
||||
backendClient.getTools(pipelineId).then((resp) => setTools(resp.tools));
|
||||
backendClient
|
||||
.getTools(pipelineId)
|
||||
.then((resp) => {
|
||||
if (!cancelled) setTools(resp.tools);
|
||||
})
|
||||
.catch(() => {
|
||||
if (!cancelled) setTools([]);
|
||||
});
|
||||
}
|
||||
if (mode !== 'tools') {
|
||||
backendClient
|
||||
.getKnowledgeBases()
|
||||
.then((resp) => setKnowledgeBases(resp.bases));
|
||||
.then((resp) => {
|
||||
if (!cancelled) setKnowledgeBases(resp.bases);
|
||||
})
|
||||
.catch(() => {
|
||||
if (!cancelled) setKnowledgeBases([]);
|
||||
});
|
||||
}
|
||||
if (pipelineId) {
|
||||
backendClient
|
||||
.getPipelineExtensions(pipelineId)
|
||||
.then((resp) => setExtensions(resp));
|
||||
.then((resp) => {
|
||||
if (!cancelled) setExtensions(resp);
|
||||
})
|
||||
.catch(() => {
|
||||
if (!cancelled) setExtensions(null);
|
||||
});
|
||||
} else {
|
||||
Promise.all([
|
||||
backendClient
|
||||
.getPlugins()
|
||||
.catch(() => ({ plugins: [] as AvailablePlugin[] })),
|
||||
backendClient
|
||||
.getMCPServers()
|
||||
.catch(() => ({ servers: [] as MCPServer[] })),
|
||||
]).then(([pluginResp, mcpResp]) => {
|
||||
if (!cancelled) {
|
||||
setExtensions(
|
||||
getGlobalExtensions(pluginResp.plugins, mcpResp.servers),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [mode, pipelineId]);
|
||||
|
||||
const enableAllTools = value['enable-all-tools'] !== false;
|
||||
@@ -480,11 +540,6 @@ export default function ToolResourceSelectors({
|
||||
],
|
||||
);
|
||||
|
||||
const availableToolNames = useMemo(
|
||||
() => new Set(availableTools.map((tool) => tool.name)),
|
||||
[availableTools],
|
||||
);
|
||||
|
||||
const resourceServers = useMemo(() => {
|
||||
return scopedMCPServers.filter(
|
||||
(server) =>
|
||||
@@ -513,6 +568,9 @@ export default function ToolResourceSelectors({
|
||||
? availableMCPResourceKeys.has(getMCPResourceKey(server, resource.uri))
|
||||
: false;
|
||||
});
|
||||
const unavailableSelectedMCPResources = selectedMCPResources.filter(
|
||||
(resource) => !scopedSelectedMCPResources.includes(resource),
|
||||
);
|
||||
|
||||
const selectedTools = selectedToolNames
|
||||
.map((name: string) => availableTools.find((tool) => tool.name === name))
|
||||
@@ -524,10 +582,10 @@ export default function ToolResourceSelectors({
|
||||
|
||||
const sourceLabels = useMemo<Record<string, string>>(
|
||||
() => ({
|
||||
builtin: t('pipelines.localAgent.builtinTools'),
|
||||
plugin: t('pipelines.localAgent.pluginTools'),
|
||||
mcp: t('pipelines.localAgent.mcpTools'),
|
||||
skill: t('pipelines.localAgent.skillTools'),
|
||||
builtin: t('pipelines.agentRunner.builtinTools'),
|
||||
plugin: t('pipelines.agentRunner.pluginTools'),
|
||||
mcp: t('pipelines.agentRunner.mcpTools'),
|
||||
skill: t('pipelines.agentRunner.skillTools'),
|
||||
}),
|
||||
[t],
|
||||
);
|
||||
@@ -546,11 +604,7 @@ export default function ToolResourceSelectors({
|
||||
};
|
||||
|
||||
const handleConfirmTools = () => {
|
||||
onChange({
|
||||
tools: tempSelectedToolNames.filter((name) =>
|
||||
availableToolNames.has(name),
|
||||
),
|
||||
});
|
||||
onChange({ tools: tempSelectedToolNames });
|
||||
setToolsDialogOpen(false);
|
||||
};
|
||||
|
||||
@@ -580,7 +634,9 @@ export default function ToolResourceSelectors({
|
||||
: scopedSelectedMCPResources.filter(
|
||||
(item) => !isSameMCPResource(item, server, resource.uri),
|
||||
);
|
||||
onChange({ 'mcp-resources': next });
|
||||
onChange({
|
||||
'mcp-resources': [...unavailableSelectedMCPResources, ...next],
|
||||
});
|
||||
};
|
||||
|
||||
const isMCPResourceSelected = (server: MCPServer, uri: string) =>
|
||||
@@ -597,25 +653,27 @@ export default function ToolResourceSelectors({
|
||||
<div>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<h3 className="text-sm font-semibold">
|
||||
{t('pipelines.localAgent.toolsTitle')}
|
||||
{t('pipelines.agentRunner.toolsTitle')}
|
||||
</h3>
|
||||
<InfoTooltip
|
||||
label={t('pipelines.localAgent.toolsScopeTooltip')}
|
||||
/>
|
||||
{pipelineId && (
|
||||
<InfoTooltip
|
||||
label={t('pipelines.agentRunner.toolsScopeTooltip')}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-muted-foreground">
|
||||
{t('pipelines.localAgent.toolsDescription')}
|
||||
{t('pipelines.agentRunner.toolsDescription')}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex shrink-0 items-center gap-2">
|
||||
<Label
|
||||
htmlFor="local-agent-enable-all-tools"
|
||||
htmlFor="agent-runner-enable-all-tools"
|
||||
className="cursor-pointer text-sm font-normal"
|
||||
>
|
||||
{t('pipelines.localAgent.enableAllTools')}
|
||||
{t('pipelines.agentRunner.enableAllTools')}
|
||||
</Label>
|
||||
<Switch
|
||||
id="local-agent-enable-all-tools"
|
||||
id="agent-runner-enable-all-tools"
|
||||
checked={enableAllTools}
|
||||
onCheckedChange={handleToggleToolMode}
|
||||
/>
|
||||
@@ -625,13 +683,13 @@ export default function ToolResourceSelectors({
|
||||
{enableAllTools ? (
|
||||
<div className="flex h-24 items-center justify-center rounded-lg border-2 border-dashed border-border bg-muted/30">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{t('pipelines.localAgent.allToolsEnabled')}
|
||||
{t('pipelines.agentRunner.allToolsEnabled')}
|
||||
</p>
|
||||
</div>
|
||||
) : selectedTools.length === 0 ? (
|
||||
<div className="flex h-24 items-center justify-center rounded-lg border-2 border-dashed border-border">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{t('pipelines.localAgent.noToolsSelected')}
|
||||
{t('pipelines.agentRunner.noToolsSelected')}
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
@@ -701,16 +759,12 @@ export default function ToolResourceSelectors({
|
||||
className="w-full"
|
||||
disabled={enableAllTools}
|
||||
onClick={() => {
|
||||
setTempSelectedToolNames(
|
||||
selectedToolNames.filter((name: string) =>
|
||||
availableToolNames.has(name),
|
||||
),
|
||||
);
|
||||
setTempSelectedToolNames(selectedToolNames);
|
||||
setToolsDialogOpen(true);
|
||||
}}
|
||||
>
|
||||
<Plus className="mr-2 h-4 w-4" />
|
||||
{t('pipelines.localAgent.editTools')}
|
||||
{t('pipelines.agentRunner.editTools')}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
@@ -719,10 +773,10 @@ export default function ToolResourceSelectors({
|
||||
<div className="space-y-4 rounded-lg border p-4">
|
||||
<div>
|
||||
<h3 className="text-sm font-semibold">
|
||||
{t('pipelines.localAgent.resourcesTitle')}
|
||||
{t('pipelines.agentRunner.resourcesTitle')}
|
||||
</h3>
|
||||
<p className="mt-1 text-sm text-muted-foreground">
|
||||
{t('pipelines.localAgent.resourcesDescription')}
|
||||
{t('pipelines.agentRunner.resourcesDescription')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -731,7 +785,7 @@ export default function ToolResourceSelectors({
|
||||
<div className="flex items-center gap-2">
|
||||
<Database className="h-4 w-4 text-muted-foreground" />
|
||||
<span className="text-sm font-medium">
|
||||
{t('pipelines.localAgent.knowledgeBases')}
|
||||
{t('pipelines.agentRunner.knowledgeBases')}
|
||||
</span>
|
||||
</div>
|
||||
<Button
|
||||
@@ -801,24 +855,26 @@ export default function ToolResourceSelectors({
|
||||
<div className="flex items-center gap-2">
|
||||
<Server className="h-4 w-4 text-muted-foreground" />
|
||||
<span className="text-sm font-medium">
|
||||
{t('pipelines.localAgent.mcpResources')}
|
||||
{t('pipelines.agentRunner.mcpResources')}
|
||||
</span>
|
||||
<InfoTooltip
|
||||
label={t('pipelines.localAgent.mcpResourcesScopeTooltip')}
|
||||
/>
|
||||
{pipelineId && (
|
||||
<InfoTooltip
|
||||
label={t('pipelines.agentRunner.mcpResourcesScopeTooltip')}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Label
|
||||
htmlFor="local-agent-mcp-resource-read"
|
||||
htmlFor="agent-runner-mcp-resource-read"
|
||||
className="cursor-pointer text-sm font-normal"
|
||||
>
|
||||
{t('pipelines.localAgent.enableMCPResourceRead')}
|
||||
{t('pipelines.agentRunner.enableMCPResourceRead')}
|
||||
</Label>
|
||||
<InfoTooltip
|
||||
label={t('pipelines.localAgent.mcpResourceReadTooltip')}
|
||||
label={t('pipelines.agentRunner.mcpResourceReadTooltip')}
|
||||
/>
|
||||
<Switch
|
||||
id="local-agent-mcp-resource-read"
|
||||
id="agent-runner-mcp-resource-read"
|
||||
checked={mcpResourceReadEnabled}
|
||||
onCheckedChange={(checked) =>
|
||||
onChange({ 'mcp-resource-agent-read-enabled': checked })
|
||||
@@ -830,7 +886,7 @@ export default function ToolResourceSelectors({
|
||||
{resourceServers.length === 0 ? (
|
||||
<div className="flex h-20 items-center justify-center rounded-lg border-2 border-dashed border-border">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{t('pipelines.localAgent.noMCPResourcesAvailable')}
|
||||
{t('pipelines.agentRunner.noMCPResourcesAvailable')}
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
@@ -900,7 +956,9 @@ export default function ToolResourceSelectors({
|
||||
<Dialog open={toolsDialogOpen} onOpenChange={setToolsDialogOpen}>
|
||||
<DialogContent className="flex max-h-[80vh] max-w-2xl flex-col overflow-hidden">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{t('pipelines.localAgent.selectTools')}</DialogTitle>
|
||||
<DialogTitle>
|
||||
{t('pipelines.agentRunner.selectTools')}
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="flex-1 space-y-5 overflow-y-auto pr-2">
|
||||
{availableToolGroups.map((sourceGroup) => {
|
||||
@@ -910,15 +968,17 @@ export default function ToolResourceSelectors({
|
||||
<span className="text-sm font-semibold">
|
||||
{sourceGroup.label}
|
||||
</span>
|
||||
{sourceGroup.key === 'mcp' && (
|
||||
{pipelineId && sourceGroup.key === 'mcp' && (
|
||||
<InfoTooltip
|
||||
label={t('pipelines.localAgent.mcpToolsScopeTooltip')}
|
||||
label={t(
|
||||
'pipelines.agentRunner.mcpToolsScopeTooltip',
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
{sourceGroup.key === 'skill' && (
|
||||
<InfoTooltip
|
||||
label={t(
|
||||
'pipelines.localAgent.skillToolsScopeTooltip',
|
||||
'pipelines.agentRunner.skillToolsScopeTooltip',
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
@@ -987,7 +1047,7 @@ export default function ToolResourceSelectors({
|
||||
{availableToolGroups.length === 0 && (
|
||||
<div className="flex h-24 items-center justify-center rounded-lg border-2 border-dashed border-border">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{t('pipelines.localAgent.noToolsSelected')}
|
||||
{t('pipelines.agentRunner.noToolsSelected')}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
@@ -1012,7 +1072,7 @@ export default function ToolResourceSelectors({
|
||||
<DialogContent className="flex max-h-[80vh] max-w-2xl flex-col overflow-hidden">
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
{t('pipelines.localAgent.selectKnowledgeBases')}
|
||||
{t('pipelines.agentRunner.selectKnowledgeBases')}
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="flex-1 space-y-2 overflow-y-auto pr-2">
|
||||
|
||||
Reference in New Issue
Block a user