refactor(agent): remove enabled state

This commit is contained in:
RockChinQ
2026-08-25 23:27:45 +08:00
parent 22d9053bf1
commit 80f1790e1d
27 changed files with 104 additions and 190 deletions
+1 -2
View File
@@ -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. 只执行一个响应目标。
@@ -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
@@ -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,
@@ -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."""
@@ -151,7 +151,6 @@ class QueryEntryAdapter:
state_policy=state_policy,
delivery_policy=delivery_policy,
event_types=[event_type],
enabled=True,
metadata={'source': 'pipeline_adapter'},
)
+5 -14
View File
@@ -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,
-20
View File
@@ -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,
@@ -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(
@@ -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()))
+3 -19
View File
@@ -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)
@@ -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):
@@ -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:
@@ -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 = [
@@ -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)
@@ -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()
@@ -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<string, unknown>) ?? {},
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<AgentRunnerStatus>(() => {
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(
}
>
<div className="overflow-x-auto">
<TabsList className="grid min-w-[34rem] w-full grid-cols-3">
<TabsList className="grid min-w-[42rem] w-full grid-cols-[repeat(3,minmax(0,1fr))_auto]">
{primarySections.map((section) => {
const Icon = section.icon;
return (
<TabsTrigger key={section.name} value={section.name}>
<TabsTrigger
key={section.name}
value={section.name}
className={
section.name === 'basic'
? 'px-4 text-muted-foreground data-[state=active]:text-foreground'
: undefined
}
>
<Icon />
{section.label}
</TabsTrigger>
@@ -580,21 +583,6 @@ function AgentFormComponent(
})}
</TabsList>
</div>
<div className="flex justify-end pt-2">
<Button
type="button"
variant={
activeSection === managementSection.name
? 'secondary'
: 'ghost'
}
size="sm"
onClick={() => setActiveSection(managementSection.name)}
>
<Power />
{managementSection.label}
</Button>
</div>
</Tabs>
</nav>
@@ -671,40 +659,6 @@ function AgentFormComponent(
{activeSection === 'basic' && (
<div className="space-y-6">
<Card>
<CardHeader>
<CardTitle>{t('agents.availability')}</CardTitle>
<CardDescription>
{t('agents.availabilityDescription')}
</CardDescription>
</CardHeader>
<CardContent>
<FormField
control={form.control}
name="basic.enabled"
render={({ field }) => (
<FormItem className="flex items-center justify-between rounded-lg border p-4">
<div className="space-y-0.5">
<FormLabel className="flex items-center gap-2">
<Power className="size-4" />
{t('agents.enabled')}
</FormLabel>
<FormDescription>
{t('agents.enabledDescription')}
</FormDescription>
</div>
<FormControl>
<Switch
checked={field.value ?? true}
onCheckedChange={field.onChange}
/>
</FormControl>
</FormItem>
)}
/>
</CardContent>
</Card>
<Card className="border-destructive/50">
<CardHeader>
<CardTitle className="text-destructive">
-1
View File
@@ -177,7 +177,6 @@ export interface Agent {
kind: AgentKind;
component_ref?: string | null;
config?: Record<string, unknown>;
enabled?: boolean;
supported_event_patterns?: string[];
capability?: AgentCapability;
created_at?: string;
-1
View File
@@ -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;
+1 -8
View File
@@ -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: ',
+1 -5
View File
@@ -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: ',
+1 -8
View File
@@ -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: '作成に失敗しました:',
+1 -4
View File
@@ -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: 'Ошибка создания: ',
+1 -4
View File
@@ -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: 'สร้างล้มเหลว: ',
+1 -4
View File
@@ -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: ',
+1 -6
View File
@@ -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: '创建失败:',
+1 -3
View File
@@ -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: '建立失敗:',
@@ -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 ({