diff --git a/backend/app/remote_sub_agent/providers/gemini_agents.py b/backend/app/remote_sub_agent/providers/gemini_agents.py index 3f2bd96d..06b86f22 100644 --- a/backend/app/remote_sub_agent/providers/gemini_agents.py +++ b/backend/app/remote_sub_agent/providers/gemini_agents.py @@ -31,7 +31,7 @@ from app.remote_sub_agent.types import ( logger = logging.getLogger(__name__) _DEFAULT_BASE_URL = "https://generativelanguage.googleapis.com/v1beta" -_DEFAULT_AGENT = "waverunner" +_DEFAULT_AGENT = "" _TERMINAL_SUCCESS_STATUSES = {"completed"} _TERMINAL_FAILURE_STATUSES = {"failed", "cancelled", "expired", "incomplete"} _UNSUPPORTED_ACTION_STATUSES = {"requires_action"} diff --git a/backend/tests/app/controller/test_remote_sub_agent_controller.py b/backend/tests/app/controller/test_remote_sub_agent_controller.py index d5c94517..21bca142 100644 --- a/backend/tests/app/controller/test_remote_sub_agent_controller.py +++ b/backend/tests/app/controller/test_remote_sub_agent_controller.py @@ -29,7 +29,7 @@ async def test_validate_remote_sub_agent_rejects_missing_fields(): provider="gemini_agents", api_key="", base_url="https://example.test/v1beta", - agent_name="waverunner", + agent_name="antigravity-preview-05-2026", ) ) @@ -61,7 +61,7 @@ async def test_validate_remote_sub_agent_success(monkeypatch): provider="gemini_agents", api_key=" test-key ", base_url=" https://example.test/v1beta ", - agent_name=" waverunner ", + agent_name=" antigravity-preview-05-2026 ", timeout_seconds=12, ) ) @@ -76,7 +76,7 @@ async def test_validate_remote_sub_agent_success(monkeypatch): "gemini_agents": { "api_key": "test-key", "base_url": "https://example.test/v1beta", - "agent_name": "waverunner", + "agent_name": "antigravity-preview-05-2026", }, } assert seen["timeout"] == 12 @@ -98,7 +98,7 @@ async def test_validate_remote_sub_agent_failure(monkeypatch): provider="gemini_agents", api_key="bad-key", base_url="https://example.test/v1beta", - agent_name="waverunner", + agent_name="antigravity-preview-05-2026", ) ) diff --git a/backend/tests/app/remote_sub_agent/test_gemini_agents_provider.py b/backend/tests/app/remote_sub_agent/test_gemini_agents_provider.py index 221ed3ff..1bdfa450 100644 --- a/backend/tests/app/remote_sub_agent/test_gemini_agents_provider.py +++ b/backend/tests/app/remote_sub_agent/test_gemini_agents_provider.py @@ -48,7 +48,7 @@ async def test_provider_posts_agent_interaction(): 200, json={ "id": "interaction-1", - "agent": "waverunner", + "agent": "antigravity-preview-05-2026", "environment_id": "env-1", "status": "completed", "outputs": [{"type": "text", "text": "done"}], @@ -58,7 +58,7 @@ async def test_provider_posts_agent_interaction(): provider = GeminiAgentsProvider( api_key="test-key", - agent_name="waverunner", + agent_name="antigravity-preview-05-2026", base_url="https://example.test/v1beta", transport=httpx.MockTransport(handler), ) @@ -77,7 +77,7 @@ async def test_provider_posts_agent_interaction(): body = seen["body"] assert isinstance(body, dict) - assert body["agent"] == "waverunner" + assert body["agent"] == "antigravity-preview-05-2026" assert body["input"] == [{"type": "text", "text": "research this"}] assert body["stream"] is False assert body["environment"] == {"enabled": True} @@ -102,7 +102,7 @@ async def test_provider_validation_posts_background_probe(): 200, json={ "id": "interaction-validation", - "agent": "waverunner", + "agent": "antigravity-preview-05-2026", "environment_id": "env-validation", "status": "in_progress", }, @@ -110,7 +110,7 @@ async def test_provider_validation_posts_background_probe(): provider = GeminiAgentsProvider( api_key="test-key", - agent_name="waverunner", + agent_name="antigravity-preview-05-2026", base_url="https://example.test/v1beta", transport=httpx.MockTransport(handler), ) @@ -119,7 +119,7 @@ async def test_provider_validation_posts_background_probe(): body = seen["body"] assert isinstance(body, dict) - assert body["agent"] == "waverunner" + assert body["agent"] == "antigravity-preview-05-2026" assert body["stream"] is False assert body["background"] is True assert body["environment"] == {"enabled": True} @@ -148,14 +148,14 @@ async def test_provider_validation_falls_back_without_background(): 200, json={ "id": "interaction-validation", - "agent": "waverunner", + "agent": "antigravity-preview-05-2026", "status": "completed", }, ) provider = GeminiAgentsProvider( api_key="test-key", - agent_name="waverunner", + agent_name="antigravity-preview-05-2026", base_url="https://example.test/v1beta", transport=httpx.MockTransport(handler), ) @@ -178,7 +178,7 @@ async def test_provider_uses_configured_agent_over_display_name(): 200, json={ "id": "interaction-1", - "agent": "waverunner", + "agent": "antigravity-preview-05-2026", "status": "completed", "outputs": [{"type": "text", "text": "done"}], }, @@ -186,7 +186,7 @@ async def test_provider_uses_configured_agent_over_display_name(): provider = GeminiAgentsProvider( api_key="test-key", - agent_name="waverunner", + agent_name="antigravity-preview-05-2026", base_url="https://example.test/v1beta", transport=httpx.MockTransport(handler), ) @@ -205,7 +205,7 @@ async def test_provider_uses_configured_agent_over_display_name(): body = seen["body"] assert isinstance(body, dict) - assert body["agent"] == "waverunner" + assert body["agent"] == "antigravity-preview-05-2026" assert events[-1].content == "done" @@ -216,7 +216,7 @@ async def test_provider_concatenates_chunked_outputs_without_extra_newlines(): 200, json={ "id": "interaction-1", - "agent": "waverunner", + "agent": "antigravity-preview-05-2026", "status": "completed", "outputs": [ {"type": "text", "text": "sha256sum orders"}, @@ -229,7 +229,7 @@ async def test_provider_concatenates_chunked_outputs_without_extra_newlines(): provider = GeminiAgentsProvider( api_key="test-key", - agent_name="waverunner", + agent_name="antigravity-preview-05-2026", base_url="https://example.test/v1beta", transport=httpx.MockTransport(handler), ) @@ -257,7 +257,7 @@ async def test_provider_merges_system_instruction_for_agent(): 200, json={ "id": "interaction-1", - "agent": "waverunner", + "agent": "antigravity-preview-05-2026", "status": "completed", "outputs": [{"type": "text", "text": "done"}], }, @@ -265,7 +265,7 @@ async def test_provider_merges_system_instruction_for_agent(): provider = GeminiAgentsProvider( api_key="test-key", - agent_name="waverunner", + agent_name="antigravity-preview-05-2026", base_url="https://example.test/v1beta", transport=httpx.MockTransport(handler), ) @@ -302,7 +302,7 @@ async def test_provider_polls_background_agent_until_completed(): 200, json={ "id": "interaction-1", - "agent": "waverunner", + "agent": "antigravity-preview-05-2026", "status": "in_progress", }, ) @@ -310,7 +310,7 @@ async def test_provider_polls_background_agent_until_completed(): 200, json={ "id": "interaction-1", - "agent": "waverunner", + "agent": "antigravity-preview-05-2026", "status": "completed", "outputs": [{"type": "text", "text": "done"}], "usage": {"total_tokens": 42}, @@ -319,7 +319,7 @@ async def test_provider_polls_background_agent_until_completed(): provider = GeminiAgentsProvider( api_key="test-key", - agent_name="waverunner", + agent_name="antigravity-preview-05-2026", base_url="https://example.test/v1beta", poll_interval_seconds=0, transport=httpx.MockTransport(handler), diff --git a/backend/tests/app/remote_sub_agent/test_provider_registry.py b/backend/tests/app/remote_sub_agent/test_provider_registry.py index f48a2aef..7594d8c3 100644 --- a/backend/tests/app/remote_sub_agent/test_provider_registry.py +++ b/backend/tests/app/remote_sub_agent/test_provider_registry.py @@ -32,7 +32,7 @@ def _config() -> dict: "gemini_agents": { "api_key": "test-key", "base_url": "https://example.test/v1beta", - "agent_name": "waverunner", + "agent_name": "antigravity-preview-05-2026", "max_wall_time_seconds": 900, "poll_interval_seconds": 7, }, @@ -62,7 +62,7 @@ def test_registry_builds_gemini_provider_from_provider_config(): assert provider.name == "gemini_agents" assert provider.api_key == "test-key" assert provider.base_url == "https://example.test/v1beta" - assert provider.agent_name == "waverunner" + assert provider.agent_name == "antigravity-preview-05-2026" assert provider.poll_interval_seconds == 7 diff --git a/backend/tests/app/remote_sub_agent/test_toolkit_config.py b/backend/tests/app/remote_sub_agent/test_toolkit_config.py index 545aa304..548c4745 100644 --- a/backend/tests/app/remote_sub_agent/test_toolkit_config.py +++ b/backend/tests/app/remote_sub_agent/test_toolkit_config.py @@ -31,7 +31,7 @@ def _valid_config() -> dict: "gemini_agents": { "api_key": "test-key", "base_url": "https://generativelanguage.googleapis.com/v1beta", - "agent_name": "waverunner", + "agent_name": "antigravity-preview-05-2026", "max_wall_time_seconds": 900, "poll_interval_seconds": 5, }, @@ -58,7 +58,7 @@ async def test_toolkit_returns_message_when_config_is_incomplete(): "gemini_agents": { "api_key": "", "base_url": "https://generativelanguage.googleapis.com/v1beta", - "agent_name": "waverunner", + "agent_name": "antigravity-preview-05-2026", }, }, ) diff --git a/server/app/model/remote_sub_agent/provider.py b/server/app/model/remote_sub_agent/provider.py index 9fca7e31..cd1b5365 100644 --- a/server/app/model/remote_sub_agent/provider.py +++ b/server/app/model/remote_sub_agent/provider.py @@ -44,15 +44,18 @@ class RemoteSubAgentProviderIn(BaseModel): config: dict | None = None @model_validator(mode="after") - def validate_enabled_config(self): - if self.enabled and ( + def validate_provider_config(self): + if not self.provider_name.strip(): + raise ValueError("Remote sub agent provider requires provider_name.") + + if ( not self.api_key.strip() or not self.endpoint_url.strip() or not self.agent_name.strip() ): raise ValueError( - "Enabled remote sub agent provider requires api_key, " - "endpoint_url, and agent_name." + "Remote sub agent provider requires api_key, endpoint_url, " + "and agent_name." ) return self diff --git a/server/tests/test_remote_sub_agent_provider.py b/server/tests/test_remote_sub_agent_provider.py index 023eb71f..47957ac4 100644 --- a/server/tests/test_remote_sub_agent_provider.py +++ b/server/tests/test_remote_sub_agent_provider.py @@ -21,10 +21,24 @@ from app.model.remote_sub_agent.provider import RemoteSubAgentProviderIn class TestRemoteSubAgentProviderSchema: - def test_disabled_provider_allows_incomplete_credentials(self): + def test_disabled_provider_requires_credentials(self): + with pytest.raises(ValidationError): + RemoteSubAgentProviderIn( + provider_name="gemini_agents", + enabled=False, + ) + + def test_disabled_provider_accepts_agent_configuration(self): config = RemoteSubAgentProviderIn( provider_name="gemini_agents", enabled=False, + api_key="test-key", + endpoint_url="https://generativelanguage.googleapis.com/v1beta", + agent_name="antigravity-preview-05-2026", + config={ + "max_wall_time_seconds": 900, + "poll_interval_seconds": 5, + }, ) assert config.provider_name == "gemini_agents" @@ -47,7 +61,7 @@ class TestRemoteSubAgentProviderSchema: enabled=True, api_key="test-key", endpoint_url="https://generativelanguage.googleapis.com/v1beta", - agent_name="waverunner", + agent_name="antigravity-preview-05-2026", config={ "max_wall_time_seconds": 900, "poll_interval_seconds": 5, diff --git a/src/i18n/locales/ar/setting.json b/src/i18n/locales/ar/setting.json index 7acfee52..c433e398 100644 --- a/src/i18n/locales/ar/setting.json +++ b/src/i18n/locales/ar/setting.json @@ -178,6 +178,15 @@ "models-default-setting-title": "الإعداد الافتراضي", "models-default-setting-description": "اختر أحد النماذج المكوّنة ليكون النموذج الافتراضي لـ Eigent، وسيتم تطبيقه عالميًا عبر مساحة العمل الخاصة بك.", "models-configuration": "التكوين", + "sub-agents": "الوكلاء الفرعيون", + "gemini-agent": "Gemini Agents API", + "gemini-remote-sub-agent": "Gemini Agents API", + "remote-sub-agent-description": "فوّض المهام المناسبة إلى صندوق عزل مُدار لوكيل بعيد.", + "agent-provider": "موفّر الوكيل", + "agent-id": "معرّف الوكيل", + "max-wall-time-seconds": "الحد الأقصى لوقت التشغيل (بالثواني)", + "poll-interval-seconds": "فاصل الاستعلام (بالثواني)", + "remote-sub-agent-required-fields": "API Key و API Host و Agent ID مطلوبة عند تفعيل Sub Agent.", "gemini-3-pro-preview-name": "Gemini 3 Pro Preview", "gemini-3.1-pro-preview-name": "Gemini 3.1 Pro Preview", diff --git a/src/i18n/locales/de/setting.json b/src/i18n/locales/de/setting.json index bcd167c8..957ff6cc 100644 --- a/src/i18n/locales/de/setting.json +++ b/src/i18n/locales/de/setting.json @@ -238,6 +238,15 @@ "models-default-setting-title": "Standard-Einstellung", "models-default-setting-description": "Wähle eines deiner konfigurierten Modelle als Standardmodell für Eigent. Es wird global in deinem Arbeitsbereich angewendet.", "models-configuration": "Konfiguration", + "sub-agents": "Sub-Agents", + "gemini-agent": "Gemini Agents API", + "gemini-remote-sub-agent": "Gemini Agents API", + "remote-sub-agent-description": "Delegiere geeignete Aufgaben an eine verwaltete Remote-Agent-Sandbox.", + "agent-provider": "Agent-Anbieter", + "agent-id": "Agent-ID", + "max-wall-time-seconds": "Max. Laufzeit (Sekunden)", + "poll-interval-seconds": "Abfrageintervall (Sekunden)", + "remote-sub-agent-required-fields": "API-Schlüssel, API-Host und Agent-ID sind erforderlich, wenn Sub Agent aktiviert ist.", "gemini-3-pro-preview-name": "Gemini 3 Pro Preview", "gemini-3.1-pro-preview-name": "Gemini 3.1 Pro Preview", diff --git a/src/i18n/locales/en-us/setting.json b/src/i18n/locales/en-us/setting.json index 31b63dd5..8464ac86 100644 --- a/src/i18n/locales/en-us/setting.json +++ b/src/i18n/locales/en-us/setting.json @@ -209,7 +209,7 @@ "sub-agents": "Sub Agents", "gemini-agent": "Gemini Agents API", "gemini-remote-sub-agent": "Gemini Agents API", - "remote-sub-agent-description": "Delegate suitable tasks to a managed Waverunner-compatible remote agent sandbox.", + "remote-sub-agent-description": "Delegate suitable tasks to a managed remote agent sandbox.", "agent-provider": "Agent Provider", "agent-id": "Agent ID", "max-wall-time-seconds": "Max Wall Time (Seconds)", diff --git a/src/i18n/locales/es/setting.json b/src/i18n/locales/es/setting.json index 4161e533..240c9096 100644 --- a/src/i18n/locales/es/setting.json +++ b/src/i18n/locales/es/setting.json @@ -238,6 +238,15 @@ "models-default-setting-title": "Configuración predeterminada", "models-default-setting-description": "Elige uno de tus modelos configurados como modelo predeterminado para Eigent. Se aplicará globalmente en tu espacio de trabajo.", "models-configuration": "Configuración", + "sub-agents": "Subagentes", + "gemini-agent": "Gemini Agents API", + "gemini-remote-sub-agent": "Gemini Agents API", + "remote-sub-agent-description": "Delega tareas adecuadas a un sandbox de agente remoto administrado.", + "agent-provider": "Proveedor de agente", + "agent-id": "ID de agente", + "max-wall-time-seconds": "Tiempo máximo de ejecución (segundos)", + "poll-interval-seconds": "Intervalo de sondeo (segundos)", + "remote-sub-agent-required-fields": "API Key, API Host y Agent ID son obligatorios cuando Sub Agent está habilitado.", "gemini-3-pro-preview-name": "Gemini 3 Pro Preview", "gemini-3.1-pro-preview-name": "Gemini 3.1 Pro Preview", diff --git a/src/i18n/locales/fr/setting.json b/src/i18n/locales/fr/setting.json index 887b22c0..8ab1b1c0 100644 --- a/src/i18n/locales/fr/setting.json +++ b/src/i18n/locales/fr/setting.json @@ -221,6 +221,15 @@ "models-default-setting-title": "Paramètre par défaut", "models-default-setting-description": "Choisissez l'un de vos modèles configurés comme modèle par défaut pour Eigent. Il sera appliqué globalement dans votre espace de travail.", "models-configuration": "Configuration", + "sub-agents": "Sous-agents", + "gemini-agent": "Gemini Agents API", + "gemini-remote-sub-agent": "Gemini Agents API", + "remote-sub-agent-description": "Déléguez les tâches adaptées à un bac à sable d'agent distant géré.", + "agent-provider": "Fournisseur d'agent", + "agent-id": "ID de l'agent", + "max-wall-time-seconds": "Temps d'exécution maximal (secondes)", + "poll-interval-seconds": "Intervalle d'interrogation (secondes)", + "remote-sub-agent-required-fields": "La clé API, l'hôte API et l'ID de l'agent sont requis lorsque Sub Agent est activé.", "gemini-3-pro-preview-name": "Gemini 3 Pro Preview", "gemini-3.1-pro-preview-name": "Gemini 3.1 Pro Preview", diff --git a/src/i18n/locales/it/setting.json b/src/i18n/locales/it/setting.json index 169285d1..d5a225ef 100644 --- a/src/i18n/locales/it/setting.json +++ b/src/i18n/locales/it/setting.json @@ -238,6 +238,15 @@ "models-default-setting-title": "Impostazione predefinita", "models-default-setting-description": "Seleziona uno dei modelli configurati come modello predefinito per Eigent. Verrà applicato globalmente nel tuo spazio di lavoro.", "models-configuration": "Configurazione", + "sub-agents": "Sub-agent", + "gemini-agent": "Gemini Agents API", + "gemini-remote-sub-agent": "Gemini Agents API", + "remote-sub-agent-description": "Delega le attività adatte a una sandbox di agent remoto gestita.", + "agent-provider": "Provider agent", + "agent-id": "ID agent", + "max-wall-time-seconds": "Tempo massimo di esecuzione (secondi)", + "poll-interval-seconds": "Intervallo di polling (secondi)", + "remote-sub-agent-required-fields": "API Key, API Host e Agent ID sono obbligatori quando Sub Agent è abilitato.", "gemini-3-pro-preview-name": "Gemini 3 Pro Preview", "gemini-3.1-pro-preview-name": "Gemini 3.1 Pro Preview", diff --git a/src/i18n/locales/ja/setting.json b/src/i18n/locales/ja/setting.json index 70b6d2ce..c5c3bf52 100644 --- a/src/i18n/locales/ja/setting.json +++ b/src/i18n/locales/ja/setting.json @@ -239,6 +239,15 @@ "models-default-setting-title": "デフォルト設定", "models-default-setting-description": "設定済みモデルの中から、Eigent のデフォルトモデルを1つ選択します。ワークスペース全体にグローバルに適用されます。", "models-configuration": "構成", + "sub-agents": "サブエージェント", + "gemini-agent": "Gemini Agents API", + "gemini-remote-sub-agent": "Gemini Agents API", + "remote-sub-agent-description": "適したタスクを管理されたリモートエージェントのサンドボックスに委任します。", + "agent-provider": "エージェントプロバイダー", + "agent-id": "エージェント ID", + "max-wall-time-seconds": "最大実行時間(秒)", + "poll-interval-seconds": "ポーリング間隔(秒)", + "remote-sub-agent-required-fields": "Sub Agent を有効にするには、API Key、API Host、Agent ID が必要です。", "gemini-3-pro-preview-name": "Gemini 3 Pro Preview", "gemini-3.1-pro-preview-name": "Gemini 3.1 Pro Preview", diff --git a/src/i18n/locales/ko/setting.json b/src/i18n/locales/ko/setting.json index 73319eaf..38639223 100644 --- a/src/i18n/locales/ko/setting.json +++ b/src/i18n/locales/ko/setting.json @@ -239,6 +239,15 @@ "models-default-setting-title": "기본 설정", "models-default-setting-description": "구성된 모델 중 하나를 Eigent의 기본 모델로 선택하세요. 워크스페이스 전체에 전역으로 적용됩니다.", "models-configuration": "구성", + "sub-agents": "서브 에이전트", + "gemini-agent": "Gemini Agents API", + "gemini-remote-sub-agent": "Gemini Agents API", + "remote-sub-agent-description": "적합한 작업을 관리형 원격 에이전트 샌드박스에 위임합니다.", + "agent-provider": "에이전트 제공자", + "agent-id": "에이전트 ID", + "max-wall-time-seconds": "최대 실행 시간(초)", + "poll-interval-seconds": "폴링 간격(초)", + "remote-sub-agent-required-fields": "Sub Agent를 활성화하려면 API Key, API Host, Agent ID가 필요합니다.", "gemini-3-pro-preview-name": "Gemini 3 Pro Preview", "gemini-3.1-pro-preview-name": "Gemini 3.1 Pro Preview", diff --git a/src/i18n/locales/ru/setting.json b/src/i18n/locales/ru/setting.json index 0e6883fa..e4844c3a 100644 --- a/src/i18n/locales/ru/setting.json +++ b/src/i18n/locales/ru/setting.json @@ -238,6 +238,15 @@ "models-default-setting-title": "Настройка по умолчанию", "models-default-setting-description": "Выберите одну из настроенных моделей как модель по умолчанию для Eigent. Она будет применяться глобально во всём рабочем пространстве.", "models-configuration": "Конфигурация", + "sub-agents": "Суб-агенты", + "gemini-agent": "Gemini Agents API", + "gemini-remote-sub-agent": "Gemini Agents API", + "remote-sub-agent-description": "Делегируйте подходящие задачи управляемой песочнице удалённого агента.", + "agent-provider": "Поставщик агента", + "agent-id": "ID агента", + "max-wall-time-seconds": "Макс. время выполнения (секунды)", + "poll-interval-seconds": "Интервал опроса (секунды)", + "remote-sub-agent-required-fields": "API Key, API Host и Agent ID обязательны, когда Sub Agent включён.", "gemini-3-pro-preview-name": "Gemini 3 Pro Preview", "gemini-3.1-pro-preview-name": "Gemini 3.1 Pro Preview", diff --git a/src/i18n/locales/zh-Hans/setting.json b/src/i18n/locales/zh-Hans/setting.json index 6cc90ac2..7c5d87ab 100644 --- a/src/i18n/locales/zh-Hans/setting.json +++ b/src/i18n/locales/zh-Hans/setting.json @@ -199,7 +199,7 @@ "sub-agents": "Sub Agents", "gemini-agent": "Gemini Agents API", "gemini-remote-sub-agent": "Gemini Agents API", - "remote-sub-agent-description": "把适合远程执行的任务委托给 Waverunner 兼容的托管智能体沙盒。", + "remote-sub-agent-description": "把适合远程执行的任务委托给托管远程智能体沙盒。", "agent-provider": "Agent Provider", "agent-id": "Agent ID", "max-wall-time-seconds": "最长运行时间(秒)", diff --git a/src/i18n/locales/zh-Hant/setting.json b/src/i18n/locales/zh-Hant/setting.json index e68af641..a229d1b1 100644 --- a/src/i18n/locales/zh-Hant/setting.json +++ b/src/i18n/locales/zh-Hant/setting.json @@ -167,6 +167,15 @@ "models-default-setting-title": "預設設定", "models-default-setting-description": "從已配置的模型中選擇一個作為 Eigent 的預設模型,它將全域套用於您的工作區。", "models-configuration": "配置", + "sub-agents": "Sub Agents", + "gemini-agent": "Gemini Agents API", + "gemini-remote-sub-agent": "Gemini Agents API", + "remote-sub-agent-description": "把適合遠端執行的任務委託給託管遠端智能體沙盒。", + "agent-provider": "Agent Provider", + "agent-id": "Agent ID", + "max-wall-time-seconds": "最長執行時間(秒)", + "poll-interval-seconds": "輪詢間隔(秒)", + "remote-sub-agent-required-fields": "啟用 Sub Agent 時,需要填寫 API Key、API Host 和 Agent ID。", "gemini-3-pro-preview-name": "Gemini 3 Pro Preview", "gemini-3.1-pro-preview-name": "Gemini 3.1 Pro Preview", diff --git a/src/lib/remoteSubAgent.ts b/src/lib/remoteSubAgent.ts index 051d165b..10a9c440 100644 --- a/src/lib/remoteSubAgent.ts +++ b/src/lib/remoteSubAgent.ts @@ -16,7 +16,7 @@ export const REMOTE_SUB_AGENT_PROVIDER = 'gemini_agents'; export const REMOTE_SUB_AGENT_PROVIDER_ID = REMOTE_SUB_AGENT_PROVIDER; export const REMOTE_SUB_AGENT_DEFAULT_BASE_URL = 'https://generativelanguage.googleapis.com/v1beta'; -export const REMOTE_SUB_AGENT_DEFAULT_AGENT = 'waverunner'; +export const REMOTE_SUB_AGENT_DEFAULT_AGENT = ''; export const REMOTE_SUB_AGENT_DEFAULT_MAX_WALL_TIME_SECONDS = '900'; export const REMOTE_SUB_AGENT_DEFAULT_POLL_INTERVAL_SECONDS = '5'; @@ -101,6 +101,12 @@ export function toRemoteSubAgentProviderPayload(form: RemoteSubAgentFormState) { const agentName = form.agentName.trim(); const maxWallTimeSeconds = Number(form.maxWallTimeSeconds); const pollIntervalSeconds = Number(form.pollIntervalSeconds); + const fallbackMaxWallTimeSeconds = Number( + REMOTE_SUB_AGENT_DEFAULT_MAX_WALL_TIME_SECONDS + ); + const fallbackPollIntervalSeconds = Number( + REMOTE_SUB_AGENT_DEFAULT_POLL_INTERVAL_SECONDS + ); return { provider_name: REMOTE_SUB_AGENT_PROVIDER_ID, @@ -110,12 +116,14 @@ export function toRemoteSubAgentProviderPayload(form: RemoteSubAgentFormState) { agent_name: agentName, model_name: '', config: { - max_wall_time_seconds: Number.isFinite(maxWallTimeSeconds) - ? maxWallTimeSeconds - : Number(REMOTE_SUB_AGENT_DEFAULT_MAX_WALL_TIME_SECONDS), - poll_interval_seconds: Number.isFinite(pollIntervalSeconds) - ? pollIntervalSeconds - : Number(REMOTE_SUB_AGENT_DEFAULT_POLL_INTERVAL_SECONDS), + max_wall_time_seconds: + Number.isFinite(maxWallTimeSeconds) && maxWallTimeSeconds > 0 + ? maxWallTimeSeconds + : fallbackMaxWallTimeSeconds, + poll_interval_seconds: + Number.isFinite(pollIntervalSeconds) && pollIntervalSeconds > 0 + ? pollIntervalSeconds + : fallbackPollIntervalSeconds, }, }; } diff --git a/src/pages/Agents/SubAgents.tsx b/src/pages/Agents/SubAgents.tsx index 37b6aa2e..857eda4f 100644 --- a/src/pages/Agents/SubAgents.tsx +++ b/src/pages/Agents/SubAgents.tsx @@ -75,10 +75,22 @@ export default function SubAgents() { return t('setting.validate-failed'); }; - const getRemoteSubAgentRequiredError = (form: RemoteSubAgentFormState) => { + const getRemoteSubAgentRequiredError = ( + form: RemoteSubAgentFormState, + requireFields = form.enabled + ) => { + const maxWallTimeSeconds = Number(form.maxWallTimeSeconds.trim()); + const pollIntervalSeconds = Number(form.pollIntervalSeconds.trim()); + if ( - form.enabled && - (!form.apiKey.trim() || !form.baseUrl.trim() || !form.agentName.trim()) + requireFields && + (!form.apiKey.trim() || + !form.baseUrl.trim() || + !form.agentName.trim() || + !Number.isFinite(maxWallTimeSeconds) || + maxWallTimeSeconds <= 0 || + !Number.isFinite(pollIntervalSeconds) || + pollIntervalSeconds <= 0) ) { return t('setting.remote-sub-agent-required-fields'); } @@ -105,8 +117,17 @@ export default function SubAgents() { }); }; - const persistRemoteSubAgentForm = async (form: RemoteSubAgentFormState) => { - const requiredError = getRemoteSubAgentRequiredError(form); + const persistRemoteSubAgentForm = async ( + form: RemoteSubAgentFormState, + { + requireFields = form.enabled, + validateConnection = form.enabled, + }: { + requireFields?: boolean; + validateConnection?: boolean; + } = {} + ) => { + const requiredError = getRemoteSubAgentRequiredError(form, requireFields); if (requiredError) { setRemoteSubAgentError(requiredError); toast.error(requiredError); @@ -116,7 +137,7 @@ export default function SubAgents() { setRemoteSubAgentSaving(true); setRemoteSubAgentError(null); try { - if (form.enabled) { + if (validateConnection) { await validateRemoteSubAgentConfig(form); } @@ -145,7 +166,10 @@ export default function SubAgents() { }; const handleRemoteSubAgentSave = async () => { - await persistRemoteSubAgentForm(remoteSubAgentForm); + await persistRemoteSubAgentForm(remoteSubAgentForm, { + requireFields: true, + validateConnection: true, + }); }; const handleRemoteSubAgentToggle = async (checked: boolean) => { @@ -155,7 +179,13 @@ export default function SubAgents() { enabled: checked, }; - const requiredError = getRemoteSubAgentRequiredError(nextForm); + if (!checked && !remoteSubAgentForm.provider_id) { + setRemoteSubAgentForm(nextForm); + setRemoteSubAgentError(null); + return; + } + + const requiredError = getRemoteSubAgentRequiredError(nextForm, checked); if (requiredError) { setRemoteSubAgentError(requiredError); toast.error(requiredError); @@ -163,7 +193,10 @@ export default function SubAgents() { } setRemoteSubAgentForm(nextForm); - const saved = await persistRemoteSubAgentForm(nextForm); + const saved = await persistRemoteSubAgentForm(nextForm, { + requireFields: checked, + validateConnection: checked, + }); if (!saved) { setRemoteSubAgentForm(previousForm); }