fix(agent-runner): align plugin runner runtime boundaries

This commit is contained in:
huanghuoguoguo
2026-06-05 09:35:17 +08:00
parent 36292102f9
commit 121a736e6a
14 changed files with 189 additions and 101 deletions

View File

@@ -6,7 +6,6 @@ import {
PipelineConfigStage,
} from '@/app/infra/entities/pipeline';
import DynamicFormComponent from '@/app/home/components/dynamic-form/DynamicFormComponent';
import N8nAuthFormComponent from '@/app/home/components/dynamic-form/N8nAuthFormComponent';
import { useBoxStatus } from '@/app/infra/hooks/useBoxStatus';
import { Button } from '@/components/ui/button';
import { useForm } from 'react-hook-form';
@@ -237,7 +236,7 @@ export default function PipelineFormComponent({
initializedStagesRef.current.clear();
});
}
}, []);
}, [form, isEditMode, pipelineId]);
useEffect(() => {
if (!isEditMode) {
@@ -310,7 +309,7 @@ export default function PipelineFormComponent({
});
}
// Called from DynamicFormComponent/N8nAuthFormComponent onSubmit callbacks.
// Called from DynamicFormComponent onSubmit callbacks.
// On the first emission for a stage (mount-time default filling), the
// snapshot is synchronously re-captured so that hasUnsavedChanges stays false.
// However, if the form is already dirty (the user has made real changes),
@@ -325,8 +324,7 @@ export default function PipelineFormComponent({
const isFirstEmission = !initializedStagesRef.current.has(stageKey);
const currentValues =
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(form.getValues(formName) as Record<string, any>) || {};
(form.getValues(formName) as Record<string, unknown>) || {};
form.setValue(formName, {
...currentValues,
[stageName]: values,
@@ -351,10 +349,8 @@ export default function PipelineFormComponent({
) {
// Special handling for AI config section
if (formName === 'ai') {
// Get the currently selected runner (use 'id' for new format, fallback to 'runner' for old)
const runnerConfig = (form.watch('ai.runner') as any) || {};
const currentRunner = runnerConfig.id || runnerConfig.runner;
const currentRunner = runnerConfig.id;
// If this is the runner selector stage, render it directly
if (stage.name === 'runner') {
@@ -372,9 +368,9 @@ export default function PipelineFormComponent({
<DynamicFormComponent
itemConfigList={stage.config}
initialValues={
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(form.watch(formName) as Record<string, any>)?.[stage.name] ||
{}
(form.watch(formName) as Record<string, unknown>)?.[
stage.name
] || {}
}
onSubmit={(values) => {
handleDynamicFormEmit(formName, stage.name, values);
@@ -390,36 +386,6 @@ export default function PipelineFormComponent({
return null;
}
// For old n8n built-in runner config, use N8nAuthFormComponent for form linkage
// New plugin:langbot/n8n-agent/default follows normal plugin runner path below
if (stage.name === 'n8n-service-api') {
return (
<Card key={stage.name}>
<CardHeader>
<CardTitle>{extractI18nObject(stage.label)}</CardTitle>
{stage.description && (
<CardDescription>
{extractI18nObject(stage.description)}
</CardDescription>
)}
</CardHeader>
<CardContent className="space-y-6">
<N8nAuthFormComponent
itemConfigList={stage.config}
initialValues={
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(form.watch(formName) as Record<string, any>)?.[stage.name] ||
{}
}
onSubmit={(values) => {
handleDynamicFormEmit(formName, stage.name, values);
}}
/>
</CardContent>
</Card>
);
}
// For plugin runner configs, store in ai.runner_config[runnerId]
const isPluginRunner =
@@ -469,7 +435,7 @@ export default function PipelineFormComponent({
// hard-coding a banner. Field-level gating keeps unrelated fields
// untouched.
const stageSystemContext =
stage.name === 'local-agent'
stage.name === 'plugin:langbot/local-agent/default'
? { box_available: boxAvailable }
: undefined;
@@ -487,8 +453,8 @@ export default function PipelineFormComponent({
<DynamicFormComponent
itemConfigList={stage.config}
initialValues={
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(form.watch(formName) as Record<string, any>)?.[stage.name] || {}
(form.watch(formName) as Record<string, unknown>)?.[stage.name] ||
{}
}
onSubmit={(values) => {
handleDynamicFormEmit(formName, stage.name, values);

View File

@@ -86,7 +86,6 @@ export default function WizardPage() {
const [selectedAdapter, setSelectedAdapter] = useState<string | null>(null);
const [selectedRunner, setSelectedRunner] = useState<string | null>(null);
const [botName, setBotName] = useState('');
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [botDescription, _setBotDescription] = useState('');
const [adapterConfig, setAdapterConfig] = useState<Record<string, unknown>>(
{},
@@ -202,9 +201,7 @@ export default function WizardPage() {
const runnerOptions = useMemo(() => {
if (!runnerStage) return [];
const runnerField =
runnerStage.config.find((c) => c.name === 'id') ??
runnerStage.config.find((c) => c.name === 'runner');
const runnerField = runnerStage.config.find((c) => c.name === 'id');
return runnerField?.options ?? [];
}, [runnerStage]);
@@ -1138,8 +1135,7 @@ function StepAIEngine({
})}
{/* Space promotion banner */}
{(selected === 'local-agent' ||
selected === 'plugin:langbot/local-agent/default') &&
{selected === 'plugin:langbot/local-agent/default' &&
isLocalAccount && (
<div className="animate-in fade-in slide-in-from-left-2 duration-300">
<div className="relative rounded-lg p-[2px] bg-gradient-to-r from-purple-500 via-pink-500 to-orange-500">