





















@@ -1,4 +1,5 @@
11import { describe, expect, it } from "vitest";
2+import { findLegacyConfigIssues } from "../../../config/legacy.js";
23import type { OpenClawConfig } from "../../../config/types.js";
34import { LEGACY_CONFIG_MIGRATIONS } from "./legacy-config-migrations.js";
45@@ -93,6 +94,275 @@ describe("legacy silent reply config migrate", () => {
9394});
9495});
959697+describe("profile configured tool section migrate", () => {
98+it("does not add grants when configured sections are the only signal", () => {
99+const raw = {
100+tools: {
101+profile: "messaging",
102+alsoAllow: ["read", "write"],
103+exec: { security: "allowlist" },
104+fs: { workspaceOnly: true },
105+},
106+agents: {
107+list: [
108+{
109+id: "sage",
110+tools: {
111+exec: { security: "allowlist" },
112+},
113+},
114+],
115+},
116+};
117+const res = migrateLegacyConfigForTest(raw);
118+119+expect(res.config).toBeNull();
120+expect(res.changes).toEqual([]);
121+expect(findLegacyConfigIssues(raw).map((issue) => issue.path)).not.toContain("tools");
122+expect(findLegacyConfigIssues(raw).map((issue) => issue.path)).not.toContain("agents.list");
123+});
124+125+it("does not add missing grants to an unrelated allowlist", () => {
126+const res = migrateLegacyConfigForTest({
127+tools: {
128+profile: "messaging",
129+allow: ["message"],
130+exec: { security: "allowlist" },
131+},
132+});
133+134+expect(res.config).toBeNull();
135+expect(res.changes).toEqual([]);
136+});
137+138+it("sets profile full when an allowlist already contains configured-section grants", () => {
139+const res = migrateLegacyConfigForTest({
140+tools: {
141+profile: "messaging",
142+allow: ["message", "exec", "process"],
143+exec: { security: "allowlist" },
144+},
145+});
146+147+expect(res.config?.tools?.allow).toEqual(["message", "exec", "process"]);
148+expect(res.config?.tools?.profile).toBe("full");
149+expect(res.config?.tools).not.toHaveProperty("alsoAllow");
150+expect(res.changes).toEqual([
151+'Replaced tools.allow entries with profile "messaging" grants plus explicit configured-section grants.',
152+'Set tools.profile to "full" so tools.allow controls explicit configured-section grants directly.',
153+]);
154+});
155+156+it("merges same-scope alsoAllow when it contains explicit configured-section grants", () => {
157+const res = migrateLegacyConfigForTest({
158+tools: {
159+profile: "messaging",
160+allow: ["message"],
161+alsoAllow: ["exec"],
162+exec: { security: "allowlist" },
163+},
164+});
165+166+expect(res.config?.tools?.allow).toEqual(["message", "exec"]);
167+expect(res.config?.tools?.profile).toBe("full");
168+expect(res.config?.tools).not.toHaveProperty("alsoAllow");
169+expect(res.changes).toContain("Merged tools.alsoAllow into tools.allow.");
170+expect(res.config?.tools?.allow).not.toContain("process");
171+});
172+173+it("repairs configured-section grants held in allow when alsoAllow is also present", () => {
174+const res = migrateLegacyConfigForTest({
175+tools: {
176+profile: "messaging",
177+allow: ["message", "exec", "process"],
178+alsoAllow: ["browser"],
179+exec: { security: "allowlist" },
180+},
181+});
182+183+expect(res.config?.tools?.allow).toEqual(["message", "browser", "exec", "process"]);
184+expect(res.config?.tools?.profile).toBe("full");
185+expect(res.config?.tools).not.toHaveProperty("alsoAllow");
186+});
187+188+it("narrows broad allowlists before making them authoritative", () => {
189+const res = migrateLegacyConfigForTest({
190+tools: {
191+profile: "messaging",
192+allow: ["*"],
193+exec: { security: "allowlist" },
194+},
195+});
196+197+expect(res.config?.tools?.profile).toBe("full");
198+expect(res.config?.tools?.allow).toContain("message");
199+expect(res.config?.tools?.allow).toContain("exec");
200+expect(res.config?.tools?.allow).toContain("process");
201+expect(res.config?.tools?.allow).not.toContain("*");
202+expect(res.config?.tools?.allow).not.toContain("read");
203+expect(res.changes).toContain(
204+'Replaced tools.allow entries with profile "messaging" grants plus explicit configured-section grants.',
205+);
206+});
207+208+it("does not treat unrelated globs or plugin allow entries as configured-section grants", () => {
209+const glob = migrateLegacyConfigForTest({
210+tools: {
211+profile: "messaging",
212+allow: ["sessions_*"],
213+exec: { security: "allowlist" },
214+},
215+});
216+const plugin = migrateLegacyConfigForTest({
217+tools: {
218+profile: "messaging",
219+allow: ["gmail_search"],
220+exec: { security: "allowlist" },
221+},
222+});
223+224+expect(glob.config).toBeNull();
225+expect(plugin.config).toBeNull();
226+});
227+228+it("repairs agent allowlists with explicit configured-section grants under an inherited profile", () => {
229+const res = migrateLegacyConfigForTest({
230+tools: {
231+profile: "messaging",
232+},
233+agents: {
234+list: [
235+{
236+id: "sage",
237+tools: {
238+allow: ["message", "exec", "process"],
239+exec: { security: "allowlist" },
240+},
241+},
242+],
243+},
244+});
245+246+expect(res.config?.agents?.list?.[0]?.tools?.profile).toBe("full");
247+expect(res.config?.agents?.list?.[0]?.tools?.allow).toEqual(["message", "exec", "process"]);
248+});
249+250+it("does not materialize provider grants when no provider grant intent is explicit", () => {
251+const raw = {
252+tools: {
253+exec: { security: "allowlist" },
254+byProvider: {
255+openai: {
256+profile: "messaging",
257+},
258+},
259+},
260+agents: {
261+list: [
262+{
263+id: "sage",
264+tools: {
265+exec: { security: "allowlist" },
266+},
267+},
268+],
269+},
270+};
271+const res = migrateLegacyConfigForTest(raw);
272+273+expect(res.config).toBeNull();
274+expect(res.changes).toEqual([]);
275+expect(findLegacyConfigIssues(raw).map((issue) => issue.path)).not.toContain("agents.list");
276+});
277+278+it("does not report inherited top-level profile provider allowlists as fixable", () => {
279+const raw = {
280+tools: {
281+profile: "messaging",
282+exec: { security: "allowlist" },
283+byProvider: {
284+openai: {
285+allow: ["message", "exec", "process"],
286+},
287+},
288+},
289+};
290+const res = migrateLegacyConfigForTest(raw);
291+292+expect(res.config).toBeNull();
293+expect(res.changes).toEqual([]);
294+expect(findLegacyConfigIssues(raw).map((issue) => issue.path)).not.toContain("tools");
295+});
296+297+it("sets provider profile full when provider allow already contains configured-section grants", () => {
298+const res = migrateLegacyConfigForTest({
299+tools: {
300+exec: { security: "allowlist" },
301+byProvider: {
302+openai: {
303+profile: "messaging",
304+allow: ["message", "exec", "process"],
305+},
306+},
307+},
308+});
309+310+expect(res.config?.tools?.byProvider?.openai?.allow).toEqual(["message", "exec", "process"]);
311+expect(res.config?.tools?.byProvider?.openai?.profile).toBe("full");
312+expect(res.changes).toContain(
313+'Set tools.byProvider.openai.profile to "full" so tools.byProvider.openai.allow controls explicit configured-section grants directly.',
314+);
315+});
316+317+it("repairs model-scoped provider allowlists with inherited provider profiles", () => {
318+const res = migrateLegacyConfigForTest({
319+tools: {
320+byProvider: {
321+qwen: {
322+profile: "messaging",
323+},
324+},
325+},
326+agents: {
327+list: [
328+{
329+id: "sage",
330+tools: {
331+exec: { security: "allowlist" },
332+byProvider: {
333+"qwen/qwen-plus": {
334+allow: ["message", "exec", "process"],
335+},
336+},
337+},
338+},
339+],
340+},
341+});
342+343+expect(res.config?.agents?.list?.[0]?.tools?.byProvider?.["qwen/qwen-plus"]?.allow).toEqual([
344+"message",
345+"exec",
346+"process",
347+]);
348+expect(res.config?.agents?.list?.[0]?.tools?.byProvider?.["qwen/qwen-plus"]?.profile).toBe(
349+"full",
350+);
351+});
352+353+it("ignores blocked inherited provider keys while resolving provider repairs", () => {
354+const raw = JSON.parse(
355+'{"tools":{"byProvider":{"__proto__":{"profile":"messaging"},"qwen":{"profile":"messaging"}}},"agents":{"list":[{"id":"sage","tools":{"exec":{"security":"allowlist"},"byProvider":{"qwen/qwen-plus":{"allow":["message","exec","process"]}}}}]}}',
356+);
357+const res = migrateLegacyConfigForTest(raw);
358+359+expect(Object.prototype).not.toHaveProperty("profile");
360+expect(res.config?.agents?.list?.[0]?.tools?.byProvider?.["qwen/qwen-plus"]?.profile).toBe(
361+"full",
362+);
363+});
364+});
365+96366describe("legacy agent model timeout migrate", () => {
97367it("removes ignored timeoutMs from agent and subagent model selection config", () => {
98368const res = migrateLegacyConfigForTest({
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。