From fb28b72b271759b6f26ca6ed8619e7fde44d6b1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=83=8A=E3=83=A0?= Date: Thu, 9 Jul 2026 17:24:58 +0800 Subject: [PATCH] fix(config): remove ignored session scoping controls (#1405) Co-authored-by: Collective Developer --- ui/web/src/i18n/locales/en/config.json | 6 -- ui/web/src/i18n/locales/ko/config.json | 6 -- ui/web/src/i18n/locales/vi/config.json | 6 -- ui/web/src/i18n/locales/zh/config.json | 6 -- .../config/sections/behavior-section.test.ts | 76 +++++++++++++++++++ .../config/sections/behavior-section.tsx | 69 ++++++++++------- .../sections/behavior-sessions-card.tsx | 72 ------------------ 7 files changed, 116 insertions(+), 125 deletions(-) create mode 100644 ui/web/src/pages/config/sections/behavior-section.test.ts delete mode 100644 ui/web/src/pages/config/sections/behavior-sessions-card.tsx diff --git a/ui/web/src/i18n/locales/en/config.json b/ui/web/src/i18n/locales/en/config.json index 82da55f7..31688ba8 100644 --- a/ui/web/src/i18n/locales/en/config.json +++ b/ui/web/src/i18n/locales/en/config.json @@ -205,8 +205,6 @@ "behavior.pendingCompactionInfo": "When a group chat exceeds the threshold, the LLM automatically summarizes older messages into a single compact entry, keeping the most recent ones intact for context.", "behavior.rateLimitTitle": "Rate Limiting", "behavior.rateLimitDescription": "Inbound message size limits and per-user request throttling", - "behavior.sessionsTitle": "Session Scoping", - "behavior.sessionsDescription": "Controls how conversation context is partitioned across users and channels", "behavior.securityTitle": "Input Security", "behavior.securityDescription": "Prompt injection detection and credential scrubbing to protect the gateway", "behavior.injectionActionHint": "What to do when a prompt injection attempt is detected in an inbound message.", @@ -215,10 +213,6 @@ "behavior.scrubCredentialsHint": "Automatically redact API keys, tokens, and passwords found in tool output before returning to the agent.", "behavior.scrubCredentialsInfo": "Credential patterns in tool responses are masked before the agent sees them.", "behavior.scrubCredentialsOffInfo": "Credential scrubbing is disabled. Secrets in tool output may be exposed to the agent.", - "sessions.scope": "Session Scope", - "sessions.scopeTip": "Determines how sessions are partitioned. 'Per Sender' gives each user a separate session; 'Global' shares one session across all users.", - "sessions.dmScope": "DM Scope", - "sessions.dmScopeTip": "How direct-message sessions are keyed. 'Per Channel Peer' is the recommended default.", "quota.title": "Usage Quota", "quota.description": "Per-user message quota limits with optional per-provider, channel, and group overrides", "quota.enabled": "Enable Quotas", diff --git a/ui/web/src/i18n/locales/ko/config.json b/ui/web/src/i18n/locales/ko/config.json index 417dd03b..ec7e2a15 100644 --- a/ui/web/src/i18n/locales/ko/config.json +++ b/ui/web/src/i18n/locales/ko/config.json @@ -171,8 +171,6 @@ "behavior.pendingCompactionInfo": "그룹 채팅이 임계값을 초과하면 LLM이 오래된 메시지를 단일 압축 항목으로 자동 요약하여 컨텍스트를 위해 가장 최근 메시지를 유지합니다.", "behavior.rateLimitTitle": "속도 제한", "behavior.rateLimitDescription": "인바운드 메시지 크기 제한 및 사용자당 요청 쓰로틀링", - "behavior.sessionsTitle": "세션 범위", - "behavior.sessionsDescription": "사용자와 채널 간에 대화 컨텍스트가 분할되는 방식을 제어합니다", "behavior.securityTitle": "입력 보안", "behavior.securityDescription": "게이트웨이를 보호하기 위한 프롬프트 주입 감지 및 자격증명 스크러빙", "behavior.injectionActionHint": "인바운드 메시지에서 프롬프트 주입 시도가 감지될 때 수행할 작업입니다.", @@ -181,10 +179,6 @@ "behavior.scrubCredentialsHint": "에이전트에 반환하기 전에 도구 출력에서 발견된 API 키, 토큰 및 비밀번호를 자동으로 수정합니다.", "behavior.scrubCredentialsInfo": "도구 응답의 자격증명 패턴은 에이전트가 보기 전에 마스킹됩니다.", "behavior.scrubCredentialsOffInfo": "자격증명 스크러빙이 비활성화되어 있습니다. 도구 출력의 시크릿이 에이전트에 노출될 수 있습니다.", - "sessions.scope": "세션 범위", - "sessions.scopeTip": "세션 분할 방법을 결정합니다. '발신자별'은 각 사용자에게 별도 세션을 제공하고, '전역'은 모든 사용자가 하나의 세션을 공유합니다.", - "sessions.dmScope": "DM 범위", - "sessions.dmScopeTip": "DM 세션이 키로 구분되는 방법입니다. '채널 피어별'이 권장 기본값입니다.", "quota.title": "사용 할당량", "quota.description": "선택적 공급자, 채널 및 그룹별 재정의가 있는 사용자별 메시지 할당량 제한", "quota.enabled": "할당량 활성화", diff --git a/ui/web/src/i18n/locales/vi/config.json b/ui/web/src/i18n/locales/vi/config.json index 17865cd7..6dfaaddb 100644 --- a/ui/web/src/i18n/locales/vi/config.json +++ b/ui/web/src/i18n/locales/vi/config.json @@ -204,8 +204,6 @@ "behavior.pendingCompactionInfo": "Khi nhóm chat vượt ngưỡng, LLM tự động tóm tắt các tin nhắn cũ thành một mục rút gọn, giữ nguyên các tin gần đây nhất làm ngữ cảnh.", "behavior.rateLimitTitle": "Giới hạn tốc độ", "behavior.rateLimitDescription": "Giới hạn kích thước tin nhắn đến và kiểm soát tốc độ yêu cầu mỗi người dùng", - "behavior.sessionsTitle": "Phạm vi phiên", - "behavior.sessionsDescription": "Kiểm soát cách phân vùng ngữ cảnh cuộc trò chuyện theo người dùng và kênh", "behavior.securityTitle": "Bảo mật đầu vào", "behavior.securityDescription": "Phát hiện injection prompt và ẩn thông tin xác thực để bảo vệ gateway", "behavior.injectionActionHint": "Hành động thực hiện khi phát hiện nỗ lực injection prompt trong tin nhắn đến.", @@ -214,10 +212,6 @@ "behavior.scrubCredentialsHint": "Tự động ẩn khóa API, token và mật khẩu tìm thấy trong đầu ra công cụ trước khi trả về agent.", "behavior.scrubCredentialsInfo": "Các mẫu thông tin xác thực trong phản hồi công cụ được che đi trước khi agent nhìn thấy.", "behavior.scrubCredentialsOffInfo": "Tính năng ẩn thông tin xác thực bị tắt. Bí mật trong đầu ra công cụ có thể bị lộ với agent.", - "sessions.scope": "Phạm vi phiên", - "sessions.scopeTip": "Xác định cách phân vùng phiên. 'Theo người gửi' cho mỗi người dùng một phiên riêng; 'Toàn cục' chia sẻ một phiên cho tất cả người dùng.", - "sessions.dmScope": "Phạm vi tin nhắn riêng", - "sessions.dmScopeTip": "Cách đặt khóa phiên tin nhắn riêng. 'Theo kênh và đối tác' là mặc định được khuyến nghị.", "quota.title": "Hạn mức sử dụng", "quota.description": "Giới hạn hạn mức tin nhắn mỗi người dùng với tùy chọn ghi đè theo provider, kênh và nhóm", "quota.enabled": "Bật hạn mức", diff --git a/ui/web/src/i18n/locales/zh/config.json b/ui/web/src/i18n/locales/zh/config.json index 9a80bb66..6c347959 100644 --- a/ui/web/src/i18n/locales/zh/config.json +++ b/ui/web/src/i18n/locales/zh/config.json @@ -204,8 +204,6 @@ "behavior.pendingCompactionInfo": "当群聊超过阈值时,LLM 自动将旧消息总结为一条精简条目,保留最近的消息作为上下文。", "behavior.rateLimitTitle": "速率限制", "behavior.rateLimitDescription": "入站消息大小限制和每用户请求限流", - "behavior.sessionsTitle": "会话范围", - "behavior.sessionsDescription": "控制跨用户和频道的对话上下文分区方式", "behavior.securityTitle": "输入安全", "behavior.securityDescription": "提示词注入检测和凭证擦除,以保护网关", "behavior.injectionActionHint": "当入站消息中检测到提示词注入尝试时的处理动作。", @@ -214,10 +212,6 @@ "behavior.scrubCredentialsHint": "在返回给 Agent 之前,自动从工具输出中删除 API 密钥、令牌和密码。", "behavior.scrubCredentialsInfo": "工具响应中的凭证模式在 Agent 看到之前被屏蔽。", "behavior.scrubCredentialsOffInfo": "凭证擦除已禁用,工具输出中的密钥可能暴露给 Agent。", - "sessions.scope": "会话范围", - "sessions.scopeTip": "确定会话分区方式。「按发送者」为每个用户提供单独的会话;「全局」在所有用户之间共享一个会话。", - "sessions.dmScope": "私信范围", - "sessions.dmScopeTip": "私信会话的键控方式。推荐默认使用「按频道对等方」。", "quota.title": "使用配额", "quota.description": "每用户消息配额限制,可选按提供商、频道和群组覆盖", "quota.enabled": "启用配额", diff --git a/ui/web/src/pages/config/sections/behavior-section.test.ts b/ui/web/src/pages/config/sections/behavior-section.test.ts new file mode 100644 index 00000000..fe7e3438 --- /dev/null +++ b/ui/web/src/pages/config/sections/behavior-section.test.ts @@ -0,0 +1,76 @@ +import { describe, expect, it } from "vitest"; +import { buildBehaviorPatch } from "./behavior-section"; + +describe("buildBehaviorPatch", () => { + it("does not include deprecated session scoping config", () => { + const patch = buildBehaviorPatch({ + rate: { + max_message_chars: 12000, + rate_limit_rpm: 30, + inbound_debounce_ms: 250, + }, + security: { + injection_action: "warn", + scrub_credentials: true, + }, + ux: { + intent_classify: true, + team_work_classify: false, + }, + pendingCompaction: { + threshold: 200, + keep_recent: 40, + }, + chatBehavior: { + enabled: true, + quick_ack: { + enabled: true, + mode: "sidecar_generated", + min_delay_ms: 1000, + provider: "", + model: "", + timeout_ms: 2500, + max_tokens: 40, + max_chars: 120, + templates: ["Got it. Working on it..."], + }, + intermediate_replies: { + enabled: false, + mode: "sidecar_generated", + provider: "", + model: "", + timeout_ms: 2500, + max_tokens: 60, + max_chars: 180, + }, + final_split: { + enabled: false, + min_chars: 1200, + max_messages: 3, + delay_ms: 500, + }, + }, + }); + + expect(Object.prototype.hasOwnProperty.call(patch, "sessions")).toBe(false); + expect(patch).toMatchObject({ + gateway: { + max_message_chars: 12000, + rate_limit_rpm: 30, + inbound_debounce_ms: 250, + injection_action: "warn", + team_work_classify: false, + }, + agents: { + defaults: { intent_classify: true }, + }, + tools: { scrub_credentials: true }, + channels: { + pending_compaction: { + threshold: 200, + keep_recent: 40, + }, + }, + }); + }); +}); diff --git a/ui/web/src/pages/config/sections/behavior-section.tsx b/ui/web/src/pages/config/sections/behavior-section.tsx index 3f9c8017..5c581388 100644 --- a/ui/web/src/pages/config/sections/behavior-section.tsx +++ b/ui/web/src/pages/config/sections/behavior-section.tsx @@ -4,27 +4,23 @@ import { useTranslation } from "react-i18next"; import { Button } from "@/components/ui/button"; import { BehaviorUxCard } from "./behavior-ux-card"; import { BehaviorRateCard } from "./behavior-rate-card"; -import { BehaviorSessionsCard } from "./behavior-sessions-card"; import { BehaviorSecurityCard } from "./behavior-security-card"; import { BehaviorPendingCompactionCard, type PendingCompactionValues } from "./behavior-pending-compaction-card"; import { BehaviorChatCard, type ChatBehaviorValues } from "./behavior-chat-card"; import { BehaviorPassiveMemoryCard } from "./behavior-passive-memory-card"; - - interface Props { config: Record; onPatch: (updates: Record) => Promise; saving: boolean; } -/** State container for Behavior tab — composes 4 sub-cards, patches multiple config keys. */ +/** State container for Behavior tab cards and their patch payload. */ export function BehaviorSection({ config, onPatch, saving }: Props) { const { t } = useTranslation("config"); const gw = config.gateway ?? {}; const ag = config.agents?.defaults ?? {}; const tl = config.tools ?? {}; - const ss = config.sessions ?? {}; const ch = config.channels ?? {}; // UX toggles (from gateway + agents.defaults) @@ -40,12 +36,6 @@ export function BehaviorSection({ config, onPatch, saving }: Props) { inbound_debounce_ms: gw.inbound_debounce_ms, }); - // Sessions - const [sessions, setSessions] = useState<{ scope?: string; dm_scope?: string }>({ - scope: ss.scope, - dm_scope: ss.dm_scope, - }); - // Security (from gateway + tools) const [security, setSecurity] = useState<{ injection_action?: string; scrub_credentials?: boolean }>({ injection_action: gw.injection_action, @@ -71,7 +61,6 @@ export function BehaviorSection({ config, onPatch, saving }: Props) { rate_limit_rpm: gw.rate_limit_rpm, inbound_debounce_ms: gw.inbound_debounce_ms, }); - setSessions({ scope: ss.scope, dm_scope: ss.dm_scope }); setSecurity({ injection_action: gw.injection_action, scrub_credentials: tl.scrub_credentials, @@ -85,22 +74,13 @@ export function BehaviorSection({ config, onPatch, saving }: Props) { (v: T) => { setter(v); setDirty(true); }; const handleSave = () => { - onPatch({ - gateway: { - max_message_chars: rate.max_message_chars, - rate_limit_rpm: rate.rate_limit_rpm, - inbound_debounce_ms: rate.inbound_debounce_ms, - injection_action: security.injection_action, - chat_behavior: chatBehavior, - team_work_classify: ux.team_work_classify, - }, - agents: { - defaults: { intent_classify: ux.intent_classify }, - }, - tools: { scrub_credentials: security.scrub_credentials }, - sessions, - channels: { pending_compaction: pendingCompaction }, - }).catch(() => {}); + onPatch(buildBehaviorPatch({ + rate, + security, + chatBehavior, + ux, + pendingCompaction, + })).catch(() => {}); }; return ( @@ -108,7 +88,6 @@ export function BehaviorSection({ config, onPatch, saving }: Props) { - @@ -124,6 +103,38 @@ export function BehaviorSection({ config, onPatch, saving }: Props) { ); } +export interface BehaviorPatchInput { + rate: { max_message_chars?: number; rate_limit_rpm?: number; inbound_debounce_ms?: number }; + security: { injection_action?: string; scrub_credentials?: boolean }; + chatBehavior: ChatBehaviorValues; + ux: { intent_classify: boolean; team_work_classify: boolean }; + pendingCompaction: PendingCompactionValues; +} + +export function buildBehaviorPatch({ + rate, + security, + chatBehavior, + ux, + pendingCompaction, +}: BehaviorPatchInput): Record { + return { + gateway: { + max_message_chars: rate.max_message_chars, + rate_limit_rpm: rate.rate_limit_rpm, + inbound_debounce_ms: rate.inbound_debounce_ms, + injection_action: security.injection_action, + chat_behavior: chatBehavior, + team_work_classify: ux.team_work_classify, + }, + agents: { + defaults: { intent_classify: ux.intent_classify }, + }, + tools: { scrub_credentials: security.scrub_credentials }, + channels: { pending_compaction: pendingCompaction }, + }; +} + function normalizeChatBehavior(value: any): ChatBehaviorValues { return { enabled: value?.enabled ?? false, diff --git a/ui/web/src/pages/config/sections/behavior-sessions-card.tsx b/ui/web/src/pages/config/sections/behavior-sessions-card.tsx deleted file mode 100644 index 54187911..00000000 --- a/ui/web/src/pages/config/sections/behavior-sessions-card.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import { useTranslation } from "react-i18next"; -import { - Card, - CardContent, - CardDescription, - CardHeader, - CardTitle, -} from "@/components/ui/card"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select"; -import { InfoLabel } from "@/components/shared/info-label"; - -interface SessionsValues { - scope?: string; - dm_scope?: string; -} - -interface Props { - value: SessionsValues; - onChange: (v: SessionsValues) => void; -} - -/** Session scoping settings (scope + DM scope). */ -export function BehaviorSessionsCard({ value, onChange }: Props) { - const { t } = useTranslation("config"); - - return ( - - - {t("behavior.sessionsTitle")} - {t("behavior.sessionsDescription")} - - -
-
- {t("sessions.scope")} - -
-
- {t("sessions.dmScope")} - -
-
-
-
- ); -}