mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-09 00:40:45 +08:00
Remove dead confirm-strategy setting from chat UI.
Plan-mode confirm gate is gone; drop the settings control and coerce API values to strict.
This commit is contained in:
parent
c901655848
commit
4277ad6b14
4 changed files with 14 additions and 68 deletions
|
|
@ -738,9 +738,9 @@ def _normalize_execution_mode(payload: dict[str, Any] | None) -> str:
|
||||||
|
|
||||||
|
|
||||||
def _normalize_confirm_strategy(payload: dict[str, Any] | None) -> str:
|
def _normalize_confirm_strategy(payload: dict[str, Any] | None) -> str:
|
||||||
body = payload or {}
|
"""Plan-mode confirm gate removed; keep a fixed wire default for older clients."""
|
||||||
raw = str(body.get("confirm_strategy") or "").strip().lower()
|
del payload
|
||||||
return raw if raw in {"auto", "strict", "off"} else "strict"
|
return "strict"
|
||||||
|
|
||||||
|
|
||||||
def _normalize_plan_agent_version(payload: dict[str, Any] | None) -> str:
|
def _normalize_plan_agent_version(payload: dict[str, Any] | None) -> str:
|
||||||
|
|
@ -755,7 +755,7 @@ def _resolve_user_menu_chat_settings(
|
||||||
tenant_id: str,
|
tenant_id: str,
|
||||||
user_id: str,
|
user_id: str,
|
||||||
) -> tuple[str, str, str, str]:
|
) -> tuple[str, str, str, str]:
|
||||||
"""User-wide settings (⋯ menu): mode + confirm — all sessions share these keys."""
|
"""User-wide settings (⋯ menu): mode + specialist — all sessions share these keys."""
|
||||||
user_mode_key = _chat_user_mode_setting_key(tenant_id=tenant_id, user_id=user_id, field="interaction_mode")
|
user_mode_key = _chat_user_mode_setting_key(tenant_id=tenant_id, user_id=user_id, field="interaction_mode")
|
||||||
user_specialist_key = _chat_user_mode_setting_key(tenant_id=tenant_id, user_id=user_id, field="specialist")
|
user_specialist_key = _chat_user_mode_setting_key(tenant_id=tenant_id, user_id=user_id, field="specialist")
|
||||||
user_confirm_strategy_key = _chat_user_mode_setting_key(tenant_id=tenant_id, user_id=user_id, field="confirm_strategy")
|
user_confirm_strategy_key = _chat_user_mode_setting_key(tenant_id=tenant_id, user_id=user_id, field="confirm_strategy")
|
||||||
|
|
@ -850,7 +850,7 @@ def _resolve_mode_settings(
|
||||||
user_id: str,
|
user_id: str,
|
||||||
session_id: str,
|
session_id: str,
|
||||||
) -> tuple[str, str, str, str, str, str]:
|
) -> tuple[str, str, str, str, str, str]:
|
||||||
"""User-wide mode/specialist + session memory/exec + user confirm/plan_agent for gateway + REST send."""
|
"""User-wide mode/specialist + session memory/exec (+ legacy confirm/plan_agent defaults) for gateway + REST send."""
|
||||||
u_im, u_sp, u_cs, u_pav = _resolve_user_menu_chat_settings(store=store, tenant_id=tenant_id, user_id=user_id)
|
u_im, u_sp, u_cs, u_pav = _resolve_user_menu_chat_settings(store=store, tenant_id=tenant_id, user_id=user_id)
|
||||||
s_mm, s_em = _resolve_session_dialog_chat_settings(
|
s_mm, s_em = _resolve_session_dialog_chat_settings(
|
||||||
store=store, tenant_id=tenant_id, user_id=user_id, session_id=str(session_id)
|
store=store, tenant_id=tenant_id, user_id=user_id, session_id=str(session_id)
|
||||||
|
|
|
||||||
|
|
@ -107,10 +107,6 @@ const I18N = {
|
||||||
"chat.execModeAgent": "Agent",
|
"chat.execModeAgent": "Agent",
|
||||||
"chat.execModePlan": "Plan",
|
"chat.execModePlan": "Plan",
|
||||||
"chat.execModeApplied": "执行态:{mode}",
|
"chat.execModeApplied": "执行态:{mode}",
|
||||||
"chat.confirmStrategyLabel": "确认策略",
|
|
||||||
"chat.confirmStrategyStrict": "Strict(需切换 Agent)",
|
|
||||||
"chat.confirmStrategyAuto": "Auto(自动确认执行)",
|
|
||||||
"chat.confirmStrategyOff": "Off(不拦截确认)",
|
|
||||||
"chat.specialistGeneralist": "通用",
|
"chat.specialistGeneralist": "通用",
|
||||||
"chat.specialistOps": "运维",
|
"chat.specialistOps": "运维",
|
||||||
"chat.specialistImage": "图像",
|
"chat.specialistImage": "图像",
|
||||||
|
|
@ -301,10 +297,6 @@ const I18N = {
|
||||||
"chat.execModeAgent": "Agent",
|
"chat.execModeAgent": "Agent",
|
||||||
"chat.execModePlan": "Plan",
|
"chat.execModePlan": "Plan",
|
||||||
"chat.execModeApplied": "Execution: {mode}",
|
"chat.execModeApplied": "Execution: {mode}",
|
||||||
"chat.confirmStrategyLabel": "Confirm Strategy",
|
|
||||||
"chat.confirmStrategyStrict": "Strict (switch to Agent first)",
|
|
||||||
"chat.confirmStrategyAuto": "Auto (confirm executes directly)",
|
|
||||||
"chat.confirmStrategyOff": "Off (no confirm-mode gate)",
|
|
||||||
"chat.specialistGeneralist": "Generalist",
|
"chat.specialistGeneralist": "Generalist",
|
||||||
"chat.specialistOps": "Ops",
|
"chat.specialistOps": "Ops",
|
||||||
"chat.specialistImage": "Image",
|
"chat.specialistImage": "Image",
|
||||||
|
|
@ -406,14 +398,11 @@ const CHAT_SPECIALIST_PREF_KEY = "ops_chat_specialist_pref";
|
||||||
const CHAT_INTERACTION_MODE_KEY = "ops_chat_interaction_mode";
|
const CHAT_INTERACTION_MODE_KEY = "ops_chat_interaction_mode";
|
||||||
const CHAT_MEMORY_MODE_KEY = "ops_chat_memory_mode";
|
const CHAT_MEMORY_MODE_KEY = "ops_chat_memory_mode";
|
||||||
const CHAT_EXECUTION_MODE_KEY = "ops_chat_execution_mode";
|
const CHAT_EXECUTION_MODE_KEY = "ops_chat_execution_mode";
|
||||||
const CHAT_CONFIRM_STRATEGY_KEY = "ops_chat_confirm_strategy";
|
|
||||||
const CHAT_USER_MENU_MODE_KEY = "ops_chat_user_menu_mode";
|
const CHAT_USER_MENU_MODE_KEY = "ops_chat_user_menu_mode";
|
||||||
const CHAT_REASONING_TOGGLE_KEY = "ops_chat_reasoning_toggle";
|
const CHAT_REASONING_TOGGLE_KEY = "ops_chat_reasoning_toggle";
|
||||||
const EXECUTION_MODE_AGENT = "agent";
|
const EXECUTION_MODE_AGENT = "agent";
|
||||||
const EXECUTION_MODE_PLAN = "plan";
|
const EXECUTION_MODE_PLAN = "plan";
|
||||||
const CONFIRM_STRATEGY_STRICT = "strict";
|
const CONFIRM_STRATEGY_STRICT = "strict";
|
||||||
const CONFIRM_STRATEGY_AUTO = "auto";
|
|
||||||
const CONFIRM_STRATEGY_OFF = "off";
|
|
||||||
const PLAN_AGENT_V1 = "v1";
|
const PLAN_AGENT_V1 = "v1";
|
||||||
/** Default on: reasoning/tool fold matches streamed behavior; new browsers have no localStorage yet. */
|
/** Default on: reasoning/tool fold matches streamed behavior; new browsers have no localStorage yet. */
|
||||||
const ADMIN_CHAT_SHOW_TOOL_OUTPUT_DEFAULT = true;
|
const ADMIN_CHAT_SHOW_TOOL_OUTPUT_DEFAULT = true;
|
||||||
|
|
@ -3064,27 +3053,6 @@ function syncAuthUserLabel() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
items.push(modeSel);
|
items.push(modeSel);
|
||||||
items.push(el("div", { class: "muted", style: "padding:2px 10px 2px;font-size:12px;", text: t("chat.confirmStrategyLabel") }));
|
|
||||||
const csSel = el("select", { class: "input", style: "width:100%;margin:4px 8px 8px;max-width:calc(100% - 16px);" });
|
|
||||||
try {
|
|
||||||
const rows = Array.isArray(bridge.getConfirmStrategyOptions && bridge.getConfirmStrategyOptions())
|
|
||||||
? bridge.getConfirmStrategyOptions()
|
|
||||||
: [];
|
|
||||||
rows.forEach((r) => csSel.appendChild(el("option", { value: String(r.value || ""), text: String(r.label || r.value || "") })));
|
|
||||||
csSel.value = String((bridge.getConfirmStrategyValue && bridge.getConfirmStrategyValue()) || "");
|
|
||||||
} catch (_) {}
|
|
||||||
csSel.addEventListener("change", async () => {
|
|
||||||
const v = csSel.value;
|
|
||||||
try {
|
|
||||||
csSel.disabled = true;
|
|
||||||
if (bridge.setConfirmStrategyValue) await bridge.setConfirmStrategyValue(v);
|
|
||||||
} catch (_) {
|
|
||||||
// errors are surfaced by saveUserGlobalModePreference()
|
|
||||||
} finally {
|
|
||||||
csSel.disabled = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
items.push(csSel);
|
|
||||||
const reasonWrap = el("label", { class: "switch-wrap", style: "margin:2px 8px 8px;" }, [
|
const reasonWrap = el("label", { class: "switch-wrap", style: "margin:2px 8px 8px;" }, [
|
||||||
el("input", { type: "checkbox", class: "switch-input" }),
|
el("input", { type: "checkbox", class: "switch-input" }),
|
||||||
el("span", { class: "switch-slider" }),
|
el("span", { class: "switch-slider" }),
|
||||||
|
|
@ -3351,16 +3319,9 @@ async function renderChatUi() {
|
||||||
const raw = String(v || "").trim().toLowerCase();
|
const raw = String(v || "").trim().toLowerCase();
|
||||||
return raw === EXECUTION_MODE_PLAN ? EXECUTION_MODE_PLAN : EXECUTION_MODE_AGENT;
|
return raw === EXECUTION_MODE_PLAN ? EXECUTION_MODE_PLAN : EXECUTION_MODE_AGENT;
|
||||||
};
|
};
|
||||||
const normalizeConfirmStrategy = (v) => {
|
|
||||||
const raw = String(v || "").trim().toLowerCase();
|
|
||||||
if (raw === CONFIRM_STRATEGY_AUTO) return CONFIRM_STRATEGY_AUTO;
|
|
||||||
if (raw === CONFIRM_STRATEGY_OFF) return CONFIRM_STRATEGY_OFF;
|
|
||||||
return CONFIRM_STRATEGY_STRICT;
|
|
||||||
};
|
|
||||||
const executionModeLabel = (v) =>
|
const executionModeLabel = (v) =>
|
||||||
normalizeExecutionMode(v) === EXECUTION_MODE_PLAN ? t("chat.execModePlan") : t("chat.execModeAgent");
|
normalizeExecutionMode(v) === EXECUTION_MODE_PLAN ? t("chat.execModePlan") : t("chat.execModeAgent");
|
||||||
let currentExecutionMode = normalizeExecutionMode(localStorage.getItem(CHAT_EXECUTION_MODE_KEY) || EXECUTION_MODE_AGENT);
|
let currentExecutionMode = normalizeExecutionMode(localStorage.getItem(CHAT_EXECUTION_MODE_KEY) || EXECUTION_MODE_AGENT);
|
||||||
let currentConfirmStrategy = normalizeConfirmStrategy(localStorage.getItem(CHAT_CONFIRM_STRATEGY_KEY) || CONFIRM_STRATEGY_STRICT);
|
|
||||||
const execSelect = el("select", {
|
const execSelect = el("select", {
|
||||||
class: "input",
|
class: "input",
|
||||||
style: "min-width:96px;max-width:140px;padding:6px 8px;",
|
style: "min-width:96px;max-width:140px;padding:6px 8px;",
|
||||||
|
|
@ -3534,7 +3495,6 @@ async function renderChatUi() {
|
||||||
const s = String((resp && resp.specialist) || "").toLowerCase();
|
const s = String((resp && resp.specialist) || "").toLowerCase();
|
||||||
const mm = String((resp && resp.memory_mode) || "").toLowerCase();
|
const mm = String((resp && resp.memory_mode) || "").toLowerCase();
|
||||||
const em = String((resp && resp.execution_mode) || "").toLowerCase();
|
const em = String((resp && resp.execution_mode) || "").toLowerCase();
|
||||||
const cs = String((resp && resp.confirm_strategy) || "").toLowerCase();
|
|
||||||
const gm = resp && resp.global_menu && typeof resp.global_menu === "object" ? resp.global_menu : null;
|
const gm = resp && resp.global_menu && typeof resp.global_menu === "object" ? resp.global_menu : null;
|
||||||
if (gm) {
|
if (gm) {
|
||||||
const gIm = String(gm.interaction_mode || "").toLowerCase();
|
const gIm = String(gm.interaction_mode || "").toLowerCase();
|
||||||
|
|
@ -3548,8 +3508,6 @@ async function renderChatUi() {
|
||||||
if (ur && ur.ok) {
|
if (ur && ur.ok) {
|
||||||
const gum = String((ur.interaction_mode || "").toLowerCase());
|
const gum = String((ur.interaction_mode || "").toLowerCase());
|
||||||
const gus = String((ur.specialist || "").toLowerCase());
|
const gus = String((ur.specialist || "").toLowerCase());
|
||||||
currentConfirmStrategy = normalizeConfirmStrategy(ur.confirm_strategy || currentConfirmStrategy);
|
|
||||||
localStorage.setItem(CHAT_CONFIRM_STRATEGY_KEY, currentConfirmStrategy);
|
|
||||||
if (isSelectableSpecialist(gus)) globalMenuModeValue = gus;
|
if (isSelectableSpecialist(gus)) globalMenuModeValue = gus;
|
||||||
else if (isSelectableSpecialist(gum)) globalMenuModeValue = gum;
|
else if (isSelectableSpecialist(gum)) globalMenuModeValue = gum;
|
||||||
else globalMenuModeValue = MAIN_MODE_VALUE;
|
else globalMenuModeValue = MAIN_MODE_VALUE;
|
||||||
|
|
@ -3564,8 +3522,6 @@ async function renderChatUi() {
|
||||||
syncHiddenModeSelectFromGlobal();
|
syncHiddenModeSelectFromGlobal();
|
||||||
if (["default", "store_only"].includes(mm)) localStorage.setItem(CHAT_MEMORY_MODE_KEY, mm);
|
if (["default", "store_only"].includes(mm)) localStorage.setItem(CHAT_MEMORY_MODE_KEY, mm);
|
||||||
setExecutionMode(em, { persistLocal: true, saveSession: false });
|
setExecutionMode(em, { persistLocal: true, saveSession: false });
|
||||||
currentConfirmStrategy = normalizeConfirmStrategy(cs || currentConfirmStrategy);
|
|
||||||
localStorage.setItem(CHAT_CONFIRM_STRATEGY_KEY, currentConfirmStrategy);
|
|
||||||
persistModeSelection();
|
persistModeSelection();
|
||||||
const mml = String(localStorage.getItem(CHAT_MEMORY_MODE_KEY) || "default").toLowerCase();
|
const mml = String(localStorage.getItem(CHAT_MEMORY_MODE_KEY) || "default").toLowerCase();
|
||||||
localStorage.setItem(CHAT_MEMORY_MODE_KEY, mml === "store_only" ? "store_only" : "default");
|
localStorage.setItem(CHAT_MEMORY_MODE_KEY, mml === "store_only" ? "store_only" : "default");
|
||||||
|
|
@ -3583,7 +3539,7 @@ async function renderChatUi() {
|
||||||
const resp = await apiPost("/admin/api/chat/user-mode", {
|
const resp = await apiPost("/admin/api/chat/user-mode", {
|
||||||
interaction_mode: "expert",
|
interaction_mode: "expert",
|
||||||
specialist,
|
specialist,
|
||||||
confirm_strategy: String(currentConfirmStrategy || CONFIRM_STRATEGY_STRICT),
|
confirm_strategy: CONFIRM_STRATEGY_STRICT,
|
||||||
plan_agent_version: PLAN_AGENT_V1,
|
plan_agent_version: PLAN_AGENT_V1,
|
||||||
});
|
});
|
||||||
localStorage.setItem(CHAT_USER_MENU_MODE_KEY, specialist);
|
localStorage.setItem(CHAT_USER_MENU_MODE_KEY, specialist);
|
||||||
|
|
@ -3915,18 +3871,6 @@ async function renderChatUi() {
|
||||||
refreshExecUi();
|
refreshExecUi();
|
||||||
publishUserMenuPrefsBridge();
|
publishUserMenuPrefsBridge();
|
||||||
},
|
},
|
||||||
getConfirmStrategyOptions: () => [
|
|
||||||
{ value: CONFIRM_STRATEGY_STRICT, label: t("chat.confirmStrategyStrict") },
|
|
||||||
{ value: CONFIRM_STRATEGY_AUTO, label: t("chat.confirmStrategyAuto") },
|
|
||||||
{ value: CONFIRM_STRATEGY_OFF, label: t("chat.confirmStrategyOff") },
|
|
||||||
],
|
|
||||||
getConfirmStrategyValue: () => String(currentConfirmStrategy || CONFIRM_STRATEGY_STRICT),
|
|
||||||
setConfirmStrategyValue: async (v) => {
|
|
||||||
currentConfirmStrategy = normalizeConfirmStrategy(v);
|
|
||||||
localStorage.setItem(CHAT_CONFIRM_STRATEGY_KEY, currentConfirmStrategy);
|
|
||||||
await saveUserGlobalModePreference();
|
|
||||||
publishUserMenuPrefsBridge();
|
|
||||||
},
|
|
||||||
getReasoningVisible: () => !!showToolOutput,
|
getReasoningVisible: () => !!showToolOutput,
|
||||||
setReasoningVisible: async (v) => {
|
setReasoningVisible: async (v) => {
|
||||||
setReasoningVisible(!!v, { showStatus: true });
|
setReasoningVisible(!!v, { showStatus: true });
|
||||||
|
|
|
||||||
|
|
@ -242,7 +242,7 @@ class AdminChatStreamAsyncTaskTests(unittest.TestCase):
|
||||||
# Session POST ignores interaction/specialist; user menu still has generalist.
|
# Session POST ignores interaction/specialist; user menu still has generalist.
|
||||||
self.assertEqual(str(body1.get("specialist") or ""), "generalist")
|
self.assertEqual(str(body1.get("specialist") or ""), "generalist")
|
||||||
self.assertEqual(str(body1.get("memory_mode") or ""), "store_only")
|
self.assertEqual(str(body1.get("memory_mode") or ""), "store_only")
|
||||||
self.assertEqual(str(body1.get("confirm_strategy") or ""), "auto")
|
self.assertEqual(str(body1.get("confirm_strategy") or ""), "strict")
|
||||||
|
|
||||||
resp2 = self.client.get(
|
resp2 = self.client.get(
|
||||||
f"/admin/api/chat/sessions/{self.session_id}/mode",
|
f"/admin/api/chat/sessions/{self.session_id}/mode",
|
||||||
|
|
@ -254,7 +254,7 @@ class AdminChatStreamAsyncTaskTests(unittest.TestCase):
|
||||||
self.assertEqual(str(body2.get("interaction_mode") or ""), "expert")
|
self.assertEqual(str(body2.get("interaction_mode") or ""), "expert")
|
||||||
self.assertEqual(str(body2.get("specialist") or ""), "generalist")
|
self.assertEqual(str(body2.get("specialist") or ""), "generalist")
|
||||||
self.assertEqual(str(body2.get("memory_mode") or ""), "store_only")
|
self.assertEqual(str(body2.get("memory_mode") or ""), "store_only")
|
||||||
self.assertEqual(str(body2.get("confirm_strategy") or ""), "auto")
|
self.assertEqual(str(body2.get("confirm_strategy") or ""), "strict")
|
||||||
|
|
||||||
def test_messages_use_session_mode_when_payload_omits_mode(self) -> None:
|
def test_messages_use_session_mode_when_payload_omits_mode(self) -> None:
|
||||||
token = self._login()
|
token = self._login()
|
||||||
|
|
|
||||||
|
|
@ -44,8 +44,8 @@ def test_mode_session_and_user_prefs_merge_for_gateway() -> None:
|
||||||
assert specialist == "generalist"
|
assert specialist == "generalist"
|
||||||
assert memory_mode == "default"
|
assert memory_mode == "default"
|
||||||
assert execution_mode == "plan"
|
assert execution_mode == "plan"
|
||||||
assert confirm_strategy == "auto"
|
assert confirm_strategy == "strict"
|
||||||
assert plan_agent_version == "v2"
|
assert plan_agent_version == "v1"
|
||||||
|
|
||||||
|
|
||||||
def test_mode_session_defaults_when_session_keys_missing() -> None:
|
def test_mode_session_defaults_when_session_keys_missing() -> None:
|
||||||
|
|
@ -72,11 +72,13 @@ def test_invalid_execution_mode_on_session_dialog_falls_back_to_agent() -> None:
|
||||||
assert interaction_mode == "expert"
|
assert interaction_mode == "expert"
|
||||||
|
|
||||||
|
|
||||||
def test_invalid_confirm_strategy_on_user_menu_falls_back_to_strict() -> None:
|
def test_legacy_confirm_and_plan_agent_prefs_coerce_to_defaults() -> None:
|
||||||
store = _DummyStore()
|
store = _DummyStore()
|
||||||
store.set_setting("chat.user.mode.t1.u1.confirm_strategy", "invalid")
|
store.set_setting("chat.user.mode.t1.u1.confirm_strategy", "auto")
|
||||||
|
store.set_setting("chat.user.mode.t1.u1.plan_agent_version", "v2")
|
||||||
interaction_mode, specialist, memory_mode, execution_mode, confirm_strategy, plan_agent_version = (
|
interaction_mode, specialist, memory_mode, execution_mode, confirm_strategy, plan_agent_version = (
|
||||||
_resolve_mode_settings(store=store, tenant_id="t1", user_id="u1", session_id="s1")
|
_resolve_mode_settings(store=store, tenant_id="t1", user_id="u1", session_id="s1")
|
||||||
)
|
)
|
||||||
assert confirm_strategy == "strict"
|
assert confirm_strategy == "strict"
|
||||||
|
assert plan_agent_version == "v1"
|
||||||
assert specialist == "generalist"
|
assert specialist == "generalist"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue