Remove the 5-model limit on multi-run groups

The per-group cap of 5 models was enforced in three client-side locations with no server-side constraint and no technical justification; the store iterates models in a plain loop and dispatches each run via Promise.allSettled. Remove the cap entirely from MultiRunLauncher, useMultiRunStore, and AgentManagerEmptyState, and drop the {max} ceiling from the launcher info copy across all locales.

Closes #2847
This commit is contained in:
Tom Rochette
2026-08-13 04:13:55 +00:00
parent e7b864e9ae
commit c4b14e2a3a
13 changed files with 27 additions and 24 deletions
@@ -32,7 +32,6 @@ import { startDesktopWindowDrag } from '@/lib/desktopNative';
import { useI18n } from '@/lib/i18n';
const MAX_FILE_SIZE = 10 * 1024 * 1024;
const MAX_MODELS_PER_GROUP = 5;
interface MultiRunAttachedFile {
id: string;
@@ -727,7 +726,6 @@ const RunGroupCard: React.FC<RunGroupCardProps> = ({
const snippetRef = React.useRef<SnippetAutocompleteHandle>(null);
const handleAddModel = React.useCallback((model: ModelSelectionWithId) => {
if (group.models.length >= MAX_MODELS_PER_GROUP) return;
onUpdate(group.id, { models: [...group.models, model] });
}, [group.id, group.models, onUpdate]);
@@ -987,7 +985,7 @@ const RunGroupCard: React.FC<RunGroupCardProps> = ({
<div className="flex flex-col gap-1.5">
<FieldLabel
required
info={<InfoTip>{t('multirun.launcher.models.info', { max: MAX_MODELS_PER_GROUP })}</InfoTip>}
info={<InfoTip>{t('multirun.launcher.models.info')}</InfoTip>}
>
{t('multirun.launcher.models.label')}
</FieldLabel>
@@ -997,7 +995,6 @@ const RunGroupCard: React.FC<RunGroupCardProps> = ({
onRemove={handleRemoveModel}
onUpdate={handleUpdateModel}
minModels={1}
maxModels={MAX_MODELS_PER_GROUP}
/>
</div>
</div>
@@ -22,8 +22,6 @@ import { useI18n } from '@/lib/i18n';
/** Max file size in bytes (10MB) */
const MAX_FILE_SIZE = 10 * 1024 * 1024;
/** Max number of concurrent runs */
const MAX_MODELS = 5;
/** Attached file for agent manager */
interface AttachedFile {
@@ -132,11 +130,8 @@ export const AgentManagerEmptyState: React.FC<AgentManagerEmptyStateProps> = ({
}, [projectRef]);
const handleAddModel = React.useCallback((model: ModelSelectionWithId) => {
if (selectedModels.length >= MAX_MODELS) {
return;
}
setSelectedModels((prev) => [...prev, model]);
}, [selectedModels.length]);
}, []);
const handleRemoveModel = React.useCallback((index: number) => {
setSelectedModels((prev) => prev.filter((_, i) => i !== index));
@@ -529,7 +524,6 @@ export const AgentManagerEmptyState: React.FC<AgentManagerEmptyStateProps> = ({
onUpdate={handleUpdateModel}
minModels={1}
addButtonLabel={t('agentManager.empty.models.addModel')}
maxModels={5}
/>
</div>
+1 -1
View File
@@ -369,7 +369,7 @@ export const dict = {
'multirun.launcher.attachments.attach': 'Attach',
'multirun.launcher.attachments.tooltip': 'Same files sent to all runs',
'multirun.launcher.models.label': 'Models',
'multirun.launcher.models.info': 'Select 2-{max} models. Same model can be added multiple times.',
'multirun.launcher.models.info': 'Select 2 or more models. Same model can be added multiple times.',
'multirun.launcher.toast.fileTooLarge': 'File "{fileName}" is too large (max 10MB)',
'multirun.launcher.toast.attachFailed': 'Failed to attach "{fileName}"',
'multirun.launcher.toast.attachedSingle': 'Attached {count} file',
+1 -1
View File
@@ -370,7 +370,7 @@ export const dict: Record<I18nKey, string> = {
"multirun.launcher.attachments.attach": "Adjuntar",
"multirun.launcher.attachments.tooltip": "Archivos idénticos enviados a todas las ejecuciones",
"multirun.launcher.models.label": "Modelos",
"multirun.launcher.models.info": "Selecciona 2-{max} modelos. El mismo modelo puede añadirse varias veces.",
"multirun.launcher.models.info": "Selecciona 2 o más modelos. El mismo modelo puede añadirse varias veces.",
"multirun.launcher.toast.fileTooLarge": "El archivo \"{fileName}\" es demasiado grande (máximo 10MB)",
"multirun.launcher.toast.attachFailed": "No se pudo adjuntar \"{fileName}\"",
"multirun.launcher.toast.attachedSingle": "Archivo adjuntado ({count})",
+1 -1
View File
@@ -201,7 +201,7 @@ export const dict = {
'multirun.launcher.attachments.attach': 'Attacher',
'multirun.launcher.attachments.tooltip': 'Mêmes fichiers envoyés à toutes les exécutions',
'multirun.launcher.models.label': 'Modèles',
'multirun.launcher.models.info': 'Sélectionnez les modèles 2-{max}. Le même modèle peut être ajouté plusieurs fois.',
'multirun.launcher.models.info': 'Sélectionnez 2 modèles ou plus. Le même modèle peut être ajouté plusieurs fois.',
'multirun.launcher.toast.fileTooLarge': 'Le fichier "{fileName}" est trop volumineux (max 10 Mo)',
'multirun.launcher.toast.attachFailed': 'Échec de la connexion de "{fileName}"',
'multirun.launcher.toast.attachedSingle': 'Fichier {count} joint',
+1 -1
View File
@@ -370,7 +370,7 @@ export const dict: Record<I18nKey, string> = {
'multirun.launcher.attachments.attach': '첨부',
'multirun.launcher.attachments.tooltip': '같은 파일을 모든 실행에 보냅니다',
'multirun.launcher.models.label': '모델',
'multirun.launcher.models.info': '모델을 2~{max}개 선택하세요. 같은 모델을 여러 번 추가할 수 있습니다.',
'multirun.launcher.models.info': '모델을 2개 이상 선택하세요. 같은 모델을 여러 번 추가할 수 있습니다.',
'multirun.launcher.toast.fileTooLarge': '파일 "{fileName}"이 너무 큽니다(최대 10MB)',
'multirun.launcher.toast.attachFailed': '"{fileName}" 첨부 실패',
'multirun.launcher.toast.attachedSingle': '파일 {count}개 첨부됨',
+1 -1
View File
@@ -497,7 +497,7 @@ export const dict: Record<I18nKey, string> = {
'multirun.launcher.attachments.attach': 'Dołącz',
'multirun.launcher.attachments.tooltip': 'Te same pliki wysłane do wszystkich uruchomień',
'multirun.launcher.models.label': 'Modele',
'multirun.launcher.models.info': 'Wybierz od 2 do {max} modeli. Ten sam model może być dodany wielokrotnie.',
'multirun.launcher.models.info': 'Wybierz 2 lub więcej modeli. Ten sam model może być dodany wielokrotnie.',
'multirun.launcher.toast.fileTooLarge': 'Plik "{fileName}" jest zbyt duży (max 10MB)',
'multirun.launcher.toast.attachFailed': 'Nie udało się dołączyć "{fileName}"',
'multirun.launcher.toast.attachedSingle': 'Dołączono {count} plik',
+1 -1
View File
@@ -370,7 +370,7 @@ export const dict: Record<I18nKey, string> = {
"multirun.launcher.attachments.attach": "Anexar",
"multirun.launcher.attachments.tooltip": "Arquivos idênticos enviados a todas as execuções",
"multirun.launcher.models.label": "Modelos",
"multirun.launcher.models.info": "Selecione 2-{max} modelos. O mesmo modelo pode ser adicionado várias vezes.",
"multirun.launcher.models.info": "Selecione 2 ou mais modelos. O mesmo modelo pode ser adicionado várias vezes.",
"multirun.launcher.toast.fileTooLarge": "O arquivo \"{fileName}\" é grande demais (máximo 10MB)",
"multirun.launcher.toast.attachFailed": "Não foi possível anexar \"{fileName}\"",
"multirun.launcher.toast.attachedSingle": "Arquivo anexado ({count})",
+1 -1
View File
@@ -370,7 +370,7 @@ export const dict: Record<I18nKey, string> = {
"multirun.launcher.attachments.attach": "Прикріпити",
"multirun.launcher.attachments.tooltip": "Ті самі файли буде надіслано в усі запуски",
"multirun.launcher.models.label": "Моделі",
"multirun.launcher.models.info": "Вибрати моделі 2-{max}. Ту саму модель можна додавати кілька разів.",
"multirun.launcher.models.info": "Вибрати 2 або більше моделей. Ту саму модель можна додавати кілька разів.",
"multirun.launcher.toast.fileTooLarge": "Файл \"{fileName}\" завеликий (макс. 10 МБ)",
"multirun.launcher.toast.attachFailed": "Не вдалося вкласти \"{fileName}\"",
"multirun.launcher.toast.attachedSingle": "Прикріплено файл: {count}",
+1 -1
View File
@@ -370,7 +370,7 @@ export const dict: Record<I18nKey, string> = {
'multirun.launcher.attachments.attach': '附加',
'multirun.launcher.attachments.tooltip': '相同文件会发送到所有运行',
'multirun.launcher.models.label': '模型',
'multirun.launcher.models.info': '选择 2-{max} 个模型。同一模型可重复添加。',
'multirun.launcher.models.info': '选择 2 个或更多模型。同一模型可重复添加。',
'multirun.launcher.toast.fileTooLarge': '文件“{fileName}”过大(最大 10MB',
'multirun.launcher.toast.attachFailed': '附加“{fileName}”失败',
'multirun.launcher.toast.attachedSingle': '已附加 {count} 个文件',
+1 -1
View File
@@ -383,7 +383,7 @@ export const dict: Record<I18nKey, string> = {
'multirun.launcher.attachments.attach': '附加',
'multirun.launcher.attachments.tooltip': '相同檔案會傳送到所有執行',
'multirun.launcher.models.label': '模型',
'multirun.launcher.models.info': '選擇 2-{max} 個模型。同一模型可重複加入。',
'multirun.launcher.models.info': '選擇 2 個或更多模型。同一模型可重複加入。',
'multirun.launcher.toast.fileTooLarge': '檔案「{fileName}」過大(最大 10MB',
'multirun.launcher.toast.attachFailed': '附加「{fileName}」失敗',
'multirun.launcher.toast.attachedSingle': '已附加 {count} 個檔案',
@@ -226,4 +226,20 @@ describe('useMultiRunStore', () => {
'createSession:/repo-worktrees/fix-thing',
]);
});
test('accepts more than 5 models per group without a "maximum 5 models" error', async () => {
const models = Array.from({ length: 6 }, (_, i) => ({
providerID: 'anthropic',
modelID: `claude-sonnet-4-5-${i}`,
}));
const result = await useMultiRunStore.getState().createMultiRun({
name: 'Many models',
isolateRuns: false,
groups: [{ prompt: 'Fix it', models }],
});
expect(useMultiRunStore.getState().error).toBeNull();
expect(result?.sessionIds).toHaveLength(6);
});
});
@@ -138,10 +138,6 @@ export const useMultiRunStore = create<MultiRunStore>()(
set({ error: `Group ${gi + 1}: select at least 1 model` });
return null;
}
if (groups[gi].models.length > 5) {
set({ error: `Group ${gi + 1}: maximum 5 models allowed` });
return null;
}
}
set({ isLoading: true, error: null });