diff --git a/config/max-lines-baseline.txt b/config/max-lines-baseline.txt index 15c5c2d455e8..01931ee4465b 100644 --- a/config/max-lines-baseline.txt +++ b/config/max-lines-baseline.txt @@ -742,7 +742,6 @@ src/config/plugin-auto-enable.core.test.ts src/config/plugin-auto-enable.shared.ts src/config/redact-snapshot.test.ts src/config/redact-snapshot.ts -src/config/schema.help.quality.test.ts src/config/schema.labels.ts src/config/schema.test.ts src/config/schema.ts diff --git a/src/config/schema.help.quality.test-fixtures.ts b/src/config/schema.help.quality.test-fixtures.ts new file mode 100644 index 000000000000..ad0e56faec6c --- /dev/null +++ b/src/config/schema.help.quality.test-fixtures.ts @@ -0,0 +1,588 @@ +// Shared target sets for config help copy quality tests. + +import { MEDIA_AUDIO_FIELD_HELP } from "./media-audio-field-metadata.js"; + +export const ROOT_SECTIONS = [ + "meta", + "env", + "wizard", + "diagnostics", + "logging", + "cli", + "update", + "commitments", + "browser", + "ui", + "tui", + "auth", + "models", + "nodeHost", + "agents", + "tools", + "bindings", + "broadcast", + "media", + "messages", + "commands", + "approvals", + "session", + "cron", + "transcripts", + "hooks", + "web", + "channels", + "discovery", + "talk", + "gateway", + "cloudWorkers", + "memory", + "plugins", +] as const; + +export const TARGET_KEYS = [ + "memory.citations", + "memory.backend", + "memory.qmd.searchMode", + "memory.qmd.rerank", + "memory.qmd.searchTool", + "memory.qmd.scope", + "memory.qmd.includeDefaultMemory", + "memory.qmd.mcporter.enabled", + "memory.qmd.mcporter.serverName", + "memory.qmd.command", + "memory.qmd.mcporter", + "memory.qmd.mcporter.startDaemon", + "memory.qmd.paths", + "memory.qmd.paths.path", + "memory.qmd.paths.pattern", + "memory.qmd.paths.name", + "memory.qmd.sessions.enabled", + "memory.qmd.sessions.exportDir", + "memory.qmd.sessions.retentionDays", + "memory.qmd.update.interval", + "memory.qmd.update.debounceMs", + "memory.qmd.update.onBoot", + "memory.qmd.update.startup", + "memory.qmd.update.startupDelayMs", + "memory.qmd.update.waitForBootSync", + "memory.qmd.update.embedInterval", + "memory.qmd.update.commandTimeoutMs", + "memory.qmd.update.updateTimeoutMs", + "memory.qmd.update.embedTimeoutMs", + "memory.qmd.limits.maxResults", + "memory.qmd.limits.maxSnippetChars", + "memory.qmd.limits.maxInjectedChars", + "memory.qmd.limits.timeoutMs", + "agents.defaults.memorySearch.provider", + "agents.defaults.memorySearch.fallback", + "agents.defaults.memorySearch.sources", + "agents.defaults.memorySearch.extraPaths", + "agents.defaults.memorySearch.qmd", + "agents.defaults.memorySearch.qmd.extraCollections", + "agents.defaults.memorySearch.qmd.extraCollections.path", + "agents.defaults.memorySearch.qmd.extraCollections.name", + "agents.defaults.memorySearch.qmd.extraCollections.pattern", + "agents.defaults.memorySearch.multimodal", + "agents.defaults.memorySearch.multimodal.enabled", + "agents.defaults.memorySearch.multimodal.modalities", + "agents.defaults.memorySearch.multimodal.maxFileBytes", + "agents.defaults.memorySearch.experimental.sessionMemory", + "agents.defaults.memorySearch.remote.baseUrl", + "agents.defaults.memorySearch.remote.apiKey", + "agents.defaults.memorySearch.remote.headers", + "agents.defaults.memorySearch.remote.nonBatchConcurrency", + "agents.defaults.memorySearch.remote.batch.enabled", + "agents.defaults.memorySearch.remote.batch.wait", + "agents.defaults.memorySearch.remote.batch.concurrency", + "agents.defaults.memorySearch.remote.batch.pollIntervalMs", + "agents.defaults.memorySearch.remote.batch.timeoutMinutes", + "agents.defaults.memorySearch.local.modelPath", + "agents.defaults.memorySearch.inputType", + "agents.defaults.memorySearch.queryInputType", + "agents.defaults.memorySearch.documentInputType", + "agents.defaults.memorySearch.outputDimensionality", + "agents.defaults.memorySearch.store.vector.enabled", + "agents.defaults.memorySearch.store.vector.extensionPath", + "agents.defaults.memorySearch.query.hybrid.enabled", + "agents.defaults.memorySearch.query.hybrid.vectorWeight", + "agents.defaults.memorySearch.query.hybrid.textWeight", + "agents.defaults.memorySearch.query.hybrid.candidateMultiplier", + "agents.defaults.memorySearch.query.hybrid.mmr.enabled", + "agents.defaults.memorySearch.query.hybrid.mmr.lambda", + "agents.defaults.memorySearch.query.hybrid.temporalDecay.enabled", + "agents.defaults.memorySearch.query.hybrid.temporalDecay.halfLifeDays", + "agents.defaults.memorySearch.cache.enabled", + "agents.defaults.memorySearch.cache.maxEntries", + "agents.defaults.memorySearch.sync.onSearch", + "agents.defaults.memorySearch.sync.watch", + "agents.defaults.memorySearch.sync.embeddingBatchTimeoutSeconds", + "agents.defaults.memorySearch.sync.sessions.deltaBytes", + "agents.defaults.memorySearch.sync.sessions.deltaMessages", + "models.mode", + "models.providers.*.auth", + "models.providers.*.authHeader", + "models.providers.*.request", + "gateway.reload.mode", + "gateway.controlUi.dangerouslyAllowHostHeaderOriginFallback", + "gateway.controlUi.allowInsecureAuth", + "gateway.controlUi.dangerouslyDisableDeviceAuth", + "gateway.controlUi.embedSandbox", + "cron", + "cron.enabled", + "cron.store", + "cron.maxConcurrentRuns", + "cron.retry", + "cron.retry.maxAttempts", + "cron.retry.backoffMs", + "cron.retry.retryOn", + "cron.webhook", + "cron.webhookToken", + "cron.sessionRetention", + "session", + "session.scope", + "session.dmScope", + "session.identityLinks", + "session.resetTriggers", + "session.idleMinutes", + "session.reset", + "session.reset.mode", + "session.reset.atHour", + "session.reset.idleMinutes", + "session.resetByType", + "session.resetByType.direct", + "session.resetByType.dm", + "session.resetByType.group", + "session.resetByType.thread", + "session.resetByChannel", + "session.store", + "session.typingIntervalSeconds", + "session.typingMode", + "session.mainKey", + "session.sendPolicy", + "session.sendPolicy.default", + "session.sendPolicy.rules", + "session.sendPolicy.rules[].action", + "session.sendPolicy.rules[].match", + "session.sendPolicy.rules[].match.channel", + "session.sendPolicy.rules[].match.chatType", + "session.sendPolicy.rules[].match.keyPrefix", + "session.sendPolicy.rules[].match.rawKeyPrefix", + "session.agentToAgent", + "session.agentToAgent.maxPingPongTurns", + "session.threadBindings", + "session.threadBindings.enabled", + "session.threadBindings.idleHours", + "session.threadBindings.maxAgeHours", + "session.threadBindings.spawnSessions", + "session.threadBindings.defaultSpawnContext", + "session.maintenance", + "session.maintenance.mode", + "session.maintenance.pruneAfter", + "session.maintenance.pruneDays", + "session.maintenance.maxEntries", + "session.maintenance.resetArchiveRetention", + "session.maintenance.maxDiskBytes", + "session.maintenance.highWaterBytes", + "approvals", + "approvals.exec", + "approvals.exec.enabled", + "approvals.exec.mode", + "approvals.exec.agentFilter", + "approvals.exec.sessionFilter", + "approvals.exec.targets", + "approvals.exec.targets[].channel", + "approvals.exec.targets[].to", + "approvals.exec.targets[].accountId", + "approvals.exec.targets[].threadId", + "nodeHost", + "nodeHost.agentRuns", + "nodeHost.agentRuns.claude", + "nodeHost.agentRuns.claude.enabled", + "nodeHost.browserProxy", + "nodeHost.browserProxy.enabled", + "nodeHost.browserProxy.allowProfiles", + "nodeHost.mcp", + "nodeHost.mcp.servers", + "nodeHost.skills", + "nodeHost.skills.enabled", + "media", + "media.preserveFilenames", + "bindings", + "bindings[].agentId", + "bindings[].match", + "bindings[].match.channel", + "bindings[].match.accountId", + "bindings[].match.peer", + "bindings[].match.peer.kind", + "bindings[].match.peer.id", + "bindings[].match.guildId", + "bindings[].match.teamId", + "bindings[].match.roles", + "broadcast", + "broadcast.strategy", + "broadcast.*", + "commands", + "commands.allowFrom", + "hooks", + "hooks.enabled", + "hooks.path", + "hooks.token", + "hooks.defaultSessionKey", + "hooks.allowRequestSessionKey", + "hooks.allowedSessionKeyPrefixes", + "hooks.allowedAgentIds", + "hooks.maxBodyBytes", + "hooks.transformsDir", + "hooks.mappings", + "hooks.mappings[].action", + "hooks.mappings[].wakeMode", + "hooks.mappings[].channel", + "hooks.mappings[].transform.module", + "hooks.gmail", + "hooks.gmail.pushToken", + "hooks.gmail.tailscale.mode", + "hooks.gmail.thinking", + "hooks.internal", + "hooks.internal.load.extraDirs", + "messages", + "messages.messagePrefix", + "messages.visibleReplies", + "messages.responsePrefix", + "messages.groupChat", + "messages.groupChat.mentionPatterns", + "messages.groupChat.historyLimit", + "messages.groupChat.unmentionedInbound", + "messages.groupChat.visibleReplies", + "messages.queue", + "messages.queue.mode", + "messages.queue.byChannel", + "messages.queue.debounceMs", + "messages.queue.debounceMsByChannel", + "messages.queue.cap", + "messages.queue.drop", + "messages.inbound", + "messages.inbound.byChannel", + "messages.removeAckAfterReply", + "messages.tts", + "channels", + "channels.defaults", + "channels.defaults.groupPolicy", + "channels.defaults.contextVisibility", + "channels.defaults.implicitMentions", + "channels.defaults.implicitMentions.replyToBot", + "channels.defaults.implicitMentions.quotedBot", + "channels.defaults.implicitMentions.threadParticipation", + "channels.defaults.heartbeat", + "channels.defaults.heartbeat.showOk", + "channels.defaults.heartbeat.showAlerts", + "channels.defaults.heartbeat.useIndicator", + "channels.defaults.botLoopProtection", + "channels.defaults.botLoopProtection.enabled", + "channels.defaults.botLoopProtection.maxEventsPerWindow", + "channels.defaults.botLoopProtection.windowSeconds", + "channels.defaults.botLoopProtection.cooldownSeconds", + "gateway", + "gateway.mode", + "gateway.bind", + "gateway.auth.mode", + "gateway.tailscale.mode", + "gateway.tools.allow", + "gateway.tools.deny", + "gateway.tls.enabled", + "gateway.tls.autoGenerate", + "gateway.http", + "gateway.http.endpoints", + "browser", + "browser.enabled", + "browser.cdpUrl", + "browser.headless", + "browser.noSandbox", + "browser.profiles", + "browser.profiles.*.userDataDir", + "browser.profiles.*.driver", + "browser.profiles.*.attachOnly", + "tools", + "tools.allow", + "tools.deny", + "tools.exec", + "tools.exec.host", + "tools.exec.mode", + "tools.exec.security", + "tools.exec.ask", + "tools.exec.node", + "tools.agentToAgent.enabled", + "tools.elevated.enabled", + "tools.elevated.allowFrom", + "tools.subagents.tools", + "tools.sandbox.tools", + "web", + "web.enabled", + "web.heartbeatSeconds", + "web.reconnect", + "web.reconnect.initialMs", + "web.reconnect.maxMs", + "web.reconnect.factor", + "web.reconnect.jitter", + "web.reconnect.maxAttempts", + "web.whatsapp", + "web.whatsapp.keepAliveIntervalMs", + "web.whatsapp.connectTimeoutMs", + "web.whatsapp.defaultQueryTimeoutMs", + "discovery", + "discovery.wideArea.domain", + "discovery.wideArea.enabled", + "discovery.mdns", + "discovery.mdns.mode", + "gateway.controlUi.embedSandbox", + "talk", + "talk.consultFastMode", + "talk.interruptOnSpeech", + "talk.silenceTimeoutMs", + "talk.consultThinkingLevel", + "meta", + "env", + "env.shellEnv", + "env.shellEnv.enabled", + "env.shellEnv.timeoutMs", + "env.vars", + "wizard", + "wizard.lastRunAt", + "wizard.lastRunVersion", + "wizard.lastRunCommit", + "wizard.lastRunCommand", + "wizard.lastRunMode", + "diagnostics", + "diagnostics.otel", + "diagnostics.cacheTrace", + "logging", + "logging.level", + "logging.file", + "logging.consoleLevel", + "logging.consoleStyle", + "logging.redactSensitive", + "logging.redactPatterns", + "update", + "ui", + "ui.assistant", + "plugins", + "plugins.enabled", + "plugins.allow", + "plugins.deny", + "plugins.load", + "plugins.load.paths", + "plugins.slots", + "plugins.entries", + "plugins.entries.*.enabled", + "plugins.entries.*.hooks", + "plugins.entries.*.hooks.allowPromptInjection", + "plugins.entries.*.hooks.allowConversationAccess", + "plugins.entries.*.hooks.timeoutMs", + "plugins.entries.*.hooks.timeouts", + "plugins.entries.*.subagent", + "plugins.entries.*.subagent.allowModelOverride", + "plugins.entries.*.subagent.allowedModels", + "plugins.entries.*.llm", + "plugins.entries.*.llm.allowModelOverride", + "plugins.entries.*.llm.allowedModels", + "plugins.entries.*.llm.allowAgentIdOverride", + "plugins.entries.*.apiKey", + "plugins.entries.*.env", + "plugins.entries.*.config", + "auth", + "auth.cooldowns", + "models", + "models.providers", + "models.providers.*.baseUrl", + "models.providers.*.apiKey", + "models.providers.*.api", + "models.providers.*.contextWindow", + "models.providers.*.contextTokens", + "models.providers.*.maxTokens", + "models.providers.*.region", + "models.providers.*.headers", + "models.providers.*.models", + "agents", + "agents.defaults", + "agents.list", + "agents.defaults.compaction", + "agents.defaults.compaction.mode", + "agents.defaults.compaction.provider", + "agents.defaults.compaction.reserveTokens", + "agents.defaults.compaction.keepRecentTokens", + "agents.defaults.compaction.reserveTokensFloor", + "agents.defaults.compaction.maxHistoryShare", + "agents.defaults.compaction.identifierPolicy", + "agents.defaults.compaction.identifierInstructions", + "agents.defaults.compaction.recentTurnsPreserve", + "agents.defaults.compaction.qualityGuard", + "agents.defaults.compaction.qualityGuard.enabled", + "agents.defaults.compaction.qualityGuard.maxRetries", + "agents.defaults.compaction.midTurnPrecheck", + "agents.defaults.compaction.midTurnPrecheck.enabled", + "agents.defaults.compaction.postCompactionSections", + "agents.defaults.compaction.timeoutSeconds", + "agents.defaults.compaction.model", + "agents.defaults.compaction.truncateAfterCompaction", + "agents.defaults.compaction.maxActiveTranscriptBytes", + "agents.defaults.compaction.memoryFlush", + "agents.defaults.compaction.memoryFlush.enabled", + "agents.defaults.compaction.memoryFlush.model", + "agents.defaults.compaction.memoryFlush.softThresholdTokens", + "agents.defaults.compaction.memoryFlush.prompt", + "agents.defaults.compaction.memoryFlush.systemPrompt", +] as const; + +export const ENUM_EXPECTATIONS: Record = { + "memory.citations": ['"auto"', '"on"', '"off"'], + "memory.backend": ['"builtin"', '"qmd"'], + "memory.qmd.searchMode": ['"query"', '"search"', '"vsearch"'], + "models.mode": ['"merge"', '"replace"'], + "models.providers.*.auth": ['"api-key"', '"token"', '"oauth"', '"aws-sdk"'], + "gateway.reload.mode": ['"off"', '"restart"', '"hot"', '"hybrid"'], + "approvals.exec.mode": ['"session"', '"targets"', '"both"'], + "bindings[].match.peer.kind": ['"direct"', '"group"', '"channel"', '"dm"'], + "broadcast.strategy": ['"parallel"', '"sequential"'], + "hooks.mappings[].action": ['"wake"', '"agent"'], + "hooks.mappings[].wakeMode": ['"now"', '"next-heartbeat"'], + "hooks.gmail.tailscale.mode": ['"off"', '"serve"', '"funnel"'], + "hooks.gmail.thinking": ['"off"', '"minimal"', '"low"', '"medium"', '"high"'], + "messages.queue.mode": ['"steer"', '"followup"', '"collect"', '"interrupt"'], + "messages.queue.drop": ['"old"', '"new"', '"summarize"'], + "channels.defaults.groupPolicy": ['"open"', '"disabled"', '"allowlist"'], + "channels.defaults.contextVisibility": ['"all"', '"allowlist"', '"allowlist_quote"'], + "gateway.mode": ['"local"', '"remote"'], + "gateway.bind": ['"auto"', '"lan"', '"loopback"', '"custom"', '"tailnet"'], + "gateway.auth.mode": ['"none"', '"token"', '"password"', '"trusted-proxy"'], + "gateway.tailscale.mode": ['"off"', '"serve"', '"funnel"'], + "browser.profiles.*.driver": ['"openclaw"', '"clawd"', '"existing-session"'], + "discovery.mdns.mode": ['"off"', '"minimal"', '"full"'], + "wizard.lastRunMode": ['"local"', '"remote"'], + "diagnostics.otel.protocol": ['"http/protobuf"', '"grpc"'], + "diagnostics.otel.logsExporter": ['"otlp"', '"stdout"', '"both"'], + "logging.level": ['"silent"', '"fatal"', '"error"', '"warn"', '"info"', '"debug"', '"trace"'], + "logging.consoleLevel": [ + '"silent"', + '"fatal"', + '"error"', + '"warn"', + '"info"', + '"debug"', + '"trace"', + ], + "logging.consoleStyle": ['"pretty"', '"compact"', '"json"'], + "logging.redactSensitive": ['"off"', '"tools"'], + "cli.banner.taglineMode": ['"random"', '"default"', '"off"'], + "update.channel": ['"stable"', '"extended-stable"', '"beta"', '"dev"'], + "agents.defaults.compaction.mode": ['"default"', '"safeguard"'], + "agents.defaults.compaction.identifierPolicy": ['"strict"', '"off"', '"custom"'], +}; + +export const TOOLS_HOOKS_TARGET_KEYS = [ + "hooks.gmail.account", + "hooks.gmail.allowUnsafeExternalContent", + "hooks.gmail.hookUrl", + "hooks.gmail.includeBody", + "hooks.gmail.label", + "hooks.gmail.model", + "hooks.gmail.serve", + "hooks.gmail.subscription", + "hooks.gmail.tailscale", + "hooks.gmail.topic", + "hooks.internal.entries", + "hooks.internal.installs", + "hooks.internal.load", + "hooks.mappings[].allowUnsafeExternalContent", + "hooks.mappings[].deliver", + "hooks.mappings[].id", + "hooks.mappings[].match", + "hooks.mappings[].messageTemplate", + "hooks.mappings[].model", + "hooks.mappings[].name", + "hooks.mappings[].textTemplate", + "hooks.mappings[].thinking", + "hooks.mappings[].transform", + "tools.alsoAllow", + "tools.byProvider", + "tools.exec.approvalRunningNoticeMs", + "tools.exec.strictInlineEval", + "tools.exec.commandHighlighting", + "tools.links.enabled", + "tools.links.maxLinks", + "tools.links.models", + "tools.links.scope", + "tools.links.timeoutSeconds", + ...Object.keys(MEDIA_AUDIO_FIELD_HELP), + "tools.media.concurrency", + "tools.media.image.attachments", + "tools.media.image.enabled", + "tools.media.image.maxBytes", + "tools.media.image.maxChars", + "tools.media.image.models", + "tools.media.image.prompt", + "tools.media.image.scope", + "tools.media.image.timeoutSeconds", + "tools.media.models", + "tools.media.video.attachments", + "tools.media.video.enabled", + "tools.media.video.maxBytes", + "tools.media.video.maxChars", + "tools.media.video.models", + "tools.media.video.prompt", + "tools.media.video.scope", + "tools.media.video.timeoutSeconds", + "tools.profile", +] as const; + +export const CHANNELS_AGENTS_TARGET_KEYS = [ + "agents.defaults.memorySearch.chunking.overlap", + "agents.defaults.memorySearch.chunking.tokens", + "agents.defaults.memorySearch.enabled", + "agents.defaults.memorySearch.model", + "agents.defaults.memorySearch.query.maxResults", + "agents.defaults.memorySearch.query.minScore", + "agents.defaults.memorySearch.sync.onSessionStart", + "agents.defaults.memorySearch.sync.watchDebounceMs", + "agents.defaults.workspace", + "agents.list[].tools.alsoAllow", + "agents.list[].tools.byProvider", + "agents.list[].tools.message.crossContext.allowAcrossProviders", + "agents.list[].tools.message.crossContext.allowWithinProvider", + "agents.list[].tools.profile", + "channels.mattermost", +] as const; + +export const FINAL_BACKLOG_TARGET_KEYS = [ + "browser.evaluateEnabled", + "browser.remoteCdpHandshakeTimeoutMs", + "browser.remoteCdpTimeoutMs", + "browser.snapshotDefaults", + "browser.snapshotDefaults.mode", + "browser.ssrfPolicy", + "browser.ssrfPolicy.dangerouslyAllowPrivateNetwork", + "browser.ssrfPolicy.allowedHostnames", + "browser.ssrfPolicy.hostnameAllowlist", + "diagnostics.enabled", + "diagnostics.otel.enabled", + "diagnostics.otel.endpoint", + "diagnostics.otel.flushIntervalMs", + "diagnostics.otel.headers", + "diagnostics.otel.logsEndpoint", + "diagnostics.otel.logs", + "diagnostics.otel.logsExporter", + "diagnostics.otel.metricsEndpoint", + "diagnostics.otel.metrics", + "diagnostics.otel.sampleRate", + "diagnostics.otel.serviceName", + "diagnostics.otel.tracesEndpoint", + "diagnostics.otel.traces", + "gateway.remote.password", + "gateway.remote.token", + "skills.load.allowSymlinkTargets", + "skills.load.extraDirs", + "skills.load.watch", + "skills.load.watchDebounceMs", + "skills.workshop.allowSymlinkTargetWrites", + "ui.assistant.avatar", + "ui.assistant.name", + "ui.seamColor", +] as const; diff --git a/src/config/schema.help.quality.test.ts b/src/config/schema.help.quality.test.ts index 694ff3e078c8..071ba37243cb 100644 --- a/src/config/schema.help.quality.test.ts +++ b/src/config/schema.help.quality.test.ts @@ -2,595 +2,17 @@ import { expectDefined } from "@openclaw/normalization-core"; import { describe, expect, it } from "vitest"; -import { MEDIA_AUDIO_FIELD_HELP } from "./media-audio-field-metadata.js"; import { FIELD_HELP } from "./schema.help.js"; +import { + CHANNELS_AGENTS_TARGET_KEYS, + ENUM_EXPECTATIONS, + FINAL_BACKLOG_TARGET_KEYS, + ROOT_SECTIONS, + TARGET_KEYS, + TOOLS_HOOKS_TARGET_KEYS, +} from "./schema.help.quality.test-fixtures.js"; import { FIELD_LABELS } from "./schema.labels.js"; -const ROOT_SECTIONS = [ - "meta", - "env", - "wizard", - "diagnostics", - "logging", - "cli", - "update", - "commitments", - "browser", - "ui", - "tui", - "auth", - "models", - "nodeHost", - "agents", - "tools", - "bindings", - "broadcast", - "media", - "messages", - "commands", - "approvals", - "session", - "cron", - "transcripts", - "hooks", - "web", - "channels", - "discovery", - "talk", - "gateway", - "cloudWorkers", - "memory", - "plugins", -] as const; - -const TARGET_KEYS = [ - "memory.citations", - "memory.backend", - "memory.qmd.searchMode", - "memory.qmd.rerank", - "memory.qmd.searchTool", - "memory.qmd.scope", - "memory.qmd.includeDefaultMemory", - "memory.qmd.mcporter.enabled", - "memory.qmd.mcporter.serverName", - "memory.qmd.command", - "memory.qmd.mcporter", - "memory.qmd.mcporter.startDaemon", - "memory.qmd.paths", - "memory.qmd.paths.path", - "memory.qmd.paths.pattern", - "memory.qmd.paths.name", - "memory.qmd.sessions.enabled", - "memory.qmd.sessions.exportDir", - "memory.qmd.sessions.retentionDays", - "memory.qmd.update.interval", - "memory.qmd.update.debounceMs", - "memory.qmd.update.onBoot", - "memory.qmd.update.startup", - "memory.qmd.update.startupDelayMs", - "memory.qmd.update.waitForBootSync", - "memory.qmd.update.embedInterval", - "memory.qmd.update.commandTimeoutMs", - "memory.qmd.update.updateTimeoutMs", - "memory.qmd.update.embedTimeoutMs", - "memory.qmd.limits.maxResults", - "memory.qmd.limits.maxSnippetChars", - "memory.qmd.limits.maxInjectedChars", - "memory.qmd.limits.timeoutMs", - "agents.defaults.memorySearch.provider", - "agents.defaults.memorySearch.fallback", - "agents.defaults.memorySearch.sources", - "agents.defaults.memorySearch.extraPaths", - "agents.defaults.memorySearch.qmd", - "agents.defaults.memorySearch.qmd.extraCollections", - "agents.defaults.memorySearch.qmd.extraCollections.path", - "agents.defaults.memorySearch.qmd.extraCollections.name", - "agents.defaults.memorySearch.qmd.extraCollections.pattern", - "agents.defaults.memorySearch.multimodal", - "agents.defaults.memorySearch.multimodal.enabled", - "agents.defaults.memorySearch.multimodal.modalities", - "agents.defaults.memorySearch.multimodal.maxFileBytes", - "agents.defaults.memorySearch.experimental.sessionMemory", - "agents.defaults.memorySearch.remote.baseUrl", - "agents.defaults.memorySearch.remote.apiKey", - "agents.defaults.memorySearch.remote.headers", - "agents.defaults.memorySearch.remote.nonBatchConcurrency", - "agents.defaults.memorySearch.remote.batch.enabled", - "agents.defaults.memorySearch.remote.batch.wait", - "agents.defaults.memorySearch.remote.batch.concurrency", - "agents.defaults.memorySearch.remote.batch.pollIntervalMs", - "agents.defaults.memorySearch.remote.batch.timeoutMinutes", - "agents.defaults.memorySearch.local.modelPath", - "agents.defaults.memorySearch.inputType", - "agents.defaults.memorySearch.queryInputType", - "agents.defaults.memorySearch.documentInputType", - "agents.defaults.memorySearch.outputDimensionality", - "agents.defaults.memorySearch.store.vector.enabled", - "agents.defaults.memorySearch.store.vector.extensionPath", - "agents.defaults.memorySearch.query.hybrid.enabled", - "agents.defaults.memorySearch.query.hybrid.vectorWeight", - "agents.defaults.memorySearch.query.hybrid.textWeight", - "agents.defaults.memorySearch.query.hybrid.candidateMultiplier", - "agents.defaults.memorySearch.query.hybrid.mmr.enabled", - "agents.defaults.memorySearch.query.hybrid.mmr.lambda", - "agents.defaults.memorySearch.query.hybrid.temporalDecay.enabled", - "agents.defaults.memorySearch.query.hybrid.temporalDecay.halfLifeDays", - "agents.defaults.memorySearch.cache.enabled", - "agents.defaults.memorySearch.cache.maxEntries", - "agents.defaults.memorySearch.sync.onSearch", - "agents.defaults.memorySearch.sync.watch", - "agents.defaults.memorySearch.sync.embeddingBatchTimeoutSeconds", - "agents.defaults.memorySearch.sync.sessions.deltaBytes", - "agents.defaults.memorySearch.sync.sessions.deltaMessages", - "models.mode", - "models.providers.*.auth", - "models.providers.*.authHeader", - "models.providers.*.request", - "gateway.reload.mode", - "gateway.controlUi.dangerouslyAllowHostHeaderOriginFallback", - "gateway.controlUi.allowInsecureAuth", - "gateway.controlUi.dangerouslyDisableDeviceAuth", - "gateway.controlUi.embedSandbox", - "cron", - "cron.enabled", - "cron.store", - "cron.maxConcurrentRuns", - "cron.retry", - "cron.retry.maxAttempts", - "cron.retry.backoffMs", - "cron.retry.retryOn", - "cron.webhook", - "cron.webhookToken", - "cron.sessionRetention", - "session", - "session.scope", - "session.dmScope", - "session.identityLinks", - "session.resetTriggers", - "session.idleMinutes", - "session.reset", - "session.reset.mode", - "session.reset.atHour", - "session.reset.idleMinutes", - "session.resetByType", - "session.resetByType.direct", - "session.resetByType.dm", - "session.resetByType.group", - "session.resetByType.thread", - "session.resetByChannel", - "session.store", - "session.typingIntervalSeconds", - "session.typingMode", - "session.mainKey", - "session.sendPolicy", - "session.sendPolicy.default", - "session.sendPolicy.rules", - "session.sendPolicy.rules[].action", - "session.sendPolicy.rules[].match", - "session.sendPolicy.rules[].match.channel", - "session.sendPolicy.rules[].match.chatType", - "session.sendPolicy.rules[].match.keyPrefix", - "session.sendPolicy.rules[].match.rawKeyPrefix", - "session.agentToAgent", - "session.agentToAgent.maxPingPongTurns", - "session.threadBindings", - "session.threadBindings.enabled", - "session.threadBindings.idleHours", - "session.threadBindings.maxAgeHours", - "session.threadBindings.spawnSessions", - "session.threadBindings.defaultSpawnContext", - "session.maintenance", - "session.maintenance.mode", - "session.maintenance.pruneAfter", - "session.maintenance.pruneDays", - "session.maintenance.maxEntries", - "session.maintenance.resetArchiveRetention", - "session.maintenance.maxDiskBytes", - "session.maintenance.highWaterBytes", - "approvals", - "approvals.exec", - "approvals.exec.enabled", - "approvals.exec.mode", - "approvals.exec.agentFilter", - "approvals.exec.sessionFilter", - "approvals.exec.targets", - "approvals.exec.targets[].channel", - "approvals.exec.targets[].to", - "approvals.exec.targets[].accountId", - "approvals.exec.targets[].threadId", - "nodeHost", - "nodeHost.agentRuns", - "nodeHost.agentRuns.claude", - "nodeHost.agentRuns.claude.enabled", - "nodeHost.browserProxy", - "nodeHost.browserProxy.enabled", - "nodeHost.browserProxy.allowProfiles", - "nodeHost.mcp", - "nodeHost.mcp.servers", - "nodeHost.skills", - "nodeHost.skills.enabled", - "media", - "media.preserveFilenames", - "bindings", - "bindings[].agentId", - "bindings[].match", - "bindings[].match.channel", - "bindings[].match.accountId", - "bindings[].match.peer", - "bindings[].match.peer.kind", - "bindings[].match.peer.id", - "bindings[].match.guildId", - "bindings[].match.teamId", - "bindings[].match.roles", - "broadcast", - "broadcast.strategy", - "broadcast.*", - "commands", - "commands.allowFrom", - "hooks", - "hooks.enabled", - "hooks.path", - "hooks.token", - "hooks.defaultSessionKey", - "hooks.allowRequestSessionKey", - "hooks.allowedSessionKeyPrefixes", - "hooks.allowedAgentIds", - "hooks.maxBodyBytes", - "hooks.transformsDir", - "hooks.mappings", - "hooks.mappings[].action", - "hooks.mappings[].wakeMode", - "hooks.mappings[].channel", - "hooks.mappings[].transform.module", - "hooks.gmail", - "hooks.gmail.pushToken", - "hooks.gmail.tailscale.mode", - "hooks.gmail.thinking", - "hooks.internal", - "hooks.internal.load.extraDirs", - "messages", - "messages.messagePrefix", - "messages.visibleReplies", - "messages.responsePrefix", - "messages.groupChat", - "messages.groupChat.mentionPatterns", - "messages.groupChat.historyLimit", - "messages.groupChat.unmentionedInbound", - "messages.groupChat.visibleReplies", - "messages.queue", - "messages.queue.mode", - "messages.queue.byChannel", - "messages.queue.debounceMs", - "messages.queue.debounceMsByChannel", - "messages.queue.cap", - "messages.queue.drop", - "messages.inbound", - "messages.inbound.byChannel", - "messages.removeAckAfterReply", - "messages.tts", - "channels", - "channels.defaults", - "channels.defaults.groupPolicy", - "channels.defaults.contextVisibility", - "channels.defaults.implicitMentions", - "channels.defaults.implicitMentions.replyToBot", - "channels.defaults.implicitMentions.quotedBot", - "channels.defaults.implicitMentions.threadParticipation", - "channels.defaults.heartbeat", - "channels.defaults.heartbeat.showOk", - "channels.defaults.heartbeat.showAlerts", - "channels.defaults.heartbeat.useIndicator", - "channels.defaults.botLoopProtection", - "channels.defaults.botLoopProtection.enabled", - "channels.defaults.botLoopProtection.maxEventsPerWindow", - "channels.defaults.botLoopProtection.windowSeconds", - "channels.defaults.botLoopProtection.cooldownSeconds", - "gateway", - "gateway.mode", - "gateway.bind", - "gateway.auth.mode", - "gateway.tailscale.mode", - "gateway.tools.allow", - "gateway.tools.deny", - "gateway.tls.enabled", - "gateway.tls.autoGenerate", - "gateway.http", - "gateway.http.endpoints", - "browser", - "browser.enabled", - "browser.cdpUrl", - "browser.headless", - "browser.noSandbox", - "browser.profiles", - "browser.profiles.*.userDataDir", - "browser.profiles.*.driver", - "browser.profiles.*.attachOnly", - "tools", - "tools.allow", - "tools.deny", - "tools.exec", - "tools.exec.host", - "tools.exec.mode", - "tools.exec.security", - "tools.exec.ask", - "tools.exec.node", - "tools.agentToAgent.enabled", - "tools.elevated.enabled", - "tools.elevated.allowFrom", - "tools.subagents.tools", - "tools.sandbox.tools", - "web", - "web.enabled", - "web.heartbeatSeconds", - "web.reconnect", - "web.reconnect.initialMs", - "web.reconnect.maxMs", - "web.reconnect.factor", - "web.reconnect.jitter", - "web.reconnect.maxAttempts", - "web.whatsapp", - "web.whatsapp.keepAliveIntervalMs", - "web.whatsapp.connectTimeoutMs", - "web.whatsapp.defaultQueryTimeoutMs", - "discovery", - "discovery.wideArea.domain", - "discovery.wideArea.enabled", - "discovery.mdns", - "discovery.mdns.mode", - "gateway.controlUi.embedSandbox", - "talk", - "talk.consultFastMode", - "talk.interruptOnSpeech", - "talk.silenceTimeoutMs", - "talk.consultThinkingLevel", - "meta", - "env", - "env.shellEnv", - "env.shellEnv.enabled", - "env.shellEnv.timeoutMs", - "env.vars", - "wizard", - "wizard.lastRunAt", - "wizard.lastRunVersion", - "wizard.lastRunCommit", - "wizard.lastRunCommand", - "wizard.lastRunMode", - "diagnostics", - "diagnostics.otel", - "diagnostics.cacheTrace", - "logging", - "logging.level", - "logging.file", - "logging.consoleLevel", - "logging.consoleStyle", - "logging.redactSensitive", - "logging.redactPatterns", - "update", - "ui", - "ui.assistant", - "plugins", - "plugins.enabled", - "plugins.allow", - "plugins.deny", - "plugins.load", - "plugins.load.paths", - "plugins.slots", - "plugins.entries", - "plugins.entries.*.enabled", - "plugins.entries.*.hooks", - "plugins.entries.*.hooks.allowPromptInjection", - "plugins.entries.*.hooks.allowConversationAccess", - "plugins.entries.*.hooks.timeoutMs", - "plugins.entries.*.hooks.timeouts", - "plugins.entries.*.subagent", - "plugins.entries.*.subagent.allowModelOverride", - "plugins.entries.*.subagent.allowedModels", - "plugins.entries.*.llm", - "plugins.entries.*.llm.allowModelOverride", - "plugins.entries.*.llm.allowedModels", - "plugins.entries.*.llm.allowAgentIdOverride", - "plugins.entries.*.apiKey", - "plugins.entries.*.env", - "plugins.entries.*.config", - "auth", - "auth.cooldowns", - "models", - "models.providers", - "models.providers.*.baseUrl", - "models.providers.*.apiKey", - "models.providers.*.api", - "models.providers.*.contextWindow", - "models.providers.*.contextTokens", - "models.providers.*.maxTokens", - "models.providers.*.region", - "models.providers.*.headers", - "models.providers.*.models", - "agents", - "agents.defaults", - "agents.list", - "agents.defaults.compaction", - "agents.defaults.compaction.mode", - "agents.defaults.compaction.provider", - "agents.defaults.compaction.reserveTokens", - "agents.defaults.compaction.keepRecentTokens", - "agents.defaults.compaction.reserveTokensFloor", - "agents.defaults.compaction.maxHistoryShare", - "agents.defaults.compaction.identifierPolicy", - "agents.defaults.compaction.identifierInstructions", - "agents.defaults.compaction.recentTurnsPreserve", - "agents.defaults.compaction.qualityGuard", - "agents.defaults.compaction.qualityGuard.enabled", - "agents.defaults.compaction.qualityGuard.maxRetries", - "agents.defaults.compaction.midTurnPrecheck", - "agents.defaults.compaction.midTurnPrecheck.enabled", - "agents.defaults.compaction.postCompactionSections", - "agents.defaults.compaction.timeoutSeconds", - "agents.defaults.compaction.model", - "agents.defaults.compaction.truncateAfterCompaction", - "agents.defaults.compaction.maxActiveTranscriptBytes", - "agents.defaults.compaction.memoryFlush", - "agents.defaults.compaction.memoryFlush.enabled", - "agents.defaults.compaction.memoryFlush.model", - "agents.defaults.compaction.memoryFlush.softThresholdTokens", - "agents.defaults.compaction.memoryFlush.prompt", - "agents.defaults.compaction.memoryFlush.systemPrompt", -] as const; - -const ENUM_EXPECTATIONS: Record = { - "memory.citations": ['"auto"', '"on"', '"off"'], - "memory.backend": ['"builtin"', '"qmd"'], - "memory.qmd.searchMode": ['"query"', '"search"', '"vsearch"'], - "models.mode": ['"merge"', '"replace"'], - "models.providers.*.auth": ['"api-key"', '"token"', '"oauth"', '"aws-sdk"'], - "gateway.reload.mode": ['"off"', '"restart"', '"hot"', '"hybrid"'], - "approvals.exec.mode": ['"session"', '"targets"', '"both"'], - "bindings[].match.peer.kind": ['"direct"', '"group"', '"channel"', '"dm"'], - "broadcast.strategy": ['"parallel"', '"sequential"'], - "hooks.mappings[].action": ['"wake"', '"agent"'], - "hooks.mappings[].wakeMode": ['"now"', '"next-heartbeat"'], - "hooks.gmail.tailscale.mode": ['"off"', '"serve"', '"funnel"'], - "hooks.gmail.thinking": ['"off"', '"minimal"', '"low"', '"medium"', '"high"'], - "messages.queue.mode": ['"steer"', '"followup"', '"collect"', '"interrupt"'], - "messages.queue.drop": ['"old"', '"new"', '"summarize"'], - "channels.defaults.groupPolicy": ['"open"', '"disabled"', '"allowlist"'], - "channels.defaults.contextVisibility": ['"all"', '"allowlist"', '"allowlist_quote"'], - "gateway.mode": ['"local"', '"remote"'], - "gateway.bind": ['"auto"', '"lan"', '"loopback"', '"custom"', '"tailnet"'], - "gateway.auth.mode": ['"none"', '"token"', '"password"', '"trusted-proxy"'], - "gateway.tailscale.mode": ['"off"', '"serve"', '"funnel"'], - "browser.profiles.*.driver": ['"openclaw"', '"clawd"', '"existing-session"'], - "discovery.mdns.mode": ['"off"', '"minimal"', '"full"'], - "wizard.lastRunMode": ['"local"', '"remote"'], - "diagnostics.otel.protocol": ['"http/protobuf"', '"grpc"'], - "diagnostics.otel.logsExporter": ['"otlp"', '"stdout"', '"both"'], - "logging.level": ['"silent"', '"fatal"', '"error"', '"warn"', '"info"', '"debug"', '"trace"'], - "logging.consoleLevel": [ - '"silent"', - '"fatal"', - '"error"', - '"warn"', - '"info"', - '"debug"', - '"trace"', - ], - "logging.consoleStyle": ['"pretty"', '"compact"', '"json"'], - "logging.redactSensitive": ['"off"', '"tools"'], - "cli.banner.taglineMode": ['"random"', '"default"', '"off"'], - "update.channel": ['"stable"', '"extended-stable"', '"beta"', '"dev"'], - "agents.defaults.compaction.mode": ['"default"', '"safeguard"'], - "agents.defaults.compaction.identifierPolicy": ['"strict"', '"off"', '"custom"'], -}; - -const TOOLS_HOOKS_TARGET_KEYS = [ - "hooks.gmail.account", - "hooks.gmail.allowUnsafeExternalContent", - "hooks.gmail.hookUrl", - "hooks.gmail.includeBody", - "hooks.gmail.label", - "hooks.gmail.model", - "hooks.gmail.serve", - "hooks.gmail.subscription", - "hooks.gmail.tailscale", - "hooks.gmail.topic", - "hooks.internal.entries", - "hooks.internal.installs", - "hooks.internal.load", - "hooks.mappings[].allowUnsafeExternalContent", - "hooks.mappings[].deliver", - "hooks.mappings[].id", - "hooks.mappings[].match", - "hooks.mappings[].messageTemplate", - "hooks.mappings[].model", - "hooks.mappings[].name", - "hooks.mappings[].textTemplate", - "hooks.mappings[].thinking", - "hooks.mappings[].transform", - "tools.alsoAllow", - "tools.byProvider", - "tools.exec.approvalRunningNoticeMs", - "tools.exec.strictInlineEval", - "tools.exec.commandHighlighting", - "tools.links.enabled", - "tools.links.maxLinks", - "tools.links.models", - "tools.links.scope", - "tools.links.timeoutSeconds", - ...Object.keys(MEDIA_AUDIO_FIELD_HELP), - "tools.media.concurrency", - "tools.media.image.attachments", - "tools.media.image.enabled", - "tools.media.image.maxBytes", - "tools.media.image.maxChars", - "tools.media.image.models", - "tools.media.image.prompt", - "tools.media.image.scope", - "tools.media.image.timeoutSeconds", - "tools.media.models", - "tools.media.video.attachments", - "tools.media.video.enabled", - "tools.media.video.maxBytes", - "tools.media.video.maxChars", - "tools.media.video.models", - "tools.media.video.prompt", - "tools.media.video.scope", - "tools.media.video.timeoutSeconds", - "tools.profile", -] as const; - -const CHANNELS_AGENTS_TARGET_KEYS = [ - "agents.defaults.memorySearch.chunking.overlap", - "agents.defaults.memorySearch.chunking.tokens", - "agents.defaults.memorySearch.enabled", - "agents.defaults.memorySearch.model", - "agents.defaults.memorySearch.query.maxResults", - "agents.defaults.memorySearch.query.minScore", - "agents.defaults.memorySearch.sync.onSessionStart", - "agents.defaults.memorySearch.sync.watchDebounceMs", - "agents.defaults.workspace", - "agents.list[].tools.alsoAllow", - "agents.list[].tools.byProvider", - "agents.list[].tools.message.crossContext.allowAcrossProviders", - "agents.list[].tools.message.crossContext.allowWithinProvider", - "agents.list[].tools.profile", - "channels.mattermost", -] as const; - -const FINAL_BACKLOG_TARGET_KEYS = [ - "browser.evaluateEnabled", - "browser.remoteCdpHandshakeTimeoutMs", - "browser.remoteCdpTimeoutMs", - "browser.snapshotDefaults", - "browser.snapshotDefaults.mode", - "browser.ssrfPolicy", - "browser.ssrfPolicy.dangerouslyAllowPrivateNetwork", - "browser.ssrfPolicy.allowedHostnames", - "browser.ssrfPolicy.hostnameAllowlist", - "diagnostics.enabled", - "diagnostics.otel.enabled", - "diagnostics.otel.endpoint", - "diagnostics.otel.flushIntervalMs", - "diagnostics.otel.headers", - "diagnostics.otel.logsEndpoint", - "diagnostics.otel.logs", - "diagnostics.otel.logsExporter", - "diagnostics.otel.metricsEndpoint", - "diagnostics.otel.metrics", - "diagnostics.otel.sampleRate", - "diagnostics.otel.serviceName", - "diagnostics.otel.tracesEndpoint", - "diagnostics.otel.traces", - "gateway.remote.password", - "gateway.remote.token", - "skills.load.allowSymlinkTargets", - "skills.load.extraDirs", - "skills.load.watch", - "skills.load.watchDebounceMs", - "skills.workshop.allowSymlinkTargetWrites", - "ui.assistant.avatar", - "ui.assistant.name", - "ui.seamColor", -] as const; - function titleCaseLabelSegment(segment: string): string { return segment .replace(/\[\]/g, "") @@ -1097,4 +519,3 @@ describe("config help copy quality", () => { expect(/today \+ yesterday|default:\s*2/i.test(dailyMemoryDays)).toBe(true); }); }); -/* oxlint-disable max-lines -- TODO: split this grandfathered oversized file. */