diff --git a/web/src/app/home/components/dynamic-form/DynamicFormComponent.tsx b/web/src/app/home/components/dynamic-form/DynamicFormComponent.tsx
index 078db6f7..057f4e52 100644
--- a/web/src/app/home/components/dynamic-form/DynamicFormComponent.tsx
+++ b/web/src/app/home/components/dynamic-form/DynamicFormComponent.tsx
@@ -198,6 +198,35 @@ function WebhookUrlField({
);
}
+// Hover-only Radix tooltips never open on touch devices (no pointer hover),
+// so the ``disabled_tooltip`` explaining why a field is locked was invisible on
+// mobile. This wrapper makes the info icon also toggle the tooltip on tap while
+// keeping hover behavior on desktop.
+function DisabledTooltipIcon({ text }: { text: string }) {
+ const [open, setOpen] = useState(false);
+ return (
+
+
+
+
+
+ {text}
+
+
+ );
+}
+
export default function DynamicFormComponent({
itemConfigList,
onSubmit,
@@ -550,18 +579,7 @@ export default function DynamicFormComponent({
? extractI18nObject(config.disabled_tooltip)
: '';
const renderDisabledTooltipIcon = () =>
- disabledTooltip ? (
-
-
-
-
-
-
- {disabledTooltip}
-
-
-
- ) : null;
+ disabledTooltip ? : null;
// Webhook URL fields are display-only; render outside of form binding
if (config.type === 'webhook-url') {
diff --git a/web/src/app/home/pipelines/components/pipeline-form/PipelineFormComponent.tsx b/web/src/app/home/pipelines/components/pipeline-form/PipelineFormComponent.tsx
index fc0a6812..863c2202 100644
--- a/web/src/app/home/pipelines/components/pipeline-form/PipelineFormComponent.tsx
+++ b/web/src/app/home/pipelines/components/pipeline-form/PipelineFormComponent.tsx
@@ -427,8 +427,9 @@ export default function PipelineFormComponent({
// 1. Box sandbox is unavailable, or
// 2. the deployment pins all pipelines to a fixed scope via
// ``system.limitation.force_box_session_id_template`` (SaaS).
- const boxScopeForced =
- !!systemInfo.limitation?.force_box_session_id_template;
+ const forcedBoxTemplate =
+ systemInfo.limitation?.force_box_session_id_template || '';
+ const boxScopeForced = !!forcedBoxTemplate;
const stageSystemContext =
stage.name === 'local-agent'
? {
@@ -437,6 +438,24 @@ export default function PipelineFormComponent({
}
: undefined;
+ // When the deployment pins every pipeline to a fixed sandbox scope (SaaS
+ // ``force_box_session_id_template``), the Sandbox Scope selector is locked.
+ // The runtime already overrides the scope on every exec, but the stored
+ // pipeline value can be anything (e.g. the per-chat default), which would
+ // make the locked selector display a scope that is NOT the one actually in
+ // effect. Coerce the displayed/saved value to the forced template so the UI
+ // truthfully reflects runtime behavior.
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ const stageInitialValues: Record =
+ (form.watch(formName) as Record)?.[stage.name] || {};
+ const effectiveInitialValues =
+ stage.name === 'local-agent' && boxScopeForced
+ ? {
+ ...stageInitialValues,
+ 'box-session-id-template': forcedBoxTemplate,
+ }
+ : stageInitialValues;
+
return (
@@ -450,10 +469,7 @@ export default function PipelineFormComponent({
)?.[stage.name] || {}
- }
+ initialValues={effectiveInitialValues}
onSubmit={(values) => {
handleDynamicFormEmit(formName, stage.name, values);
}}