From 11bd5c6fd776b796c6c8e29dba4f6dfc8f338b0b Mon Sep 17 00:00:00 2001 From: huanghuoguoguo <60681390+huanghuoguoguo@users.noreply.github.com> Date: Sat, 11 Jul 2026 12:37:07 +0800 Subject: [PATCH] feat(wizard): add scenario templates and EBA release gate --- skills/scripts/e2e/onebot-runtime-probe.py | 93 +++++ .../e2e/wizard-onebot-agent-runtime.mjs | 348 ++++++++++++++++++ skills/skills.index.json | 46 +++ .../cases/wizard-onebot-agent-runtime.yaml | 61 +++ .../suites/eba-release-gate.yaml | 14 + web/src/app/wizard/page.tsx | 38 +- web/src/i18n/locales/en-US.ts | 9 +- web/src/i18n/locales/ja-JP.ts | 9 +- web/src/i18n/locales/zh-Hans.ts | 8 +- 9 files changed, 603 insertions(+), 23 deletions(-) create mode 100644 skills/scripts/e2e/onebot-runtime-probe.py create mode 100644 skills/scripts/e2e/wizard-onebot-agent-runtime.mjs create mode 100644 skills/skills/langbot-testing/cases/wizard-onebot-agent-runtime.yaml create mode 100644 skills/skills/langbot-testing/suites/eba-release-gate.yaml diff --git a/skills/scripts/e2e/onebot-runtime-probe.py b/skills/scripts/e2e/onebot-runtime-probe.py new file mode 100644 index 000000000..a080571fa --- /dev/null +++ b/skills/scripts/e2e/onebot-runtime-probe.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python3 +"""Drive one deterministic event through an enabled OneBot reverse WebSocket.""" + +from __future__ import annotations + +import argparse +import asyncio +import json +import time + +import websockets + + +async def run(port: int) -> None: + uri = f'ws://127.0.0.1:{port}/ws' + headers = { + 'X-Self-ID': '900001', + 'X-Client-Role': 'Universal', + 'User-Agent': 'LangBot-E2E-OneBot/1.0', + } + connect_deadline = time.monotonic() + 15 + connection = None + while time.monotonic() < connect_deadline: + try: + connection = await websockets.connect(uri, additional_headers=headers) + break + except OSError: + await asyncio.sleep(0.25) + if connection is None: + raise RuntimeError(f'OneBot reverse WebSocket did not open on port {port}') + + actions: list[str] = [] + event_deadline = time.monotonic() + 20 + async with connection as ws: + await ws.send( + json.dumps( + { + 'post_type': 'notice', + 'notice_type': 'group_increase', + 'sub_type': 'invite', + 'time': int(time.time()), + 'self_id': 900001, + 'group_id': 20001, + 'operator_id': 10002, + 'user_id': 10003, + } + ) + ) + + delivered = False + while time.monotonic() < event_deadline and not delivered: + try: + raw = await asyncio.wait_for(ws.recv(), timeout=2) + except asyncio.TimeoutError: + continue + request = json.loads(raw) + action = request.get('action', '') + actions.append(action) + if action == 'get_group_info': + data = {'group_id': 20001, 'group_name': 'LangBot Runtime QA'} + elif action == 'get_group_member_info': + data = { + 'group_id': 20001, + 'user_id': 10003, + 'nickname': 'Runtime QA Member', + 'card': 'Runtime QA Member', + } + elif action == 'send_group_msg': + data = {'message_id': 70001} + delivered = True + else: + data = {} + await ws.send( + json.dumps( + { + 'status': 'ok', + 'retcode': 0, + 'data': data, + 'echo': request.get('echo'), + } + ) + ) + + if not delivered: + raise RuntimeError(f'Agent output was not delivered; actions={actions}') + print(json.dumps({'connected': True, 'actions': actions, 'delivered': True})) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('--port', type=int, required=True) + args = parser.parse_args() + asyncio.run(run(args.port)) diff --git a/skills/scripts/e2e/wizard-onebot-agent-runtime.mjs b/skills/scripts/e2e/wizard-onebot-agent-runtime.mjs new file mode 100644 index 000000000..b270634e3 --- /dev/null +++ b/skills/scripts/e2e/wizard-onebot-agent-runtime.mjs @@ -0,0 +1,348 @@ +#!/usr/bin/env node + +import { spawn } from "node:child_process"; +import { createServer } from "node:net"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; +import { + createBrowser, + ensureAuthenticatedBrowser, + ensureEvidence, + evidencePaths, + exitCode, + loadEnvFiles, + localIsoWithOffset, + pathExists, + resolveLangBotRepo, + safeScreenshot, + scanBrowserDiagnostics, + writeResult, +} from "./lib/langbot-e2e.mjs"; + +const caseId = "wizard-onebot-agent-runtime"; +await loadEnvFiles(); +const paths = evidencePaths(caseId); +await ensureEvidence(paths); + +const startedAt = new Date(); +const frontendUrl = process.env.LANGBOT_FRONTEND_URL || ""; +const backendUrl = process.env.LANGBOT_BACKEND_URL || ""; +let browser; +let botId = ""; +let agentId = ""; +let token = ""; + +const result = { + source: "automation", + case_id: caseId, + run_id: paths.runId, + started_at: startedAt.toISOString(), + started_at_local: localIsoWithOffset(startedAt), + finished_at: "", + finished_at_local: "", + status: "fail", + reason: "", + url: "", + bot_id: "", + agent_id: "", + visible_signals: [], + api: {}, + runtime: {}, + cleanup: {}, + diagnostics: null, + evidence: { + screenshot: paths.screenshot, + console_log: paths.consoleLog, + network_log: paths.networkLog, + automation_result_json: paths.automationResultJson, + result_json: paths.resultJson, + }, + evidence_collected: ["ui", "screenshot", "console", "api_diagnostic"], +}; + +async function getFreePort() { + const server = createServer(); + await new Promise((resolve, reject) => { + server.once("error", reject); + server.listen(0, "127.0.0.1", resolve); + }); + const address = server.address(); + const port = typeof address === "object" && address ? address.port : 0; + await new Promise((resolve) => server.close(resolve)); + if (!port) throw new Error("Could not allocate a temporary OneBot port."); + return port; +} + +async function api(page, path, options = {}) { + return await page.evaluate( + async ({ baseUrl, path, options, token }) => { + const response = await fetch(`${baseUrl.replace(/\/$/, "")}${path}`, { + method: options.method || "GET", + headers: { + Authorization: `Bearer ${token}`, + "Content-Type": "application/json", + }, + body: + options.body === undefined ? undefined : JSON.stringify(options.body), + }); + return { + status: response.status, + json: await response.json().catch(() => ({})), + }; + }, + { baseUrl: backendUrl, path, options, token }, + ); +} + +async function runProbe(port) { + const repo = await resolveLangBotRepo(); + const python = join(repo, ".venv", "bin", "python"); + const script = join( + dirname(fileURLToPath(import.meta.url)), + "onebot-runtime-probe.py", + ); + if (!(await pathExists(python))) { + throw new Error(`LangBot virtualenv Python is missing: ${python}`); + } + return await new Promise((resolve, reject) => { + const child = spawn(python, [script, "--port", String(port)], { + cwd: repo, + }); + let stdout = ""; + let stderr = ""; + child.stdout.on("data", (chunk) => (stdout += chunk)); + child.stderr.on("data", (chunk) => (stderr += chunk)); + child.on("error", reject); + child.on("close", (code) => { + if (code === 0) { + resolve(JSON.parse(stdout.trim().split("\n").at(-1))); + } else { + reject( + new Error( + stderr.trim() || stdout.trim() || `OneBot probe exited ${code}`, + ), + ); + } + }); + }); +} + +try { + if (!frontendUrl) throw new Error("LANGBOT_FRONTEND_URL is not configured."); + if (!backendUrl) throw new Error("LANGBOT_BACKEND_URL is not configured."); + const port = await getFreePort(); + + browser = await createBrowser(paths); + const { page } = browser; + await page.goto(frontendUrl, { waitUntil: "domcontentloaded" }); + const auth = await ensureAuthenticatedBrowser(page, { + frontendUrl, + backendUrl, + }); + if (auth.status !== "pass") { + result.status = auth.status; + throw new Error(auth.reason); + } + token = await page.evaluate(() => localStorage.getItem("token") || ""); + if (!token) { + result.status = "blocked"; + throw new Error("Authenticated browser token is unavailable."); + } + + // Keep this case isolated from the instance's real onboarding state. + await page.route("**/api/v1/system/wizard/progress", async (route) => { + await route.fulfill({ + status: 200, + contentType: "application/json", + body: JSON.stringify({ code: 0, msg: "ok", data: {} }), + }); + }); + + await page.goto(`${frontendUrl.replace(/\/$/, "")}/wizard`, { + waitUntil: "domcontentloaded", + }); + await page + .getByRole("button", { + name: /Welcome new members|欢迎新成员|新しいメンバーを歓迎/, + }) + .click(); + await page.getByText("OneBot v11", { exact: true }).last().click(); + + const botResponse = page.waitForResponse( + (response) => + response.request().method() === "POST" && + /\/api\/v1\/platform\/bots$/.test(new URL(response.url()).pathname), + ); + await page + .getByRole("button", { + name: /Confirm, Create Bot|确定,创建机器人|確定、ボットを作成/, + }) + .click(); + const botPayload = await (await botResponse).json(); + botId = botPayload.data?.uuid || ""; + if (!botId) throw new Error("Wizard did not create a Bot."); + result.bot_id = botId; + result.api.create_bot = { code: botPayload.code ?? null }; + + await page + .getByText(/Configure Your Bot|配置机器人|ボットを設定/) + .first() + .waitFor({ timeout: 15_000 }); + const portInput = page.getByRole("spinbutton").first(); + await portInput.fill(String(port)); + await portInput.press("Tab"); + await page + .getByRole("button", { + name: /Save & Enable Bot|保存并启用|保存して有効化/, + }) + .click(); + await page + .getByText( + /Bot configuration saved and enabled|机器人配置已保存并启用|ボット設定が保存され、有効になりました/, + ) + .waitFor({ timeout: 15_000 }); + result.visible_signals.push("bot-created", "adapter-enabled"); + + await page.getByRole("button", { name: /Next|下一步|次へ/ }).click(); + await page + .getByText(/Local Agent|本地 Agent/) + .first() + .click(); + await page.waitForFunction( + () => + Array.from(document.querySelectorAll("textarea")).some((element) => { + const value = element.value || ""; + return [ + "Welcome new group members", + "欢迎新群成员", + "新しいグループメンバー", + ].some((expected) => value.includes(expected)); + }), + null, + { timeout: 15_000 }, + ); + result.visible_signals.push("scenario-prompt-visible"); + + await page + .getByText(/QA Deterministic Runner|QA 确定性 Runner/) + .first() + .click(); + + const agentResponse = page.waitForResponse( + (response) => + response.request().method() === "POST" && + /\/api\/v1\/agents$/.test(new URL(response.url()).pathname), + ); + await page + .getByRole("button", { + name: /Create & Deploy|创建并部署|作成&デプロイ/, + }) + .click(); + const agentPayload = await (await agentResponse).json(); + agentId = agentPayload.data?.uuid || ""; + if (!agentId) throw new Error("Wizard did not create an Agent."); + result.agent_id = agentId; + result.api.create_agent = { code: agentPayload.code ?? null }; + await page.getByText(/All Set!|一切就绪!/).waitFor({ timeout: 15_000 }); + result.visible_signals.push("agent-created", "wizard-complete"); + + const savedBot = await api(page, `/api/v1/platform/bots/${botId}`); + const route = savedBot.json.data?.bot?.event_bindings?.[0]; + result.api.saved_route = { + http_status: savedBot.status, + event_pattern: route?.event_pattern || null, + target_type: route?.target_type || null, + target_matches: route?.target_uuid === agentId, + }; + if ( + route?.event_pattern !== "group.member_joined" || + route?.target_type !== "agent" || + route?.target_uuid !== agentId + ) { + throw new Error("Wizard did not persist the expected Agent route."); + } + result.visible_signals.push("route-persisted"); + + result.runtime.onebot = await runProbe(port); + let routeStatus; + for (let attempt = 0; attempt < 30; attempt += 1) { + routeStatus = await api( + page, + `/api/v1/platform/bots/${botId}/event-routes/status`, + ); + if (routeStatus.json.data?.routes?.[0]?.last_status === "delivered") break; + await page.waitForTimeout(250); + } + const latest = routeStatus?.json?.data?.routes?.[0]; + result.runtime.route_status = latest || null; + if (latest?.last_status !== "delivered") { + throw new Error( + `Runtime route did not reach delivered status: ${latest?.last_status || "none"}`, + ); + } + result.visible_signals.push( + "platform-event-converted", + "agent-ran", + "reply-delivered", + ); + + const botUrl = `${frontendUrl.replace(/\/$/, "")}/home/bots?id=${encodeURIComponent(botId)}`; + await page.goto(botUrl, { waitUntil: "domcontentloaded" }); + result.url = page.url(); + const deliveredStatus = page + .getByText(/Delivered|已投递|配信済み/, { exact: true }) + .first(); + await deliveredStatus.waitFor({ timeout: 15_000 }); + await deliveredStatus.scrollIntoViewIfNeeded(); + await page.waitForTimeout(250); + result.visible_signals.push("delivered-status-visible"); + await safeScreenshot(page, paths.screenshot); + + result.diagnostics = await scanBrowserDiagnostics(paths); + if (result.diagnostics.status !== "pass") { + throw new Error(result.diagnostics.reason); + } + result.status = "pass"; + result.reason = + "Quick Start created an enabled OneBot scenario that converted a platform event, ran an Agent, delivered its reply, and showed the final status."; +} catch (error) { + if (!["blocked", "env_issue"].includes(result.status)) result.status = "fail"; + result.reason = result.reason || error.message; + if (browser?.page) await safeScreenshot(browser.page, paths.screenshot); +} finally { + if (browser?.page && token) { + if (botId) { + const deleted = await api( + browser.page, + `/api/v1/platform/bots/${encodeURIComponent(botId)}`, + { method: "DELETE" }, + ).catch(() => ({ status: 0, json: {} })); + result.cleanup.bot_deleted = + deleted.status < 400 && deleted.json.code === 0; + } + if (agentId) { + const deleted = await api( + browser.page, + `/api/v1/agents/${encodeURIComponent(agentId)}`, + { method: "DELETE" }, + ).catch(() => ({ status: 0, json: {} })); + result.cleanup.agent_deleted = + deleted.status < 400 && deleted.json.code === 0; + } + } + const cleanupFailed = + (botId && !result.cleanup.bot_deleted) || + (agentId && !result.cleanup.agent_deleted); + if (cleanupFailed && result.status === "pass") { + result.status = "fail"; + result.reason = "The temporary OneBot wizard resources were not deleted."; + } + if (browser) await browser.close().catch(() => {}); + const finishedAt = new Date(); + result.finished_at = finishedAt.toISOString(); + result.finished_at_local = localIsoWithOffset(finishedAt); + await writeResult(paths, result); + console.log(JSON.stringify(result, null, 2)); +} + +process.exit(exitCode(result.status)); diff --git a/skills/skills.index.json b/skills/skills.index.json index d5bed5995..e9e8a1ebb 100644 --- a/skills/skills.index.json +++ b/skills/skills.index.json @@ -191,6 +191,7 @@ "sandbox-skill-authoring-edit-existing-e2e", "skill-discovery-via-mcp-gateway", "webui-login-state", + "wizard-onebot-agent-runtime", "wizard-scenario-routing" ], "case_summaries": [ @@ -1649,6 +1650,32 @@ "console" ] }, + { + "id": "wizard-onebot-agent-runtime", + "title": "Quick Start runs a real OneBot member event through an Agent", + "mode": "agent-browser", + "area": "wizard", + "type": "feature", + "priority": "p0", + "risk": "high", + "ci_eligible": false, + "tags": [ + "wizard", + "eba", + "onebot", + "agent-runner", + "release-gate" + ], + "automation": "scripts/e2e/wizard-onebot-agent-runtime.mjs", + "setup_automation": [], + "setup_provides_env": [], + "evidence_required": [ + "ui", + "screenshot", + "console", + "api_diagnostic" + ] + }, { "id": "wizard-scenario-routing", "title": "Quick Start filters channels by the selected bot scenario", @@ -1678,6 +1705,7 @@ "suites": [ "agent-runner-release-gate", "core-smoke", + "eba-release-gate", "langbot-debug-chat-isolation-gate", "langbot-debug-chat-load-gate", "langbot-live-backend-gate", @@ -1751,6 +1779,24 @@ "local-agent-basic-debug-chat" ] }, + { + "id": "eba-release-gate", + "title": "Event-based bot product release gate", + "description": "Browser and runtime gate for scenario onboarding, route diagnostics, and real platform-to-Agent execution.", + "type": "release_gate", + "priority": "p0", + "tags": [ + "eba", + "event-routing", + "wizard", + "release-gate" + ], + "cases": [ + "wizard-scenario-routing", + "bot-event-routing-product-flow", + "wizard-onebot-agent-runtime" + ] + }, { "id": "langbot-debug-chat-isolation-gate", "title": "LangBot Debug Chat isolation gate", diff --git a/skills/skills/langbot-testing/cases/wizard-onebot-agent-runtime.yaml b/skills/skills/langbot-testing/cases/wizard-onebot-agent-runtime.yaml new file mode 100644 index 000000000..220c5b3f5 --- /dev/null +++ b/skills/skills/langbot-testing/cases/wizard-onebot-agent-runtime.yaml @@ -0,0 +1,61 @@ +id: wizard-onebot-agent-runtime +title: "Quick Start runs a real OneBot member event through an Agent" +mode: agent-browser +area: wizard +type: feature +priority: p0 +risk: high +ci_eligible: false +tags: + - wizard + - eba + - onebot + - agent-runner + - release-gate +skills: + - langbot-env-setup + - langbot-testing +env: + - LANGBOT_FRONTEND_URL + - LANGBOT_BACKEND_URL + - LANGBOT_BROWSER_PROFILE +automation: scripts/e2e/wizard-onebot-agent-runtime.mjs +automation_env: + - LANGBOT_FRONTEND_URL + - LANGBOT_BACKEND_URL + - LANGBOT_BROWSER_PROFILE + - LANGBOT_CHROMIUM_EXECUTABLE +preconditions: + - "The target is a local test instance where temporary Bots and Agents may be created and deleted." + - "Local Agent is installed so the scenario prompt can be verified in its prompt editor." + - "The QA Deterministic Runner is installed and registered as plugin:qa/agent-runner/default." + - "The LangBot virtualenv includes the websockets package used by the local OneBot probe." +steps: + - "Open Quick Start and choose Welcome new members with the OneBot v11 channel." + - "Create and enable a temporary OneBot Bot on an available local port." + - "Select Local Agent and confirm the editable welcome prompt is prefilled." + - "Choose QA Deterministic Runner and finish the wizard." + - "Connect a local OneBot reverse WebSocket and send a group_increase notice." + - "Confirm the Agent output is sent back with send_group_msg." + - "Open the Bot route editor and confirm the route status is Delivered." +checks: + - "UI: The wizard reaches All Set after creating the Agent route." + - "UI: Local Agent receives a scenario-specific editable welcome prompt." + - "Runtime: OneBot group_increase converts to group.member_joined." + - "Runtime: The saved Agent route reaches delivered status." + - "Runtime: The deterministic Agent reply invokes send_group_msg." + - "UI: Delivered is visible on the saved route card." + - "Console: No unexpected frontend errors appear during the flow." + - "Cleanup: The temporary Bot and Agent are deleted." +evidence_required: + - ui + - screenshot + - console + - api_diagnostic +diagnostics: + - "The case uses a local reverse WebSocket and deterministic runner, so it requires no platform or model credentials." + - "A passing API route status without the visible Delivered route card is not a pass." +troubleshooting: + - backend-not-listening + - plugin-runtime-timeout + - proxy-env-mismatch diff --git a/skills/skills/langbot-testing/suites/eba-release-gate.yaml b/skills/skills/langbot-testing/suites/eba-release-gate.yaml new file mode 100644 index 000000000..594258808 --- /dev/null +++ b/skills/skills/langbot-testing/suites/eba-release-gate.yaml @@ -0,0 +1,14 @@ +id: eba-release-gate +title: "Event-based bot product release gate" +description: "Browser and runtime gate for scenario onboarding, route diagnostics, and real platform-to-Agent execution." +type: release_gate +priority: p0 +tags: + - eba + - event-routing + - wizard + - release-gate +cases: + - wizard-scenario-routing + - bot-event-routing-product-flow + - wizard-onebot-agent-runtime diff --git a/web/src/app/wizard/page.tsx b/web/src/app/wizard/page.tsx index 6bc9ceb27..28eb2b2db 100644 --- a/web/src/app/wizard/page.tsx +++ b/web/src/app/wizard/page.tsx @@ -14,7 +14,6 @@ import { Loader2, MessageSquare, ShieldCheck, - UserCheck, UserMinus, UserPlus, X, @@ -34,7 +33,10 @@ import { Pipeline, WizardProgress, } from '@/app/infra/entities/api'; -import { IDynamicFormItemSchema } from '@/app/infra/entities/form/dynamic'; +import { + DynamicFormItemType, + IDynamicFormItemSchema, +} from '@/app/infra/entities/form/dynamic'; import { PipelineConfigTab, PipelineConfigStage, @@ -84,9 +86,14 @@ type WizardScenarioId = | 'message_reply' | 'welcome_members' | 'handle_departures' - | 'review_friend_requests' | 'handle_moderation'; +const WIZARD_SCENARIO_PROMPT_KEYS: Partial> = { + welcome_members: 'wizard.scenario.welcomeMembersPrompt', + handle_departures: 'wizard.scenario.handleDeparturesPrompt', + handle_moderation: 'wizard.scenario.handleModerationPrompt', +}; + const WIZARD_SCENARIOS = [ { id: 'message_reply' as const, @@ -115,15 +122,6 @@ const WIZARD_SCENARIOS = [ icon: UserMinus, emoji: '👤', }, - { - id: 'review_friend_requests' as const, - eventType: 'friend.request_received', - processorKind: 'agent' as const, - labelKey: 'wizard.scenario.reviewFriendRequests', - descriptionKey: 'wizard.scenario.reviewFriendRequestsDescription', - icon: UserCheck, - emoji: '✅', - }, { id: 'handle_moderation' as const, eventType: 'group.member_banned', @@ -363,10 +361,20 @@ export default function WizardPage() { (runner: string) => { setSelectedRunner(runner); const configStage = aiConfigTab?.stages.find((s) => s.name === runner); - setRunnerConfig(configStage ? getDefaultValues(configStage.config) : {}); + const defaults = configStage ? getDefaultValues(configStage.config) : {}; + const promptKey = selectedScenario + ? WIZARD_SCENARIO_PROMPT_KEYS[selectedScenario] + : undefined; + const supportsPromptEditor = configStage?.config.some( + (item) => item.type === DynamicFormItemType.PROMPT_EDITOR, + ); + if (promptKey && supportsPromptEditor) { + defaults.prompt = [{ role: 'system', content: t(promptKey) }]; + } + setRunnerConfig(defaults); saveProgress({ step: 2, selected_runner: runner }); }, - [aiConfigTab, saveProgress], + [aiConfigTab, saveProgress, selectedScenario, t], ); // ---- Navigation helpers ---- @@ -981,7 +989,7 @@ function StepPlatform({ {t('wizard.scenario.description')}

-
+
{WIZARD_SCENARIOS.map((scenario) => { const Icon = scenario.icon; const isSelected = selectedScenario === scenario.id; diff --git a/web/src/i18n/locales/en-US.ts b/web/src/i18n/locales/en-US.ts index adb5be595..eb1653418 100644 --- a/web/src/i18n/locales/en-US.ts +++ b/web/src/i18n/locales/en-US.ts @@ -1936,15 +1936,18 @@ const enUS = { 'Answer incoming private or group messages with an AI Pipeline.', welcomeMembers: 'Welcome new members', welcomeMembersDescription: 'Run an Agent when someone joins a group.', + welcomeMembersPrompt: + 'Welcome new group members with a short, friendly message. Use the available member and group context when present. Do not mention internal event names or system details.', handleDepartures: 'Handle member departures', handleDeparturesDescription: 'Run an Agent when someone leaves or is removed.', - reviewFriendRequests: 'Review friend requests', - reviewFriendRequestsDescription: - 'Let an Agent decide how to handle new friend requests.', + handleDeparturesPrompt: + 'Respond to group member departures with a brief, respectful message when a public response is appropriate. Do not speculate about why the member left or mention internal event names.', handleModeration: 'Handle moderation events', handleModerationDescription: 'Run an Agent when a group member is restricted.', + handleModerationPrompt: + 'Write a concise, neutral group notice about the member restriction using only the available context. Do not invent details or mention internal event names.', pipelineBadge: 'Pipeline', agentBadge: 'Agent', }, diff --git a/web/src/i18n/locales/ja-JP.ts b/web/src/i18n/locales/ja-JP.ts index 1d0721046..5ca5c6d6f 100644 --- a/web/src/i18n/locales/ja-JP.ts +++ b/web/src/i18n/locales/ja-JP.ts @@ -1858,15 +1858,18 @@ const jaJP = { welcomeMembers: '新しいメンバーを歓迎', welcomeMembersDescription: 'メンバーがグループに参加したときに Agent を実行します。', + welcomeMembersPrompt: + '新しいグループメンバーを短く親しみやすいメッセージで歓迎してください。利用可能なメンバーとグループの情報を活用し、内部イベント名やシステムの詳細には言及しないでください。', handleDepartures: 'メンバーの退出を処理', handleDeparturesDescription: 'メンバーが退出または削除されたときに Agent を実行します。', - reviewFriendRequests: '友だち申請を確認', - reviewFriendRequestsDescription: - '新しい申請の処理方法を Agent に判断させます。', + handleDeparturesPrompt: + '公開の応答が適切な場合に、メンバーの退出へ短く敬意のあるメッセージで対応してください。退出理由を推測したり、内部イベント名に言及したりしないでください。', handleModeration: 'モデレーションイベントを処理', handleModerationDescription: 'グループメンバーが制限されたときに Agent を実行します。', + handleModerationPrompt: + '利用可能な情報だけを使い、メンバーへの制限について簡潔で中立的なグループ通知を書いてください。詳細を作り上げたり、内部イベント名に言及したりしないでください。', pipelineBadge: 'Pipeline', agentBadge: 'Agent', }, diff --git a/web/src/i18n/locales/zh-Hans.ts b/web/src/i18n/locales/zh-Hans.ts index 960963817..aca81292b 100644 --- a/web/src/i18n/locales/zh-Hans.ts +++ b/web/src/i18n/locales/zh-Hans.ts @@ -1855,12 +1855,16 @@ const zhHans = { messageReplyDescription: '使用 AI Pipeline 回复私聊或群聊消息。', welcomeMembers: '欢迎新成员', welcomeMembersDescription: '有人加入群组时运行 Agent。', + welcomeMembersPrompt: + '用简短、友好的消息欢迎新群成员。有成员和群组上下文时请合理使用,不要提及内部事件名称或系统细节。', handleDepartures: '处理成员离群', handleDeparturesDescription: '有人离开或被移出群组时运行 Agent。', - reviewFriendRequests: '审核好友请求', - reviewFriendRequestsDescription: '让 Agent 决定如何处理新的好友请求。', + handleDeparturesPrompt: + '当适合公开回应时,用简短、尊重的消息处理群成员离开。不要猜测成员离开的原因,也不要提及内部事件名称。', handleModeration: '处理群管理事件', handleModerationDescription: '群成员受到限制时运行 Agent。', + handleModerationPrompt: + '仅根据现有上下文,写一条简洁、中立的群管理通知,说明成员受到的限制。不要编造细节或提及内部事件名称。', pipelineBadge: 'Pipeline', agentBadge: 'Agent', },