Spaces:
Paused
Paused
| import fs from "node:fs/promises"; | |
| import path from "node:path"; | |
| import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; | |
| import { DEFAULT_AGENT_MAX_CONCURRENT, DEFAULT_SUBAGENT_MAX_CONCURRENT } from "./agent-limits.js"; | |
| import { withTempHome } from "./test-helpers.js"; | |
| describe("config identity defaults", () => { | |
| let previousHome: string | undefined; | |
| beforeEach(() => { | |
| previousHome = process.env.HOME; | |
| }); | |
| afterEach(() => { | |
| process.env.HOME = previousHome; | |
| }); | |
| it("does not derive mentionPatterns when identity is set", async () => { | |
| await withTempHome(async (home) => { | |
| const configDir = path.join(home, ".openclaw"); | |
| await fs.mkdir(configDir, { recursive: true }); | |
| await fs.writeFile( | |
| path.join(configDir, "openclaw.json"), | |
| JSON.stringify( | |
| { | |
| agents: { | |
| list: [ | |
| { | |
| id: "main", | |
| identity: { | |
| name: "Samantha", | |
| theme: "helpful sloth", | |
| emoji: "🦥", | |
| }, | |
| }, | |
| ], | |
| }, | |
| messages: {}, | |
| }, | |
| null, | |
| 2, | |
| ), | |
| "utf-8", | |
| ); | |
| vi.resetModules(); | |
| const { loadConfig } = await import("./config.js"); | |
| const cfg = loadConfig(); | |
| expect(cfg.messages?.responsePrefix).toBeUndefined(); | |
| expect(cfg.messages?.groupChat?.mentionPatterns).toBeUndefined(); | |
| }); | |
| }); | |
| it("defaults ackReactionScope without setting ackReaction", async () => { | |
| await withTempHome(async (home) => { | |
| const configDir = path.join(home, ".openclaw"); | |
| await fs.mkdir(configDir, { recursive: true }); | |
| await fs.writeFile( | |
| path.join(configDir, "openclaw.json"), | |
| JSON.stringify( | |
| { | |
| agents: { | |
| list: [ | |
| { | |
| id: "main", | |
| identity: { | |
| name: "Samantha", | |
| theme: "helpful sloth", | |
| emoji: "🦥", | |
| }, | |
| }, | |
| ], | |
| }, | |
| messages: {}, | |
| }, | |
| null, | |
| 2, | |
| ), | |
| "utf-8", | |
| ); | |
| vi.resetModules(); | |
| const { loadConfig } = await import("./config.js"); | |
| const cfg = loadConfig(); | |
| expect(cfg.messages?.ackReaction).toBeUndefined(); | |
| expect(cfg.messages?.ackReactionScope).toBe("group-mentions"); | |
| }); | |
| }); | |
| it("keeps ackReaction unset when identity is missing", async () => { | |
| await withTempHome(async (home) => { | |
| const configDir = path.join(home, ".openclaw"); | |
| await fs.mkdir(configDir, { recursive: true }); | |
| await fs.writeFile( | |
| path.join(configDir, "openclaw.json"), | |
| JSON.stringify( | |
| { | |
| messages: {}, | |
| }, | |
| null, | |
| 2, | |
| ), | |
| "utf-8", | |
| ); | |
| vi.resetModules(); | |
| const { loadConfig } = await import("./config.js"); | |
| const cfg = loadConfig(); | |
| expect(cfg.messages?.ackReaction).toBeUndefined(); | |
| expect(cfg.messages?.ackReactionScope).toBe("group-mentions"); | |
| }); | |
| }); | |
| it("does not override explicit values", async () => { | |
| await withTempHome(async (home) => { | |
| const configDir = path.join(home, ".openclaw"); | |
| await fs.mkdir(configDir, { recursive: true }); | |
| await fs.writeFile( | |
| path.join(configDir, "openclaw.json"), | |
| JSON.stringify( | |
| { | |
| agents: { | |
| list: [ | |
| { | |
| id: "main", | |
| identity: { | |
| name: "Samantha Sloth", | |
| theme: "space lobster", | |
| emoji: "🦞", | |
| }, | |
| groupChat: { mentionPatterns: ["@openclaw"] }, | |
| }, | |
| ], | |
| }, | |
| messages: { | |
| responsePrefix: "✅", | |
| }, | |
| }, | |
| null, | |
| 2, | |
| ), | |
| "utf-8", | |
| ); | |
| vi.resetModules(); | |
| const { loadConfig } = await import("./config.js"); | |
| const cfg = loadConfig(); | |
| expect(cfg.messages?.responsePrefix).toBe("✅"); | |
| expect(cfg.agents?.list?.[0]?.groupChat?.mentionPatterns).toEqual(["@openclaw"]); | |
| }); | |
| }); | |
| it("supports provider textChunkLimit config", async () => { | |
| await withTempHome(async (home) => { | |
| const configDir = path.join(home, ".openclaw"); | |
| await fs.mkdir(configDir, { recursive: true }); | |
| await fs.writeFile( | |
| path.join(configDir, "openclaw.json"), | |
| JSON.stringify( | |
| { | |
| messages: { | |
| messagePrefix: "[openclaw]", | |
| responsePrefix: "🦞", | |
| }, | |
| channels: { | |
| whatsapp: { allowFrom: ["+15555550123"], textChunkLimit: 4444 }, | |
| telegram: { enabled: true, textChunkLimit: 3333 }, | |
| discord: { | |
| enabled: true, | |
| textChunkLimit: 1999, | |
| maxLinesPerMessage: 17, | |
| }, | |
| signal: { enabled: true, textChunkLimit: 2222 }, | |
| imessage: { enabled: true, textChunkLimit: 1111 }, | |
| }, | |
| }, | |
| null, | |
| 2, | |
| ), | |
| "utf-8", | |
| ); | |
| vi.resetModules(); | |
| const { loadConfig } = await import("./config.js"); | |
| const cfg = loadConfig(); | |
| expect(cfg.channels?.whatsapp?.textChunkLimit).toBe(4444); | |
| expect(cfg.channels?.telegram?.textChunkLimit).toBe(3333); | |
| expect(cfg.channels?.discord?.textChunkLimit).toBe(1999); | |
| expect(cfg.channels?.discord?.maxLinesPerMessage).toBe(17); | |
| expect(cfg.channels?.signal?.textChunkLimit).toBe(2222); | |
| expect(cfg.channels?.imessage?.textChunkLimit).toBe(1111); | |
| const legacy = (cfg.messages as unknown as Record<string, unknown>).textChunkLimit; | |
| expect(legacy).toBeUndefined(); | |
| }); | |
| }); | |
| it("accepts blank model provider apiKey values", async () => { | |
| await withTempHome(async (home) => { | |
| const configDir = path.join(home, ".openclaw"); | |
| await fs.mkdir(configDir, { recursive: true }); | |
| await fs.writeFile( | |
| path.join(configDir, "openclaw.json"), | |
| JSON.stringify( | |
| { | |
| models: { | |
| mode: "merge", | |
| providers: { | |
| minimax: { | |
| baseUrl: "https://api.minimax.io/anthropic", | |
| apiKey: "", | |
| api: "anthropic-messages", | |
| models: [ | |
| { | |
| id: "MiniMax-M2.1", | |
| name: "MiniMax M2.1", | |
| reasoning: false, | |
| input: ["text"], | |
| cost: { | |
| input: 0, | |
| output: 0, | |
| cacheRead: 0, | |
| cacheWrite: 0, | |
| }, | |
| contextWindow: 200000, | |
| maxTokens: 8192, | |
| }, | |
| ], | |
| }, | |
| }, | |
| }, | |
| }, | |
| null, | |
| 2, | |
| ), | |
| "utf-8", | |
| ); | |
| vi.resetModules(); | |
| const { loadConfig } = await import("./config.js"); | |
| const cfg = loadConfig(); | |
| expect(cfg.models?.providers?.minimax?.baseUrl).toBe("https://api.minimax.io/anthropic"); | |
| }); | |
| }); | |
| it("respects empty responsePrefix to disable identity defaults", async () => { | |
| await withTempHome(async (home) => { | |
| const configDir = path.join(home, ".openclaw"); | |
| await fs.mkdir(configDir, { recursive: true }); | |
| await fs.writeFile( | |
| path.join(configDir, "openclaw.json"), | |
| JSON.stringify( | |
| { | |
| agents: { | |
| list: [ | |
| { | |
| id: "main", | |
| identity: { | |
| name: "Samantha", | |
| theme: "helpful sloth", | |
| emoji: "🦥", | |
| }, | |
| }, | |
| ], | |
| }, | |
| messages: { responsePrefix: "" }, | |
| }, | |
| null, | |
| 2, | |
| ), | |
| "utf-8", | |
| ); | |
| vi.resetModules(); | |
| const { loadConfig } = await import("./config.js"); | |
| const cfg = loadConfig(); | |
| expect(cfg.messages?.responsePrefix).toBe(""); | |
| }); | |
| }); | |
| it("does not synthesize agent list/session when absent", async () => { | |
| await withTempHome(async (home) => { | |
| const configDir = path.join(home, ".openclaw"); | |
| await fs.mkdir(configDir, { recursive: true }); | |
| await fs.writeFile( | |
| path.join(configDir, "openclaw.json"), | |
| JSON.stringify( | |
| { | |
| messages: {}, | |
| }, | |
| null, | |
| 2, | |
| ), | |
| "utf-8", | |
| ); | |
| vi.resetModules(); | |
| const { loadConfig } = await import("./config.js"); | |
| const cfg = loadConfig(); | |
| expect(cfg.messages?.responsePrefix).toBeUndefined(); | |
| expect(cfg.messages?.groupChat?.mentionPatterns).toBeUndefined(); | |
| expect(cfg.agents?.list).toBeUndefined(); | |
| expect(cfg.agents?.defaults?.maxConcurrent).toBe(DEFAULT_AGENT_MAX_CONCURRENT); | |
| expect(cfg.agents?.defaults?.subagents?.maxConcurrent).toBe(DEFAULT_SUBAGENT_MAX_CONCURRENT); | |
| expect(cfg.session).toBeUndefined(); | |
| }); | |
| }); | |
| it("does not derive responsePrefix from identity emoji", async () => { | |
| await withTempHome(async (home) => { | |
| const configDir = path.join(home, ".openclaw"); | |
| await fs.mkdir(configDir, { recursive: true }); | |
| await fs.writeFile( | |
| path.join(configDir, "openclaw.json"), | |
| JSON.stringify( | |
| { | |
| agents: { | |
| list: [ | |
| { | |
| id: "main", | |
| identity: { | |
| name: "OpenClaw", | |
| theme: "space lobster", | |
| emoji: "🦞", | |
| }, | |
| }, | |
| ], | |
| }, | |
| messages: {}, | |
| }, | |
| null, | |
| 2, | |
| ), | |
| "utf-8", | |
| ); | |
| vi.resetModules(); | |
| const { loadConfig } = await import("./config.js"); | |
| const cfg = loadConfig(); | |
| expect(cfg.messages?.responsePrefix).toBeUndefined(); | |
| }); | |
| }); | |
| }); | |