From 80f1790e1d2c4f6e453cddfd64f504bc333b5404 Mon Sep 17 00:00:00 2001 From: RockChinQ Date: Tue, 25 Aug 2026 23:27:45 +0800 Subject: [PATCH] refactor(agent): remove enabled state --- docs/event-based-agents/04-event-routing.md | 3 +- .../08-agent-page-and-event-orchestration.md | 1 - .../pkg/agent/runner/binding_resolver.py | 5 +- src/langbot/pkg/agent/runner/host_models.py | 6 -- .../pkg/agent/runner/query_entry_adapter.py | 1 - src/langbot/pkg/api/http/service/agent.py | 19 ++--- src/langbot/pkg/api/http/service/bot.py | 20 ----- src/langbot/pkg/entity/persistence/agent.py | 1 - .../versions/0023_drop_agent_enabled.py | 37 +++++++++ src/langbot/pkg/platform/botmgr.py | 22 +----- .../persistence/test_migrations.py | 22 +++++- .../agent/test_context_validation.py | 1 - .../agent/test_orchestrator_integration.py | 1 - .../api/service/test_agent_service.py | 7 +- .../unit_tests/platform/test_routing_rules.py | 6 +- .../agents/components/AgentFormComponent.tsx | 76 ++++--------------- web/src/app/infra/entities/api/index.ts | 1 - web/src/app/wizard/page.tsx | 1 - web/src/i18n/locales/en-US.ts | 9 +-- web/src/i18n/locales/es-ES.ts | 6 +- web/src/i18n/locales/ja-JP.ts | 9 +-- web/src/i18n/locales/ru-RU.ts | 5 +- web/src/i18n/locales/th-TH.ts | 5 +- web/src/i18n/locales/vi-VN.ts | 5 +- web/src/i18n/locales/zh-Hans.ts | 7 +- web/src/i18n/locales/zh-Hant.ts | 4 +- .../e2e/processor-detail-workbench.spec.ts | 14 ++-- 27 files changed, 104 insertions(+), 190 deletions(-) create mode 100644 src/langbot/pkg/persistence/alembic/versions/0023_drop_agent_enabled.py diff --git a/docs/event-based-agents/04-event-routing.md b/docs/event-based-agents/04-event-routing.md index cb2003b1c..d45a9199d 100644 --- a/docs/event-based-agents/04-event-routing.md +++ b/docs/event-based-agents/04-event-routing.md @@ -34,7 +34,6 @@ class Agent(Base): kind: str # 固定为 "agent" component_ref: str # AgentRunner id config: dict # runner + runner_config - enabled: bool supported_event_patterns: list[str] ``` @@ -113,7 +112,7 @@ Binding 只保存引用与路由条件。它不复制 Pipeline 或 Agent 配置 1. 忽略 `enabled = false` 的 binding。 2. 检查 `event_pattern` 与结构化 filters。 -3. 校验目标存在、启用且声明支持该事件。 +3. 校验目标存在且声明支持该事件。 4. 按 `priority` 从高到低选择;同优先级按稳定列表顺序。 5. 只执行一个响应目标。 diff --git a/docs/event-based-agents/08-agent-page-and-event-orchestration.md b/docs/event-based-agents/08-agent-page-and-event-orchestration.md index c3b9eb2b6..b3ac4693a 100644 --- a/docs/event-based-agents/08-agent-page-and-event-orchestration.md +++ b/docs/event-based-agents/08-agent-page-and-event-orchestration.md @@ -70,7 +70,6 @@ class Agent(Base): kind: str # 首版固定为 "agent" component_ref: str # runner id / workflow id / future external ref config: dict # runner 与 runner_config - enabled: bool supported_event_patterns: list[str] created_at: datetime updated_at: datetime diff --git a/src/langbot/pkg/agent/runner/binding_resolver.py b/src/langbot/pkg/agent/runner/binding_resolver.py index 97b7d7e71..199e30d28 100644 --- a/src/langbot/pkg/agent/runner/binding_resolver.py +++ b/src/langbot/pkg/agent/runner/binding_resolver.py @@ -22,7 +22,7 @@ class AgentBindingResolver: event: AgentEventEnvelope, agents: list[AgentConfig], ) -> AgentBinding: - """Resolve exactly one enabled Agent for the event. + """Resolve exactly one Agent for the event. Callers that source agents from bot/workspace/global configuration must pre-filter candidates to the event scope before calling this resolver. @@ -30,7 +30,7 @@ class AgentBindingResolver: Agent and does not carry enough scope metadata to make that decision safely here. """ - matches = [agent for agent in agents if agent.enabled and event.event_type in agent.event_types] + matches = [agent for agent in agents if event.event_type in agent.event_types] if not matches: raise AgentBindingResolutionError(f'No Agent binding matches event_type={event.event_type}') @@ -59,7 +59,6 @@ class AgentBindingResolver: resource_policy=agent.resource_policy, state_policy=agent.state_policy, delivery_policy=agent.delivery_policy, - enabled=agent.enabled, agent_id=agent.agent_id, processor_type=agent.processor_type, processor_id=agent.processor_id or agent.agent_id, diff --git a/src/langbot/pkg/agent/runner/host_models.py b/src/langbot/pkg/agent/runner/host_models.py index 89604525e..ba9f10079 100644 --- a/src/langbot/pkg/agent/runner/host_models.py +++ b/src/langbot/pkg/agent/runner/host_models.py @@ -181,9 +181,6 @@ class AgentConfig(pydantic.BaseModel): event_types: list[str] = pydantic.Field(default_factory=lambda: ['message.received']) """Event types this Agent handles.""" - enabled: bool = True - """Whether this Agent can be selected by a binding resolver.""" - metadata: dict[str, typing.Any] = pydantic.Field(default_factory=dict) """Non-protocol diagnostic metadata, such as legacy config source.""" @@ -219,9 +216,6 @@ class AgentBinding(pydantic.BaseModel): delivery_policy: DeliveryPolicy = pydantic.Field(default_factory=DeliveryPolicy) """Delivery policy.""" - enabled: bool = True - """Whether binding is enabled.""" - agent_id: str | None = None """Host-side Agent/config identifier for this binding.""" diff --git a/src/langbot/pkg/agent/runner/query_entry_adapter.py b/src/langbot/pkg/agent/runner/query_entry_adapter.py index 448453fbb..ba9aa7d73 100644 --- a/src/langbot/pkg/agent/runner/query_entry_adapter.py +++ b/src/langbot/pkg/agent/runner/query_entry_adapter.py @@ -151,7 +151,6 @@ class QueryEntryAdapter: state_policy=state_policy, delivery_policy=delivery_policy, event_types=[event_type], - enabled=True, metadata={'source': 'pipeline_adapter'}, ) diff --git a/src/langbot/pkg/api/http/service/agent.py b/src/langbot/pkg/api/http/service/agent.py index df2271f29..13a53f363 100644 --- a/src/langbot/pkg/api/http/service/agent.py +++ b/src/langbot/pkg/api/http/service/agent.py @@ -201,7 +201,6 @@ class AgentService: enable_reply=False, enable_interactions=False, ), - enabled=True, agent_id=agent_uuid, processor_type='agent', processor_id=agent_uuid, @@ -293,7 +292,6 @@ class AgentService: 'kind': AGENT_KIND_AGENT, 'component_ref': runner_id, 'config': config, - 'enabled': agent_data.get('enabled', True), 'supported_event_patterns': agent_data.get('supported_event_patterns') or AGENT_DEFAULT_EVENT_PATTERNS, } await self.ap.persistence_mgr.execute_async(sqlalchemy.insert(persistence_agent.Agent).values(**values)) @@ -308,17 +306,11 @@ class AgentService: await self.ap.pipeline_service.update_pipeline(context, agent_uuid, agent_data) return - update_data = agent_data.copy() - for protected_field in ( - 'uuid', - 'workspace_uuid', - 'kind', - 'component_ref', - 'created_at', - 'updated_at', - 'capability', - ): - update_data.pop(protected_field, None) + update_data = { + field: agent_data[field] + for field in ('name', 'description', 'emoji', 'config', 'supported_event_patterns') + if field in agent_data + } if 'config' in update_data: config, runner_id, _ = RunnerConfigResolver.resolve_agent_runner_config(update_data['config']) update_data['config'] = config @@ -425,7 +417,6 @@ class AgentService: item = pipeline.copy() item['kind'] = AGENT_KIND_PIPELINE item['component_ref'] = 'pipeline' - item['enabled'] = True item['supported_event_patterns'] = PIPELINE_EVENT_PATTERNS item['capability'] = { 'supported_event_patterns': PIPELINE_EVENT_PATTERNS, diff --git a/src/langbot/pkg/api/http/service/bot.py b/src/langbot/pkg/api/http/service/bot.py index fba19bab5..a187085ab 100644 --- a/src/langbot/pkg/api/http/service/bot.py +++ b/src/langbot/pkg/api/http/service/bot.py @@ -18,7 +18,6 @@ class BotService: ap: app.Application FAILURE_ROUTE_NOT_FOUND = 'route_not_found' - FAILURE_PROCESSOR_DISABLED = 'processor_disabled' FAILURE_PROCESSOR_NOT_FOUND = 'processor_not_found' FAILURE_PROCESSOR_INCOMPATIBLE = 'processor_incompatible' FAILURE_INVALID_EVENT = 'invalid_event' @@ -436,25 +435,6 @@ class BotService: } ], ) - if not getattr(agent, 'enabled', True): - return self._diagnostic_result( - matched=False, - binding=selected_binding, - failure_code=self.FAILURE_PROCESSOR_DISABLED, - reason='Agent target is disabled', - diagnostic_steps=diagnostic_steps - + [ - { - 'step': 'validate_processor', - 'binding_id': selected_binding.get('id'), - 'target_type': target_type, - 'target_uuid': target_uuid, - 'matched': False, - 'failure_code': self.FAILURE_PROCESSOR_DISABLED, - 'reason': 'Agent target is disabled', - } - ], - ) if not RuntimeBot._agent_supports_event_type(getattr(agent, 'supported_event_patterns', None), event_type): return self._diagnostic_result( matched=False, diff --git a/src/langbot/pkg/entity/persistence/agent.py b/src/langbot/pkg/entity/persistence/agent.py index f2d7acbc2..3cfd428a6 100644 --- a/src/langbot/pkg/entity/persistence/agent.py +++ b/src/langbot/pkg/entity/persistence/agent.py @@ -20,7 +20,6 @@ class Agent(Base): kind = sqlalchemy.Column(sqlalchemy.String(50), nullable=False, default='agent') component_ref = sqlalchemy.Column(sqlalchemy.String(255), nullable=True) config = sqlalchemy.Column(sqlalchemy.JSON, nullable=False, default={}) - enabled = sqlalchemy.Column(sqlalchemy.Boolean, nullable=False, default=True) supported_event_patterns = sqlalchemy.Column(sqlalchemy.JSON, nullable=False, default=['*']) created_at = sqlalchemy.Column(sqlalchemy.DateTime, nullable=False, server_default=sqlalchemy.func.now()) updated_at = sqlalchemy.Column( diff --git a/src/langbot/pkg/persistence/alembic/versions/0023_drop_agent_enabled.py b/src/langbot/pkg/persistence/alembic/versions/0023_drop_agent_enabled.py new file mode 100644 index 000000000..1d6ebbc4b --- /dev/null +++ b/src/langbot/pkg/persistence/alembic/versions/0023_drop_agent_enabled.py @@ -0,0 +1,37 @@ +"""Drop the obsolete Agent enabled state. + +Revision ID: 0023_drop_agent_enabled +Revises: 0022_merge_agent_reasoning_heads +Create Date: 2026-08-25 +""" + +from __future__ import annotations + +from alembic import op +import sqlalchemy as sa + + +revision = '0023_drop_agent_enabled' +down_revision = '0022_merge_agent_reasoning_heads' +branch_labels = None +depends_on = None + + +def _column_exists(inspector: sa.Inspector, table_name: str, column_name: str) -> bool: + if table_name not in inspector.get_table_names(): + return False + return any(column['name'] == column_name for column in inspector.get_columns(table_name)) + + +def upgrade() -> None: + inspector = sa.inspect(op.get_bind()) + if _column_exists(inspector, 'agents', 'enabled'): + with op.batch_alter_table('agents') as batch_op: + batch_op.drop_column('enabled') + + +def downgrade() -> None: + inspector = sa.inspect(op.get_bind()) + if 'agents' in inspector.get_table_names() and not _column_exists(inspector, 'agents', 'enabled'): + with op.batch_alter_table('agents') as batch_op: + batch_op.add_column(sa.Column('enabled', sa.Boolean(), nullable=False, server_default=sa.true())) diff --git a/src/langbot/pkg/platform/botmgr.py b/src/langbot/pkg/platform/botmgr.py index 5cd9d178d..c03fc1ac2 100644 --- a/src/langbot/pkg/platform/botmgr.py +++ b/src/langbot/pkg/platform/botmgr.py @@ -1135,7 +1135,6 @@ class RuntimeBot: enable_reply=True, enable_interactions=True, ), - enabled=True, agent_id=agent.get('uuid'), processor_type='agent', processor_id=agent.get('uuid'), @@ -1322,17 +1321,6 @@ class RuntimeBot: reason='Agent target not found', text=f'EBA event {event_type} target agent not found: {target_uuid}', ) - if not agent.get('enabled', True): - return await self._record_event_route_trace( - event_type=event_type, - status='failed', - binding=event_binding, - target_type=target_type, - target_uuid=target_uuid, - failure_code='processor_disabled', - reason='Agent target is disabled', - text=f'EBA event {event_type} target agent disabled: {target_uuid}', - ) if not self._agent_supports_event_type(agent.get('supported_event_patterns'), event_type): return await self._record_event_route_trace( event_type=event_type, @@ -1711,7 +1699,7 @@ class RuntimeBot: self.execution_context, record['processor_id'], ) - if not agent or agent.get('kind') != 'agent' or not agent.get('enabled', True): + if not agent or agent.get('kind') != 'agent': raise ValueError(f'Interaction target Agent is unavailable: {record["processor_id"]}') binding = self._agent_product_to_binding( @@ -1810,9 +1798,7 @@ class RuntimeBot: def tenant_scoped_listener(listener): @functools.wraps(listener) async def wrapped(*args, **kwargs): - tenant_scope = getattr( - self.ap.persistence_mgr, 'tenant_scope', None - ) + tenant_scope = getattr(self.ap.persistence_mgr, 'tenant_scope', None) cloud_runtime = ( getattr( getattr(self.ap.persistence_mgr, 'mode', None), @@ -1823,9 +1809,7 @@ class RuntimeBot: ) if cloud_runtime: if not callable(tenant_scope): - raise RuntimeError( - 'Cloud platform callbacks require a tenant scope' - ) + raise RuntimeError('Cloud platform callbacks require a tenant scope') async with tenant_scope(self.workspace_uuid): return await listener(*args, **kwargs) return await listener(*args, **kwargs) diff --git a/tests/integration/persistence/test_migrations.py b/tests/integration/persistence/test_migrations.py index 022794825..da0ffd89d 100644 --- a/tests/integration/persistence/test_migrations.py +++ b/tests/integration/persistence/test_migrations.py @@ -108,7 +108,7 @@ class TestSQLiteMigrationUpgrade: await run_alembic_upgrade(sqlite_engine, 'head') assert await get_alembic_current(sqlite_engine) == _get_script_head() - assert _get_script_head() == '0022_merge_agent_reasoning_heads' + assert _get_script_head() == '0023_drop_agent_enabled' @pytest.mark.asyncio async def test_upgrade_from_development_workspace_head_to_merged_head(self, sqlite_engine): @@ -120,7 +120,7 @@ class TestSQLiteMigrationUpgrade: await run_alembic_upgrade(sqlite_engine, 'head') assert await get_alembic_current(sqlite_engine) == _get_script_head() - assert _get_script_head() == '0022_merge_agent_reasoning_heads' + assert _get_script_head() == '0023_drop_agent_enabled' @pytest.mark.asyncio async def test_upgrade_from_reasoning_config_head_to_merged_head(self, sqlite_engine): @@ -131,7 +131,23 @@ class TestSQLiteMigrationUpgrade: await run_alembic_stamp(sqlite_engine, '0018_llm_reasoning_config') await run_alembic_upgrade(sqlite_engine, 'head') - assert await get_alembic_current(sqlite_engine) == '0022_merge_agent_reasoning_heads' + assert await get_alembic_current(sqlite_engine) == '0023_drop_agent_enabled' + + @pytest.mark.asyncio + async def test_upgrade_removes_agent_enabled_column(self, sqlite_engine): + async with sqlite_engine.begin() as conn: + await conn.run_sync(Base.metadata.create_all) + await conn.exec_driver_sql('ALTER TABLE agents ADD COLUMN enabled BOOLEAN NOT NULL DEFAULT 1') + + await run_alembic_stamp(sqlite_engine, '0022_merge_agent_reasoning_heads') + await run_alembic_upgrade(sqlite_engine, 'head') + + async with sqlite_engine.connect() as conn: + columns = await conn.run_sync( + lambda sync_conn: {column['name'] for column in sqlalchemy.inspect(sync_conn).get_columns('agents')} + ) + + assert 'enabled' not in columns @pytest.mark.asyncio async def test_upgrade_from_baseline_to_head(self, sqlite_engine): diff --git a/tests/unit_tests/agent/test_context_validation.py b/tests/unit_tests/agent/test_context_validation.py index 09c0b9a9c..00834a2d9 100644 --- a/tests/unit_tests/agent/test_context_validation.py +++ b/tests/unit_tests/agent/test_context_validation.py @@ -74,7 +74,6 @@ class TestContextValidation: runner_id='plugin:test/plugin/runner', runner_config={'timeout': 300}, agent_id='pipeline_1', - enabled=True, ) def _make_resources(self) -> BuilderResources: diff --git a/tests/unit_tests/agent/test_orchestrator_integration.py b/tests/unit_tests/agent/test_orchestrator_integration.py index f8c5f06da..345e33781 100644 --- a/tests/unit_tests/agent/test_orchestrator_integration.py +++ b/tests/unit_tests/agent/test_orchestrator_integration.py @@ -1069,7 +1069,6 @@ class TestQueryEntrySessionQueryId: resource_policy=ResourcePolicy(), state_policy=StatePolicy(enable_state=False, state_scopes=[]), delivery_policy=DeliveryPolicy(enable_streaming=True, enable_reply=True), - enabled=True, ) messages = [ diff --git a/tests/unit_tests/api/service/test_agent_service.py b/tests/unit_tests/api/service/test_agent_service.py index c0e0e7486..ad06a96b1 100644 --- a/tests/unit_tests/api/service/test_agent_service.py +++ b/tests/unit_tests/api/service/test_agent_service.py @@ -46,7 +46,6 @@ def _agent_row( 'runner': {'id': 'plugin:test/runner/default', 'expire-time': 0}, 'runner_config': {'plugin:test/runner/default': {'temperature': 0.2}}, }, - enabled=True, supported_event_patterns=supported_event_patterns or ['*'], created_at=dt.datetime(2026, 1, 1, 9, 0, 0), updated_at=updated_at or dt.datetime(2026, 1, 1, 10, 0, 0), @@ -63,7 +62,6 @@ def _serialize_agent(model_cls, entity, masked_columns=None): 'kind': entity.kind, 'component_ref': entity.component_ref, 'config': entity.config, - 'enabled': entity.enabled, 'supported_event_patterns': entity.supported_event_patterns, 'created_at': entity.created_at, 'updated_at': entity.updated_at, @@ -145,7 +143,6 @@ class TestAgentServiceDebug: return_value={ 'uuid': 'agent-1', 'kind': AGENT_KIND_AGENT, - 'enabled': True, 'supported_event_patterns': ['*'], 'config': _agent_row().config, } @@ -288,7 +285,7 @@ class TestAgentServiceListAndLookup: result = await AgentService(app).get_agent(WORKSPACE_UUID, 'pipeline-1') assert result['kind'] == AGENT_KIND_PIPELINE - assert result['enabled'] is True + assert 'enabled' not in result assert result['config'] == {'ai': {'runner': {'id': 'pipeline-runner'}}} assert result['capability']['message_only'] is True @@ -329,7 +326,7 @@ class TestAgentServiceCreateUpdateDelete: 'runner': {'id': runner.id, 'expire-time': 0}, 'runner_config': {runner.id: {'model': 'gpt-4.1', 'temperature': 0.2}}, } - assert insert_values['enabled'] is True + assert 'enabled' not in insert_values assert insert_values['supported_event_patterns'] == AGENT_DEFAULT_EVENT_PATTERNS app.pipeline_service._get_default_values_from_schema.assert_called_once_with(runner.config_schema) diff --git a/tests/unit_tests/platform/test_routing_rules.py b/tests/unit_tests/platform/test_routing_rules.py index 56b366585..ece6fdc75 100644 --- a/tests/unit_tests/platform/test_routing_rules.py +++ b/tests/unit_tests/platform/test_routing_rules.py @@ -81,9 +81,9 @@ class TestEventRouteTrace: 'target_type': 'agent', 'target_uuid': 'agent-1', }, - failure_code='processor_disabled', - reason='Agent target is disabled', - text='disabled', + failure_code='processor_not_found', + reason='Agent target is unavailable', + text='unavailable', ) bot.logger.warning.assert_awaited_once() diff --git a/web/src/app/home/agents/components/AgentFormComponent.tsx b/web/src/app/home/agents/components/AgentFormComponent.tsx index 80435c5e4..46730a6f0 100644 --- a/web/src/app/home/agents/components/AgentFormComponent.tsx +++ b/web/src/app/home/agents/components/AgentFormComponent.tsx @@ -24,7 +24,6 @@ import DynamicFormComponent from '@/app/home/components/dynamic-form/DynamicForm import { extractI18nObject } from '@/i18n/I18nProvider'; import { Button } from '@/components/ui/button'; import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs'; -import { Switch } from '@/components/ui/switch'; import { Textarea } from '@/components/ui/textarea'; import { Card, @@ -141,7 +140,6 @@ function AgentFormComponent( name: z.string().min(1, { message: t('agents.nameRequired') }), description: z.string().optional(), emoji: z.string().optional(), - enabled: z.boolean().optional(), }), runner: z.record(z.string(), z.any()), runner_config: z.record(z.string(), z.any()), @@ -156,7 +154,6 @@ function AgentFormComponent( name: '', description: '', emoji: '🤖', - enabled: true, }, runner: {}, runner_config: {}, @@ -190,7 +187,6 @@ function AgentFormComponent( name: agent.name ?? '', description: agent.description ?? '', emoji: agent.emoji || '🤖', - enabled: agent.enabled ?? true, }, runner: (config.runner as Record) ?? {}, runner_config: @@ -287,12 +283,12 @@ function AgentFormComponent( : t('pipelines.configuration'), icon: SlidersHorizontal, }, + { + name: 'basic', + label: t('common.management'), + icon: Power, + }, ]; - const managementSection = { - name: 'basic' as const, - label: t('common.management'), - icon: Power, - }; const runnerStatus = useMemo(() => { if (pluginStatusLoading) { @@ -465,7 +461,6 @@ function AgentFormComponent( name: values.basic.name, description: values.basic.description ?? '', emoji: values.basic.emoji, - enabled: values.basic.enabled ?? true, component_ref: (runner.id as string) || null, supported_event_patterns: normalizeEventPatterns( values.supported_event_patterns_text, @@ -568,11 +563,19 @@ function AgentFormComponent( } >
- + {primarySections.map((section) => { const Icon = section.icon; return ( - + {section.label} @@ -580,21 +583,6 @@ function AgentFormComponent( })}
-
- -
@@ -671,40 +659,6 @@ function AgentFormComponent( {activeSection === 'basic' && (
- - - {t('agents.availability')} - - {t('agents.availabilityDescription')} - - - - ( - -
- - - {t('agents.enabled')} - - - {t('agents.enabledDescription')} - -
- - - -
- )} - /> -
-
- diff --git a/web/src/app/infra/entities/api/index.ts b/web/src/app/infra/entities/api/index.ts index 7992eec90..bc815f279 100644 --- a/web/src/app/infra/entities/api/index.ts +++ b/web/src/app/infra/entities/api/index.ts @@ -177,7 +177,6 @@ export interface Agent { kind: AgentKind; component_ref?: string | null; config?: Record; - enabled?: boolean; supported_event_patterns?: string[]; capability?: AgentCapability; created_at?: string; diff --git a/web/src/app/wizard/page.tsx b/web/src/app/wizard/page.tsx index 3a3973183..db695d983 100644 --- a/web/src/app/wizard/page.tsx +++ b/web/src/app/wizard/page.tsx @@ -839,7 +839,6 @@ export default function WizardPage() { runner: { id: selectedRunner, 'expire-time': 0 }, runner_config: { [selectedRunner]: runnerConfig }, }, - enabled: true, supported_event_patterns: [selectedScenarioDefinition.eventType], }); processorUuid = agentResp.uuid; diff --git a/web/src/i18n/locales/en-US.ts b/web/src/i18n/locales/en-US.ts index a77ad2f9c..db51e7264 100644 --- a/web/src/i18n/locales/en-US.ts +++ b/web/src/i18n/locales/en-US.ts @@ -469,7 +469,6 @@ const enUS = { processor_incompatible: 'The selected processor cannot handle this event.', processor_not_found: 'The selected processor is unavailable.', - processor_disabled: 'The selected processor is disabled.', bot_runtime_unavailable: 'The bot is not running. Check its platform settings and enable it before running a full test.', runner_failed: 'The Agent runner failed while processing the event.', @@ -691,10 +690,7 @@ const enUS = { allEvents: 'Supports all events', messageEventsOnly: 'Message events only', basicInfo: 'Basic Information', - basicInfoDescription: 'Set the name, icon, description and enabled state', - availability: 'Availability', - availabilityDescription: - 'Control whether this Agent can receive and process events.', + basicInfoDescription: 'Set the name, icon and description', runnerSettings: 'Runner', advanced: 'Advanced', bindableEvents: 'Bindable Event Range', @@ -703,9 +699,6 @@ const enUS = { supportedEvents: 'Event Range', supportedEventsDescription: 'Use one event pattern per line, for example *, message.received, group.*. Pipelines are fixed to message.*.', - enabled: 'Enable Agent', - enabledDescription: - 'When disabled, this Agent should not be selected by event routing.', nameRequired: 'Name cannot be empty', createSuccess: 'Created successfully', createError: 'Creation failed: ', diff --git a/web/src/i18n/locales/es-ES.ts b/web/src/i18n/locales/es-ES.ts index c447f049d..41d4661d7 100644 --- a/web/src/i18n/locales/es-ES.ts +++ b/web/src/i18n/locales/es-ES.ts @@ -525,8 +525,7 @@ const esES = { allEvents: 'Compatible con todos los eventos', messageEventsOnly: 'Solo eventos de mensaje', basicInfo: 'Información básica', - basicInfoDescription: - 'Establece el nombre, icono, descripción y estado de habilitación', + basicInfoDescription: 'Establece el nombre, icono y descripción', runnerSettings: 'Runner', advanced: 'Avanzado', bindableEvents: 'Rango de eventos vinculables', @@ -535,9 +534,6 @@ const esES = { supportedEvents: 'Rango de eventos', supportedEventsDescription: 'Usa un patrón de evento por línea, por ejemplo *, message.received, group.*. Los Pipelines están fijos en message.*.', - enabled: 'Habilitar Agent', - enabledDescription: - 'Cuando está deshabilitado, este Agent no debe ser seleccionado por el enrutamiento de eventos.', nameRequired: 'El nombre no puede estar vacío', createSuccess: 'Creado correctamente', createError: 'Error al crear: ', diff --git a/web/src/i18n/locales/ja-JP.ts b/web/src/i18n/locales/ja-JP.ts index 0a9369702..0ad0ee711 100644 --- a/web/src/i18n/locales/ja-JP.ts +++ b/web/src/i18n/locales/ja-JP.ts @@ -476,7 +476,6 @@ const jaJP = { processor_incompatible: '選択したプロセッサーはこのイベントを処理できません。', processor_not_found: '選択したプロセッサーを利用できません。', - processor_disabled: '選択したプロセッサーは無効です。', bot_runtime_unavailable: 'ボットが実行されていません。プラットフォーム設定を確認してボットを有効にした後、完全テストを実行してください。', runner_failed: 'Agent Runner がイベント処理中に失敗しました。', @@ -706,10 +705,7 @@ const jaJP = { allEvents: 'すべてのイベントに対応', messageEventsOnly: 'メッセージイベントのみ', basicInfo: '基本情報', - basicInfoDescription: '名前、アイコン、説明、有効状態を設定します', - availability: '有効状態', - availabilityDescription: - 'この Agent がイベントを受信して処理できるかを制御します。', + basicInfoDescription: '名前、アイコン、説明を設定します', runnerSettings: 'Runner', advanced: '詳細', bindableEvents: '紐付け可能なイベント範囲', @@ -718,9 +714,6 @@ const jaJP = { supportedEvents: 'イベント範囲', supportedEventsDescription: '1 行に 1 つのイベントパターンを指定します。例: *、message.received、group.*。Pipeline は message.* 固定です。', - enabled: 'Agent を有効化', - enabledDescription: - '無効化すると、この Agent はイベントルーティングで選択されません。', nameRequired: '名前は必須です', createSuccess: '作成に成功しました', createError: '作成に失敗しました:', diff --git a/web/src/i18n/locales/ru-RU.ts b/web/src/i18n/locales/ru-RU.ts index 79bc004e5..b313e7ae6 100644 --- a/web/src/i18n/locales/ru-RU.ts +++ b/web/src/i18n/locales/ru-RU.ts @@ -522,7 +522,7 @@ const ruRU = { allEvents: 'Поддерживает все события', messageEventsOnly: 'Только события сообщений', basicInfo: 'Основная информация', - basicInfoDescription: 'Задайте имя, иконку, описание и статус активации', + basicInfoDescription: 'Задайте имя, иконку и описание', runnerSettings: 'Runner', advanced: 'Дополнительно', bindableEvents: 'Диапазон привязываемых событий', @@ -531,9 +531,6 @@ const ruRU = { supportedEvents: 'Диапазон событий', supportedEventsDescription: 'Один шаблон события в строке, например *, message.received, group.*. Pipeline фиксирован на message.*.', - enabled: 'Включить Agent', - enabledDescription: - 'При отключении этот Agent не должен выбираться маршрутизацией событий.', nameRequired: 'Имя не может быть пустым', createSuccess: 'Успешно создано', createError: 'Ошибка создания: ', diff --git a/web/src/i18n/locales/th-TH.ts b/web/src/i18n/locales/th-TH.ts index b67e197b8..3599745d4 100644 --- a/web/src/i18n/locales/th-TH.ts +++ b/web/src/i18n/locales/th-TH.ts @@ -508,7 +508,7 @@ const thTH = { allEvents: 'รองรับทุกเหตุการณ์', messageEventsOnly: 'เฉพาะเหตุการณ์ข้อความ', basicInfo: 'ข้อมูลพื้นฐาน', - basicInfoDescription: 'ตั้งชื่อ ไอคอน คำอธิบาย และสถานะการเปิดใช้งาน', + basicInfoDescription: 'ตั้งชื่อ ไอคอน และคำอธิบาย', runnerSettings: 'Runner', advanced: 'ขั้นสูง', bindableEvents: 'ช่วงเหตุการณ์ที่ผูกได้', @@ -517,9 +517,6 @@ const thTH = { supportedEvents: 'ช่วงเหตุการณ์', supportedEventsDescription: 'หนึ่งรูปแบบเหตุการณ์ต่อบรรทัด เช่น *, message.received, group.* Pipeline ถูกกำหนดไว้ที่ message.*', - enabled: 'เปิดใช้งาน Agent', - enabledDescription: - 'เมื่อปิดใช้งาน Agent นี้จะไม่ถูกเลือกโดยการกำหนดเส้นทางเหตุการณ์', nameRequired: 'ชื่อต้องไม่ว่างเปล่า', createSuccess: 'สร้างสำเร็จ', createError: 'สร้างล้มเหลว: ', diff --git a/web/src/i18n/locales/vi-VN.ts b/web/src/i18n/locales/vi-VN.ts index 77a42415d..78bca088d 100644 --- a/web/src/i18n/locales/vi-VN.ts +++ b/web/src/i18n/locales/vi-VN.ts @@ -518,7 +518,7 @@ const viVN = { allEvents: 'Hỗ trợ tất cả sự kiện', messageEventsOnly: 'Chỉ sự kiện tin nhắn', basicInfo: 'Thông tin cơ bản', - basicInfoDescription: 'Đặt tên, biểu tượng, mô tả và trạng thái kích hoạt', + basicInfoDescription: 'Đặt tên, biểu tượng và mô tả', runnerSettings: 'Runner', advanced: 'Nâng cao', bindableEvents: 'Phạm vi sự kiện có thể gắn', @@ -527,9 +527,6 @@ const viVN = { supportedEvents: 'Phạm vi sự kiện', supportedEventsDescription: 'Mỗi dòng một mẫu sự kiện, ví dụ *, message.received, group.*. Pipeline cố định ở message.*.', - enabled: 'Kích hoạt Agent', - enabledDescription: - 'Khi bị tắt, Agent này sẽ không được định tuyến sự kiện chọn.', nameRequired: 'Tên không được để trống', createSuccess: 'Tạo thành công', createError: 'Tạo thất bại: ', diff --git a/web/src/i18n/locales/zh-Hans.ts b/web/src/i18n/locales/zh-Hans.ts index b62aced05..21561f66c 100644 --- a/web/src/i18n/locales/zh-Hans.ts +++ b/web/src/i18n/locales/zh-Hans.ts @@ -447,7 +447,6 @@ const zhHans = { route_not_found: '没有路由命中此事件。', processor_incompatible: '所选处理器无法处理此事件。', processor_not_found: '所选处理器不可用。', - processor_disabled: '所选处理器已禁用。', bot_runtime_unavailable: '机器人尚未运行。请检查平台配置并启用机器人,再运行完整测试。', runner_failed: 'Agent Runner 处理事件时失败。', @@ -661,9 +660,7 @@ const zhHans = { allEvents: '支持全部事件', messageEventsOnly: '仅支持消息事件', basicInfo: '基础信息', - basicInfoDescription: '设置名称、图标、描述和启用状态', - availability: '启用状态', - availabilityDescription: '控制此 Agent 是否可以接收并处理事件。', + basicInfoDescription: '设置名称、图标和描述', runnerSettings: '运行器', advanced: '高级', bindableEvents: '可绑定事件范围', @@ -672,8 +669,6 @@ const zhHans = { supportedEvents: '事件范围', supportedEventsDescription: '每行一个事件模式,例如 *、message.received、group.*。Pipeline 固定仅支持 message.*。', - enabled: '启用 Agent', - enabledDescription: '禁用后,此 Agent 不应被事件路由选中。', nameRequired: '名称不能为空', createSuccess: '创建成功', createError: '创建失败:', diff --git a/web/src/i18n/locales/zh-Hant.ts b/web/src/i18n/locales/zh-Hant.ts index 34c6b1121..ea7e81c0b 100644 --- a/web/src/i18n/locales/zh-Hant.ts +++ b/web/src/i18n/locales/zh-Hant.ts @@ -491,7 +491,7 @@ const zhHant = { allEvents: '支援全部事件', messageEventsOnly: '僅支援訊息事件', basicInfo: '基本資訊', - basicInfoDescription: '設定名稱、圖示、描述和啟用狀態', + basicInfoDescription: '設定名稱、圖示和描述', runnerSettings: '執行器', advanced: '進階', bindableEvents: '可綁定事件範圍', @@ -500,8 +500,6 @@ const zhHant = { supportedEvents: '事件範圍', supportedEventsDescription: '每行一個事件模式,例如 *、message.received、group.*。Pipeline 固定僅支援 message.*。', - enabled: '啟用 Agent', - enabledDescription: '停用後,此 Agent 不應被事件路由選中。', nameRequired: '名稱不能為空', createSuccess: '建立成功', createError: '建立失敗:', diff --git a/web/tests/e2e/processor-detail-workbench.spec.ts b/web/tests/e2e/processor-detail-workbench.spec.ts index 840514c9f..93b3c4345 100644 --- a/web/tests/e2e/processor-detail-workbench.spec.ts +++ b/web/tests/e2e/processor-detail-workbench.spec.ts @@ -50,11 +50,9 @@ test.describe('processor detail workbench', () => { ); await expect(flow.getByRole('tab').nth(1)).toContainText('Runner'); await expect(flow.getByRole('tab').nth(2)).toContainText('Local Agent'); - const agentManagement = configPanel.getByRole('button', { - name: 'Management', - }); - await expect(agentManagement).toBeVisible(); - await expect(flow.getByText('Management')).toHaveCount(0); + const agentManagement = flow.getByRole('tab').nth(3); + await expect(agentManagement).toContainText('Management'); + await expect(agentManagement).toHaveClass(/text-muted-foreground/); await expect( page.getByRole('heading', { name: /agent-workbench/ }), @@ -85,6 +83,12 @@ test.describe('processor detail workbench', () => { await expect( configPanel.getByText('Local Agent', { exact: true }).last(), ).toBeVisible(); + await agentManagement.click(); + await expect(configPanel.getByText('Danger Zone')).toBeVisible(); + await expect(configPanel.getByText('Availability')).toHaveCount(0); + await expect( + configPanel.getByRole('switch', { name: 'Enable Agent' }), + ).toHaveCount(0); }); test('agent saves edits before debugging and shows the real output', async ({