diff --git a/src/config/types.hooks.ts b/src/config/types.hooks.ts index 2345c9ba7df..dc9086ed706 100644 --- a/src/config/types.hooks.ts +++ b/src/config/types.hooks.ts @@ -87,19 +87,7 @@ export type HookConfig = { [key: string]: unknown; }; -export type HookInstallRecord = { - source: "npm" | "archive" | "path"; - spec?: string; - sourcePath?: string; - installPath?: string; - version?: string; - resolvedName?: string; - resolvedVersion?: string; - resolvedSpec?: string; - integrity?: string; - shasum?: string; - resolvedAt?: string; - installedAt?: string; +export type HookInstallRecord = InstallRecordBase & { hooks?: string[]; }; @@ -151,3 +139,4 @@ export type HooksConfig = { /** Internal agent event hooks */ internal?: InternalHooksConfig; }; +import type { InstallRecordBase } from "./types.installs.js"; diff --git a/src/config/types.installs.ts b/src/config/types.installs.ts new file mode 100644 index 00000000000..dfb7a4dec90 --- /dev/null +++ b/src/config/types.installs.ts @@ -0,0 +1,14 @@ +export type InstallRecordBase = { + source: "npm" | "archive" | "path"; + spec?: string; + sourcePath?: string; + installPath?: string; + version?: string; + resolvedName?: string; + resolvedVersion?: string; + resolvedSpec?: string; + integrity?: string; + shasum?: string; + resolvedAt?: string; + installedAt?: string; +}; diff --git a/src/config/types.plugins.ts b/src/config/types.plugins.ts index 48e2d090edf..5884bba05c4 100644 --- a/src/config/types.plugins.ts +++ b/src/config/types.plugins.ts @@ -13,20 +13,7 @@ export type PluginsLoadConfig = { paths?: string[]; }; -export type PluginInstallRecord = { - source: "npm" | "archive" | "path"; - spec?: string; - sourcePath?: string; - installPath?: string; - version?: string; - resolvedName?: string; - resolvedVersion?: string; - resolvedSpec?: string; - integrity?: string; - shasum?: string; - resolvedAt?: string; - installedAt?: string; -}; +export type PluginInstallRecord = InstallRecordBase; export type PluginsConfig = { /** Enable or disable plugin loading. */ @@ -40,3 +27,4 @@ export type PluginsConfig = { entries?: Record; installs?: Record; }; +import type { InstallRecordBase } from "./types.installs.js"; diff --git a/src/config/zod-schema.agent-defaults.ts b/src/config/zod-schema.agent-defaults.ts index 5f6025e58e5..76386659018 100644 --- a/src/config/zod-schema.agent-defaults.ts +++ b/src/config/zod-schema.agent-defaults.ts @@ -10,6 +10,7 @@ import { BlockStreamingCoalesceSchema, CliBackendSchema, HumanDelaySchema, + TypingModeSchema, } from "./zod-schema.core.js"; export const AgentDefaultsSchema = z @@ -130,14 +131,7 @@ export const AgentDefaultsSchema = z mediaMaxMb: z.number().positive().optional(), imageMaxDimensionPx: z.number().int().positive().optional(), typingIntervalSeconds: z.number().int().positive().optional(), - typingMode: z - .union([ - z.literal("never"), - z.literal("instant"), - z.literal("thinking"), - z.literal("message"), - ]) - .optional(), + typingMode: TypingModeSchema.optional(), heartbeat: HeartbeatSchema, maxConcurrent: z.number().int().positive().optional(), subagents: z diff --git a/src/config/zod-schema.core.ts b/src/config/zod-schema.core.ts index 2b4dab28c48..9018eb1e2f1 100644 --- a/src/config/zod-schema.core.ts +++ b/src/config/zod-schema.core.ts @@ -129,6 +129,12 @@ export const QueueDropSchema = z.union([ z.literal("summarize"), ]); export const ReplyToModeSchema = z.union([z.literal("off"), z.literal("first"), z.literal("all")]); +export const TypingModeSchema = z.union([ + z.literal("never"), + z.literal("instant"), + z.literal("thinking"), + z.literal("message"), +]); // GroupPolicySchema: controls how group messages are handled // Used with .default("allowlist").optional() pattern: diff --git a/src/config/zod-schema.logging-levels.test.ts b/src/config/zod-schema.logging-levels.test.ts new file mode 100644 index 00000000000..80a970720b5 --- /dev/null +++ b/src/config/zod-schema.logging-levels.test.ts @@ -0,0 +1,32 @@ +import { describe, expect, it } from "vitest"; +import { OpenClawSchema } from "./zod-schema.js"; + +describe("OpenClawSchema logging levels", () => { + it("accepts valid logging level values for level and consoleLevel", () => { + expect(() => + OpenClawSchema.parse({ + logging: { + level: "debug", + consoleLevel: "warn", + }, + }), + ).not.toThrow(); + }); + + it("rejects invalid logging level values", () => { + expect(() => + OpenClawSchema.parse({ + logging: { + level: "loud", + }, + }), + ).toThrow(); + expect(() => + OpenClawSchema.parse({ + logging: { + consoleLevel: "verbose", + }, + }), + ).toThrow(); + }); +}); diff --git a/src/config/zod-schema.session.ts b/src/config/zod-schema.session.ts index edf73584a21..0f38fafd887 100644 --- a/src/config/zod-schema.session.ts +++ b/src/config/zod-schema.session.ts @@ -8,6 +8,7 @@ import { InboundDebounceSchema, NativeCommandsSettingSchema, QueueSchema, + TypingModeSchema, TtsConfigSchema, } from "./zod-schema.core.js"; import { sensitive } from "./zod-schema.sensitive.js"; @@ -50,14 +51,7 @@ export const SessionSchema = z resetByChannel: z.record(z.string(), SessionResetConfigSchema).optional(), store: z.string().optional(), typingIntervalSeconds: z.number().int().positive().optional(), - typingMode: z - .union([ - z.literal("never"), - z.literal("instant"), - z.literal("thinking"), - z.literal("message"), - ]) - .optional(), + typingMode: TypingModeSchema.optional(), mainKey: z.string().optional(), sendPolicy: SessionSendPolicySchema.optional(), agentToAgent: z diff --git a/src/config/zod-schema.ts b/src/config/zod-schema.ts index 8c8608b5997..3f1b89f980f 100644 --- a/src/config/zod-schema.ts +++ b/src/config/zod-schema.ts @@ -80,6 +80,16 @@ const MemoryQmdMcporterSchema = z }) .strict(); +const LoggingLevelSchema = z.union([ + z.literal("silent"), + z.literal("fatal"), + z.literal("error"), + z.literal("warn"), + z.literal("info"), + z.literal("debug"), + z.literal("trace"), +]); + const MemoryQmdSchema = z .object({ command: z.string().optional(), @@ -178,30 +188,10 @@ export const OpenClawSchema = z .optional(), logging: z .object({ - level: z - .union([ - z.literal("silent"), - z.literal("fatal"), - z.literal("error"), - z.literal("warn"), - z.literal("info"), - z.literal("debug"), - z.literal("trace"), - ]) - .optional(), + level: LoggingLevelSchema.optional(), file: z.string().optional(), maxFileBytes: z.number().int().positive().optional(), - consoleLevel: z - .union([ - z.literal("silent"), - z.literal("fatal"), - z.literal("error"), - z.literal("warn"), - z.literal("info"), - z.literal("debug"), - z.literal("trace"), - ]) - .optional(), + consoleLevel: LoggingLevelSchema.optional(), consoleStyle: z .union([z.literal("pretty"), z.literal("compact"), z.literal("json")]) .optional(), diff --git a/src/config/zod-schema.typing-mode.test.ts b/src/config/zod-schema.typing-mode.test.ts new file mode 100644 index 00000000000..7dc218676be --- /dev/null +++ b/src/config/zod-schema.typing-mode.test.ts @@ -0,0 +1,15 @@ +import { describe, expect, it } from "vitest"; +import { AgentDefaultsSchema } from "./zod-schema.agent-defaults.js"; +import { SessionSchema } from "./zod-schema.session.js"; + +describe("typing mode schema reuse", () => { + it("accepts supported typingMode values for session and agent defaults", () => { + expect(() => SessionSchema.parse({ typingMode: "thinking" })).not.toThrow(); + expect(() => AgentDefaultsSchema.parse({ typingMode: "message" })).not.toThrow(); + }); + + it("rejects unsupported typingMode values for session and agent defaults", () => { + expect(() => SessionSchema.parse({ typingMode: "always" })).toThrow(); + expect(() => AgentDefaultsSchema.parse({ typingMode: "soon" })).toThrow(); + }); +});