feat(runner): unify plugin execution across agents and event processors

This commit is contained in:
RockChinQ
2026-09-10 18:04:38 +08:00
parent 8903a40c41
commit f24a7c9bb2
223 changed files with 4091 additions and 3068 deletions
@@ -23,12 +23,12 @@ import {
DialogTitle,
} from '@/components/ui/dialog';
import PipelineDetailContent from '@/app/home/pipelines/PipelineDetailContent';
import EventProcessorDetailContent from './EventProcessorDetailContent';
import PluginProcessorDetailContent from './PluginProcessorDetailContent';
import AgentCreateContent from './components/AgentCreateContent';
import AgentDebugPanel from './components/AgentDebugPanel';
import AgentFormComponent, {
AgentFormHandle,
AgentRunnerStatus,
RunnerStatus,
} from './components/AgentFormComponent';
export default function AgentDetailContent({ id }: { id: string }) {
@@ -49,9 +49,7 @@ export default function AgentDetailContent({ id }: { id: string }) {
const [basicInfoOpen, setBasicInfoOpen] = useState(false);
const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false);
const [deleting, setDeleting] = useState(false);
const [runnerStatus, setRunnerStatus] = useState<AgentRunnerStatus | null>(
null,
);
const [runnerStatus, setRunnerStatus] = useState<RunnerStatus | null>(null);
const [availableEventTypes, setAvailableEventTypes] = useState<string[]>([
'message.received',
]);
@@ -169,7 +167,7 @@ export default function AgentDetailContent({ id }: { id: string }) {
return (
<>
{agent.kind === 'event_processor' ? (
<EventProcessorDetailContent
<PluginProcessorDetailContent
key={id}
id={id}
agent={agent}
@@ -8,7 +8,7 @@ import { toast } from 'sonner';
import type {
Agent,
AgentPlatformTool,
EventProcessorDescriptor,
RunnerDescriptor,
ProcessorRun,
ProcessorRunEvent,
} from '@/app/infra/entities/api';
@@ -21,14 +21,14 @@ import { ScrollArea } from '@/components/ui/scroll-area';
import ProcessorDetailWorkbench from '@/app/home/components/processor-detail/ProcessorDetailWorkbench';
import EntityTitleEditButton from '@/app/home/components/entity-basic-info/EntityTitleEditButton';
import AgentDebugPanel from './components/AgentDebugPanel';
import EventProcessorTrace, {
import PluginProcessorTrace, {
ProcessorPayload,
} from './components/EventProcessorTrace';
} from './components/PluginProcessorTrace';
import ProcessorRunList from './components/ProcessorRunList';
import EventProcessorSettings from './components/EventProcessorSettings';
import PluginProcessorSettings from './components/PluginProcessorSettings';
import DynamicFormComponent from '@/app/home/components/dynamic-form/DynamicFormComponent';
export default function EventProcessorDetailContent({
export default function PluginProcessorDetailContent({
agent,
id,
canManage,
@@ -53,7 +53,7 @@ export default function EventProcessorDetailContent({
const toolLabels = Object.fromEntries(
platformTools.map((tool) => [tool.name, extractI18nObject(tool.label)]),
);
const [components, setComponents] = useState<EventProcessorDescriptor[]>([]);
const [components, setComponents] = useState<RunnerDescriptor[]>([]);
const [componentRef, setComponentRef] = useState(agent.component_ref ?? '');
const initialParameters =
(
@@ -358,7 +358,7 @@ export default function EventProcessorDetailContent({
value={selected.metadata.delivery}
/>
)}
<EventProcessorTrace events={events} toolLabels={toolLabels} />
<PluginProcessorTrace events={events} toolLabels={toolLabels} />
{selected.status === 'failed' && selected.status_reason && (
<Alert variant="destructive">
<AlertDescription className="break-words">
@@ -389,7 +389,7 @@ export default function EventProcessorDetailContent({
canManage ? <EntityTitleEditButton onClick={onEdit} /> : undefined
}
titleControls={
<EventProcessorSettings
<PluginProcessorSettings
components={components}
value={componentRef}
disabled={!canManage || saving || loading}
@@ -46,7 +46,7 @@ import {
groupEventPatterns,
} from '@/app/home/components/event-patterns/event-pattern-groups';
import EventSelectOptionContent from '@/app/home/components/event-patterns/EventSelectOptionContent';
import EventProcessorTrace from './EventProcessorTrace';
import PluginProcessorTrace from './PluginProcessorTrace';
import AgentExecutionTrace from './AgentExecutionTrace';
import AgentEventDataEditor from './AgentEventDataEditor';
import {
@@ -455,7 +455,7 @@ export default function AgentDebugPanel({
</div>
{entry.events &&
(processor ? (
<EventProcessorTrace
<PluginProcessorTrace
events={entry.events}
toolLabels={toolLabels}
/>
@@ -28,10 +28,10 @@ import {
import DynamicFormComponent from '@/app/home/components/dynamic-form/DynamicFormComponent';
import {
getErrorMessage,
readPendingAgentRunnerInstall,
resumePendingAgentRunnerInstall,
type InstalledAgentRunner,
} from '@/app/home/agents/agent-runner-marketplace';
readPendingRunnerInstall,
resumePendingRunnerInstall,
type InstalledRunner,
} from '@/app/home/agents/runner-marketplace';
import { extractI18nObject } from '@/i18n/I18nProvider';
import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs';
import {
@@ -43,7 +43,7 @@ import {
} from '@/components/ui/card';
import { Form, FormField, FormItem, FormMessage } from '@/components/ui/form';
import AgentEventPatternPicker from './AgentEventPatternPicker';
import AgentRunnerSelect from './AgentRunnerSelect';
import RunnerSelect from './RunnerSelect';
import AgentApiToolPicker from './AgentApiToolPicker';
const OTHER_TOOL_SCOPES = [
@@ -54,7 +54,7 @@ const OTHER_TOOL_SCOPES = [
'skill',
] as const;
export interface AgentRunnerStatus {
export interface RunnerStatus {
label: string;
description?: string;
tone: 'neutral' | 'success' | 'warning' | 'error';
@@ -66,7 +66,7 @@ interface AgentFormComponentProps {
onFinish: (agent?: Partial<Agent>) => void;
onDirtyChange?: (dirty: boolean) => void;
onSavingChange?: (saving: boolean) => void;
onRunnerStatusChange?: (status: AgentRunnerStatus) => void;
onRunnerStatusChange?: (status: RunnerStatus) => void;
onSupportedEventPatternsChange?: (patterns: string[]) => void;
onPlatformToolsChange?: (tools: AgentPlatformTool[]) => void;
}
@@ -184,12 +184,9 @@ function AgentFormComponent(
});
const runnerInstallScope = `agent:${agentId}`;
const applyInstalledRunner = useCallback(
(installed: InstalledAgentRunner) => {
setRunnerConfigSchema(installed.configTab);
},
[],
);
const applyInstalledRunner = useCallback((installed: InstalledRunner) => {
setRunnerConfigSchema(installed.configTab);
}, []);
const savedSnapshotRef = useRef('');
const initializedStagesRef = useRef<Set<string>>(new Set());
@@ -285,15 +282,12 @@ function AgentFormComponent(
}, [agentId, form, t]);
useEffect(() => {
if (
!initialDataLoaded ||
!readPendingAgentRunnerInstall(runnerInstallScope)
) {
if (!initialDataLoaded || !readPendingRunnerInstall(runnerInstallScope)) {
return;
}
let cancelled = false;
setRunnerInstallRecovering(true);
void resumePendingAgentRunnerInstall(runnerInstallScope)
void resumePendingRunnerInstall(runnerInstallScope)
.then((installed) => {
if (cancelled || !installed) return;
applyInstalledRunner(installed);
@@ -395,7 +389,7 @@ function AgentFormComponent(
},
];
const runnerStatus = useMemo<AgentRunnerStatus>(() => {
const runnerStatus = useMemo<RunnerStatus>(() => {
if (pluginStatusLoading) {
return {
label: t('agents.runnerStatusLoading'),
@@ -540,7 +534,7 @@ function AgentFormComponent(
isRunnerSelector
? ({ config, field }) =>
config.name === 'id' ? (
<AgentRunnerSelect
<RunnerSelect
options={config.options ?? []}
label={extractI18nObject(config.label)}
value={String(field.value ?? '')}
@@ -1,7 +1,7 @@
import { Puzzle } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { Link } from 'react-router-dom';
import type { EventProcessorDescriptor } from '@/app/infra/entities/api';
import type { RunnerDescriptor } from '@/app/infra/entities/api';
import { httpClient } from '@/app/infra/http';
import { extractI18nObject } from '@/i18n/I18nProvider';
import {
@@ -18,7 +18,7 @@ function ProcessorComponentContent({
component,
option = false,
}: {
component: EventProcessorDescriptor;
component: RunnerDescriptor;
option?: boolean;
}) {
const label = extractI18nObject({
@@ -62,13 +62,13 @@ function ProcessorComponentContent({
);
}
export default function EventProcessorSettings({
export default function PluginProcessorSettings({
components,
value,
onChange,
disabled = false,
}: {
components: EventProcessorDescriptor[];
components: RunnerDescriptor[];
value: string;
onChange: (value: string) => void;
disabled?: boolean;
@@ -38,7 +38,7 @@ export function ProcessorPayload({
);
}
export default function EventProcessorTrace({
export default function PluginProcessorTrace({
events,
toolLabels = {},
}: {
@@ -373,7 +373,7 @@ function PipelineDiagram() {
);
}
function EventProcessorDiagram() {
function RunnerDiagram() {
const { t } = useTranslation();
const codeLines = [
<>
@@ -459,6 +459,6 @@ function EventProcessorDiagram() {
}
export default function ProcessorTypeDiagram({ kind }: { kind: AgentKind }) {
if (kind === 'event_processor') return <EventProcessorDiagram />;
if (kind === 'event_processor') return <RunnerDiagram />;
return kind === 'agent' ? <AgentDiagram /> : <PipelineDiagram />;
}
@@ -7,17 +7,17 @@ import { getCloudServiceClientSync, httpClient } from '@/app/infra/http';
import type { IDynamicFormItemOption } from '@/app/infra/entities/form/dynamic';
import type { PluginV4 } from '@/app/infra/entities/plugin';
import {
AgentRunnerMarketplaceError,
RunnerMarketplaceError,
getErrorMessage,
installMarketplaceAgentRunner,
loadAgentRunnerCatalog,
installMarketplaceRunner,
loadRunnerCatalog,
marketplacePluginId,
runnerPluginPrefix,
readPendingAgentRunnerInstall,
subscribePendingAgentRunnerInstall,
type AgentRunnerCatalog,
type InstalledAgentRunner,
} from '@/app/home/agents/agent-runner-marketplace';
readPendingRunnerInstall,
subscribePendingRunnerInstall,
type RunnerCatalog,
type InstalledRunner,
} from '@/app/home/agents/runner-marketplace';
import {
InstallStage,
usePluginInstallTasks,
@@ -39,7 +39,7 @@ function installErrorMessage(
error: unknown,
t: ReturnType<typeof useTranslation>['t'],
) {
if (error instanceof AgentRunnerMarketplaceError) {
if (error instanceof RunnerMarketplaceError) {
if (error.code === 'version-unavailable') {
return t('wizard.aiEngine.versionUnavailable');
}
@@ -123,7 +123,7 @@ function runnerPluginId(optionName: string) {
function installedRunnerDescription(
option: IDynamicFormItemOption,
marketplaceRunners: PluginV4[],
installedPluginDescriptions: AgentRunnerCatalog['installedPluginDescriptions'],
installedPluginDescriptions: RunnerCatalog['installedPluginDescriptions'],
) {
const pluginId = runnerPluginId(option.name);
if (!pluginId) return option.name;
@@ -192,7 +192,7 @@ function MarketplaceRunnerContent({
);
}
export default function AgentRunnerSelect({
export default function RunnerSelect({
options,
label,
value,
@@ -205,18 +205,18 @@ export default function AgentRunnerSelect({
value: string;
onValueChange: (value: string) => void;
installScope: string;
onInstalled: (installed: InstalledAgentRunner) => void;
onInstalled: (installed: InstalledRunner) => void;
}) {
const { t } = useTranslation();
const { addTask, tasks } = usePluginInstallTasks();
const [marketplaceRunners, setMarketplaceRunners] = useState<PluginV4[]>([]);
const [installedPluginIds, setInstalledPluginIds] = useState<string[]>([]);
const [installedPluginDescriptions, setInstalledPluginDescriptions] =
useState<AgentRunnerCatalog['installedPluginDescriptions']>({});
useState<RunnerCatalog['installedPluginDescriptions']>({});
const [catalogLoading, setCatalogLoading] = useState(true);
const [catalogError, setCatalogError] = useState(false);
const [pendingInstall, setPendingInstall] = useState(() =>
readPendingAgentRunnerInstall(installScope),
readPendingRunnerInstall(installScope),
);
const [installError, setInstallError] = useState<string | null>(null);
const [installingPluginId, setInstallingPluginId] = useState<string | null>(
@@ -227,12 +227,12 @@ export default function AgentRunnerSelect({
setCatalogLoading(true);
setCatalogError(false);
try {
const catalog = await loadAgentRunnerCatalog();
const catalog = await loadRunnerCatalog();
setMarketplaceRunners(catalog.marketplaceRunners);
setInstalledPluginIds(catalog.installedPluginIds);
setInstalledPluginDescriptions(catalog.installedPluginDescriptions);
} catch (error) {
console.error('Failed to load AgentRunner catalog', error);
console.error('Failed to load Runner catalog', error);
setCatalogError(true);
} finally {
setCatalogLoading(false);
@@ -245,9 +245,9 @@ export default function AgentRunnerSelect({
useEffect(() => {
const syncPendingInstall = () =>
setPendingInstall(readPendingAgentRunnerInstall(installScope));
setPendingInstall(readPendingRunnerInstall(installScope));
syncPendingInstall();
return subscribePendingAgentRunnerInstall(installScope, syncPendingInstall);
return subscribePendingRunnerInstall(installScope, syncPendingInstall);
}, [installScope]);
const marketplaceOptions = useMemo(
@@ -291,7 +291,7 @@ export default function AgentRunnerSelect({
setInstallingPluginId(pluginId);
setInstallError(null);
try {
const installed = await installMarketplaceAgentRunner(plugin, {
const installed = await installMarketplaceRunner(plugin, {
scope: installScope,
onTaskCreated: (taskId) =>
addTask({
@@ -313,7 +313,7 @@ export default function AgentRunnerSelect({
setInstallError(message);
toast.error(message);
} finally {
const current = readPendingAgentRunnerInstall(installScope);
const current = readPendingRunnerInstall(installScope);
setPendingInstall(current);
if (!current) setInstallingPluginId(null);
}
@@ -392,7 +392,7 @@ export default function AgentRunnerSelect({
{t('agents.marketplaceRunners')}
</span>
<a
href="https://space.langbot.app/market?type=plugin&component=AgentRunner"
href="https://space.langbot.app/market?type=plugin&component=Runner"
target="_blank"
rel="noreferrer"
className="inline-flex shrink-0 items-center gap-1 rounded border px-1.5 py-0.5 text-[10px] font-medium text-foreground hover:bg-accent"
@@ -6,38 +6,38 @@ import type { PipelineConfigTab } from '@/app/infra/entities/pipeline';
import type { PluginV4 } from '@/app/infra/entities/plugin';
import type { I18nObject } from '@/app/infra/entities/common';
export const RUNNER_COMPONENT_FILTER = 'AgentRunner';
export const RUNNER_COMPONENT_FILTER = 'Runner';
const RUNNER_CATALOG_PAGE_SIZE = 100;
const RUNNER_INSTALL_TIMEOUT_MS = 120_000;
const RUNNER_REGISTRATION_TIMEOUT_MS = 60_000;
const RUNNER_INSTALL_INTENT_KEY_PREFIX = 'langbot-agent-runner-install';
const RUNNER_INSTALL_INTENT_EVENT = 'langbot-agent-runner-install-change';
const RUNNER_INSTALL_INTENT_KEY_PREFIX = 'langbot-runner-install';
const RUNNER_INSTALL_INTENT_EVENT = 'langbot-runner-install-change';
export type AgentRunnerMarketplaceErrorCode =
export type RunnerMarketplaceErrorCode =
| 'version-unavailable'
| 'install-timeout'
| 'registration-timeout';
export class AgentRunnerMarketplaceError extends Error {
constructor(public readonly code: AgentRunnerMarketplaceErrorCode) {
export class RunnerMarketplaceError extends Error {
constructor(public readonly code: RunnerMarketplaceErrorCode) {
super(code);
this.name = 'AgentRunnerMarketplaceError';
this.name = 'RunnerMarketplaceError';
}
}
export interface AgentRunnerCatalog {
export interface RunnerCatalog {
marketplaceRunners: PluginV4[];
installedPluginIds: string[];
installedPluginDescriptions: Record<string, I18nObject>;
}
export interface InstalledAgentRunner {
export interface InstalledRunner {
configTab: PipelineConfigTab;
runner: IDynamicFormItemOption;
}
export interface PendingAgentRunnerInstall {
export interface PendingRunnerInstall {
taskId: number;
pluginId: string;
pluginAuthor: string;
@@ -47,7 +47,7 @@ export interface PendingAgentRunnerInstall {
startedAt: number;
}
interface InstallAgentRunnerOptions {
interface InstallRunnerOptions {
scope: string;
onTaskCreated?: (taskId: number) => void;
}
@@ -83,14 +83,14 @@ function emitInstallIntentChange(scope: string) {
);
}
export function readPendingAgentRunnerInstall(
export function readPendingRunnerInstall(
scope: string,
): PendingAgentRunnerInstall | null {
): PendingRunnerInstall | null {
if (typeof window === 'undefined') return null;
try {
const raw = sessionStorage.getItem(installIntentStorageKey(scope));
if (!raw) return null;
const value = JSON.parse(raw) as Partial<PendingAgentRunnerInstall>;
const value = JSON.parse(raw) as Partial<PendingRunnerInstall>;
if (
value.scope !== scope ||
typeof value.taskId !== 'number' ||
@@ -103,13 +103,13 @@ export function readPendingAgentRunnerInstall(
sessionStorage.removeItem(installIntentStorageKey(scope));
return null;
}
return value as PendingAgentRunnerInstall;
return value as PendingRunnerInstall;
} catch {
return null;
}
}
function writePendingAgentRunnerInstall(intent: PendingAgentRunnerInstall) {
function writePendingRunnerInstall(intent: PendingRunnerInstall) {
if (typeof window === 'undefined') return;
sessionStorage.setItem(
installIntentStorageKey(intent.scope),
@@ -118,15 +118,15 @@ function writePendingAgentRunnerInstall(intent: PendingAgentRunnerInstall) {
emitInstallIntentChange(intent.scope);
}
export function clearPendingAgentRunnerInstall(scope: string, taskId?: number) {
export function clearPendingRunnerInstall(scope: string, taskId?: number) {
if (typeof window === 'undefined') return;
const current = readPendingAgentRunnerInstall(scope);
const current = readPendingRunnerInstall(scope);
if (taskId !== undefined && current?.taskId !== taskId) return;
sessionStorage.removeItem(installIntentStorageKey(scope));
emitInstallIntentChange(scope);
}
export function subscribePendingAgentRunnerInstall(
export function subscribePendingRunnerInstall(
scope: string,
listener: () => void,
) {
@@ -140,7 +140,7 @@ export function subscribePendingAgentRunnerInstall(
window.removeEventListener(RUNNER_INSTALL_INTENT_EVENT, handleChange);
}
export async function loadAgentRunnerCatalog(): Promise<AgentRunnerCatalog> {
export async function loadRunnerCatalog(): Promise<RunnerCatalog> {
const cloudClient = await getCloudServiceClient();
const [firstSearchResult, recommendationResult, installedResult] =
await Promise.all([
@@ -218,12 +218,12 @@ export async function loadAgentRunnerCatalog(): Promise<AgentRunnerCatalog> {
};
}
export async function installMarketplaceAgentRunner(
export async function installMarketplaceRunner(
plugin: PluginV4,
options: InstallAgentRunnerOptions,
): Promise<InstalledAgentRunner> {
options: InstallRunnerOptions,
): Promise<InstalledRunner> {
if (!plugin.latest_version) {
throw new AgentRunnerMarketplaceError('version-unavailable');
throw new RunnerMarketplaceError('version-unavailable');
}
const { task_id: taskId } = await httpClient.installPluginFromMarketplace(
@@ -231,7 +231,7 @@ export async function installMarketplaceAgentRunner(
plugin.name,
plugin.latest_version,
);
const pending: PendingAgentRunnerInstall = {
const pending: PendingRunnerInstall = {
taskId,
pluginId: marketplacePluginId(plugin),
pluginAuthor: plugin.author,
@@ -240,9 +240,9 @@ export async function installMarketplaceAgentRunner(
scope: options.scope,
startedAt: Date.now(),
};
writePendingAgentRunnerInstall(pending);
writePendingRunnerInstall(pending);
options.onTaskCreated?.(taskId);
return finishAgentRunnerInstall(pending);
return finishRunnerInstall(pending);
}
function extractPluginLabel(plugin: PluginV4) {
@@ -257,9 +257,9 @@ function extractPluginLabel(plugin: PluginV4) {
return plugin.name;
}
async function finishAgentRunnerInstall(
pending: PendingAgentRunnerInstall,
): Promise<InstalledAgentRunner> {
async function finishRunnerInstall(
pending: PendingRunnerInstall,
): Promise<InstalledRunner> {
// A refreshed page receives a fresh observation window. The backend task is
// authoritative; `startedAt` is display metadata, not a reason to abandon a
// still-running installation immediately after recovery.
@@ -269,7 +269,7 @@ async function finishAgentRunnerInstall(
const task = await httpClient.getAsyncTask(pending.taskId);
if (task.runtime.done) {
if (task.runtime.exception) {
clearPendingAgentRunnerInstall(pending.scope, pending.taskId);
clearPendingRunnerInstall(pending.scope, pending.taskId);
throw new Error(task.runtime.exception);
}
installCompleted = true;
@@ -279,7 +279,7 @@ async function finishAgentRunnerInstall(
await wait(1000);
}
if (!installCompleted) {
throw new AgentRunnerMarketplaceError('install-timeout');
throw new RunnerMarketplaceError('install-timeout');
}
const registrationDeadline = Date.now() + RUNNER_REGISTRATION_TIMEOUT_MS;
@@ -303,20 +303,20 @@ async function finishAgentRunnerInstall(
pluginRunnerOptions[0];
if (configTab && runner) {
clearPendingAgentRunnerInstall(pending.scope, pending.taskId);
clearPendingRunnerInstall(pending.scope, pending.taskId);
return { configTab, runner };
}
await wait(1000);
}
clearPendingAgentRunnerInstall(pending.scope, pending.taskId);
throw new AgentRunnerMarketplaceError('registration-timeout');
clearPendingRunnerInstall(pending.scope, pending.taskId);
throw new RunnerMarketplaceError('registration-timeout');
}
export async function resumePendingAgentRunnerInstall(
export async function resumePendingRunnerInstall(
scope: string,
): Promise<InstalledAgentRunner | null> {
const pending = readPendingAgentRunnerInstall(scope);
): Promise<InstalledRunner | null> {
const pending = readPendingRunnerInstall(scope);
if (!pending) return null;
return finishAgentRunnerInstall(pending);
return finishRunnerInstall(pending);
}
@@ -582,10 +582,10 @@ export default function ToolResourceSelectors({
const sourceLabels = useMemo<Record<string, string>>(
() => ({
builtin: t('pipelines.agentRunner.builtinTools'),
plugin: t('pipelines.agentRunner.pluginTools'),
mcp: t('pipelines.agentRunner.mcpTools'),
skill: t('pipelines.agentRunner.skillTools'),
builtin: t('pipelines.runner.builtinTools'),
plugin: t('pipelines.runner.pluginTools'),
mcp: t('pipelines.runner.mcpTools'),
skill: t('pipelines.runner.skillTools'),
}),
[t],
);
@@ -653,27 +653,27 @@ export default function ToolResourceSelectors({
<div>
<div className="flex items-center gap-1.5">
<h3 className="text-sm font-semibold">
{t('pipelines.agentRunner.toolsTitle')}
{t('pipelines.runner.toolsTitle')}
</h3>
{pipelineId && (
<InfoTooltip
label={t('pipelines.agentRunner.toolsScopeTooltip')}
label={t('pipelines.runner.toolsScopeTooltip')}
/>
)}
</div>
<p className="mt-1 text-sm text-muted-foreground">
{t('pipelines.agentRunner.toolsDescription')}
{t('pipelines.runner.toolsDescription')}
</p>
</div>
<div className="flex shrink-0 items-center gap-2">
<Label
htmlFor="agent-runner-enable-all-tools"
htmlFor="runner-enable-all-tools"
className="cursor-pointer text-sm font-normal"
>
{t('pipelines.agentRunner.enableAllTools')}
{t('pipelines.runner.enableAllTools')}
</Label>
<Switch
id="agent-runner-enable-all-tools"
id="runner-enable-all-tools"
checked={enableAllTools}
onCheckedChange={handleToggleToolMode}
/>
@@ -683,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.agentRunner.allToolsEnabled')}
{t('pipelines.runner.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.agentRunner.noToolsSelected')}
{t('pipelines.runner.noToolsSelected')}
</p>
</div>
) : (
@@ -764,7 +764,7 @@ export default function ToolResourceSelectors({
}}
>
<Plus className="mr-2 h-4 w-4" />
{t('pipelines.agentRunner.editTools')}
{t('pipelines.runner.editTools')}
</Button>
</div>
)}
@@ -773,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.agentRunner.resourcesTitle')}
{t('pipelines.runner.resourcesTitle')}
</h3>
<p className="mt-1 text-sm text-muted-foreground">
{t('pipelines.agentRunner.resourcesDescription')}
{t('pipelines.runner.resourcesDescription')}
</p>
</div>
@@ -785,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.agentRunner.knowledgeBases')}
{t('pipelines.runner.knowledgeBases')}
</span>
</div>
<Button
@@ -855,26 +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.agentRunner.mcpResources')}
{t('pipelines.runner.mcpResources')}
</span>
{pipelineId && (
<InfoTooltip
label={t('pipelines.agentRunner.mcpResourcesScopeTooltip')}
label={t('pipelines.runner.mcpResourcesScopeTooltip')}
/>
)}
</div>
<div className="flex items-center gap-2">
<Label
htmlFor="agent-runner-mcp-resource-read"
htmlFor="runner-mcp-resource-read"
className="cursor-pointer text-sm font-normal"
>
{t('pipelines.agentRunner.enableMCPResourceRead')}
{t('pipelines.runner.enableMCPResourceRead')}
</Label>
<InfoTooltip
label={t('pipelines.agentRunner.mcpResourceReadTooltip')}
label={t('pipelines.runner.mcpResourceReadTooltip')}
/>
<Switch
id="agent-runner-mcp-resource-read"
id="runner-mcp-resource-read"
checked={mcpResourceReadEnabled}
onCheckedChange={(checked) =>
onChange({ 'mcp-resource-agent-read-enabled': checked })
@@ -886,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.agentRunner.noMCPResourcesAvailable')}
{t('pipelines.runner.noMCPResourcesAvailable')}
</p>
</div>
) : (
@@ -956,9 +956,7 @@ 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.agentRunner.selectTools')}
</DialogTitle>
<DialogTitle>{t('pipelines.runner.selectTools')}</DialogTitle>
</DialogHeader>
<div className="flex-1 space-y-5 overflow-y-auto pr-2">
{availableToolGroups.map((sourceGroup) => {
@@ -970,16 +968,12 @@ export default function ToolResourceSelectors({
</span>
{pipelineId && sourceGroup.key === 'mcp' && (
<InfoTooltip
label={t(
'pipelines.agentRunner.mcpToolsScopeTooltip',
)}
label={t('pipelines.runner.mcpToolsScopeTooltip')}
/>
)}
{sourceGroup.key === 'skill' && (
<InfoTooltip
label={t(
'pipelines.agentRunner.skillToolsScopeTooltip',
)}
label={t('pipelines.runner.skillToolsScopeTooltip')}
/>
)}
<Badge variant="outline" className="ml-auto">
@@ -1047,7 +1041,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.agentRunner.noToolsSelected')}
{t('pipelines.runner.noToolsSelected')}
</p>
</div>
)}
@@ -1072,7 +1066,7 @@ export default function ToolResourceSelectors({
<DialogContent className="flex max-h-[80vh] max-w-2xl flex-col overflow-hidden">
<DialogHeader>
<DialogTitle>
{t('pipelines.agentRunner.selectKnowledgeBases')}
{t('pipelines.runner.selectKnowledgeBases')}
</DialogTitle>
</DialogHeader>
<div className="flex-1 space-y-2 overflow-y-auto pr-2">
@@ -58,13 +58,13 @@ import {
Copy,
} from 'lucide-react';
import PipelineExtension from '@/app/home/pipelines/components/pipeline-extensions/PipelineExtension';
import AgentRunnerSelect from '@/app/home/agents/components/AgentRunnerSelect';
import RunnerSelect from '@/app/home/agents/components/RunnerSelect';
import {
getErrorMessage,
readPendingAgentRunnerInstall,
resumePendingAgentRunnerInstall,
type InstalledAgentRunner,
} from '@/app/home/agents/agent-runner-marketplace';
readPendingRunnerInstall,
resumePendingRunnerInstall,
type InstalledRunner,
} from '@/app/home/agents/runner-marketplace';
interface PipelineFormComponentProps {
pipelineId?: string;
@@ -229,12 +229,9 @@ const PipelineFormComponent = forwardRef<
},
});
const runnerInstallScope = `pipeline:${pipelineId || 'new'}`;
const applyInstalledRunner = useCallback(
(installed: InstalledAgentRunner) => {
setAIConfigTabSchema(installed.configTab);
},
[],
);
const applyInstalledRunner = useCallback((installed: InstalledRunner) => {
setAIConfigTabSchema(installed.configTab);
}, []);
const dynamicFormSystemContext = useMemo(
() => ({ pipeline_id: pipelineId }),
[pipelineId],
@@ -307,12 +304,12 @@ const PipelineFormComponent = forwardRef<
if (
!metadataLoaded ||
!pipelineLoaded ||
!readPendingAgentRunnerInstall(runnerInstallScope)
!readPendingRunnerInstall(runnerInstallScope)
) {
return;
}
let cancelled = false;
void resumePendingAgentRunnerInstall(runnerInstallScope)
void resumePendingRunnerInstall(runnerInstallScope)
.then((installed) => {
if (cancelled || !installed) return;
applyInstalledRunner(installed);
@@ -575,7 +572,7 @@ const PipelineFormComponent = forwardRef<
systemContext={dynamicFormSystemContext}
renderItem={({ config, field }) =>
config.name === 'id' ? (
<AgentRunnerSelect
<RunnerSelect
options={config.options ?? []}
label={extractI18nObject(config.label)}
value={String(field.value ?? '')}
@@ -7,7 +7,6 @@ import {
FileText,
PanelTop,
Bot,
Zap,
} from 'lucide-react';
import { Badge } from '@/components/ui/badge';
@@ -33,8 +32,7 @@ export default function PluginComponentList({
KnowledgeEngine: <Book className="w-5 h-5" />,
Parser: <FileText className="w-5 h-5" />,
Page: <PanelTop className="w-5 h-5" />,
AgentRunner: <Bot className="w-5 h-5" />,
EventProcessor: <Zap className="w-5 h-5" />,
Runner: <Bot className="w-5 h-5" />,
};
const componentKindList = Object.keys(components || {});
@@ -5,7 +5,6 @@ import {
Book,
FileText,
Hash,
Zap,
Wrench,
type LucideIcon,
} from 'lucide-react';
@@ -17,6 +16,5 @@ export const pluginComponentIconMap: Record<string, LucideIcon> = {
KnowledgeEngine: Book,
Parser: FileText,
Page: AppWindow,
AgentRunner: Bot,
EventProcessor: Zap,
Runner: Bot,
};
@@ -18,7 +18,6 @@ import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group';
import {
Search,
Puzzle,
Zap,
Server,
Sparkles,
Wrench,
@@ -72,8 +71,7 @@ const MARKET_COMPONENT_VALUES = [
'KnowledgeEngine',
'Parser',
'Page',
'AgentRunner',
'EventProcessor',
'Runner',
];
function getComponentFilterFromQuery(
@@ -242,15 +240,10 @@ function MarketPageContent({
icon: AppWindow,
},
{
value: 'AgentRunner',
label: t('market.componentName.AgentRunner'),
value: 'Runner',
label: t('market.componentName.Runner'),
icon: Bot,
},
{
value: 'EventProcessor',
label: t('market.componentName.EventProcessor'),
icon: Zap,
},
];
// 获取当前排序参数