mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 00:20:43 +00:00
fix(ui): read exec policy from tools config (#79119) thanks @BunsDev
Co-authored-by: Nova <nova@openclaw.local>
This commit is contained in:
@@ -6,6 +6,7 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
### Changes
|
||||
|
||||
- Control UI: read the Quick Settings exec policy badge from `tools.exec.security` instead of the non-schema `agents.defaults.exec.security` path, so configured `full`/`deny` values render accurately. Fixes #78311. Thanks @FriedBack.
|
||||
- Control UI/usage: add transcript-backed historical lineage rollups for rotated logical sessions, with current-instance vs historical-lineage scope controls and long-range presets so usage history stays visible after restarts and updates. Fixes #50701. Thanks @dev-gideon-llc and @BunsDev.
|
||||
- Agents/failover: harden state-aware lane suspension by persisting quota resume transitions, restoring configured lane concurrency, preserving non-quota failure reasons, and exporting model failover events through diagnostics OTLP. Thanks @BunsDev.
|
||||
- Channels/streaming: make progress draft labels scroll away with other progress lines, render structured tool rows as compact emoji/title/details, show web-search queries from provider-native argument shapes, and skip empty Discord apply-patch starts until a patch summary exists. (#79146)
|
||||
|
||||
62
ui/src/ui/app-render.exec-policy.test.ts
Normal file
62
ui/src/ui/app-render.exec-policy.test.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
// @vitest-environment jsdom
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { extractQuickSettingsSecurity } from "./app-render.ts";
|
||||
import type { AppViewState } from "./app-view-state.ts";
|
||||
|
||||
function makeState(config: Record<string, unknown>): AppViewState {
|
||||
return { configForm: config } as unknown as AppViewState;
|
||||
}
|
||||
|
||||
describe("extractQuickSettingsSecurity", () => {
|
||||
it("reads execPolicy from the canonical tools.exec.security path", () => {
|
||||
const result = extractQuickSettingsSecurity(
|
||||
makeState({ tools: { exec: { security: "full" } } }),
|
||||
);
|
||||
|
||||
expect(result.execPolicy).toBe("full");
|
||||
});
|
||||
|
||||
it("reads execPolicy from tools.exec.security when set to deny", () => {
|
||||
const result = extractQuickSettingsSecurity(
|
||||
makeState({ tools: { exec: { security: "deny" } } }),
|
||||
);
|
||||
|
||||
expect(result.execPolicy).toBe("deny");
|
||||
});
|
||||
|
||||
it("falls back to allowlist when tools.exec.security is missing", () => {
|
||||
expect(extractQuickSettingsSecurity(makeState({})).execPolicy).toBe("allowlist");
|
||||
expect(extractQuickSettingsSecurity(makeState({ tools: { exec: {} } })).execPolicy).toBe(
|
||||
"allowlist",
|
||||
);
|
||||
});
|
||||
|
||||
it("ignores agents.defaults.exec.security because it is not a schema path", () => {
|
||||
const result = extractQuickSettingsSecurity(
|
||||
makeState({
|
||||
tools: { exec: { security: "full" } },
|
||||
agents: { defaults: { exec: { security: "deny" } } },
|
||||
}),
|
||||
);
|
||||
|
||||
expect(result.execPolicy).toBe("full");
|
||||
});
|
||||
|
||||
it("does not treat agents.defaults.exec.security as a fallback", () => {
|
||||
const result = extractQuickSettingsSecurity(
|
||||
makeState({ agents: { defaults: { exec: { security: "full" } } } }),
|
||||
);
|
||||
|
||||
expect(result.execPolicy).toBe("allowlist");
|
||||
});
|
||||
|
||||
it("trims whitespace and ignores empty strings", () => {
|
||||
expect(
|
||||
extractQuickSettingsSecurity(makeState({ tools: { exec: { security: " full " } } }))
|
||||
.execPolicy,
|
||||
).toBe("full");
|
||||
expect(
|
||||
extractQuickSettingsSecurity(makeState({ tools: { exec: { security: " " } } })).execPolicy,
|
||||
).toBe("allowlist");
|
||||
});
|
||||
});
|
||||
@@ -545,7 +545,7 @@ function extractMcpServerCount(state: AppViewState): number {
|
||||
return Object.keys(servers).length;
|
||||
}
|
||||
|
||||
function extractQuickSettingsSecurity(state: AppViewState): {
|
||||
export function extractQuickSettingsSecurity(state: AppViewState): {
|
||||
gatewayAuth: string;
|
||||
execPolicy: string;
|
||||
deviceAuth: boolean;
|
||||
@@ -578,16 +578,16 @@ function extractQuickSettingsSecurity(state: AppViewState): {
|
||||
gatewayAuth = "none";
|
||||
}
|
||||
}
|
||||
const agents = cfg.agents;
|
||||
let execPolicy = "allowlist";
|
||||
if (agents && typeof agents === "object") {
|
||||
const defaults = (agents as Record<string, unknown>).defaults;
|
||||
if (defaults && typeof defaults === "object") {
|
||||
const exec = (defaults as Record<string, unknown>).exec;
|
||||
if (exec && typeof exec === "object") {
|
||||
const security = (exec as Record<string, unknown>).security;
|
||||
if (typeof security === "string") {
|
||||
execPolicy = security;
|
||||
const tools = cfg.tools;
|
||||
if (tools && typeof tools === "object") {
|
||||
const exec = (tools as Record<string, unknown>).exec;
|
||||
if (exec && typeof exec === "object") {
|
||||
const security = (exec as Record<string, unknown>).security;
|
||||
if (typeof security === "string") {
|
||||
const trimmedSecurity = security.trim();
|
||||
if (trimmedSecurity) {
|
||||
execPolicy = trimmedSecurity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user