UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

9,881 lines • 341 kB
import { E as normalizeOptionalLowercaseString, T as normalizeLowercaseStringOrEmpty, _ as isPlainObject, b as hasLegacyAccountStreamingAliases, d as normalizeAccountId, g as mergeDeep, h as resolveGlobalSingleton, j as isRecord, k as asNullableRecord, l as resolveNormalizedAccountEntry, m as resolveGlobalSet, p as pruneMapToMaxSize, u as DEFAULT_ACCOUNT_ID, x as normalizeChannelAccounts, y as isMissingPathError } from "./runtime-doctor-migrations-KsHvG31D.js";
import { t as createLegacyWebhookListenerDoctorContract } from "./legacy-webhook-listener-migration-DvKCjZ4o.js";
import "./src-BRYbjtIr.js";
import { i as parseEnvTemplateSecretRef, u as expectDefined } from "./types.secrets-Xs0-1fWv.js";
import "./number-coercion-CWp8Ho-Y.js";
import { a as tryProcessCwd, c as mergeAccountConfig, o as createAccountListHelpers, r as resolveRequiredHomeDir, s as hasConfiguredAccountValue } from "./account-core-CptjyPlI.js";
import "./string-coerce-runtime-CM4JCyJk.js";
import "./ansi-twpzQrRE.js";
import { i as resolveIncludeRoots, n as openRootFileSync, o as resolveStateDir, r as resolveConfigPath, s as resolveProfileStateDir, t as canUseRootFileOpen } from "./boundary-file-read-Boou96o-.js";
import { createRequire } from "node:module";
import fs from "node:fs/promises";
import { isPathInside } from "@openclaw/fs-safe/path";
import path from "node:path";
import "node:util";
import fs$1 from "node:fs";
import { appendRegularFile, appendRegularFileSync } from "@openclaw/fs-safe/advanced";
import "node:crypto";
import os from "node:os";
import { AsyncLocalStorage } from "node:async_hooks";
import chalk, { Chalk } from "chalk";
import "tslog";
import { channel } from "node:diagnostics_channel";
import { performance } from "node:perf_hooks";
import { isMainThread, threadId } from "node:worker_threads";
//#region packages/normalization-core/src/error-coercion.ts
function readProperty(value, key) {
	try {
		return value[key];
	} catch {
		return;
	}
}
function extractErrorCode(err) {
	if (!err || typeof err !== "object") return;
	const code = readProperty(err, "code");
	return typeof code === "string" || typeof code === "number" ? String(code) : void 0;
}
//#endregion
//#region packages/normalization-core/src/utf16-slice.ts
function isHighSurrogate(codeUnit) {
	return codeUnit >= 55296 && codeUnit <= 56319;
}
function isLowSurrogate(codeUnit) {
	return codeUnit >= 56320 && codeUnit <= 57343;
}
/** Slices a UTF-16 string without returning dangling surrogate halves at either edge. */
function sliceUtf16Safe(input, start, end) {
	const len = input.length;
	let from = start < 0 ? Math.max(len + start, 0) : Math.min(start, len);
	let to = end === void 0 ? len : end < 0 ? Math.max(len + end, 0) : Math.min(end, len);
	if (to <= from) return "";
	if (from > 0 && from < len) {
		if (isLowSurrogate(input.charCodeAt(from)) && isHighSurrogate(input.charCodeAt(from - 1))) from += 1;
	}
	if (to > 0 && to < len) {
		if (isHighSurrogate(input.charCodeAt(to - 1)) && isLowSurrogate(input.charCodeAt(to))) to -= 1;
	}
	return input.slice(from, to);
}
//#endregion
//#region src/shared/regexp.ts
/** Escape text so it can be embedded literally inside a RegExp pattern. */
function escapeRegExp(value) {
	return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
//#endregion
//#region extensions/telegram/src/account-config.ts
function resolveTelegramAccountConfig(cfg, accountId) {
	const normalized = normalizeAccountId(accountId);
	return resolveNormalizedAccountEntry(cfg.channels?.telegram?.accounts, normalized, normalizeAccountId);
}
function mergeTelegramAccountConfig(cfg, accountId) {
	const channelConfig = cfg.channels?.telegram;
	const isMultiAccount = Object.keys(channelConfig?.accounts ?? {}).length > 1;
	return mergeAccountConfig({
		channelConfig,
		accountConfig: resolveTelegramAccountConfig(cfg, accountId),
		omitKeys: ["defaultAccount"],
		inheritEmptyKeys: {
			capabilities: "array",
			...isMultiAccount ? {} : { groups: "object" }
		},
		preserveRootAllowFrom: true
	});
}
//#endregion
//#region src/agents/workspace-default-path.ts
function resolveDefaultAgentWorkspaceDir(env = process.env, homedir = os.homedir) {
	const workspaceDir = env.OPENCLAW_WORKSPACE_DIR?.trim();
	if (workspaceDir) return path.resolve(workspaceDir);
	if (env.OPENCLAW_STATE_DIR?.trim()) return path.join(resolveStateDir(env, homedir), "workspace");
	const home = resolveRequiredHomeDir(env, homedir);
	const profile = env.OPENCLAW_PROFILE?.trim();
	if (profile && normalizeOptionalLowercaseString(profile) !== "default") return path.join(resolveProfileStateDir(profile, env, homedir), "workspace");
	return path.join(home, ".openclaw", "workspace");
}
resolveDefaultAgentWorkspaceDir();
(/* @__PURE__ */ new Map()).keys();
//#endregion
//#region packages/terminal-core/src/palette.ts
const LOBSTER_PALETTE = {
	accent: "#FF5A2D",
	accentBright: "#FF7A3D",
	accentDim: "#D14A22",
	info: "#FF8A5B",
	success: "#2FBF71",
	warn: "#FFB020",
	error: "#E23D2D",
	muted: "#8B7F77"
};
//#endregion
//#region packages/terminal-core/src/theme.ts
const hasForceColor = typeof process.env.FORCE_COLOR === "string" && process.env.FORCE_COLOR.trim().length > 0 && process.env.FORCE_COLOR.trim() !== "0";
const baseChalk = process.env.NO_COLOR && !hasForceColor ? new Chalk({ level: 0 }) : chalk;
const hex = (value) => baseChalk.hex(value);
/** Shared terminal theme color functions. */
const theme = {
	accent: hex(LOBSTER_PALETTE.accent),
	accentBright: hex(LOBSTER_PALETTE.accentBright),
	accentDim: hex(LOBSTER_PALETTE.accentDim),
	info: hex(LOBSTER_PALETTE.info),
	success: hex(LOBSTER_PALETTE.success),
	warn: hex(LOBSTER_PALETTE.warn),
	error: hex(LOBSTER_PALETTE.error),
	muted: hex(LOBSTER_PALETTE.muted),
	heading: baseChalk.bold.hex(LOBSTER_PALETTE.accent),
	command: hex(LOBSTER_PALETTE.accentBright),
	option: hex(LOBSTER_PALETTE.warn)
};
resolveGlobalSingleton(Symbol.for("openclaw.diagnosticToolExecutionLiveness"), () => ({
	context: new AsyncLocalStorage(),
	events: /* @__PURE__ */ new WeakMap()
}));
Object.freeze({});
//#endregion
//#region src/shared/dot-path.ts
/** Appends one config path segment without confusing literal record keys with traversal. */
function appendConfigPathSegment(path, segment) {
	if (typeof segment === "number") return `${path}[${segment}]`;
	if (!/^[A-Za-z_$][A-Za-z0-9_$:-]*$/.test(segment)) return `${path}[${JSON.stringify(segment)}]`;
	return path ? `${path}.${segment}` : segment;
}
//#endregion
//#region src/config/env-substitution.ts
const ENV_VAR_NAME_PATTERN = /^[A-Z_][A-Z0-9_]*$/;
const DEFAULT_VALUE_OPERATOR = ":-";
/** Error thrown when a config value references a missing or empty environment variable. */
var MissingEnvVarError = class extends Error {
	constructor(varName, configPath) {
		super(`Missing env var "${varName}" referenced at config path: ${configPath}`);
		this.varName = varName;
		this.configPath = configPath;
		this.name = "MissingEnvVarError";
	}
};
/** Rebuilds the authored placeholder text for a parsed token. */
function renderEnvTemplateToken(token) {
	return token.defaultValue === void 0 ? `\${${token.name}}` : `\${${token.name}${DEFAULT_VALUE_OPERATOR}${token.defaultValue}}`;
}
function* iterateEnvTemplateTokens(value) {
	for (let index = value.indexOf("$"); index !== -1; index = value.indexOf("$", index + 1)) {
		const escaped = value[index + 1] === "$" && value[index + 2] === "{";
		if (!escaped && value[index + 1] !== "{") continue;
		const start = index + (escaped ? 3 : 2);
		const end = value.indexOf("}", start);
		if (end === -1) continue;
		const body = value.slice(start, end);
		const operatorIndex = body.indexOf(DEFAULT_VALUE_OPERATOR);
		const name = operatorIndex === -1 ? body : body.slice(0, operatorIndex);
		const defaultValue = operatorIndex === -1 ? void 0 : body.slice(operatorIndex + 2);
		if (!ENV_VAR_NAME_PATTERN.test(name) || defaultValue?.includes("$") || defaultValue?.includes("{")) continue;
		yield {
			kind: escaped ? "escaped" : "substitution",
			name,
			defaultValue,
			start: index,
			end
		};
		index = end;
	}
}
function substituteString(value, env, configPath, opts) {
	if (!value.includes("$")) return value;
	const authoredRef = parseEnvTemplateSecretRef(value);
	if (authoredRef && !containsEnvVarReference(value)) opts?.onPendingEnvSecretRef?.(authoredRef.id, configPath);
	const chunks = [];
	let end = 0;
	for (const token of iterateEnvTemplateTokens(value)) {
		chunks.push(value.slice(end, token.start));
		end = token.end + 1;
		if (token.kind === "escaped") {
			chunks.push(renderEnvTemplateToken(token));
			continue;
		}
		const envValue = env[token.name];
		if (envValue === void 0 || envValue === "") {
			if (token.defaultValue !== void 0) {
				chunks.push(token.defaultValue);
				continue;
			}
			if (opts?.onMissing) {
				opts.onMissing({
					varName: token.name,
					configPath
				});
				if (authoredRef?.id === token.name) opts.onPendingEnvSecretRef?.(token.name, configPath);
				chunks.push(renderEnvTemplateToken(token));
				continue;
			}
			throw new MissingEnvVarError(token.name, configPath);
		}
		if (authoredRef?.id === token.name) opts?.onResolvedEnvSecretRef?.(token.name, configPath);
		chunks.push(envValue);
	}
	chunks.push(value.slice(end));
	return chunks.join("");
}
/** Detects unescaped `${VAR}` references without treating escaped `$${VAR}` as references. */
function containsEnvVarReference(value) {
	for (const token of iterateEnvTemplateTokens(value)) if (token.kind === "substitution") return true;
	return false;
}
/** Resolve config string templates, preserving unresolved text when onMissing is supplied. */
function resolveConfigEnvVars(value, env = process.env, opts) {
	const pending = [];
	const visit = (current, currentPath) => {
		if (typeof current === "string") return substituteString(current, env, currentPath, opts);
		if (Array.isArray(current)) {
			const length = current.length;
			const result = [];
			result.length = length;
			let index = 0;
			pending.push(() => {
				while (index < length) {
					const key = index++;
					if (key in current) {
						result[key] = visit(current[key], `${currentPath}[${key}]`);
						return true;
					}
				}
				return false;
			});
			return result;
		}
		if (isPlainObject(current)) {
			const result = {};
			const entries = Object.entries(current)[Symbol.iterator]();
			pending.push(() => {
				const entry = entries.next();
				if (entry.done) return false;
				const [key, child] = entry.value;
				result[key] = visit(child, appendConfigPathSegment(currentPath, key));
				return true;
			});
			return result;
		}
		return current;
	};
	const result = visit(value, "");
	for (let next = pending.at(-1); next; next = pending.at(-1)) if (!next()) pending.pop();
	return result;
}
//#endregion
//#region src/utils/parse-json-compat.ts
/**
* JSON parser compatibility helper for persisted config, manifests, and legacy stores.
* Strict JSON stays the fast path; JSON5 is only the authored/legacy fallback.
*/
let json5Runtime;
function isJson5Parser(value) {
	return typeof value === "object" && value !== null && "parse" in value && typeof value.parse === "function";
}
function setJson5Runtime(runtime) {
	const parser = isJson5Parser(runtime) ? runtime : typeof runtime === "object" && runtime !== null && "default" in runtime ? runtime.default : void 0;
	if (!isJson5Parser(parser)) throw new Error("json5 parser unavailable");
	json5Runtime = parser;
	return parser;
}
function loadJson5Parser() {
	if (json5Runtime) return json5Runtime;
	if (typeof SEALED_RUNTIME_BUILD === "boolean" && SEALED_RUNTIME_BUILD) throw new Error("sealed JSON5 runtime was not registered before use");
	return setJson5Runtime(createRequire(import.meta.url)("json5"));
}
/** Parses strict JSON first, then accepts JSON5 syntax such as comments and trailing commas. */
function parseJsonWithJson5Fallback(raw, json5) {
	try {
		return JSON.parse(raw);
	} catch {
		return (json5 ?? loadJson5Parser()).parse(raw);
	}
}
//#endregion
//#region src/config/includes.ts
const INCLUDE_KEY = "$include";
const MAX_INCLUDE_FILE_BYTES = 2097152;
/** Maximum length for $include path and resolved path (CWE-22 hardening). */
const MAX_INCLUDE_PATH_LENGTH = 4096;
function isConfigContainer(value) {
	return Array.isArray(value) || isPlainObject(value);
}
function includeLogicalPath(link) {
	const parts = [];
	for (let current = link; current; current = current.parent) parts.push(current.key);
	return parts.toReversed();
}
var ConfigIncludeError = class extends Error {
	constructor(message, includePath, cause) {
		super(message);
		this.includePath = includePath;
		this.cause = cause;
		this.name = "ConfigIncludeError";
	}
};
/** File access failed; the included configuration has not been validated. */
var ConfigIncludeReadError = class extends ConfigIncludeError {
	constructor(..._args) {
		super(..._args);
		this.name = "ConfigIncludeReadError";
	}
};
var CircularIncludeError = class extends ConfigIncludeError {
	constructor(chain) {
		super(`Circular include detected: ${chain.join(" -> ")}`, expectDefined(chain[chain.length - 1], "chain entry at chain.length 1"));
		this.chain = chain;
		this.name = "CircularIncludeError";
	}
};
/** Deep merge: arrays concatenate, objects merge recursively, primitives: source wins */
function deepMerge(target, source) {
	return mergeDeep(target, source, {
		arrays: "concat",
		undefinedValues: "replace"
	});
}
var IncludeProcessor = class {
	constructor(basePath, resolver, boundary, rootProjectionKeys) {
		this.basePath = basePath;
		this.resolver = resolver;
		this.boundary = boundary;
		this.rootProjectionKeys = rootProjectionKeys;
	}
	get rootDir() {
		return this.boundary.configRoot.rootDir;
	}
	process(obj) {
		const stack = [];
		let current = this.traverseValue(obj, null, false, {
			basePath: this.basePath,
			visited: /* @__PURE__ */ new Set([path.normalize(this.basePath)]),
			depth: 0
		});
		let delivered = void 0;
		for (;;) {
			const step = current.next(delivered);
			if (step.done) {
				const parent = stack.pop();
				if (!parent) return step.value;
				delivered = step.value;
				current = parent;
				continue;
			}
			stack.push(current);
			current = this.traverseValue(step.value.value, step.value.pathLink, step.value.hasArrayAncestor, step.value.context);
		}
	}
	*traverseValue(value, pathLink, hasArrayAncestor, context) {
		if (Array.isArray(value)) {
			const result = [];
			for (const [index, item] of value.entries()) result.push(isConfigContainer(item) ? yield {
				value: item,
				pathLink: {
					parent: pathLink,
					key: String(index)
				},
				hasArrayAncestor: true,
				context
			} : item);
			return result;
		}
		if (!isPlainObject(value)) return value;
		if (!("$include" in value)) {
			const isRoot = pathLink === null;
			const result = {};
			for (const [key, entry] of Object.entries(value)) {
				if (isRoot && this.rootProjectionKeys && !this.rootProjectionKeys.has(key)) continue;
				result[key] = isConfigContainer(entry) ? yield {
					value: entry,
					pathLink: {
						parent: pathLink,
						key
					},
					hasArrayAncestor,
					context
				} : entry;
			}
			return result;
		}
		return yield* this.traverseInclude(value, pathLink, hasArrayAncestor, context);
	}
	*traverseInclude(obj, pathLink, hasArrayAncestor, context) {
		const includeValue = obj[INCLUDE_KEY];
		const isRoot = pathLink === null;
		const siblingKeys = Object.keys(obj).filter((key) => key !== "$include" && (!isRoot || !this.rootProjectionKeys || this.rootProjectionKeys.has(key)));
		const multiple = Array.isArray(includeValue);
		if (!multiple && typeof includeValue !== "string") throw new ConfigIncludeError(`Invalid $include value: expected string or array of strings, got ${typeof includeValue}`, String(includeValue));
		const entries = [];
		for (const item of multiple ? includeValue : [includeValue]) {
			if (typeof item !== "string") throw new ConfigIncludeError(`Invalid $include array item: expected string, got ${typeof item}`, String(item));
			const loaded = this.loadFileSync(item, context);
			entries.push({
				value: yield {
					value: loaded.parsed,
					pathLink,
					hasArrayAncestor,
					context: loaded.nestedContext
				},
				targetPath: loaded.resolvedPath
			});
		}
		const included = multiple ? entries.reduce((current, entry) => deepMerge(current, entry.value), {}) : entries[0].value;
		this.resolver.onIncludeResolved?.({
			path: includeLogicalPath(pathLink),
			value: included,
			kind: multiple ? "multiple" : "single",
			hasSiblingOverrides: siblingKeys.length > 0,
			hasArrayAncestor,
			...multiple ? { targetPaths: entries.map((entry) => entry.targetPath) } : { targetPath: entries[0].targetPath }
		});
		if (siblingKeys.length === 0) return included;
		if (!isPlainObject(included)) throw new ConfigIncludeError("Sibling keys require included content to be an object", typeof includeValue === "string" ? includeValue : INCLUDE_KEY);
		const rest = {};
		for (const key of siblingKeys) {
			const sibling = obj[key];
			rest[key] = isConfigContainer(sibling) ? yield {
				value: sibling,
				pathLink: {
					parent: pathLink,
					key
				},
				hasArrayAncestor,
				context
			} : sibling;
		}
		return deepMerge(included, rest);
	}
	loadFileSync(includePath, context) {
		const { resolvedPath, root } = this.resolvePath(includePath, context);
		if (context.visited.has(resolvedPath)) throw new CircularIncludeError([...context.visited, resolvedPath]);
		if (context.depth >= 10) throw new ConfigIncludeError(`Maximum include depth (10) exceeded at: ${includePath}`, includePath);
		const raw = this.readFile(includePath, resolvedPath, root);
		return {
			parsed: this.parseFile(includePath, resolvedPath, raw),
			resolvedPath,
			nestedContext: {
				basePath: resolvedPath,
				visited: /* @__PURE__ */ new Set([...context.visited, resolvedPath]),
				depth: context.depth + 1
			}
		};
	}
	resolvePath(includePath, context) {
		if (includePath.includes("\0")) throw new ConfigIncludeError("Include path must not contain null bytes", includePath);
		if (includePath.length >= MAX_INCLUDE_PATH_LENGTH) throw new ConfigIncludeError(`Include path exceeds maximum length (${MAX_INCLUDE_PATH_LENGTH} characters)`, includePath);
		const configDir = path.dirname(context.basePath);
		const resolved = path.isAbsolute(includePath) ? includePath : path.resolve(configDir, includePath);
		const normalized = path.normalize(resolved);
		if (normalized.length >= MAX_INCLUDE_PATH_LENGTH) throw new ConfigIncludeError(`Resolved include path exceeds maximum length (${MAX_INCLUDE_PATH_LENGTH} characters)`, includePath);
		const lexicalMatch = this.findContainingRoot(normalized, "rootDir");
		if (!lexicalMatch) throw new ConfigIncludeError(`Include path escapes config directory: ${includePath} (root: ${this.rootDir})`, includePath);
		this.resolver.onLexicalPath?.(normalized);
		try {
			const real = fs$1.realpathSync(normalized);
			const realMatch = this.findContainingRoot(real, "rootRealDir");
			if (!realMatch) throw new ConfigIncludeError(`Include path resolves outside config directory (symlink): ${includePath} (root: ${this.rootDir})`, includePath);
			return {
				resolvedPath: normalized,
				root: realMatch
			};
		} catch (err) {
			if (err instanceof ConfigIncludeError) throw err;
			if (isMissingPathError(err)) return {
				resolvedPath: normalized,
				root: lexicalMatch
			};
			throw new ConfigIncludeReadError(`Failed to resolve include file realpath: ${includePath} (resolved: ${normalized})`, includePath, err instanceof Error ? err : void 0);
		}
	}
	findContainingRoot(candidate, field) {
		if (isPathInside(this.boundary.configRoot[field], candidate)) return this.boundary.configRoot;
		for (const root of this.boundary.allowedRoots) if (isPathInside(root[field], candidate)) return root;
		return null;
	}
	readFile(includePath, resolvedPath, root) {
		try {
			if (this.resolver.readFileWithGuards) return this.resolver.readFileWithGuards({
				includePath,
				resolvedPath,
				rootRealDir: root.rootRealDir
			});
			return this.resolver.readFile(resolvedPath);
		} catch (err) {
			if (err instanceof ConfigIncludeError) throw err;
			throw new ConfigIncludeReadError(`Failed to read include file: ${includePath} (resolved: ${resolvedPath})`, includePath, err instanceof Error ? err : void 0);
		}
	}
	parseFile(includePath, resolvedPath, raw) {
		try {
			return this.resolver.parseJson(raw);
		} catch (err) {
			throw new ConfigIncludeError(`Failed to parse include file: ${includePath} (resolved: ${resolvedPath})`, includePath, err instanceof Error ? err : void 0);
		}
	}
};
function safeRealpath(target) {
	try {
		return fs$1.realpathSync(target);
	} catch {
		return target;
	}
}
/** Capture the lexical and canonical include roots once for a resolver traversal. */
function createConfigIncludeBoundary(configPath, allowedRoots = []) {
	const configRootDir = path.normalize(path.dirname(configPath));
	return {
		configRoot: {
			rootDir: configRootDir,
			rootRealDir: path.normalize(safeRealpath(configRootDir))
		},
		allowedRoots: allowedRoots.filter((entry) => typeof entry === "string" && entry.length > 0 && path.isAbsolute(entry)).map((entry) => {
			const rootDir = path.normalize(entry);
			return {
				rootDir,
				rootRealDir: path.normalize(safeRealpath(rootDir))
			};
		})
	};
}
function readConfigIncludeFileWithGuards(params) {
	const ioFs = params.ioFs ?? fs$1;
	const maxBytes = params.maxBytes ?? MAX_INCLUDE_FILE_BYTES;
	if (!canUseRootFileOpen(ioFs)) {
		const raw = ioFs.readFileSync(params.resolvedPath, "utf-8");
		try {
			params.onResolvedPath?.(path.normalize(ioFs.realpathSync(params.resolvedPath)));
		} catch {}
		return raw;
	}
	const opened = openRootFileSync({
		absolutePath: params.resolvedPath,
		rootPath: params.rootRealDir,
		rootRealPath: params.rootRealDir,
		boundaryLabel: "config directory",
		skipLexicalRootCheck: true,
		rejectSymlinks: false,
		maxBytes,
		ioFs
	});
	if (!opened.ok) {
		if (opened.reason === "validation") throw new ConfigIncludeError(`Include file failed security checks (regular file, max ${maxBytes} bytes, no hardlinks): ${params.includePath}`, params.includePath);
		throw new ConfigIncludeReadError(`Failed to read include file: ${params.includePath} (resolved: ${params.resolvedPath})`, params.includePath, opened.error instanceof Error ? opened.error : void 0);
	}
	try {
		const raw = ioFs.readFileSync(opened.fd, "utf-8");
		params.onResolvedPath?.(path.normalize(opened.path));
		return raw;
	} finally {
		ioFs.closeSync(opened.fd);
	}
}
const defaultResolver = {
	readFile: (p) => fs$1.readFileSync(p, "utf-8"),
	readFileWithGuards: readConfigIncludeFileWithGuards,
	parseJson: parseJsonWithJson5Fallback
};
/**
* Resolves all $include directives in a parsed config object.
*/
function resolveConfigIncludes(obj, configPath, resolver = defaultResolver, options = {}) {
	return new IncludeProcessor(configPath, resolver, createConfigIncludeBoundary(configPath, options.allowedRoots ?? [])).process(obj);
}
/**
* Resolves one top-level config field through the canonical include graph while
* leaving unrelated top-level branches untouched. Early bootstrap readers use
* this when a malformed sibling must not hide an independently valid setting.
*/
function resolveConfigIncludesForTopLevelKey(obj, configPath, key, resolver = defaultResolver, options = {}) {
	return new IncludeProcessor(configPath, resolver, createConfigIncludeBoundary(configPath, options.allowedRoots ?? []), /* @__PURE__ */ new Set([key])).process(obj);
}
//#endregion
//#region src/logging/state.ts
const LOGGING_STATE_KEY = Symbol.for("openclaw.loggingState");
const APPLIED_LOGGING_CONFIG_UNOWNED = "unowned";
function createUnownedAppliedLoggingConfig() {
	return APPLIED_LOGGING_CONFIG_UNOWNED;
}
function createLoggingState() {
	return {
		appliedConfig: createUnownedAppliedLoggingConfig(),
		cachedLogger: null,
		cachedSettings: null,
		cachedConsoleSettings: null,
		overrideSettings: null,
		invalidEnvLogLevelValue: null,
		consolePatched: false,
		forceConsoleToStderr: false,
		earlyConsoleRoutingRestore: null,
		consoleTimestampPrefix: false,
		consoleSubsystemFilter: null,
		streamErrorHandlersInstalled: false,
		rawConsole: null
	};
}
const globalStore = globalThis;
const loggingState = globalStore[LOGGING_STATE_KEY] ?? createLoggingState();
if (!Object.hasOwn(loggingState, "appliedConfig")) loggingState.appliedConfig = APPLIED_LOGGING_CONFIG_UNOWNED;
globalStore[LOGGING_STATE_KEY] = loggingState;
//#endregion
//#region src/logging/config.ts
let cachedLoggingConfig;
function resolveLoggingConfigSelector() {
	const env = process.env;
	return [
		env.OPENCLAW_CONFIG_PATH,
		env.OPENCLAW_STATE_DIR,
		env.OPENCLAW_HOME,
		env.OPENCLAW_PROFILE,
		env.HOME,
		env.USERPROFILE,
		env.HOMEDRIVE,
		env.HOMEPATH,
		env.PREFIX,
		env.ANDROID_DATA,
		env.OPENCLAW_TEST_FAST,
		tryProcessCwd() ?? ""
	].map((value) => value ?? "").join("\0");
}
function resolvePartialDiagnosticLoggingConfig(logging) {
	if (!isRecord(logging)) return;
	const partial = {};
	if (typeof logging.consoleStyle === "string") try {
		const resolved = resolveConfigEnvVars({ consoleStyle: logging.consoleStyle });
		if (isRecord(resolved) && (resolved.consoleStyle === "pretty" || resolved.consoleStyle === "compact" || resolved.consoleStyle === "json")) partial.consoleStyle = resolved.consoleStyle;
	} catch {}
	if (Array.isArray(logging.redactPatterns)) try {
		const resolved = resolveConfigEnvVars({ redactPatterns: logging.redactPatterns });
		if (isRecord(resolved) && Array.isArray(resolved.redactPatterns) && resolved.redactPatterns.every((entry) => typeof entry === "string")) partial.redactPatterns = resolved.redactPatterns;
	} catch {}
	return Object.keys(partial).length > 0 ? partial : void 0;
}
/** Reads the logging block from config, caching by resolved config path. */
function readLoggingConfig() {
	try {
		if (loggingState.appliedConfig !== "unowned") return loggingState.appliedConfig;
		const selector = resolveLoggingConfigSelector();
		if (cachedLoggingConfig?.selector === selector) return cachedLoggingConfig.logging;
		const configPath = resolveConfigPath();
		if (!fs$1.existsSync(configPath)) {
			cachedLoggingConfig = {
				selector,
				logging: void 0
			};
			return;
		}
		const parsed = parseJsonWithJson5Fallback(fs$1.readFileSync(configPath, "utf8"));
		const allowedRoots = resolveIncludeRoots();
		let includedConfig;
		try {
			includedConfig = resolveConfigIncludesForTopLevelKey(parsed, configPath, "logging", void 0, { allowedRoots });
		} catch {
			const directLogging = isRecord(parsed) ? parsed.logging : void 0;
			if (directLogging === void 0) return;
			try {
				includedConfig = resolveConfigIncludes({ logging: directLogging }, configPath, void 0, { allowedRoots });
			} catch {
				return resolvePartialDiagnosticLoggingConfig(directLogging);
			}
		}
		let resolvedConfig;
		try {
			resolvedConfig = resolveConfigEnvVars(includedConfig);
		} catch {
			return resolvePartialDiagnosticLoggingConfig(isRecord(includedConfig) ? includedConfig.logging : void 0);
		}
		const logging = isRecord(resolvedConfig) ? resolvedConfig.logging : void 0;
		const resolvedLogging = isRecord(logging) ? logging : void 0;
		cachedLoggingConfig = {
			selector,
			logging: resolvedLogging
		};
		return resolvedLogging;
	} catch {
		return;
	}
}
//#endregion
//#region packages/net-policy/src/redact-sensitive-url.ts
const SENSITIVE_URL_QUERY_PARAM_NAMES = /* @__PURE__ */ new Set([
	"token",
	"key",
	"api_key",
	"apikey",
	"secret",
	"access_token",
	"auth_token",
	"password",
	"pass",
	"passwd",
	"auth",
	"jwt",
	"session",
	"id_token",
	"code",
	"client_secret",
	"app_secret",
	"hook_token",
	"refresh_token",
	"signature",
	"x_amz_signature",
	"x_amz_security_token",
	"private_key",
	"credential",
	"authorization",
	"sig",
	"x_api_key",
	"x_access_token",
	"x_auth_token"
]);
const URL_QUERY_NAME_SEPARATOR_RE = /[\p{C}\p{Z}\u115F\u1160\u3164\uFFA0+]/gu;
const SUFFIXED_OR_SCOPED_TOKEN_QUERY_PARAM_RE = /(?:^|_)token(?:_[a-f0-9]{16,})?$/u;
const MAX_NESTED_URL_REDACTION_DEPTH = 8;
function normalizeUrlQueryParamName(name) {
	let current = name.replace(URL_QUERY_NAME_SEPARATOR_RE, "");
	for (let depth = 0; depth <= MAX_NESTED_URL_REDACTION_DEPTH; depth += 1) {
		let decoded;
		try {
			decoded = decodeURIComponent(current).replace(URL_QUERY_NAME_SEPARATOR_RE, "");
		} catch {
			break;
		}
		if (decoded === current) return {
			value: normalizeLowercaseStringOrEmpty(current).replaceAll("-", "_"),
			unresolvedEncoding: false
		};
		current = decoded;
	}
	return {
		value: normalizeLowercaseStringOrEmpty(current).replaceAll("-", "_"),
		unresolvedEncoding: current.includes("%")
	};
}
/** True for auth-like URL query parameter names that should be redacted. */
function isSensitiveUrlQueryParamName(name) {
	const normalized = normalizeUrlQueryParamName(name);
	return normalized.unresolvedEncoding || SENSITIVE_URL_QUERY_PARAM_NAMES.has(normalized.value) || SUFFIXED_OR_SCOPED_TOKEN_QUERY_PARAM_RE.test(normalized.value);
}
//#endregion
//#region packages/acp-core/src/structured-auth-redaction.ts
const HTTP_AUTH_SCHEME_PATTERN = "[A-Za-z0-9!#$%&'*+.^_`|~-]+";
const HTTP_AUTH_OPAQUE_CREDENTIAL_PATTERN = String.raw`(?:\[REDACTED\]|[^\s\\"',;&#?<>)}\]]+)`;
const HTTP_AUTH_SERIALIZED_TAB_PATTERN = String.raw`\\{1,64}t`;
const HTTP_AUTH_SERIALIZED_INDENT_PATTERN = String.raw`(?:[ \t]+|${HTTP_AUTH_SERIALIZED_TAB_PATTERN})`;
const HTTP_AUTH_OPTIONAL_VALUE_WHITESPACE_PATTERN = String.raw`(?:[ \t]*\r?\n${HTTP_AUTH_SERIALIZED_INDENT_PATTERN}|[ \t]*\\{1,64}r\\{1,64}n${HTTP_AUTH_SERIALIZED_INDENT_PATTERN}|[ \t]*\\{1,64}n${HTTP_AUTH_SERIALIZED_INDENT_PATTERN}|[ \t]*${HTTP_AUTH_SERIALIZED_TAB_PATTERN}[ \t]*|[ \t]*)`;
const HTTP_AUTH_REQUIRED_VALUE_WHITESPACE_PATTERN = String.raw`(?:[ \t]*\r?\n${HTTP_AUTH_SERIALIZED_INDENT_PATTERN}|[ \t]*\\{1,64}r\\{1,64}n${HTTP_AUTH_SERIALIZED_INDENT_PATTERN}|[ \t]*\\{1,64}n${HTTP_AUTH_SERIALIZED_INDENT_PATTERN}|[ \t]*${HTTP_AUTH_SERIALIZED_TAB_PATTERN}[ \t]*|[ \t]+)`;
const HTTP_AUTH_LEGACY_VALUE_WHITESPACE_PATTERN = String.raw`(?:[ \t\r\n]*|[ \t]*\\{1,64}r\\{1,64}n(?:[ \t]*|${HTTP_AUTH_SERIALIZED_TAB_PATTERN})|[ \t]*\\{1,64}n(?:[ \t]*|${HTTP_AUTH_SERIALIZED_TAB_PATTERN})|[ \t]*${HTTP_AUTH_SERIALIZED_TAB_PATTERN}[ \t]*)`;
const HTTP_AUTH_HEADER_BOUNDARY_PATTERN = String.raw`(^|[^A-Za-z0-9_-]|\\{1,64}[rn])`;
const HTTP_AUTH_SERIALIZED_QUOTE_PATTERN = String.raw`(?:\\{1,64}["']|["']|)`;
String.raw`${HTTP_AUTH_HEADER_BOUNDARY_PATTERN}(?:x-goog-api-key|api-key|apikey|x-api-token|x-access-token)${HTTP_AUTH_SERIALIZED_QUOTE_PATTERN}[ \t]*[:=]${HTTP_AUTH_OPTIONAL_VALUE_WHITESPACE_PATTERN}${HTTP_AUTH_SERIALIZED_QUOTE_PATTERN}([^\s\\"',;]+)`;
const STRUCTURED_AUTH_HEADER_RE = new RegExp(String.raw`${HTTP_AUTH_HEADER_BOUNDARY_PATTERN}(?:Proxy-)?Authorization${HTTP_AUTH_SERIALIZED_QUOTE_PATTERN}[ \t]*[:=]${HTTP_AUTH_OPTIONAL_VALUE_WHITESPACE_PATTERN}${HTTP_AUTH_SERIALIZED_QUOTE_PATTERN}(${HTTP_AUTH_SCHEME_PATTERN})${HTTP_AUTH_REQUIRED_VALUE_WHITESPACE_PATTERN}`, "giu");
const AUTH_PARAM_TOKEN_RE = /^[A-Za-z0-9!#$%&'*+.^_`|~-]+/u;
const AWS_SCOPE_VALUE_RE = /^[A-Za-z0-9!#$%&'*+.^_`|~:/-]+/u;
function skipHorizontalWhitespace(value, start) {
	let cursor = start;
	while (value[cursor] === " " || value[cursor] === "	") cursor += 1;
	return cursor;
}
function skipSerializedBackslashes(value, start) {
	let cursor = start;
	while (cursor - start < 64 && value[cursor] === "\\") cursor += 1;
	return cursor;
}
function readSerializedLineEnd(value, start) {
	const cursor = skipSerializedBackslashes(value, start);
	if (cursor === start) return null;
	if (value[cursor] === "n") return cursor + 1;
	if (value[cursor] !== "r") return null;
	const lineEnd = skipSerializedBackslashes(value, cursor + 1);
	return lineEnd > cursor + 1 && value[lineEnd] === "n" ? lineEnd + 1 : null;
}
function readSerializedTabEnd(value, start) {
	const cursor = skipSerializedBackslashes(value, start);
	return cursor > start && value[cursor] === "t" ? cursor + 1 : null;
}
function skipAuthWhitespace(value, start) {
	let cursor = start;
	for (;;) {
		cursor = skipHorizontalWhitespace(value, cursor);
		const tabEnd = readSerializedTabEnd(value, cursor);
		if (tabEnd !== null) {
			cursor = tabEnd;
			continue;
		}
		const lineEnd = value[cursor] === "\r" && value[cursor + 1] === "\n" ? cursor + 2 : value[cursor] === "\n" ? cursor + 1 : readSerializedLineEnd(value, cursor);
		if (lineEnd === null || value[lineEnd] !== " " && value[lineEnd] !== "	" && readSerializedTabEnd(value, lineEnd) === null) return cursor;
		cursor = lineEnd;
	}
}
function readAuthParamName(value, start) {
	const match = AUTH_PARAM_TOKEN_RE.exec(value.slice(start));
	return match ? {
		name: match[0].toLowerCase(),
		end: start + match[0].length
	} : null;
}
function isAuthHeaderStart(value, index) {
	const previous = value[index - 1];
	if (!((previous === "n" || previous === "r") && value[index - 2] === "\\") && previous !== void 0 && /[A-Za-z0-9_-]/u.test(previous)) return false;
	const proxyName = "proxy-authorization";
	const directName = "authorization";
	const candidate = value.slice(index, index + 19).toLowerCase();
	const name = candidate === proxyName ? proxyName : candidate.startsWith(directName) ? directName : null;
	if (!name) return false;
	const quoteStart = index + name.length;
	let cursor = skipSerializedBackslashes(value, quoteStart);
	if (value[cursor] === "\"" || value[cursor] === "'") cursor += 1;
	else if (cursor > quoteStart) return false;
	cursor = skipHorizontalWhitespace(value, cursor);
	return value[cursor] === ":" || value[cursor] === "=";
}
function findNextAuthParamStart(value, start) {
	let cursor = start;
	for (;;) {
		cursor = skipAuthWhitespace(value, cursor);
		if (cursor > start && isAuthHeaderStart(value, cursor)) return null;
		if (cursor >= value.length || value[cursor] === "\r" || value[cursor] === "\n" || value[cursor] === ";") return null;
		if (value[cursor] === ",") {
			cursor += 1;
			continue;
		}
		const param = readAuthParamName(value, cursor);
		if (param) {
			const equals = skipAuthWhitespace(value, param.end);
			if (value[equals] === "=" && value[equals + 1] !== "=") return cursor;
		}
		while (cursor < value.length) {
			const whitespaceEnd = skipAuthWhitespace(value, cursor);
			if (whitespaceEnd > cursor) {
				cursor = whitespaceEnd;
				continue;
			}
			if (cursor > start && isAuthHeaderStart(value, cursor)) return null;
			const char = value[cursor];
			if (char === "\r" || char === "\n" || char === ";") return null;
			cursor += 1;
			if (char === ",") break;
		}
	}
}
function usesAuthParams(scheme) {
	return scheme === "digest" || scheme === "hawk" || scheme.startsWith("aws4-");
}
function isAuthFieldEnd(char) {
	return char === void 0 || char === "\r" || char === "\n" || char === ";" || char === "\\" || char === "\"" || char === "'" || char === "}" || char === "]";
}
function findAuthFieldEnd(value, start) {
	let cursor = start;
	while (cursor < value.length) {
		const whitespaceEnd = skipAuthWhitespace(value, cursor);
		if (whitespaceEnd > cursor) {
			cursor = whitespaceEnd;
			continue;
		}
		if (cursor > start && isAuthHeaderStart(value, cursor)) break;
		if (isAuthFieldEnd(value[cursor])) break;
		cursor += 1;
	}
	return cursor;
}
function readParamValue(value, start, options) {
	let escapedQuoteSlashCount = 0;
	while (value[start + escapedQuoteSlashCount] === "\\") escapedQuoteSlashCount += 1;
	const escapedQuotes = escapedQuoteSlashCount > 0 && value[start + escapedQuoteSlashCount] === "\"";
	const quote = value[start] === "\"" || value[start] === "'" ? value[start] : void 0;
	if (quote || escapedQuotes) {
		let cursor = start + (escapedQuotes ? escapedQuoteSlashCount + 1 : 1);
		while (cursor < value.length) {
			if (value[cursor] === "\r" || value[cursor] === "\n") {
				const whitespaceEnd = skipAuthWhitespace(value, cursor);
				if (whitespaceEnd === cursor) break;
				cursor = whitespaceEnd;
				continue;
			}
			if (escapedQuotes && value[cursor] === "\\") {
				let slashEnd = cursor + 1;
				while (value[slashEnd] === "\\") slashEnd += 1;
				if (value[slashEnd] === "\"") {
					if ((slashEnd - cursor) % (2 * (escapedQuoteSlashCount + 1)) === escapedQuoteSlashCount) return slashEnd + 1;
					cursor = slashEnd + 1;
					continue;
				}
				cursor = slashEnd;
				continue;
			}
			if (!escapedQuotes && value[cursor] === "\\" && cursor + 1 < value.length) {
				cursor += 2;
				continue;
			}
			if (!escapedQuotes && value[cursor] === quote) return cursor + 1;
			cursor += 1;
		}
		return cursor > start + 1 ? cursor : null;
	}
	if (options.signedHeaders) {
		const match = /^:?[A-Za-z0-9!#$%&'*+.^_`|~-]+(?:;:?[A-Za-z0-9!#$%&'*+.^_`|~-]+)*/u.exec(value.slice(start));
		if (!match) return null;
		const end = start + match[0].length;
		const next = value[end];
		return next === void 0 || next === "," || next === " " || next === "	" || next === "\r" || next === "\n" ? end : null;
	}
	const match = (options.awsScope ? AWS_SCOPE_VALUE_RE : AUTH_PARAM_TOKEN_RE).exec(value.slice(start));
	return match ? start + match[0].length : null;
}
function findStructuredAuthParamRanges(value) {
	const ranges = [];
	for (const header of value.matchAll(STRUCTURED_AUTH_HEADER_RE)) {
		const scheme = (header[2] ?? "").toLowerCase();
		let cursor = (header.index ?? 0) + header[0].length;
		const rangeStart = cursor;
		let rangeEnd = cursor;
		const directParam = readAuthParamName(value, cursor);
		const directEquals = directParam ? skipAuthWhitespace(value, directParam.end) : void 0;
		if (!directParam || directEquals === void 0 || value[directEquals] !== "=" || value[directEquals + 1] === "=") {
			if (value[skipAuthWhitespace(value, cursor)] !== "," && !usesAuthParams(scheme)) continue;
			const firstParamStart = findNextAuthParamStart(value, cursor);
			if (firstParamStart === null) continue;
			cursor = firstParamStart;
		}
		for (;;) {
			const param = readAuthParamName(value, cursor);
			if (!param) break;
			cursor = skipAuthWhitespace(value, param.end);
			if (value[cursor] !== "=") break;
			cursor = skipAuthWhitespace(value, cursor + 1);
			const valueEnd = readParamValue(value, cursor, {
				awsScope: scheme.startsWith("aws4-") && param.name === "credential",
				signedHeaders: param.name === "signedheaders"
			});
			if (valueEnd === null) {
				const nextParamStart = findNextAuthParamStart(value, cursor);
				if (nextParamStart !== null) {
					cursor = nextParamStart;
					continue;
				}
				rangeEnd = Math.max(rangeEnd, findAuthFieldEnd(value, cursor));
				break;
			}
			rangeEnd = valueEnd;
			const separator = skipAuthWhitespace(value, valueEnd);
			if (value[separator] !== ",") {
				if (!isAuthFieldEnd(value[separator])) {
					const nextParamStart = findNextAuthParamStart(value, separator);
					if (nextParamStart !== null) {
						cursor = nextParamStart;
						continue;
					}
					rangeEnd = Math.max(rangeEnd, findAuthFieldEnd(value, separator));
				}
				break;
			}
			const nextParamStart = findNextAuthParamStart(value, separator + 1);
			if (nextParamStart === null) break;
			cursor = nextParamStart;
		}
		if (rangeEnd > rangeStart) ranges.push({
			start: rangeStart,
			end: rangeEnd
		});
	}
	return ranges;
}
function redactStructuredAuthHeaders(value, replacement) {
	const parts = [];
	let cursor = 0;
	for (const range of findStructuredAuthParamRanges(value)) {
		if (parts.length === 0 || range.start > cursor) parts.push(value.slice(cursor, range.start), replacement);
		cursor = Math.max(cursor, range.end);
	}
	parts.push(value.slice(cursor));
	return parts.join("");
}
//#endregion
//#region src/security/safe-regex.ts
const SAFE_REGEX_CACHE_MAX = 256;
const safeRegexCache = /* @__PURE__ */ new Map();
function createParseFrame() {
	return {
		lastToken: null,
		containsRepetition: false,
		hasAlternation: false,
		branchMinLength: 0,
		branchMaxLength: 0,
		altMinLength: null,
		altMaxLength: null
	};
}
function addLength(left, right) {
	if (!Number.isFinite(left) || !Number.isFinite(right)) return Number.POSITIVE_INFINITY;
	return left + right;
}
function multiplyLength(length, factor) {
	if (!Number.isFinite(length)) return factor === 0 ? 0 : Number.POSITIVE_INFINITY;
	return length * factor;
}
function recordAlternative(frame) {
	if (frame.altMinLength === null || frame.altMaxLength === null) {
		frame.altMinLength = frame.branchMinLength;
		frame.altMaxLength = frame.branchMaxLength;
		return;
	}
	frame.altMinLength = Math.min(frame.altMinLength, frame.branchMinLength);
	frame.altMaxLength = Math.max(frame.altMaxLength, frame.branchMaxLength);
}
function readQuantifier(source, index) {
	const ch = source[index];
	const consumed = source[index + 1] === "?" ? 2 : 1;
	if (ch === "*") return {
		consumed,
		minRepeat: 0,
		maxRepeat: null
	};
	if (ch === "+") return {
		consumed,
		minRepeat: 1,
		maxRepeat: null
	};
	if (ch === "?") return {
		consumed,
		minRepeat: 0,
		maxRepeat: 1
	};
	if (ch !== "{") return null;
	let i = index + 1;
	while (i < source.length && /\d/.test(source.charAt(i))) i += 1;
	if (i === index + 1) return null;
	const minRepeat = Number.parseInt(source.slice(index + 1, i), 10);
	let maxRepeat = minRepeat;
	if (source[i] === ",") {
		i += 1;
		const maxStart = i;
		while (i < source.length && /\d/.test(source.charAt(i))) i += 1;
		maxRepeat = i === maxStart ? null : Number.parseInt(source.slice(maxStart, i), 10);
	}
	if (source[i] !== "}") return null;
	i += 1;
	if (source[i] === "?") i += 1;
	if (maxRepeat !== null && maxRepeat < minRepeat) return null;
	return {
		consumed: i - index,
		minRepeat,
		maxRepeat
	};
}
function hasNestedRepetition(source) {
	const frames = [createParseFrame()];
	let inCharClass = false;
	const emitToken = (token) => {
		const frame = expectDefined(frames[frames.length - 1], "frames entry at frames.length 1");
		frame.lastToken = token;
		if (token.containsRepetition) frame.containsRepetition = true;
		frame.branchMinLength = addLength(frame.branchMinLength, token.minLength);
		frame.branchMaxLength = addLength(frame.branchMaxLength, token.maxLength);
	};
	const emitSimpleToken = () => {
		emitToken({
			containsRepetition: false,
			hasAmbiguousAlternation: false,
			minLength: 1,
			maxLength: 1
		});
	};
	for (let i = 0; i < source.length; i += 1) {
		const ch = source[i];
		if (inCharClass) {
			if (ch === "\\") i += 1;
			else if (ch === "]") inCharClass = false;
			continue;
		}
		if (ch === "\\") {
			i += 1;
			emitSimpleToken();
			continue;
		}
		if (ch === "[") {
			inCharClass = true;
			emitSimpleToken();
			continue;
		}
		if (ch === "(") {
			frames.push(createParseFrame());
			continue;
		}
		if (ch === ")") {
			if (frames.length > 1) {
				const frame = frames.pop();
				if (frame.hasAlternation) recordAlternative(frame);
				const groupMinLength = frame.hasAlternation ? frame.altMinLength ?? 0 : frame.branchMinLength;
				const groupMaxLength = frame.hasAlternation ? frame.altMaxLength ?? 0 : frame.branchMaxLength;
				emitToken({
					containsRepetition: frame.containsRepetition,
					hasAmbiguousAlternation: frame.hasAlternation && frame.altMinLength !== null && frame.altMaxLength !== null && frame.altMinLength !== frame.altMaxLength,
					minLength: groupMinLength,
					maxLength: groupMaxLength
				});
			}
			continue;
		}
		if (ch === "|") {
			const frame = expectDefined(frames[frames.length - 1], "frames entry at frames.length 1");
			frame.hasAlternation = true;
			recordAlternative(frame);
			frame.branchMinLength = 0;
			frame.branchMaxLength = 0;
			frame.lastToken = null;
			continue;
		}
		const quantifier = readQuantifier(source, i);
		if (!quantifier) {
			emitSimpleToken();
			continue;
		}
		i += quantifier.consumed - 1;
		const frame = expectDefined(frames[frames.length - 1], "frames entry at frames.length 1");
		const previousToken = frame.lastToken;
		if (!previousToken) continue;
		if (previousToken.containsRepetition) return true;
		if (previousToken.hasAmbiguousAlternation && quantifier.maxRepeat === null) return true;
		const previousMinLength = previousToken.minLength;
		const previousMaxLength = previousToken.maxLength;
		previousToken.minLength = multiplyLength(previousToken.minLength, quantifier.minRepeat);
		previousToken.maxLength = quantifier.maxRepeat === null ? Number.POSITIVE_INFINITY : multiplyLength(previousToken.maxLength, quantifier.maxRepeat);
		previousToken.containsRepetition = true;
		frame.containsRepetition = true;
		frame.branchMinLength = frame.branchMinLength - previousMinLength + previousToken.minLength;
		frame.branchMaxLength = addLength(Number.isFinite(frame.branchMaxLength) && Number.isFinite(previousMaxLength) ? frame.branchMaxLength - previousMaxLength : Number.POSITIVE_INFINITY, previousToken.maxLength);
	}
	return false;
}
function compileSafeRegexDetailed(source, flags = "") {
	const trimmed = source.trim();
	if (!trimmed) return {
		regex: null,
		source: trimmed,
		flags,
		reason: "empty"
	};
	const cacheKey = `${flags}::${trimmed}`;
	const cached = safeRegexCache.get(cacheKey);
	if (cached) return cached;
	let result;
	if (hasNestedRepetition(trimmed)) result = {
		regex: null,
		source: trimmed,
		flags,
		reason: "unsafe-nested-repetition"
	};
	else try {
		result = {
			regex: new RegExp(trimmed, flags),
			source: trimmed,
			flags,
			reason: null
		};
	} catch {
		result = {
			regex: null,
			source: trimmed,
			flags,
			reason: "invalid-regex"
		};
	}
	safeRegexCache.set(cacheKey, result);
	pruneMapToMaxSize(safeRegexCache, SAFE_REGEX_CACHE_MAX);
	return result;
}
//#endregion
//#region src/security/config-regex.ts
function normalizeRejectReason(result) {
	if (result.reason === null || result.reason === "empty") return null;
	return result.reason;
}
/**
* Compile a single user-configured regex with the shared safe-regex guardrails.
* Returns null for blank patterns so optional config entries can be skipped silently.
*/
function compileConfigRegex(pattern, flags = "") {
	const result = compileSafeRegexDetailed(pattern, flags);
	if (result.reason === "empty") return null;
	return {
		regex: result.regex,
		pattern: result.source,
		flags: result.flags,
		reason: normalizeRejectReason(result)
	};
}
//#endregion
//#region src/logging/redact-bounded.ts
const REDACT_REGEX_CHUNK_THRESHOLD = 32768;
const REDACT_REGEX_CHUNK_SIZE = 16384;
/** Applies a regex replacement in chunks once input crosses the redaction size threshold. */
function replacePatternBounded(text, pattern, replacer) {
	if (text.length <= REDACT_REGEX_CHUNK_THRESHOLD) return text.replace(pattern, replacer);
	let output;
	for (let index = 0; index < text.length; index += REDACT_REGEX_CHUNK_SIZE) {
		const chunk = text.slice(index, index + REDACT_REGEX_CHUNK_SIZE);
		const replaced = chunk.replace(pattern, replacer);
		if (output !== void 0) output += replaced;
		else if (replaced !== chunk) output = text.slice(0, index) + replaced;
	}
	return output ?? text;
}
//#endregion
//#region src/logging/redact-edit-composition.ts
/** Compose sorted, disjoint current-value edits while retaining original source spans. */
function composeRedactionEdits(length, previous, edits) {
	if (previous.length === 0) return edits;
	const pieces = [];
	let source = 0;
	for (const edit of previous) {
		if (source < edit.start) pieces.push({
			start: source,
			end: edit.start
		});
		pieces.push(edit);
		source = edit.end;
	}
	if (source < length) pieces.push({
		start: source,
		end: length
	});
	const output = [];
	let index = 0;
	let offset = 0;
	let position = 0;
	const consume = (end, keep) => {
		if (end === position) {
			const piece = pieces[index];
			if (!piece) return {
				start: length,
				end: length
			};
			const start = piece.replacement === void 0 ? piece.start + offset : piece.start;
			return {
				start,
				end: piece.replacement !== void 0 && offset > 0 ? piece.end : start
			};
		}
		let sourceStart;
		let sourceEnd = 0;
		while (position < end) {
			const piece = expectDefined(pieces[index], "current redaction piece");
			const size = piece.replacement?.length ?? piece.end - piece.start;
			const count = Math.min(size - offset, end - position);
			const start = piece.replacement === void 0 ? piece.start + offset : piece.start;
			const finish = piece.replacement === void 0 ? start + count : piece.end;
			sourceStart ??= start;
			sourceEnd = finish;
			if (keep && count > 0) output.push({
				start,
				end: finish,
				replacement: piece.replacement?.slice(offset, offset + count)
			});
			position += count;
			offset += count;
			if (offset === size) {
				index += 1;
				offset = 0;
			}
		}
		return {
			start: sourceStart ?? sourceEnd,
			end: sourceEnd
		};
	};
	for (const edit of edits) {
		consume(edit.start, true);
		output.push({
			...consume(edit.end, false),
			replacement: edit.replacement
		});
	}
	while (index < pieces.length) {
		const piece = expectDefined(pieces[index], "remaining redaction piece");
		consume(position + (piece.replacement?.length ?? piece.end - piece.start) - offset, true);
	}
	const composed = [];
	let fragments = [];
	let pending;
	const flush = () => {
		if (pending) {
			pending.replacement = fragments.join("");
			composed.push(pending);
			pending = void 0;
			fragments = [];
		}
	};
	for (const piece of output) {
		if (piece.replacement === void 0) {
			flush();
			continue;
		}
		const sameEmptySpan = pending && pending.start === piece.start && pending.end === piece.end && piece.start === piece.end;
		if (!pending || piece.start >= pending.end && !sameEmptySpan) {
			flush();
			pending = {
				start: piece.start,
				end: piece.end,
				replacement: ""
			};
		} else pending.end = Math.max(pending.end, piece.end);
		fragments.push(piece.replacement);
	}
	flush();
	return composed;
}
/** Project source-span replacements onto a value that already contains replacements. */
function rebaseRedactionEdits(previous, edits) {
	let shift = 0;
	const spans = previous.map((edit) => {
		const start = edit.start + shift;
		shift += edit.replacement.length - (edit.end - edit.start);
		return {
			...edit,
			currentStart: start,
			currentEnd: edit.end + shift
		};
	});
	const boundary = (position, end) => {
		let low = 0;
		let high = spans.length;
		while (low < high) {
			const middle = low + high >>> 1;
			const span = expectDefined(spans[middle], "source redaction span");
			if (span.end < position || span.end === position && span.start !== span.end) low = middle + 1;
			else high = middle;
		}
		const span = spans[low];
		if (span && (position > span.start || position === span.start && span.start === span.end)) return end ? span.currentEnd : span.currentStart;
		const before = spans[low - 1];
		return position + (before ? before.currentEnd - before.end : 0);
	};
	return edits.map((edit) => ({
		start: boundary(edit.start, false),
		end: boundary(edit.end, true),
		replacement: edit.replacement
	}));
}
function mergeRedactionEdits(edits) {
	edits.sort((left, right) => left.start - right.start || left.end - right.end);
	const merged = [];
	for (const edit of edits) {
		const previous = merged.at(-1);
		if (!previous || edit.start >= previous.end) merged.push({ ...edit });
		else if (edit.start !== previous.start || edit.end !== previous.end || edit.replacement !== previous.replacement) {
			previous.end = Math.max(previous.end, edit.end);
			previous.replacement = "***";
		}
	}
	return merged;
}
function applyRedactionEdits(value, edits) {
	const parts = [];
	let cursor = 0;
	for (const edit of mergeRedactionEdits(edits)) {
		parts.push(value.slice(cursor, edit.start), edit.replacement);
		cursor = edit.end;
	}
	return parts.join("") + value.slice(cursor);
}
//#endregion
//#region src/logging/secret-redaction-registry.ts
const MIN_SECRET_VALUE_LENGTH = 6;
const state$1 = resolveGlobalSingleton(Symbol.for("openclaw.secretRedactionRegistry"), () => ({
	registeredValues: /* @__PURE__ */ new Map(),
	registryRevision: 0,
	registeredValueRedactor: void 0
}));
const registrySnapshots = resolveGlobalSingleton(Symbol.for("openclaw.secretRedactionRegistrySnapshots"), () => new AsyncLocalStorage());
function invalidateMatcher() {
	state$1.registryRevision += 1;
	state$1.registeredValueRedactor = void 0;
}
/** Replaces registered exact values while preserving the caller's mask convention. */
function redactRegisteredSecretValues(text, mask) {
	const scoped = registrySnapshots.getStore();
	if (scoped) {
		scoped.redactor ??= createSecretValueRedactor(scoped.snapshot.values);
		return scoped.redactor(text, mask);
	}
	if (!text || state$1.registeredValues.size === 0) return text;
	state$1.registeredValueRedactor ??= createSecretValueRedactor([...state$1.registeredValues.keys()]);
	return state$1.registeredValueRedactor(text, mask);
}
function createSecretValueRedactor(values) {
	let compiledMatcher;
	let firstChars;
	return (text, mask) => {
		if (!text || values.length === 0) return text;
		let couldMatch = false;
		firstChars ??= new Set(values.map((value) => value.charAt(0)));
		for (const firstChar of firstChars) if (text.includes(firstChar)) {
			couldMatch = true;
			break;
		}
		if (!couldMatch) return text;
		if (!compiledMatcher) {
			const buckets = /* @__PURE__ */ new Map();
			for (const value of values.toSorted((left, right) => right.length - left.length)) {
				const prefix = value.slice(0, MIN_SECRET_VALUE_LENGTH);
				const bucket = buckets.get(prefix);
				if (bucket) bucket.push(value);
				else buckets.set(prefix, [value]);
			}
			compiledMatcher = {
				prefixes: new RegExp([...buckets.keys()].map(escapeRegExp).join("|"), "g"),
				buckets
			};
		}
		const { prefixes, buckets } = compiledMatcher;
		const matches = [];
		prefixes.lastIndex = 0;
		for (let match = prefixes.exec(text); match; match = prefixes.exec(text)) {
			const index = match.index;
			const value = buckets.get(match[0])?.find((candidate) => text.startsWith(candidate, index));
			if (value !== void 0) matches.push({
				index,
				value
			});
			prefixes.lastIndex = index + (value?.length ?? 1);
		}
		let result = "";
		let cursor = 0;
		for (const match of matches) {
			result += `${text.slice(cursor, match.index)}${mask(match.value, match.index)}`;
			cursor = match.index + match.value.length;
		}
		return result + text.slice(cursor);
	};
}
function resetSecretRedactionRegistryForTest() {
	state$1.registeredValues.clear();
	invalidateMatcher();
}
if (process.env.VITEST || false) globalThis[Symbol.for("openclaw.secretRedactionRegistryTestApi")] = { resetSecretRedactionRegistryForTest };
//#endregion
//#region src/logging/redact-json-tokens.ts
const JSON_TOKEN_RE = /"(?:[^"\\]|\\.)*"|-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?|true|false|null|[{}[\]]/g;
function skipWhitespace(text, start) {
	let offset = start;
	while (text[offset] === " " || text[offset] === "	" || text[offset] === "\r" || text[offset] === "\n") offset += 1;
	return offset;
}
function readScalarTokens(text, origins) {
	const tokens = [];
	const containers = [];
	const root = {
		key: "",
		path: [],
		origins,
		origin: origins.value,
		objectPath: true
	};
	const valueContext = (parent) => !parent ? root : parent.array ? {
		...parent.context,
		origin: parent.context.origins?.value ?? parent.context.origin,
		origins: parent.context.origins?.children.get(String(parent.nextIndex++))
	} : expectDefined(parent.field, "JSON object field context");
	for (const match of text.matchAll(JSON_TOKEN_RE)) {
		const raw = match[0];
		const parent = containers.at(-1);
		if (raw === "{" || raw === "[") {
			const context = valueContext(parent);
			const array = raw === "[";
			containers.push({
				array,
				nextIndex: 0,
				context: array ? {
					...context,
					objectPath: false
				} : context,
				start: match.index,
				tokenStart: tokens.length
			});
			continue;
		}
		if (raw === "}" || raw === "]") {
			const container = expectDefined(containers.pop(), "JSON container");
			if (container.tokenStart === tokens.length) {
				const end = match.index + 1;
				const value = text.slice(container.start, end);
				tokens.push({
					...container.context,
					isKey: false,
					string: false,
					value,
					start: container.start,
					end,
					escaped: false,
					edits: [],
					projectedEdits: [],
					currentValue: value,
					currentStart: container.start,
					currentEnd: end
				});
			}
			continue;
		}
		const start = match.index;
		const end = start + raw.length;
		const string = raw.startsWith("\"");
		const value = string ? JSON.parse(raw) : raw;
		const next = skipWhitespace(text, end);
		const isKey = string && text[next] === ":";
		let context;
		if (isKey) {
			const container = expectDefined(parent, "JSON property container");
			const inherited = container.context;
			context = {
				key: "",
				path: [],
				origin: inherited.origin,
				objectPath: false,
				rootKey: inherited.rootKey,
				rootValueStart: inherited.rootValueStart
			};
			const valueStart = skipWhitespace(text, next + 1);
			container.field = {
				key: value,
				path: [...inherited.path, value],
				origins: inherited.origins?.children.get(value),
				origin: inherited.origins?.value ?? inherited.origin,
				objectPath: inherited.objectPath,
				rootKey: containers.length === 1 ? value : inherited.rootKey,
				rootValueStart: containers.length === 1 ? valueStart : inherited.rootValueStart
			};
		} else context = valueContext(parent);
		const origin = isKey ? {
			structured: false,
			primitiveMask: false
		} : context.origins?.value ?? context.origin;
		tokens.push({
			...context,
			origin,
			start,
			end,
			isKey,
			string,
			value,
			escaped: string && raw.includes("\\"),
			edits: [],
			projectedEdits: [],
			currentValue: value,
			currentStart: start,
			currentEnd: end
		});
	}
	return tokens;
}
function readBatchTokens(input, origins, preserveLines = false) {
	const tokens = [];
	let offset = 0;
	for (const line of input.split("\n")) {
		let json = false;
		try {
			JSON.parse(line);
			json = true;
		} catch {}
		if (json) for (const token of readScalarTokens(line, origins)) {
			token.deferEncoding = !token.string;
			token.start += offset;
			token.end += offset;
			token.currentStart += offset;
			token.currentEnd += offset;
			tokens.push(token);
		}
		else {
			const previous = tokens.at(-1);
			if (!preserveLines && previous?.raw && previous.end + 1 === offset) {
				previous.value += `\n${line}`;
				previous.currentValue = previous.value;
				previous.end = previous.currentEnd = offset + line.length;
			} else tokens.push({
				raw: true,
				origin: origins.value,
				key: "",
				path: [],
				objectPath: false,
				isKey: false,
				string: false,
				value: line,
				escaped: false,
				start: offset,
				end: offset + line.length,
				currentStart: offset,
				currentEnd: offset + line.length,
				currentValue: line,
				edits: [],
				projectedEdits: []
			});
		}
		offset += line.length + 1;
	}
	return tokens;
}
//#endregion
//#region src/logging/redact-pattern-runtime.ts
function parseRedactPatternSource(raw) {
	const literal = raw.match(/^\/(.+)\/([gimsuy]*)$/);
	if (!literal) return [raw, "gi"];
	const source = literal[1] ?? "";
	const flags = literal[2] ?? "";
	return [source, flags.includes("g") ? flags : `${flags}g`];
}
const OPEN_REPEAT_RE = /^\{(\d+),\}/;
const BOUNDED_REPEAT_RE = /^\{(?:\d+(?:,\d*)?|,\d+)\}/;
const HEX_DIGIT_RE = /[0-9A-Fa-f]/;
const ASSERTION_ESCAPE_CHARS = /* @__PURE__ */ new Set(["b", "B"]);
const escapeHexValue = (source, at) => {
	let value = 0;
	for (let index = 0; index < 4; index += 1) {
		const digit = source[at + index];
		if (digit === void 0 || !HEX_DIGIT_RE.test(digit)) return null;
		value = value * 16 + Number.parseInt(digit, 16);
	}
	return value;
};
/** True when the escape at `i` is a `\uXXXX` high-surrogate escape. */
const isHighSurrogateEscape = (source, i) => {
	const value = escapeHexValue(source, i + 2);
	return value !== null && value >= 55296 && value <= 56319;
};
/** True when the escape at `i` is a `\uXXXX` low-surrogate escape. */
const isLowSurrogateEscape = (source, i) => {
	const value = escapeHexValue(source, i + 2);
	return value !== null && value >= 56320 && value <= 57343;
};
/** End index (exclusive) of the single atom introduced by the escape at `i` (a backslash), or -1. */
function escapeAtomEnd(source, i) {
	const kind = source[i + 1];
	if (kind === void 0) return -1;
	if (ASSERTION_ESCAPE_CHARS.has(kind)) return i + 2;
	if (kind === "x") return HEX_DIGIT_RE.test(source[i + 2] ?? "") && HEX_DIGIT_RE.test(source[i + 3] ?? "") ? i + 4 : i + 2;
	if ((kind === "u" || kind === "p" || kind === "P") && source[i + 2] === "{") {
		const close = source.indexOf("}", i + 3);
		return close === -1 ? i + 2 : close + 1;
	}
	if (kind === "u") {
		let end = i + 2;
		while (end < i + 6 && HEX_DIGIT_RE.test(source[end] ?? "")) end += 1;
		return end;
	}
	if (kind === "c") return i + 3;
	if (kind === "k" && source[i + 2] === "<") {
		const close = source.indexOf(">", i + 3);
		return close === -1 ? i + 2 : close + 1;
	}
	if (kind >= "0" && kind <= "9") {
		let end = i + 2;
		for (let cursor = end; cursor < source.length; cursor += 1) {
			const digit = source[cursor];
			if (digit === void 0 || digit < "0" || digit > "9") break;
			end = cursor + 1;
		}
		return end;
	}
	return i + 2;
}
/** End index (exclusive) of the character class starting at `i` (an unescaped bracket). */
function classAtomEnd(source, i) {
	let cursor = i + 1;
	if (source[cursor] === "^") cursor += 1;
	if (source[cursor] === "]") return -1;
	while (cursor < source.length) {
		const char = source[cursor];
		if (char === "\\") {
			cursor += 2;
			continue;
		}
		if (char === "]") return cursor + 1;
		cursor += 1;
	}
	return source.length;
}
/**
* Rewrites open-ended repeats `X{n,}` into `X{n}X*` for flat single-character atoms `X`
* (one literal, one escaped atom, or one bracket class). Greedy `X{n,}` and `X{n}X*`
* accept the same strings in the same backtracking order, but the latter never grows one
* backtrack stack entry per repetition, so multi-megabyte runs no longer overflow.
* Bounded repeats, quantifiers after groups, and quantified atoms are left untouched.
* `flags` decides atom boundaries for literal astral characters: under `u` a surrogate pair
* is one atom; without `u` JavaScript quantifies only the trailing code unit, so the pair
* must keep its per-unit handling and the original language.
*/
function rewriteOpenEndedRepeats(source, flags = "") {
	if (!source.includes("{")) return source;
	let out = "";
	let atomStart = -1;
	let atomEnd = -1;
	let i = 0;
	while (i < source.length) {
		const char = source[i];
		if (char === "\\") {
			const end = escapeAtomEnd(source, i);
			if (end < 0) {
				out += char;
				i += 1;
				atomStart = -1;
				continue;
			}
			if (flags.includes("u") && isHighSurrogateEscape(source, i) && source[end] === "\\" && isLowSurrogateEscape(source, end)) {
				const pairEnd = escapeAtomEnd(source, end);
				out += source.slice(i, pairEnd);
				atomStart = i;
				atomEnd = pairEnd;
				i = pairEnd;
				continue;
			}
			out += source.slice(i, end);
			if (ASSERTION_ESCAPE_CHARS.has(source[i + 1])) atomStart = -1;
			else {
				atomStart = i;
				atomEnd = end;
			}
			i = end;
			continue;
		}
		if (char === "[") {
			const end = classAtomEnd(source, i);
			if (end < 0) return source;
			out += source.slice(i, end);
			atomStart = i;
			atomEnd = end;
			i = end;
			continue;
		}
		if (char === "{") {
			const open = OPEN_REPEAT_RE.exec(source.slice(i));
			if (open && atomStart >= 0) {
				const atom = source.slice(atomStart, atomEnd);
				out += `{${open[1]}}${atom}*`;
				i += open[0].length;
				if (source[i] === "?") {
					out += "?";
					i += 1;
				}
				atomStart = -1;
				continue;
			}
			const bounded = BOUNDED_REPEAT_RE.exec(source.slice(i));
			if (bounded) {
				out += bounded[0];
				i += bounded[0].length;
				if (source[i] === "?") {
					out += "?";
					i += 1;
				}
				atomStart = -1;
				continue;
			}
			out += char;
			atomStart = i;
			atomEnd = i + 1;
			i += 1;
			continue;
		}
		out += char;
		if (flags.includes("u") && char >= "\ud800" && char <= "\udbff" && i + 1 < source.length && source[i + 1] >= "\udc00" && source[i + 1] <= "\udfff") {
			out += source[i + 1];
			atomStart = i;
			atomEnd = i + 2;
			i += 2;
			continue;
		}
		if (char === "*" || char === "+" || char === "?" || char === "(" || char === ")" || char === "|" || char === "^" || char === "$") atomStart = -1;
		else {
			atomStart = i;
			atomEnd = i + 1;
		}
		i += 1;
	}
	return out;
}
function readRedactMatch(args) {
	const inputIndex = args.length > 0 && typeof args[args.length - 1] === "object" && args[args.length - 1] !== null ? args.length - 2 : args.length - 1;
	const offsetIndex = inputIndex - 1;
	const match = typeof args[0] === "string" ? args[0] : "";
	const groups = args.slice(1, offsetIndex).map((value) => typeof value === "string" ? value : "");
	const offset = typeof args[offsetIndex] === "number" ? args[offsetIndex] : -1;
	return {
		match,
		groups,
		input: typeof args[inputIndex] === "string" ? args[inputIndex] : "",
		offset
	};
}
const patternPrefilters = /* @__PURE__ */ new WeakMap();
function setRedactPatternPrefilter(pattern, probe) {
	if (patternPrefilters.has(pattern)) return;
	const source = pattern.source;
	const exec = pattern.exec;
	const flags = pattern instanceof RegExp ? pattern.flags : void 0;
	const replace = pattern instanceof RegExp ? pattern[Symbol.replace] : void 0;
	patternPrefilters.set(pattern, (text) => pattern.source !== source || pattern.exec !== exec || pattern instanceof RegExp && (pattern.flags !== flags || pattern[Symbol.replace] !== replace) || probe(text));
}
const indexedPatterns = /* @__PURE__ */ new WeakMap();
function getIndexedCaptureStart(pattern, input, match, matchOffset, captureIndex) {
	if (!(pattern instanceof RegExp) || matchOffset < 0 || !input) return null;
	let indexedPattern = indexedPatterns.get(pattern);
	if (!indexedPattern) {
		indexedPattern = new RegExp(pattern.source, `${pattern.flags.replace("d", "").replace("g", "")}dg`);
		indexedPatterns.set(pattern, indexedPattern);
	}
	indexedPattern.lastIndex = matchOffset;
	const indexedMatch = indexedPattern.exec(input);
	const captureIndices = indexedMatch?.indices?.[captureIndex + 1];
	return indexedMatch?.index === matchOffset && indexedMatch[0] === match && captureIndices ? captureIndices[0] - matchOffset : null;
}
function selectSecretCapture(match, groups) {
	const selected = {
		index: -1,
		value: match
	};
	for (let index = 0; index < groups.length; index++) {
		const value = groups[index];
		if (typeof value === "string" && value.length > 0) {
			selected.index = index;
			selected.value = value;
		}
	}
	return selected;
}
function getSecretCaptureStart(pattern, input, match, matchOffset, selected) {
	return getIndexedCaptureStart(pattern, input, match, matchOffset, selected.index) ?? match.lastIndexOf(selected.value);
}
const globalPatterns = /* @__PURE__ */ new WeakMap();
function visitRedactMatches(text, pattern, visit) {
	if (patternPrefilters.get(pattern)?.(text) === false) return;
	if (!(pattern instanceof RegExp)) {
		for (const match of pattern.exec(text)) visit(match, pattern);
		return;
	}
	let regex = pattern.global ? pattern : globalPatterns.get(pattern);
	if (!regex) {
		regex = new RegExp(pattern.source, `${pattern.flags}g`);
		globalPatterns.set(pattern, regex);
	}
	const unicode = regex.unicode || regex.flags.includes("v");
	let cursor = 0;
	while (cursor <= text.length) {
		const previousIndex = regex.lastIndex;
		let match;
		try {
			regex.lastIndex = cursor;
			match = regex.exec(text);
		} finally {
			regex.lastIndex = previousIndex;
		}
		if (!match) return;
		cursor = match.index + match[0].length;
		if (!match[0]) {
			const codePoint = text.codePointAt(cursor);
			cursor += unicode && codePoint !== void 0 && codePoint > 65535 ? 2 : 1;
		}
		visit({
			match: match[0],
			groups: match.slice(1).map((group) => group ?? ""),
			input: text,
			offset: match.index
		}, pattern);
	}
}
function replaceRedactPattern(text, pattern, replace, replaceRegex) {
	if (pattern instanceof RegExp) {
		if (patternPrefilters.get(pattern)?.(text) === false) {
			pattern.lastIndex = 0;
			return text;
		}
		return text.replace(pattern, replaceRegex ?? ((...args) => replace(readRedactMatch(args))));
	}
	const parts = [];
	let end = 0;
	visitRedactMatches(text, pattern, (match) => {
		parts.push(text.slice(end, match.offset), replace(match));
		end = match.offset + match.match.length;
	});
	return parts.length ? parts.join("") + text.slice(end) : text;
}
function redactPemBlock(block, marker) {
	const lines = block.split(/\r?\n/).filter(Boolean);
	if (lines.length < 2) return "***";
	return `${lines[0]}\n${marker}\n${lines[lines.length - 1]}`;
}
//#endregion
//#region src/logging/redact-json.ts
function getPatternRedactionEdits(value, pattern, getCapture) {
	const edits = [];
	visitRedactMatches(value, pattern, (match) => {
		const capture = getCapture(match, pattern);
		const edit = capture?.redact(capture);
		if (edit && edit.end >= edit.start) edits.push(edit);
	});
	return edits;
}
function stringBoundaries(text, start, end) {
	const boundaries = /* @__PURE__ */ new Map();
	let decoded = 0;
	for (let offset = start; offset < end; decoded += 1) {
		boundaries.set(offset, decoded);
		offset += text[offset] === "\\" ? text[offset + 1] === "u" ? 6 : 2 : 1;
	}
	boundaries.set(end, decoded);
	return boundaries;
}
function decodedBoundary(text, token, offset) {
	if (!token.escaped) return offset - token.start - 1;
	token.boundaries ??= stringBoundaries(text, token.start + 1, token.end - 1);
	return token.boundaries.get(offset);
}
function encodedBoundary(text, token, offset) {
	if (!token.escaped) return token.start + 1 + offset;
	if (!token.encodedBoundaries) {
		token.boundaries ??= stringBoundaries(text, token.start + 1, token.end - 1);
		token.encodedBoundaries = [];
		for (const [encoded, decoded] of token.boundaries) token.encodedBoundaries[decoded] = encoded;
	}
	return expectDefined(token.encodedBoundaries[offset], "decoded JSON edit boundary");
}
function projectMessageEdits(input, tokens, message, getEdits) {
	const parts = new Map(message.parts.map((part) => [part.key, part]));
	const projected = [];
	for (const token of tokens) {
		if (!token.isKey && token.path.length === 1 && token.key === "message") continue;
		const part = token.rootKey === void 0 ? void 0 : parts.get(token.rootKey);
		if (!part) continue;
		if (!part.json && (token.isKey || (part.messageField ? token.path.length !== 2 || token.key !== "message" : token.path.length !== 1))) continue;
		const edits = getEdits(token);
		for (const edit of edits) {
			let start;
			let end;
			let replacement = edit.replacement;
			if (part.json) {
				const base = part.start - expectDefined(token.rootValueStart, "displayed JSON argument");
				if (token.string) {
					start = base + encodedBoundary(input, token, edit.start);
					end = base + encodedBoundary(input, token, edit.end);
					replacement = JSON.stringify(replacement).slice(1, -1);
				} else {
					start = base + token.start;
					end = base + token.end;
					replacement = JSON.stringify(replacement);
				}
			} else {
				start = part.start + edit.start;
				end = part.start + (part.primitiveLength ?? edit.end);
			}
			if (start < message.contentLength) projected.push({
				start,
				end: Math.min(end, message.contentLength),
				replacement,
				scalar: part.json && !token.string
			});
		}
	}
	return projected.toSorted((left, right) => left.start - right.start || left.end - right.end);
}
function firstIntersectingToken(tokens, start) {
	let low = 0;
	let high = tokens.length;
	while (low < high) {
		const middle = low + high >>> 1;
		if (expectDefined(tokens[middle], "bounded JSON token search").currentEnd <= start) low = middle + 1;
		else high = middle;
	}
	return low;
}
function updateCurrentToken(input, token) {
	if (token.raw || token.deferEncoding) {
		token.currentRaw = token.currentValue;
		return;
	}
	const parts = [];
	const encodedEdits = [];
	let cursor = token.start + (token.string ? 1 : 0);
	let encodedLength = 0;
	let decodedShift = 0;
	for (const edit of token.edits) {
		const start = token.string ? encodedBoundary(input, token, edit.start) : token.start + edit.start;
		const end = token.string ? encodedBoundary(input, token, edit.end) : token.start + edit.end;
		const replacement = JSON.stringify(edit.replacement).slice(1, -1);
		const encodedStart = encodedLength + start - cursor;
		const encodedEnd = encodedStart + replacement.length;
		encodedEdits.push({
			start: encodedStart,
			end: encodedEnd,
			decodedStart: edit.start + decodedShift,
			decodedEnd: edit.start + decodedShift + edit.replacement.length,
			sourceEnd: end,
			sourceDecodedEnd: edit.end,
			replacement
		});
		parts.push(input.slice(cursor, start), replacement);
		encodedLength = encodedEnd;
		decodedShift += edit.replacement.length - (edit.end - edit.start);
		cursor = end;
	}
	parts.push(input.slice(cursor, token.end - (token.string ? 1 : 0)));
	token.currentRaw = `"${parts.join("")}"`;
	token.encodedEdits = encodedEdits;
}
function currentDecodedBoundary(input, token, position, replacements) {
	const offset = position - token.currentStart - 1;
	const edits = token.encodedEdits;
	if (!edits) return decodedBoundary(input, token, token.start + 1 + offset);
	let low = 0;
	let high = edits.length;
	while (low < high) {
		const middle = low + high >>> 1;
		if (expectDefined(edits[middle], "current encoded edit").end < offset) low = middle + 1;
		else high = middle;
	}
	const edit = edits[low];
	if (edit && offset >= edit.start) {
		const within = offset - edit.start;
		if (!edit.replacement.includes("\\")) return edit.decodedStart + within;
		let boundaries = replacements.get(edit.replacement);
		if (!boundaries) {
			boundaries = stringBoundaries(edit.replacement, 0, edit.replacement.length);
			replacements.set(edit.replacement, boundaries);
		}
		const decoded = boundaries.get(within);
		return decoded === void 0 ? void 0 : edit.decodedStart + decoded;
	}
	const previous = edits[low - 1];
	const decoded = decodedBoundary(input, token, offset + (previous ? previous.sourceEnd - previous.end : token.start + 1));
	return decoded === void 0 ? void 0 : decoded + (previous ? previous.decodedEnd - previous.sourceDecodedEnd : 0);
}
function commitPatternEdits(token) {
	const pending = token.pending;
	token.pending = void 0;
	if (!pending) return false;
	const edits = mergeRedactionEdits(pending);
	const value = applyRedactionEdits(token.currentValue, edits);
	if (value === token.currentValue) return false;
	token.edits = composeRedactionEdits(token.value.length, token.edits, edits);
	token.currentValue = value;
	return true;
}
function changedRedactionEdits(previous, current) {
	let index = 0;
	return current.filter((edit) => {
		while (previous[index] && expectDefined(previous[index], "previous configured edit").start < edit.start) index += 1;
		const before = previous[index];
		return !before || before.start !== edit.start || before.end !== edit.end || before.replacement !== edit.replacement;
	});
}
function commitOriginalEdits(token, edits) {
	if (edits.length === 0) return false;
	const combined = mergeRedactionEdits([...token.edits, ...edits]);
	const value = applyRedactionEdits(token.value, combined);
	if (value === token.currentValue) return false;
	token.edits = combined;
	token.currentValue = value;
	return true;
}
function updateCurrentRecord(input, current, tokens, changed) {
	const parts = [];
	let cursor = 0;
	let shift = 0;
	for (const token of tokens) {
		const start = token.currentStart;
		const end = token.currentEnd;
		if (changed.has(token)) {
			updateCurrentToken(input, token);
			const raw = expectDefined(token.currentRaw, "changed JSON token");
			parts.push(current.slice(cursor, start), raw);
			cursor = end;
			token.currentStart = start + shift;
			shift += raw.length - (end - start);
		} else token.currentStart = start + shift;
		token.currentEnd = end + shift;
	}
	parts.push(current.slice(cursor));
	return parts.join("");
}
function projectedStringEnd(input, tokens, message, token, start, end) {
	const jsonParts = new Set(message.parts.filter((part) => part.json).map((part) => part.key));
	const spans = projectMessageEdits(input, tokens, message, (source) => source.string && !source.isKey && source.rootKey !== void 0 && jsonParts.has(source.rootKey) ? [{
		start: 0,
		end: source.value.length,
		replacement: ""
	}] : []).filter((span) => span.end < message.contentLength);
	const containing = rebaseRedactionEdits(token.edits, spans).filter((span) => span.start <= start && start < span.end && end <= span.end);
	return containing.length === 1 ? expectDefined(containing[0], "displayed source string").end : token.currentValue.length;
}
function redactJsonRecord(input, origins, patternPhases, getCapture, legacyFieldEdits, fieldEdits, prepEdits, skipDecodedPatterns, message, batch) {
	let tokens = batch ? [] : readScalarTokens(input, origins);
	const messageToken = message ? tokens.find((token) => !token.isKey && token.path.length === 1 && token.key === "message") : void 0;
	const replacementBoundaries = /* @__PURE__ */ new Map();
	let projectedMessageEdits = [];
	let projectedMessageValue = messageToken?.value ?? "";
	let current = input;
	const prepared = /* @__PURE__ */ new Set();
	for (const token of tokens) if (commitOriginalEdits(token, prepEdits(token))) prepared.add(token);
	if (prepared.size > 0) current = updateCurrentRecord(input, current, tokens, prepared);
	const decodedTokens = tokens.filter((token) => !token.isKey && token.string && !skipDecodedPatterns(token, token.currentValue));
	const projectMessage = () => {
		if (!messageToken || !message) return false;
		const projected = projectMessageEdits(input, tokens, message, (token) => {
			const edits = changedRedactionEdits(token.projectedEdits, token.edits);
			token.projectedEdits = token.edits;
			return edits;
		});
		if (projected.length === 0) return false;
		const sourceEdits = rebaseRedactionEdits(projectedMessageEdits, projected);
		const messageEdits = rebaseRedactionEdits(messageToken.edits, projected);
		let generatedIndex = 0;
		for (let index = 0; index < messageEdits.length; index += 1) {
			const edit = expectDefined(messageEdits[index], "projected message edit");
			const sourceEdit = expectDefined(sourceEdits[index], "projected source edit");
			const projection = expectDefined(projected[index], "source projection");
			while (messageToken.edits[generatedIndex] && expectDefined(messageToken.edits[generatedIndex], "generated message span").start < projection.start) generatedIndex += 1;
			const generated = messageToken.edits[generatedIndex];
			const scalarPromotion = projection.scalar && generated?.start === projection.start && generated.end === projection.end && edit.replacement === JSON.stringify(generated.replacement);
			const before = messageToken.currentValue.slice(edit.start, edit.end);
			if (!scalarPromotion && before !== edit.replacement && before !== projectedMessageValue.slice(sourceEdit.start, sourceEdit.end)) edit.replacement = projection.scalar ? JSON.stringify("***") : "***";
		}
		messageToken.pending = messageEdits;
		projectedMessageValue = applyRedactionEdits(projectedMessageValue, sourceEdits);
		projectedMessageEdits = composeRedactionEdits(messageToken.value.length, projectedMessageEdits, sourceEdits);
		return commitPatternEdits(messageToken);
	};
	for (const [phase, groups] of patternPhases.entries()) {
		const changed = /* @__PURE__ */ new Set();
		const pending = /* @__PURE__ */ new Set();
		const add = (token, edit) => {
			(token.pending ??= []).push(edit);
			pending.add(token);
		};
		let decodedToken;
		const visitDecodedMatch = (match, pattern) => {
			const capture = getCapture(match, pattern);
			const edit = capture?.redact(capture);
			if (edit && edit.end >= edit.start) add(decodedToken, edit);
		};
		for (const group of groups) {
			if (group.couldMatch && !group.couldMatch(current)) continue;
			for (const pattern of group.patterns) {
				if (phase === 0) for (decodedToken of decodedTokens) visitRedactMatches(decodedToken.currentValue, pattern, visitDecodedMatch);
				else visitRedactMatches(current, pattern, (match) => {
					const capture = getCapture(match, pattern);
					if (!capture || capture.end < capture.start) return;
					if (batch && tokens.length === 0) tokens = readBatchTokens(input, origins, batch.preserveLines);
					for (let index = firstIntersectingToken(tokens, capture.start); index < tokens.length; index += 1) {
						const token = expectDefined(tokens[index], "serialized capture token");
						if (token.currentStart >= capture.end) break;
						const unquoted = token.raw || token.deferEncoding;
						const padding = unquoted ? 0 : 1;
						const captureInsideToken = capture.start >= token.currentStart + padding && capture.end <= token.currentEnd - padding;
						if (batch && token.isKey && !captureInsideToken) continue;
						const value = token.currentValue;
						if (!token.raw && !token.string && token.edits.length === 0) {
							add(token, {
								start: 0,
								end: value.length,
								replacement: "***"
							});
							continue;
						}
						let startPosition = Math.max(capture.start, token.currentStart + padding);
						let start = unquoted ? startPosition - token.currentStart : currentDecodedBoundary(input, token, startPosition, replacementBoundaries);
						let endPosition = Math.min(capture.end, token.currentEnd - padding);
						let end = unquoted ? endPosition - token.currentStart : currentDecodedBoundary(input, token, endPosition, replacementBoundaries);
						if (start === void 0 || end === void 0) {
							while (start === void 0) start = currentDecodedBoundary(input, token, --startPosition, replacementBoundaries);
							while (end === void 0) end = currentDecodedBoundary(input, token, ++endPosition, replacementBoundaries);
							const maskEnd = token === messageToken && message ? projectedStringEnd(input, tokens, message, token, start, end) : value.length;
							add(token, {
								start,
								end: maskEnd,
								replacement: "***"
							});
							continue;
						}
						if (end < start || batch && end === start) continue;
						const { start: captureStart, end: captureEnd } = capture;
						const edit = capture.redact({
							start,
							end,
							value: current.slice(Math.max(captureStart, token.currentStart + padding), Math.min(captureEnd, token.currentEnd - padding))
						});
						if (!edit) continue;
						let replacement = "***";
						if (captureInsideToken) try {
							replacement = unquoted ? edit.replacement : JSON.parse(`"${edit.replacement}"`);
						} catch {}
						add(token, {
							...edit,
							replacement
						});
					}
				});
				if (pending.size === 0) continue;
				for (const token of pending) if (!commitPatternEdits(token)) pending.delete(token);
				else if (phase === 0) changed.add(token);
				if (phase !== 0 && pending.size > 0) current = updateCurrentRecord(input, current, tokens, pending);
				pending.clear();
			}
		}
		for (const token of tokens) if (phase === 0) {
			token.pending = legacyFieldEdits(token, token.currentValue);
			if (commitPatternEdits(token)) changed.add(token);
		} else if (commitOriginalEdits(token, fieldEdits(token))) changed.add(token);
		if ((phase !== 0 || prepared.size > 0 || changed.size > 0) && projectMessage() && messageToken) changed.add(messageToken);
		if (changed.size > 0) current = updateCurrentRecord(input, current, tokens, changed);
	}
	if (messageToken && message) {
		const finished = message.finish(messageToken.currentValue);
		if (finished !== messageToken.currentValue) return applyRedactionEdits(current, [{
			start: messageToken.currentStart,
			end: messageToken.currentEnd,
			replacement: JSON.stringify(finished)
		}]);
	}
	return applyRedactionEdits(current, tokens.flatMap((token) => token.deferEncoding && token.edits.length > 0 ? [{
		start: token.currentStart,
		end: token.currentEnd,
		replacement: JSON.stringify(token.currentValue)
	}] : []));
}
//#endregion
//#region src/logging/redact-pem.ts
const PEM_REDACT_PATTERN_SOURCE = String.raw`-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]+?-----END [A-Z ]*PRIVATE KEY-----`;
function createState() {
	return {
		scanner: {
			offset: 0,
			phase: "search",
			dashes: 0,
			keyword: "BEGIN ",
			keywordOffset: 0,
			start: 0,
			suffix: ""
		},
		matchEnd: 0
	};
}
function resetScanner(scanner, char) {
	scanner.phase = "search";
	scanner.dashes = char === "-" ? 1 : 0;
	scanner.suffix = "";
}
function* readDelimiters(text, scanner) {
	for (let index = 0; index < text.length; index += 1) {
		if (scanner.phase === "search" && scanner.dashes === 0) {
			const nextDash = text.indexOf("-", index);
			if (nextDash === -1) {
				scanner.offset += text.length - index;
				return;
			}
			scanner.offset += nextDash - index;
			index = nextDash;
		}
		const code = text.charCodeAt(index);
		const char = code >= 97 && code <= 122 ? String.fromCharCode(code - 32) : text.charAt(index);
		const offset = scanner.offset++;
		switch (scanner.phase) {
			case "search":
				if (char === "-") scanner.dashes = Math.min(5, scanner.dashes + 1);
				else if (scanner.dashes === 5 && (char === "B" || char === "E")) {
					scanner.phase = "keyword";
					scanner.keyword = char === "B" ? "BEGIN " : "END ";
					scanner.keywordOffset = 1;
					scanner.start = offset - 5;
				} else scanner.dashes = 0;
				break;
			case "keyword":
				if (char !== scanner.keyword.charAt(scanner.keywordOffset)) resetScanner(scanner, char);
				else if (++scanner.keywordOffset === scanner.keyword.length) {
					scanner.phase = "label";
					scanner.suffix = "";
				}
				break;
			case "label":
				if (char >= "A" && char <= "Z" || char === " ") scanner.suffix = (scanner.suffix + char).slice(-11);
				else if (char === "-" && scanner.suffix === "PRIVATE KEY") {
					scanner.phase = "close";
					scanner.dashes = 1;
				} else resetScanner(scanner, char);
				break;
			case "close": if (char !== "-") resetScanner(scanner, char);
			else if (++scanner.dashes === 5) {
				scanner.phase = "search";
				scanner.suffix = "";
				yield {
					kind: scanner.keyword === "BEGIN " ? "begin" : "end",
					start: scanner.start,
					end: offset + 1
				};
			}
		}
	}
}
function consumeDelimiter(state, delimiter) {
	if (!state.open) {
		if (delimiter.kind === "begin" && delimiter.start >= state.matchEnd) state.open = {
			start: delimiter.start,
			end: delimiter.end
		};
		return;
	}
	if (delimiter.kind !== "end" || delimiter.start <= state.open.end) return;
	const block = {
		start: state.open.start,
		end: delimiter.end,
		bodyStart: state.open.end,
		bodyEnd: delimiter.start
	};
	state.open = void 0;
	state.matchEnd = delimiter.end;
	return block;
}
function* matchPem(text, context, incomplete) {
	const state = {
		...context,
		scanner: { ...context.scanner },
		open: context.open && { ...context.open }
	};
	const origin = state.scanner.offset;
	for (const delimiter of readDelimiters(text, state.scanner)) {
		const block = consumeDelimiter(state, delimiter);
		if (!block) continue;
		const start = Math.max(0, (incomplete ? block.bodyStart : block.start) - origin);
		const end = (incomplete ? block.bodyEnd : block.end) - origin;
		if (end <= start) continue;
		yield {
			match: text.slice(start, end),
			groups: [],
			input: text,
			offset: start,
			...incomplete ? { replacement: "…redacted…" } : {}
		};
	}
	if (incomplete && state.open) {
		const start = Math.max(0, state.open.end - origin);
		if (start < text.length) yield {
			match: text.slice(start),
			groups: [],
			input: text,
			offset: start,
			replacement: "…redacted…"
		};
	}
}
const PEM_REDACT_MATCHER = {
	source: PEM_REDACT_PATTERN_SOURCE,
	exec(text) {
		return matchPem(text, createState(), false);
	},
	createContext() {
		let whenClosed = false;
		let whenOpen = true;
		return {
			resolve(active) {
				whenClosed = active ? whenOpen : whenClosed;
				whenOpen = whenClosed;
			},
			advance(text) {
				const state = createState();
				if (whenClosed) state.open = {
					start: -1,
					end: -1
				};
				for (const delimiter of readDelimiters(text, state.scanner)) consumeDelimiter(state, delimiter);
				return state.open !== void 0;
			},
			prepend() {
				const closed = createState();
				const open = createState();
				open.open = {
					start: -1,
					end: -1
				};
				return {
					consume(text) {
						for (const delimiter of readDelimiters(text, closed.scanner)) {
							consumeDelimiter(closed, delimiter);
							consumeDelimiter(open, delimiter);
						}
					},
					finish() {
						const nextClosed = closed.open ? whenOpen : whenClosed;
						const nextOpen = open.open ? whenOpen : whenClosed;
						whenClosed = nextClosed;
						whenOpen = nextOpen;
						return whenClosed === whenOpen;
					}
				};
			},
			pattern: {
				source: PEM_REDACT_PATTERN_SOURCE,
				exec(text) {
					const state = createState();
					if (whenClosed) state.open = {
						start: -1,
						end: -1
					};
					return matchPem(text, state, true);
				}
			}
		};
	}
};
//#endregion
//#region src/logging/redact-patterns.ts
const PAYMENT_CREDENTIAL_ENV_KEYS = String.raw`CARD[_-]?NUMBER|CARD[_-]?CVC|CARD[_-]?CVV|CVC|CVV|SECURITY[_-]?CODE|PAYMENT[_-]?CREDENTIAL|SHARED[_-]?PAYMENT[_-]?TOKEN`;
const PAYMENT_CREDENTIAL_QUERY_KEYS = String.raw`card[-_]?number|card[-_]?cvc|card[-_]?cvv|cvc|cvv|security[-_]?code|payment[-_]?credential|shared[-_]?payment[-_]?token`;
const PAYMENT_CREDENTIAL_JSON_KEYS = String.raw`cardNumber|card_number|cardCvc|card_cvc|cardCvv|card_cvv|cvc|cvv|securityCode|security_code|paymentCredential|payment_credential|sharedPaymentToken|shared_payment_token`;
const AWS_SECRET_ACCESS_KEY_FIELD_KEYS = String.raw`aws[-_]?secret[-_]?access[-_]?key|awsSecretAccessKey|SecretAccessKey`;
const AUTH_QUERY_KEYS = String.raw`access[-_]?token|auth[-_]?token|hook[-_]?token|refresh[-_]?token|id[-_]?token|api[-_]?key|apikey|client[-_]?secret|app[-_]?secret|private[-_]?key|${AWS_SECRET_ACCESS_KEY_FIELD_KEYS}|credential|authorization|token|key|secret|password|pass|passwd|auth|jwt|session|code|signature|x[-_]?amz[-_]?(?:signature|security[-_]?token)`;
const FORM_BODY_FIRST_PAIR_KEYS = String.raw`${AUTH_QUERY_KEYS}|app[-_]?secret|credential|${PAYMENT_CREDENTIAL_QUERY_KEYS}`;
const STANDALONE_ASSIGNMENT_SECRET_KEYS = String.raw`access_token|refresh_token|id_token|auth[-_]?token|hook[-_]?token|api[-_]?key|client[-_]?secret|app[-_]?secret|private[-_]?key|authorization|jwt|token|secret|password|pass|passwd|credential|${PAYMENT_CREDENTIAL_QUERY_KEYS}`;
const CONFIG_ASSIGNMENT_SECRET_KEYS = String.raw`access[-_]?token|refresh[-_]?token|id[-_]?token|auth[-_]?token|hook[-_]?token|api[-_]?(?:key|secret)|client[-_]?secret|app[-_]?secret|private[-_]?key|secret[-_]?key|key[-_]?material|authorization|jwt|token|secret|password|passphrase|pass|passwd|credential|${PAYMENT_CREDENTIAL_QUERY_KEYS}`;
const CONFIG_COLON_ASSIGNMENT_SECRET_KEYS = String.raw`access[-_]?token|refresh[-_]?token|id[-_]?token|auth[-_]?token|hook[-_]?token|api[-_]?(?:key|secret)|client[-_]?secret|app[-_]?secret|private[-_]?key|secret[-_]?key|key[-_]?material|authorization|jwt|token|secret|password|passphrase|passwd|credential|${PAYMENT_CREDENTIAL_QUERY_KEYS}`;
const CONFIG_DIRECT_ASSIGNMENT_SECRET_KEYS = String.raw`access-token|refresh-token|id-token|auth-token|hook-token|api[-_]?(?:key|secret)|secret[-_]?key|key[-_]?material|passphrase`;
const CONFIG_PREFIXED_PASSWORD_ASSIGNMENT_SECRET_KEYS = String.raw`password|passphrase|pass|passwd`;
const CLI_SECRET_FLAG_KEYS = String.raw`${AWS_SECRET_ACCESS_KEY_FIELD_KEYS}|api[-_]?key|hook[-_]?token|access[-_]?token|refresh[-_]?token|id[-_]?token|token|secret|password|passwd|credential|private[-_]?key|client[-_]?secret|${PAYMENT_CREDENTIAL_QUERY_KEYS}`;
const FORM_BODY_KEY_INVISIBLE_CHARS = String.raw`\p{C}\u00A0\u1680\u2000-\u200A\u202F\u205F\u3000\u115F\u1160\u3164\uFFA0`;
const ENV_ASSIGNMENT_REDACT_PATTERN = String.raw`/\b[A-Z0-9_]*(?:KEY|TOKEN|SECRET|PASSWORD|PASSWD|${PAYMENT_CREDENTIAL_ENV_KEYS})\b\s*[=:]\s*(["']?)([^\s"'\\]+)\1/g`;
const ESCAPED_ENV_ASSIGNMENT_REDACT_PATTERN = String.raw`/\b[A-Z0-9_]*(?:KEY|TOKEN|SECRET|PASSWORD|PASSWD|${PAYMENT_CREDENTIAL_ENV_KEYS})\b\s*[=:]\s*\\+(["'])([^\s"'\\]+)\\+\1/g`;
const STANDALONE_ASSIGNMENT_QUOTED_REDACT_PATTERN = String.raw`(^|[\s,;({\["])(?:${STANDALONE_ASSIGNMENT_SECRET_KEYS})=(["'\x60])((?:(?!\2)[^\r\n])+)\2`;
const STANDALONE_ASSIGNMENT_REDACT_PATTERN = String.raw`(^|[\s,;({\["])(?:${STANDALONE_ASSIGNMENT_SECRET_KEYS})=(["'\x60]?[^\s&#"'\x60<>]+)`;
const CONFIG_QUOTED_ASSIGNMENT_SECRET_KEYS = String.raw`access[-_]?token|refresh[-_]?token|id[-_]?token|auth[-_]?token|hook[-_]?token|api[-_]?(?:key|secret)|secret[-_]?key|key[-_]?material|authorization|jwt|token|secret|password|passphrase|pass|passwd|${PAYMENT_CREDENTIAL_QUERY_KEYS}`;
const CONFIG_QUOTED_ASSIGNMENT_REDACT_PATTERN = String.raw`/(^|[\s,{])(?:(?:${CONFIG_QUOTED_ASSIGNMENT_SECRET_KEYS})(?:\s*:\s*|\s+=\s*|=\s*)|[a-z0-9][a-z0-9._-]{0,79}[-_](?:${CONFIG_PREFIXED_PASSWORD_ASSIGNMENT_SECRET_KEYS})\s*[:=]\s*|[a-z0-9_.-]{1,80}\.(?:${CONFIG_ASSIGNMENT_SECRET_KEYS})\s*[:=]\s*)(["'\x60])((?:(?!\2)[^\r\n])+)\2/g`;
const CONFIG_ASSIGNMENT_REDACT_PATTERN = String.raw`/(^|[\s,{])(?:(?:${CONFIG_COLON_ASSIGNMENT_SECRET_KEYS})\s*:\s*|(?:${CONFIG_ASSIGNMENT_SECRET_KEYS})(?:\s+=\s*|=\s+))([^\s#"'\x60<>]+)/g`;
const CONFIG_DIRECT_ASSIGNMENT_REDACT_PATTERN = String.raw`/(^|[\s,{])(?:${CONFIG_DIRECT_ASSIGNMENT_SECRET_KEYS})=([^\s#"'\x60<>]+)/g`;
const CONFIG_PREFIXED_PASSWORD_ASSIGNMENT_REDACT_PATTERN = String.raw`/(^|[\s,{])[a-z0-9][a-z0-9._-]{0,79}[-_](?:${CONFIG_PREFIXED_PASSWORD_ASSIGNMENT_SECRET_KEYS})\s*[:=]\s*([^\s#"'\x60<>]+)/g`;
const CONFIG_NAMESPACED_ASSIGNMENT_REDACT_PATTERN = String.raw`/(^|[\s,{])[a-z0-9_.-]{1,80}\.(?:${CONFIG_ASSIGNMENT_SECRET_KEYS})\s*[:=]\s*([^\s#"'\x60<>]+)/g`;
const STRUCTURED_JSON_SECRET_REDACT_PATTERN = String.raw`"(?:apiKey|api_key|apiToken|api_token|bearerToken|bearer_token|token|secret|password|passwd|${AWS_SECRET_ACCESS_KEY_FIELD_KEYS}|credential|authorization|proxy-authorization|cookie|set-cookie|x-api-key|x-auth-token|accessToken|access_token|refreshToken|refresh_token|idToken|id_token|authToken|auth_token|clientSecret|client_secret|privateKey|private_key|secret_value|raw_secret|secret_input|key_material)"\s*:\s*"([^"]+)"`;
const STRUCTURED_JSON_PAYMENT_REDACT_PATTERN = String.raw`"(?:${PAYMENT_CREDENTIAL_JSON_KEYS})"\s*:\s*"([^"]+)"`;
const AMBIGUOUS_QUOTED_SECRET_FIELD_REDACT_PATTERN = String.raw`(^|[\s,{])["']?(?:api[-_]key|access[-_]token|refresh[-_]token|id[-_]token|authToken|auth[-_]token|clientSecret|client[-_]secret|appSecret|app[-_]secret|private[-_]key|credential|authorization|secret[-_]value|raw[-_]secret|secret[-_]input|key[-_]material)["']?\s*[:=]\s*(["'])([^"'\r\n]+)\2`;
const AMBIGUOUS_QUOTED_AUTH_FIELD_REDACT_PATTERN = String.raw`(^|[\s,{])["']?(?:authorization|proxy-authorization|cookie|set-cookie|x-api-key|x-auth-token)["']?\s*[:=]\s*(["'])([^"'\r\n]+)\2`;
const BASE64_SAFE_TOKEN_BOUNDARY = String.raw`(^|[^A-Za-z0-9])`;
const base64SafeToken = (token) => String.raw`${BASE64_SAFE_TOKEN_BOUNDARY}(?=${token})(?<!;base64,[A-Za-z0-9+/=]*)(${token})`;
const IDENTIFIER_SAFE_TOKEN_BOUNDARY = String.raw`(^|[^A-Za-z0-9_])`;
function isAwsValueCharacter(char) {
	const code = char.charCodeAt(0);
	return code >= 65 && code <= 90 || code >= 97 && code <= 122 || code >= 48 && code <= 57 || char === "/" || char === "+" || char === "=";
}
const AWS_SECRET_ACCESS_KEY_VALUE_RE = /(?=[A-Za-z0-9/+=]{0,39}[A-Z])(?=[A-Za-z0-9/+=]{0,39}[a-z])(?=[A-Za-z0-9/+=]{0,39}[0-9/+=])(?=[A-Za-z0-9/+=]{0,39}[G-Zg-z/+=])[A-Za-z0-9/+=]{40}/u;
function couldMatchAwsSecretAccessKey(text) {
	for (let end = 39; end < text.length;) {
		let cursor = end;
		while (cursor > end - 40 && isAwsValueCharacter(text[cursor])) cursor--;
		if (cursor === end - 40) return AWS_SECRET_ACCESS_KEY_VALUE_RE.test(text);
		end = cursor + 40;
	}
	return false;
}
const AWS_VALUE_WHITESPACE_RE = /\s/;
const AWS_URL_SCHEME_CHARACTER_RE = /[A-Za-z0-9+.-]/;
const AWS_URL_SCHEME_START_RE = /[A-Za-z]/;
function* matchAwsSecretAccessKeys(text) {
	if (!couldMatchAwsSecretAccessKey(text)) return;
	const closingDelimiters = {
		"\"": "\"",
		"'": "'",
		"`": "`",
		"<": ">",
		"(": ")",
		"[": "]",
		"{": "}"
	};
	let runStart = -1;
	let schemeStart = -1;
	let url;
	let backslashes = 0;
	const runs = [];
	for (let index = 0; index <= text.length; index++) {
		const char = text[index] ?? "";
		const whitespace = index === text.length || AWS_VALUE_WHITESPACE_RE.test(char);
		const escapeDepth = backslashes;
		backslashes = char === "\\" ? backslashes + 1 : 0;
		if (char && isAwsValueCharacter(char)) {
			if (runStart === -1) runStart = index;
		} else if (runStart !== -1) {
			if (index - runStart === 40) runs.push({
				start: runStart,
				end: index,
				origin: runStart,
				url
			});
			else if (index - runStart > 40 && char === "@" && text[index - 41] === "/") runs.push({
				start: index - 40,
				end: index,
				origin: runStart,
				url
			});
			runStart = -1;
		}
		if (whitespace) {
			if (url) url.end = Math.min(url.end, index);
			for (const run of runs) {
				const context = run.url;
				const publicUrl = context && !context.hasAt && context.portValid && (context.portStart === -1 || (context.authorityEnd === -1 ? context.end : context.authorityEnd) > context.portStart + 1);
				const before = text[run.start - 1] ?? "";
				const after = text[run.end] ?? "";
				const slashCredential = run.start !== run.origin;
				const portCredential = context && context.portStart !== -1 && run.origin === context.portStart + 1 && context.authorityEnd > run.origin && run.end > context.authorityEnd && after === "@";
				if (!slashCredential && (before === "_" || isAwsValueCharacter(before)) || after === "_" || text.slice(run.origin - 8, run.origin) === ";base64," || publicUrl && !portCredential && run.start >= context.start && run.end <= context.end) continue;
				const match = text.slice(run.start, run.end);
				if (AWS_SECRET_ACCESS_KEY_VALUE_RE.test(match)) yield {
					match,
					groups: [match],
					input: text,
					offset: run.start
				};
			}
			runs.length = 0;
			schemeStart = -1;
			url = void 0;
			continue;
		}
		if (url && index >= url.start) {
			if (char === url.closing && escapeDepth === url.closingEscapeDepth) {
				url.end = Math.min(url.end, index);
				url = void 0;
			} else {
				if ("?#\"'<>`|()[]{}".includes(char)) url.end = Math.min(url.end, index);
				url.queryOrFragment ||= char === "?" || char === "#";
				if (url.authorityEnd === -1) {
					url.hasAt ||= char === "@";
					if ("/?#".includes(char)) url.authorityEnd = index;
					else if (index < url.end) {
						if (char === ":") {
							url.portValid &&= url.portStart === -1;
							url.portStart = index;
						} else if (url.portStart !== -1 && (char < "0" || char > "9")) url.portValid = false;
					}
				}
			}
		}
		if (AWS_URL_SCHEME_CHARACTER_RE.test(char)) {
			if (schemeStart === -1) schemeStart = index;
		} else {
			if ((!url || index >= url.end && !url.queryOrFragment) && char === ":" && text.startsWith("//", index + 1) && schemeStart !== -1 && AWS_URL_SCHEME_START_RE.test(text[schemeStart])) {
				const opening = text[schemeStart - 1];
				let closingEscapeDepth = 0;
				for (let offset = schemeStart - 2; offset >= 0 && text[offset] === "\\"; offset--) closingEscapeDepth++;
				url = {
					start: index + 3,
					end: text.length,
					authorityEnd: -1,
					portStart: -1,
					portValid: true,
					hasAt: false,
					queryOrFragment: false,
					closing: opening ? closingDelimiters[opening] : void 0,
					closingEscapeDepth
				};
			}
			schemeStart = -1;
		}
	}
}
const AWS_SECRET_ACCESS_KEY_MATCHER = Object.freeze({
	source: "aws-secret-access-key",
	exec: matchAwsSecretAccessKeys,
	couldMatch: couldMatchAwsSecretAccessKey
});
const BARE_PASS_KEY_RE = new RegExp(String.raw`(?<![A-Za-z0-9])(pass|${CONFIG_COLON_ASSIGNMENT_SECRET_KEYS})\s*:\s*`, "gi");
const BARE_PASS_VALUE_RE = /[^\s#"'\x60<>]+/y;
const ASCII_WORD_CHAR_RE = /[A-Za-z0-9]/;
const INLINE_WHITESPACE_RE = /[ \t\r\n]/;
function* matchBarePassAssignments(text) {
	if (!/pass\s*:/.test(text)) return;
	const keys = [...text.matchAll(BARE_PASS_KEY_RE)];
	if (keys.length === 0) return;
	let next = 0;
	let assignmentSeen = false;
	for (let index = 0; index < text.length; index++) {
		const key = keys[next];
		if (!key) return;
		if (index === key.index) {
			next++;
			const before = text[index - 1] ?? "";
			const owned = key[1] !== "pass" || before === "." || before === "-" || before === "_";
			if (!(!owned && !assignmentSeen && before === " " && ASCII_WORD_CHAR_RE.test(text[index - 2] ?? ""))) {
				assignmentSeen = true;
				let end = index + key[0].length;
				if (!owned) {
					BARE_PASS_VALUE_RE.lastIndex = end;
					const value = BARE_PASS_VALUE_RE.exec(text)?.[0];
					if (value) {
						end += value.length;
						yield {
							match: text.slice(index, end),
							groups: [value],
							input: text,
							offset: index
						};
					}
				}
				let following = keys[next];
				while (following && following.index < end) {
					next++;
					following = keys[next];
				}
				index = end - 1;
				continue;
			}
		}
		const char = text[index];
		if (char === "\n" || char === "\r") assignmentSeen = false;
		else if (char === "=") assignmentSeen = true;
		else if (char === ":" && !INLINE_WHITESPACE_RE.test(text[index + 1] ?? " ")) assignmentSeen = true;
	}
}
const BARE_PASS_ASSIGNMENT_MATCHER = Object.freeze({
	source: "bare-pass-assignment",
	exec: matchBarePassAssignments
});
const TELEGRAM_BOT_TOKEN_REDACT_PATTERN = String.raw`\bbot(\d{6,}:[A-Za-z0-9_-]{20,})\b`;
const TELEGRAM_TOKEN_REDACT_PATTERN = String.raw`\b(\d{6,}:[A-Za-z0-9_-]{20,})\b`;
const CREDENTIAL_STYLE_HEADER_KEYS = "x-goog-api-key|api-key|apikey|x-api-token|x-access-token";
const GATEWAY_SECURITY_HEADER_KEYS = "X-OpenClaw-Token|x-pomerium-jwt-assertion|X-Api-Key|X-Auth-Token";
const LOG_HEADER_BOUNDARY_PATTERN = String.raw`(^|[^A-Za-z0-9_?&-]|\\{1,64}[rn])`;
const CREDENTIAL_STYLE_COLON_HEADER_REDACT_PATTERN = String.raw`${LOG_HEADER_BOUNDARY_PATTERN}(?:${CREDENTIAL_STYLE_HEADER_KEYS})${HTTP_AUTH_SERIALIZED_QUOTE_PATTERN}[ \t]*:${HTTP_AUTH_OPTIONAL_VALUE_WHITESPACE_PATTERN}${HTTP_AUTH_SERIALIZED_QUOTE_PATTERN}([^\s\\"',;]+)`;
const CREDENTIAL_STYLE_EQUALS_ASSIGNMENT_REDACT_PATTERN = String.raw`${LOG_HEADER_BOUNDARY_PATTERN}(?:${CREDENTIAL_STYLE_HEADER_KEYS})${HTTP_AUTH_SERIALIZED_QUOTE_PATTERN}[ \t]*=${HTTP_AUTH_OPTIONAL_VALUE_WHITESPACE_PATTERN}${HTTP_AUTH_SERIALIZED_QUOTE_PATTERN}([^\s\\"',;]+)`;
const GATEWAY_SECURITY_COLON_HEADER_REDACT_PATTERN = String.raw`${LOG_HEADER_BOUNDARY_PATTERN}(?:${GATEWAY_SECURITY_HEADER_KEYS})\s*:\s*([^\s"',;]+)`;
const GATEWAY_SECURITY_EQUALS_ASSIGNMENT_REDACT_PATTERN = String.raw`${LOG_HEADER_BOUNDARY_PATTERN}(?:${GATEWAY_SECURITY_HEADER_KEYS})\s*=\s*([^\s"',;]+)`;
const HTTP_AUTH_HEADER_REDACT_PATTERNS = [
	String.raw`${HTTP_AUTH_HEADER_BOUNDARY_PATTERN}Proxy-Authorization${HTTP_AUTH_SERIALIZED_QUOTE_PATTERN}[ \t]*[:=]${HTTP_AUTH_OPTIONAL_VALUE_WHITESPACE_PATTERN}${HTTP_AUTH_SERIALIZED_QUOTE_PATTERN}${HTTP_AUTH_SCHEME_PATTERN}${HTTP_AUTH_REQUIRED_VALUE_WHITESPACE_PATTERN}(${HTTP_AUTH_OPAQUE_CREDENTIAL_PATTERN})`,
	String.raw`${HTTP_AUTH_HEADER_BOUNDARY_PATTERN}Proxy-Authorization${HTTP_AUTH_SERIALIZED_QUOTE_PATTERN}[ \t]*[:=]${HTTP_AUTH_OPTIONAL_VALUE_WHITESPACE_PATTERN}${HTTP_AUTH_SERIALIZED_QUOTE_PATTERN}(${HTTP_AUTH_OPAQUE_CREDENTIAL_PATTERN})[ \t]*(?=${HTTP_AUTH_SERIALIZED_QUOTE_PATTERN}(?:$|[,;)}\]]|\r?\n(?![ \t])))`,
	String.raw`${HTTP_AUTH_HEADER_BOUNDARY_PATTERN}Authorization${HTTP_AUTH_SERIALIZED_QUOTE_PATTERN}[ \t]*[:=]${HTTP_AUTH_OPTIONAL_VALUE_WHITESPACE_PATTERN}${HTTP_AUTH_SERIALIZED_QUOTE_PATTERN}(?!(?:Bearer|Basic|Bot)(?=${HTTP_AUTH_REQUIRED_VALUE_WHITESPACE_PATTERN}))${HTTP_AUTH_SCHEME_PATTERN}${HTTP_AUTH_REQUIRED_VALUE_WHITESPACE_PATTERN}(${HTTP_AUTH_OPAQUE_CREDENTIAL_PATTERN})`,
	String.raw`${HTTP_AUTH_HEADER_BOUNDARY_PATTERN}Authorization${HTTP_AUTH_SERIALIZED_QUOTE_PATTERN}[ \t]*[:=]${HTTP_AUTH_OPTIONAL_VALUE_WHITESPACE_PATTERN}${HTTP_AUTH_SERIALIZED_QUOTE_PATTERN}(?!(?:Bearer|Basic|Bot)(?=${HTTP_AUTH_REQUIRED_VALUE_WHITESPACE_PATTERN}))(${HTTP_AUTH_OPAQUE_CREDENTIAL_PATTERN})[ \t]*(?=${HTTP_AUTH_SERIALIZED_QUOTE_PATTERN}(?:$|[,;)}\]]|\r?\n(?![ \t])))`,
	CREDENTIAL_STYLE_COLON_HEADER_REDACT_PATTERN,
	CREDENTIAL_STYLE_EQUALS_ASSIGNMENT_REDACT_PATTERN
];
const AUTHORIZATION_BEARER_REDACT_PATTERN = String.raw`Authorization${HTTP_AUTH_SERIALIZED_QUOTE_PATTERN}[ \t]*[:=]${HTTP_AUTH_LEGACY_VALUE_WHITESPACE_PATTERN}${HTTP_AUTH_SERIALIZED_QUOTE_PATTERN}Bearer${HTTP_AUTH_REQUIRED_VALUE_WHITESPACE_PATTERN}(${HTTP_AUTH_OPAQUE_CREDENTIAL_PATTERN})`;
const AUTHORIZATION_BASIC_REDACT_PATTERN = String.raw`Authorization${HTTP_AUTH_SERIALIZED_QUOTE_PATTERN}[ \t]*[:=]${HTTP_AUTH_LEGACY_VALUE_WHITESPACE_PATTERN}${HTTP_AUTH_SERIALIZED_QUOTE_PATTERN}Basic${HTTP_AUTH_REQUIRED_VALUE_WHITESPACE_PATTERN}(${HTTP_AUTH_OPAQUE_CREDENTIAL_PATTERN})`;
const AUTHORIZATION_BOT_REDACT_PATTERN = String.raw`Authorization${HTTP_AUTH_SERIALIZED_QUOTE_PATTERN}[ \t]*[:=]${HTTP_AUTH_LEGACY_VALUE_WHITESPACE_PATTERN}${HTTP_AUTH_SERIALIZED_QUOTE_PATTERN}Bot${HTTP_AUTH_REQUIRED_VALUE_WHITESPACE_PATTERN}(${HTTP_AUTH_OPAQUE_CREDENTIAL_PATTERN})`;
const STANDALONE_BEARER_REDACT_PATTERN = String.raw`\bBearer\s+([-A-Za-z0-9._~+/=]{18,})(?![-A-Za-z0-9._~+/=])`;
const DEFAULT_REDACT_FIELD_PATTERNS = [
	ENV_ASSIGNMENT_REDACT_PATTERN,
	ESCAPED_ENV_ASSIGNMENT_REDACT_PATTERN,
	STRUCTURED_JSON_SECRET_REDACT_PATTERN,
	STRUCTURED_JSON_PAYMENT_REDACT_PATTERN,
	AMBIGUOUS_QUOTED_SECRET_FIELD_REDACT_PATTERN,
	AMBIGUOUS_QUOTED_AUTH_FIELD_REDACT_PATTERN,
	String.raw`--(?:${CLI_SECRET_FLAG_KEYS})=([^\s"']+)`,
	String.raw`--(?:${CLI_SECRET_FLAG_KEYS})\s+(?!(?:or|and)\b(?=\s+--))(["']?)([^\s"']+)\1`,
	AUTHORIZATION_BEARER_REDACT_PATTERN,
	AUTHORIZATION_BASIC_REDACT_PATTERN,
	AUTHORIZATION_BOT_REDACT_PATTERN,
	...HTTP_AUTH_HEADER_REDACT_PATTERNS,
	GATEWAY_SECURITY_COLON_HEADER_REDACT_PATTERN,
	GATEWAY_SECURITY_EQUALS_ASSIGNMENT_REDACT_PATTERN,
	STANDALONE_BEARER_REDACT_PATTERN,
	String.raw`\b(?:https?|wss?|ftp):\/\/[^\/\s:@]*:([^\/\s@]+)@`,
	String.raw`\b(?:postgres(?:ql)?|mysql|mongodb(?:\+srv)?|rediss?|amqps?):\/\/[^:\s/@]*:([^@\s]+)@`,
	String.raw`(^|[\s,;])(?:${FORM_BODY_FIRST_PAIR_KEYS})=([^&\s]+)(?=&[A-Za-z_][A-Za-z0-9_.-]*=)`,
	STANDALONE_ASSIGNMENT_QUOTED_REDACT_PATTERN,
	STANDALONE_ASSIGNMENT_REDACT_PATTERN,
	CONFIG_QUOTED_ASSIGNMENT_REDACT_PATTERN,
	CONFIG_ASSIGNMENT_REDACT_PATTERN,
	BARE_PASS_ASSIGNMENT_MATCHER,
	CONFIG_DIRECT_ASSIGNMENT_REDACT_PATTERN,
	CONFIG_PREFIXED_PASSWORD_ASSIGNMENT_REDACT_PATTERN,
	CONFIG_NAMESPACED_ASSIGNMENT_REDACT_PATTERN,
	PEM_REDACT_PATTERN_SOURCE,
	String.raw`(^|[\s,{])["']?(?:${AWS_SECRET_ACCESS_KEY_FIELD_KEYS})["']?\s*[:=]\s*(["']?)([A-Za-z0-9/+=]{40})(?![A-Za-z0-9/+=])\2`
];
const VENDOR_TOKEN_REDACT_PATTERNS = [
	String.raw`\b(sk-[A-Za-z0-9_-]{8,})\b`,
	String.raw`(ghp_[A-Za-z0-9]{10,})`,
	String.raw`(github_pat_[A-Za-z0-9_]{10,})`,
	String.raw`(gho_[A-Za-z0-9]{10,})`,
	String.raw`(ghu_[A-Za-z0-9]{10,})`,
	String.raw`(ghs_[A-Za-z0-9]{10,})`,
	String.raw`(ghr_[A-Za-z0-9]{10,})`,
	String.raw`(glpat-[A-Za-z0-9._=\-]{20,})`,
	String.raw`(gloas-(?:[A-Fa-f0-9]{65,}|[A-Za-z0-9_-]{64}|[A-Fa-f0-9]{32,}))`,
	String.raw`(gldt-[A-Za-z0-9_-]{20,})`,
	String.raw`(glcbt-[A-Za-z0-9]{1,5}_[A-Za-z0-9_-]{20,})`,
	String.raw`(glptt-[A-Za-z0-9_-]{40,})`,
	String.raw`(glft-(?:[A-Za-z0-9_-]{20,}|[a-h0-9]+-[0-9]+_))`,
	String.raw`(glimt-[A-Za-z0-9_-]{25,})`,
	String.raw`(glagent-[A-Za-z0-9_-]{50,})`,
	String.raw`(glwt-[A-Za-z0-9_-]{20,})`,
	String.raw`(glsoat-[A-Za-z0-9_-]{20,})`,
	String.raw`(glffct-[A-Za-z0-9_-]{20,})`,
	String.raw`(glrt-[A-Za-z0-9._-]{20,})`,
	String.raw`(glrtr?-[A-Za-z0-9_-]{27,300}\.[0-9a-z]{2}\.[0-9a-z]{9})`,
	String.raw`(GR1348941[A-Za-z0-9_-]{20,})`,
	String.raw`(_gitlab_session=[A-Za-z0-9%._-]{20,})`,
	String.raw`(xox[baprs]-[A-Za-z0-9-]{10,})`,
	String.raw`(xapp-[A-Za-z0-9-]{10,})`,
	String.raw`(https:\/\/hooks\.slack\.com\/(?:services\/T[A-Z0-9]+\/B[A-Z0-9]+|workflows\/T[A-Z0-9]+\/A[A-Z0-9]+\/[0-9]{17,19})\/[A-Za-z0-9]{20,})`,
	String.raw`(https:\/\/discord(?:app)?\.com\/api\/webhooks\/[0-9]{17,20}\/[A-Za-z0-9_-]{60,})`,
	String.raw`discord(?:.|\n|\r){0,40}?\b([A-Za-z0-9_-]{24}\.[A-Za-z0-9_-]{6}\.[A-Za-z0-9_-]{27})\b`,
	String.raw`(gsk_[A-Za-z0-9_-]{10,})`,
	String.raw`(AIza[0-9A-Za-z\-_]{20,})`,
	String.raw`(ya29\.[0-9A-Za-z_\-./+=]{10,})`,
	String.raw`(1//0[0-9A-Za-z_\-./+=]{10,})`,
	String.raw`(GOCSPX-[A-Za-z0-9_-]{20,})`,
	String.raw`(eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,})`,
	String.raw`(pplx-[A-Za-z0-9_-]{10,})`,
	String.raw`(fal_[A-Za-z0-9_-]{10,})`,
	String.raw`${IDENTIFIER_SAFE_TOKEN_BOUNDARY}(fc-[A-Za-z0-9]{10,})`,
	String.raw`(bb_live_[A-Za-z0-9_-]{10,})`,
	base64SafeToken(String.raw`gAAAA[A-Za-z0-9_=-]{20,}`),
	String.raw`(sk_live_[A-Za-z0-9]{10,})`,
	String.raw`(sk_test_[A-Za-z0-9]{10,})`,
	String.raw`(rk_live_[A-Za-z0-9]{10,})`,
	String.raw`(SG\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,})`,
	String.raw`(npm_[A-Za-z0-9]{10,})`,
	String.raw`(pypi-[A-Za-z0-9_-]{10,})`,
	String.raw`(dop_v1_[A-Za-z0-9]{10,})`,
	String.raw`(doo_v1_[A-Za-z0-9]{10,})`,
	String.raw`(dor_v1_[A-Za-z0-9]{10,})`,
	String.raw`(dp\.(?:ct|pt|sa|scim|audit)\.[A-Za-z0-9]{40,44})`,
	String.raw`(dp\.st\.[A-Za-z0-9]{40,44})`,
	String.raw`(dp\.st\.[a-z0-9_-]{2,35}\.[A-Za-z0-9]{40,44})`,
	String.raw`(dckr_(?:pat|oat)_[A-Za-z0-9_-]{27,32})`,
	String.raw`(bkua_[a-z0-9]{40})`,
	String.raw`(CCIPAT_[A-Za-z0-9]{22}_[A-Fa-f0-9]{40})`,
	String.raw`(sbp_[a-z0-9]{40})`,
	base64SafeToken(String.raw`dapi[0-9a-f]{32}(?:-\d)?`),
	String.raw`(dd[pw]_[A-Za-z0-9]{36})`,
	String.raw`(glsa_[A-Za-z0-9_]{41})`,
	String.raw`(glc_eyJ[A-Za-z0-9+/=]{60,160})`,
	String.raw`(nfp_[A-Za-z0-9_]{36})`,
	String.raw`(CFPAT-[A-Za-z0-9_\-]{40,})`,
	base64SafeToken(String.raw`ATCTT3xFfG[A-Za-z0-9+/=_-]+=[A-Za-z0-9]{8}`),
	base64SafeToken(String.raw`ATATT[A-Za-z0-9+/=_-]+=[A-Za-z0-9]{8}`),
	base64SafeToken(String.raw`ATBB[A-Za-z0-9_=.-]{16,}`),
	String.raw`(BBDC-[A-Za-z0-9+/@_-]{40,50})`,
	String.raw`(HRKU-AA[A-Za-z0-9_-]{20,})`,
	String.raw`(pat-(?:eu|na)1-[A-Za-z0-9]{8}\-[A-Za-z0-9]{4}\-[A-Za-z0-9]{4}\-[A-Za-z0-9]{4}\-[A-Za-z0-9]{12})`,
	String.raw`(apify_api_[A-Za-z0-9\-]{20,})`,
	String.raw`(FlyV1 fm\d+_[A-Za-z0-9+/=,_-]{100,})`,
	String.raw`(fio-u-[A-Za-z0-9_-]{40,})`,
	String.raw`(^|[^A-Za-z0-9_])(am_[A-Za-z0-9_-]{10,})`,
	String.raw`(^|[^A-Za-z0-9_])(sk_[A-Za-z0-9_]{10,})`,
	String.raw`(tvly-[A-Za-z0-9]{10,})`,
	String.raw`(exa_[A-Za-z0-9]{10,})`,
	String.raw`(syt_[A-Za-z0-9]{10,})`,
	String.raw`(retaindb_[A-Za-z0-9]{10,})`,
	String.raw`(hsk-[A-Za-z0-9]{10,})`,
	String.raw`(mem0_[A-Za-z0-9]{10,})`,
	String.raw`(brv_[A-Za-z0-9]{10,})`,
	String.raw`(xai-[A-Za-z0-9]{30,})`,
	String.raw`${IDENTIFIER_SAFE_TOKEN_BOUNDARY}(fw-[A-Za-z0-9]{30,})`,
	String.raw`${IDENTIFIER_SAFE_TOKEN_BOUNDARY}(fw_[A-Za-z0-9]{30,})`,
	String.raw`${IDENTIFIER_SAFE_TOKEN_BOUNDARY}(fpk_[A-Za-z0-9]{30,})`,
	base64SafeToken(String.raw`AKIA[A-Z0-9]{16}`),
	base64SafeToken(String.raw`ASIA[A-Z0-9]{16}`),
	String.raw`(AKID[A-Za-z0-9]{10,})`,
	String.raw`(LTAI[A-Za-z0-9]{10,})`,
	String.raw`(hf_[A-Za-z0-9]{10,})`,
	String.raw`(api_org_[A-Za-z0-9]{20,})`,
	String.raw`(r8_[A-Za-z0-9]{10,})`,
	TELEGRAM_BOT_TOKEN_REDACT_PATTERN,
	TELEGRAM_TOKEN_REDACT_PATTERN
];
const DEFAULT_REDACT_PATTERNS = [
	...DEFAULT_REDACT_FIELD_PATTERNS,
	...VENDOR_TOKEN_REDACT_PATTERNS,
	AWS_SECRET_ACCESS_KEY_MATCHER
];
function createBackendRedactPatterns() {
	const bodySecretKeys = /* @__PURE__ */ new Set([
		"access_token",
		"auth_token",
		"awssecretaccesskey",
		"aws_secret_access_key",
		"hook_token",
		"refresh_token",
		"id_token",
		"token",
		"api_key",
		"apikey",
		"client_secret",
		"app_secret",
		"password",
		"pass",
		"passwd",
		"auth",
		"jwt",
		"session",
		"code",
		"signature",
		"x_amz_signature",
		"x_amz_security_token",
		"secret",
		"secretaccesskey",
		"credential",
		"private_key",
		"authorization",
		"key",
		"card_number",
		"card_cvc",
		"card_cvv",
		"cvc",
		"cvv",
		"security_code",
		"payment_credential",
		"shared_payment_token"
	]);
	const credentialHeaderField = new RegExp(`^(?:${CREDENTIAL_STYLE_HEADER_KEYS}|${GATEWAY_SECURITY_HEADER_KEYS})$`, "i");
	const formAware = /* @__PURE__ */ new Set([CREDENTIAL_STYLE_EQUALS_ASSIGNMENT_REDACT_PATTERN, GATEWAY_SECURITY_EQUALS_ASSIGNMENT_REDACT_PATTERN]);
	const shellReferencePreserving = /* @__PURE__ */ new Set([
		ENV_ASSIGNMENT_REDACT_PATTERN,
		ESCAPED_ENV_ASSIGNMENT_REDACT_PATTERN,
		STANDALONE_ASSIGNMENT_QUOTED_REDACT_PATTERN,
		STANDALONE_ASSIGNMENT_REDACT_PATTERN
	]);
	const CONFIG_KEY_PROBE = new RegExp(String.raw`[:=](?<=(?:${CONFIG_ASSIGNMENT_SECRET_KEYS})\s*[:=])`);
	const CONFIG_QUOTED_PROBE = new RegExp(String.raw`[:=](?<=(?:${CONFIG_ASSIGNMENT_SECRET_KEYS})\s*[:=])\s*["'\x60]`);
	const CONFIG_NAMESPACED_PROBE = new RegExp(String.raw`[:=](?<=\.(?:${CONFIG_ASSIGNMENT_SECRET_KEYS})\s*[:=])`);
	const STANDALONE_ASSIGNMENT_PROBE = new RegExp(String.raw`=(?<=(?:^|[\s,;({\["])(?:${STANDALONE_ASSIGNMENT_SECRET_KEYS})=)`, "i");
	const prefilters = new Map([
		[ENV_ASSIGNMENT_REDACT_PATTERN, (text) => /KEY|TOKEN|SECRET|PASSWORD|PASSWD|CARD|CVC|CVV|SECURITY|PAYMENT/.test(text)],
		[ESCAPED_ENV_ASSIGNMENT_REDACT_PATTERN, (text) => text.includes("\\")],
		[STANDALONE_ASSIGNMENT_QUOTED_REDACT_PATTERN, (text) => /[=]["'`]/.test(text)],
		[STANDALONE_ASSIGNMENT_REDACT_PATTERN, (text) => STANDALONE_ASSIGNMENT_PROBE.test(text)],
		[CONFIG_QUOTED_ASSIGNMENT_REDACT_PATTERN, (text) => /["'`]/.test(text) && CONFIG_QUOTED_PROBE.test(text)],
		[CONFIG_ASSIGNMENT_REDACT_PATTERN, (text) => (text.includes(":") || text.includes("=")) && CONFIG_KEY_PROBE.test(text)],
		[CONFIG_DIRECT_ASSIGNMENT_REDACT_PATTERN, (text) => text.includes("=")],
		[CONFIG_PREFIXED_PASSWORD_ASSIGNMENT_REDACT_PATTERN, (text) => /[-_](?:password|passphrase|pass|passwd)\s*[:=]/.test(text)],
		[CONFIG_NAMESPACED_ASSIGNMENT_REDACT_PATTERN, (text) => CONFIG_NAMESPACED_PROBE.test(text)],
		[AMBIGUOUS_QUOTED_SECRET_FIELD_REDACT_PATTERN, (text) => /["']/.test(text)],
		[AMBIGUOUS_QUOTED_AUTH_FIELD_REDACT_PATTERN, (text) => /["']/.test(text)],
		...HTTP_AUTH_HEADER_REDACT_PATTERNS.slice(0, 4).map((raw) => [raw, (text) => /authorization/i.test(text)]),
		[CREDENTIAL_STYLE_COLON_HEADER_REDACT_PATTERN, (text) => /api-key|apikey|api-token|access-token/i.test(text)],
		[CREDENTIAL_STYLE_EQUALS_ASSIGNMENT_REDACT_PATTERN, (text) => /api-key|apikey|api-token|access-token/i.test(text)],
		[STRUCTURED_JSON_SECRET_REDACT_PATTERN, (text) => text.includes("\"")],
		[STRUCTURED_JSON_PAYMENT_REDACT_PATTERN, (text) => text.includes("\"")],
		[GATEWAY_SECURITY_COLON_HEADER_REDACT_PATTERN, (text) => /x-openclaw-token|x-pomerium-jwt-assertion|x-api-key|x-auth-token/i.test(text)],
		[GATEWAY_SECURITY_EQUALS_ASSIGNMENT_REDACT_PATTERN, (text) => /x-openclaw-token|x-pomerium-jwt-assertion|x-api-key|x-auth-token/i.test(text)]
	]);
	const chunkUnsafe = /* @__PURE__ */ new Set([
		TELEGRAM_BOT_TOKEN_REDACT_PATTERN,
		TELEGRAM_TOKEN_REDACT_PATTERN,
		AUTHORIZATION_BEARER_REDACT_PATTERN,
		AUTHORIZATION_BASIC_REDACT_PATTERN,
		AUTHORIZATION_BOT_REDACT_PATTERN,
		STANDALONE_BEARER_REDACT_PATTERN,
		...HTTP_AUTH_HEADER_REDACT_PATTERNS,
		...VENDOR_TOKEN_REDACT_PATTERNS.filter((source) => source.startsWith(BASE64_SAFE_TOKEN_BOUNDARY))
	]);
	const ambiguousAssignments = /* @__PURE__ */ new Set([
		ENV_ASSIGNMENT_REDACT_PATTERN,
		ESCAPED_ENV_ASSIGNMENT_REDACT_PATTERN,
		STRUCTURED_JSON_SECRET_REDACT_PATTERN,
		AMBIGUOUS_QUOTED_SECRET_FIELD_REDACT_PATTERN,
		AMBIGUOUS_QUOTED_AUTH_FIELD_REDACT_PATTERN,
		STANDALONE_ASSIGNMENT_QUOTED_REDACT_PATTERN,
		STANDALONE_ASSIGNMENT_REDACT_PATTERN,
		CONFIG_QUOTED_ASSIGNMENT_REDACT_PATTERN,
		CONFIG_ASSIGNMENT_REDACT_PATTERN,
		CONFIG_DIRECT_ASSIGNMENT_REDACT_PATTERN,
		CONFIG_PREFIXED_PASSWORD_ASSIGNMENT_REDACT_PATTERN,
		CONFIG_NAMESPACED_ASSIGNMENT_REDACT_PATTERN
	]);
	/** Programmatic counterparts of the ambiguous assignment rules: kept out of tool payloads, preserved in source. */
	const ambiguousMatchers = /* @__PURE__ */ new Set([BARE_PASS_ASSIGNMENT_MATCHER]);
	return {
		bodySecretKeys,
		credentialHeaderField,
		formAware,
		shellReferencePreserving,
		prefilters,
		chunkUnsafe,
		ambiguousAssignments,
		ambiguousMatchers,
		toolPayload: DEFAULT_REDACT_PATTERNS.filter((pattern) => typeof pattern === "string" ? !ambiguousAssignments.has(pattern) : !ambiguousMatchers.has(pattern))
	};
}
//#endregion
//#region src/logging/redact-linear-matchers.ts
const JS_WHITESPACE_CODES = /* @__PURE__ */ new Set([
	9,
	10,
	11,
	12,
	13,
	32,
	160,
	5760,
	8232,
	8233,
	8239,
	8287,
	12288,
	65279
]);
function isJsWhitespaceAt(text, index) {
	if (index >= text.length) return false;
	const code = text.charCodeAt(index);
	return JS_WHITESPACE_CODES.has(code) || code >= 8192 && code <= 8202;
}
function isJsWhitespaceChar(char) {
	return char !== void 0 && isJsWhitespaceAt(char, 0);
}
function isAlnumChar(char) {
	if (char === void 0) return false;
	const code = char.charCodeAt(0);
	return code >= 48 && code <= 57 || code >= 65 && code <= 90 || code >= 97 && code <= 122;
}
function isLetterChar(char) {
	if (char === void 0) return false;
	const code = char.charCodeAt(0);
	return code >= 65 && code <= 90 || code >= 97 && code <= 122;
}
function isWordChar(char) {
	return char === "_" || isAlnumChar(char);
}
function isHexChar(char) {
	if (char === void 0) return false;
	const code = char.charCodeAt(0);
	return code >= 48 && code <= 57 || code >= 65 && code <= 70 || code >= 97 && code <= 102;
}
function isDigitChar(char) {
	return char !== void 0 && char.charCodeAt(0) >= 48 && char.charCodeAt(0) <= 57;
}
function isBase64UrlChar(char) {
	return isAlnumChar(char) || char === "+" || char === "/" || char === "=";
}
const prefixRegexPools = /* @__PURE__ */ new Map();
/** Reuse compiled prefixes without sharing mutable lastIndex with a reentrant scan. */
function* scanPrefixOccurrences(text, prefix) {
	let pool = prefixRegexPools.get(prefix);
	if (!pool) {
		pool = [];
		prefixRegexPools.set(prefix, pool);
	}
	const re = pool.pop() ?? new RegExp(prefix, "gi");
	try {
		re.lastIndex = 0;
		for (let match = re.exec(text); match; match = re.exec(text)) yield match.index;
	} finally {
		re.lastIndex = 0;
		pool.push(re);
	}
}
/** The `;base64,` exemption under the shared case-insensitive compilation of these rules. */
function isDataUrlPrefix(text, runStart) {
	return runStart >= 8 && text.slice(runStart - 8, runStart).toLowerCase() === ";base64,";
}
function runEndOf(text, start, isMember) {
	let end = start;
	while (end < text.length && isMember(text[end])) end += 1;
	return end;
}
function makeCachedRunEnd(text, isMember) {
	let start = -1;
	let end = -1;
	return (position) => {
		if (position < start || position >= end) {
			start = position;
			end = runEndOf(text, position, isMember);
		}
		return end;
	};
}
/**
* Shared matcher for the `BASE64_SAFE_TOKEN_BOUNDARY` vendor rules. The boundary group is
* checked per candidate in O(1); the `(?<!;base64,[A-Za-z0-9+/=]*)` exemption walks each
* base64 run once because candidates advance left to right and share runs, replacing the
* unbounded lookbehind that rescanned the whole run from every candidate. The token-end
* function is created per exec() call so concurrent redactions never share scanner state.
*/
function makeBase64SafeTokenMatcher(source, prefix, makeTokenEnd) {
	function* exec(text) {
		const tokenEnd = makeTokenEnd();
		let runStart = -1;
		let runEnd = -1;
		let searchFloor = 0;
		for (const candidate of scanPrefixOccurrences(text, prefix)) {
			if (candidate < searchFloor) continue;
			if (candidate > 0 && isAlnumChar(text[candidate - 1])) continue;
			if (candidate > 0) {
				let runAtCandidate;
				if (isBase64UrlChar(text[candidate - 1])) {
					if (candidate - 1 >= runStart && candidate - 1 < runEnd) runAtCandidate = runStart;
					else {
						let walk = candidate - 1;
						while (walk > 0 && isBase64UrlChar(text[walk - 1])) walk -= 1;
						runStart = walk;
						runEnd = runEndOf(text, candidate, isBase64UrlChar);
						runAtCandidate = walk;
					}
				} else runAtCandidate = candidate;
				if (isDataUrlPrefix(text, runAtCandidate)) continue;
			}
			const end = tokenEnd(text, candidate);
			if (end === null) continue;
			const matchStart = candidate > 0 ? candidate - 1 : 0;
			yield {
				match: text.slice(matchStart, end),
				groups: [candidate > 0 ? text[candidate - 1] : "", text.slice(candidate, end)],
				input: text,
				offset: matchStart
			};
			searchFloor = end + 1;
		}
	}
	return Object.freeze({
		source,
		exec
	});
}
const GAAAA_VALUE_CHAR = (char) => isAlnumChar(char) || char === "_" || char === "=" || char === "-";
const ATBB_VALUE_CHAR = (char) => isAlnumChar(char) || char === "_" || char === "=" || char === "." || char === "-";
const AT_VALUE_CHAR = (char) => isAlnumChar(char) || char === "+" || char === "/" || char === "=" || char === "-" || char === "_";
function fixedTailTokenEnd(tailStart, tailLength, isMember) {
	return (text, start) => {
		const end = start + tailStart + tailLength;
		for (let index = start + tailStart; index < end; index++) if (!isMember(text[index])) return null;
		return end;
	};
}
/** `dapi` + 32 hex digits + an optional `-<digit>`; the optional tail is greedy like the regex. */
const dapiBodyEnd = fixedTailTokenEnd(4, 32, isHexChar);
function dapiTokenEnd(text, start) {
	const bodyEnd = dapiBodyEnd(text, start);
	if (bodyEnd === null) return null;
	if (text[bodyEnd] === "-" && isDigitChar(text[bodyEnd + 1])) return bodyEnd + 2;
	return bodyEnd;
}
/**
* `ATCTT3xFfG`/`ATATT` value: one or more class characters, then `=`, then exactly eight
* alphanumerics. Greedy backtracking lands on the rightmost `=` in the run whose next
* eight characters are alphanumerics; each run is scanned once and candidates share it.
*/
function makeAtEqualsTokenMatcher(source, prefix) {
	const prefixLength = prefix.length;
	const equalsTailEnd = fixedTailTokenEnd(1, 8, isAlnumChar);
	return makeBase64SafeTokenMatcher(source, prefix, () => {
		let runStart = -1;
		let runEnd = -1;
		let lastEquals;
		return (text, start) => {
			const valueStart = start + prefixLength;
			if (valueStart < runStart || valueStart >= runEnd) {
				runEnd = runEndOf(text, valueStart, AT_VALUE_CHAR);
				runStart = valueStart;
				lastEquals = void 0;
				for (let index = valueStart; index < runEnd; index++) if (text[index] === "=" && equalsTailEnd(text, index) !== null) lastEquals = index;
			}
			const minimum = valueStart + 1;
			return lastEquals !== void 0 && lastEquals >= minimum ? lastEquals + 9 : null;
		};
	});
}
const GAAAA_TOKEN_MATCHER = makeBase64SafeTokenMatcher("vendor-token gAAAA (linear)", "gAAAA", () => (text, start) => {
	const end = runEndOf(text, start + 5, GAAAA_VALUE_CHAR);
	return end - (start + 5) >= 20 ? end : null;
});
const ATBB_TOKEN_MATCHER = makeBase64SafeTokenMatcher("vendor-token ATBB (linear)", "ATBB", () => (text, start) => {
	const end = runEndOf(text, start + 4, ATBB_VALUE_CHAR);
	return end - (start + 4) >= 16 ? end : null;
});
const AKIA_TOKEN_MATCHER = makeBase64SafeTokenMatcher("vendor-token AKIA (linear)", "AKIA", () => fixedTailTokenEnd(4, 16, isAlnumChar));
const ASIA_TOKEN_MATCHER = makeBase64SafeTokenMatcher("vendor-token ASIA (linear)", "ASIA", () => fixedTailTokenEnd(4, 16, isAlnumChar));
const DAPI_TOKEN_MATCHER = makeBase64SafeTokenMatcher("vendor-token dapi (linear)", "dapi", () => dapiTokenEnd);
const ATCTT_TOKEN_MATCHER = makeAtEqualsTokenMatcher("vendor-token ATCTT3xFfG (linear)", "ATCTT3xFfG");
const ATATT_TOKEN_MATCHER = makeAtEqualsTokenMatcher("vendor-token ATATT (linear)", "ATATT");
const JWT_REDACT_PATTERN = String.raw`(eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,})`;
const JWT_SEGMENT_CHAR = (char) => isAlnumChar(char) || char === "_" || char === "-";
/** JWT rule: each `eyJ` candidate parses its three segments forward; class runs are shared. */
const JWT_MATCHER = Object.freeze({
	source: "jwt (linear)",
	*exec(text) {
		const firstSegmentEnd = makeCachedRunEnd(text, JWT_SEGMENT_CHAR);
		const secondSegmentEnd = makeCachedRunEnd(text, JWT_SEGMENT_CHAR);
		const thirdSegmentEnd = makeCachedRunEnd(text, JWT_SEGMENT_CHAR);
		let searchFloor = 0;
		for (const candidate of scanPrefixOccurrences(text, "eyj")) {
			if (candidate < searchFloor) continue;
			const firstEnd = firstSegmentEnd(candidate + 3);
			if (firstEnd - (candidate + 3) < 10 || text[firstEnd] !== ".") continue;
			const secondEnd = secondSegmentEnd(firstEnd + 1);
			if (secondEnd - (firstEnd + 1) < 10 || text[secondEnd] !== ".") continue;
			const thirdEnd = thirdSegmentEnd(secondEnd + 1);
			if (thirdEnd - (secondEnd + 1) < 10) continue;
			const token = text.slice(candidate, thirdEnd);
			yield {
				match: token,
				groups: [token],
				input: text,
				offset: candidate
			};
			searchFloor = thirdEnd;
		}
	}
});
const URL_USERINFO_REDACT_PATTERN = String.raw`\b(?:https?|wss?|ftp):\/\/[^\/\s:@]*:([^\/\s@]+)@`;
const CONNECTION_STRING_REDACT_PATTERN = String.raw`\b(?:postgres(?:ql)?|mysql|mongodb(?:\+srv)?|rediss?|amqps?):\/\/[^:\s/@]*:([^@\s]+)@`;
const URL_USERINFO_SCHEMES = /* @__PURE__ */ new Set([
	"http",
	"https",
	"ws",
	"wss",
	"ftp"
]);
const CONNECTION_STRING_SCHEMES = /* @__PURE__ */ new Set([
	"postgres",
	"postgresql",
	"mysql",
	"mongodb",
	"mongodb+srv",
	"redis",
	"rediss",
	"amqp",
	"amqps"
]);
/**
* Shared matcher for the URL-userinfo and connection-string password rules. Both regexes are
* deterministic parses (each character class excludes its own terminators), so every `://`
* candidate parses forward once; failed password scans are memoized because later candidates
* share their tail regions.
*/
function makeUserInfoMatcher(source, schemes, isPassTerminator) {
	function* exec(text) {
		let searchFloor = 0;
		let failedPassScanEnd = -1;
		let separator = text.indexOf("://");
		while (separator !== -1) {
			let schemeStart = separator;
			while (schemeStart > 0 && isWordChar(text[schemeStart - 1])) schemeStart -= 1;
			let scheme = text.slice(schemeStart, separator).toLowerCase();
			let matchStart = schemeStart;
			if (!schemes.has(scheme) && schemeStart >= 8 && scheme === "srv" && text.slice(schemeStart - 8, schemeStart).toLowerCase() === "mongodb+") {
				scheme = "mongodb+srv";
				matchStart = schemeStart - 8;
			}
			if (separator >= searchFloor && schemes.has(scheme) && (matchStart === 0 || !isWordChar(text[matchStart - 1]))) {
				let cursor = separator + 3;
				while (cursor < text.length && text[cursor] !== ":" && text[cursor] !== "/" && text[cursor] !== "@" && !isJsWhitespaceAt(text, cursor)) cursor += 1;
				if (text[cursor] === ":") {
					const passStart = cursor + 1;
					if (passStart >= failedPassScanEnd) {
						const passEnd = runEndOf(text, passStart, (char) => !isPassTerminator(char));
						if (text[passEnd] === "@" && passEnd > passStart) {
							yield {
								match: text.slice(matchStart, passEnd + 1),
								groups: [text.slice(passStart, passEnd)],
								input: text,
								offset: matchStart
							};
							searchFloor = passEnd + 1;
							separator = text.indexOf("://", passEnd + 1);
							continue;
						}
						failedPassScanEnd = passEnd;
					}
				}
			}
			separator = text.indexOf("://", separator + 1);
		}
	}
	return Object.freeze({
		source,
		exec
	});
}
const URL_USERINFO_MATCHER = makeUserInfoMatcher("url-userinfo (linear)", URL_USERINFO_SCHEMES, (char) => char === "/" || char === "@" || isJsWhitespaceChar(char));
const CONNECTION_STRING_MATCHER = makeUserInfoMatcher("connection-string (linear)", CONNECTION_STRING_SCHEMES, (char) => char === "@" || isJsWhitespaceChar(char));
const FORM_BODY_FIRST_PAIR_REDACT_PATTERN = String.raw`(^|[\s,;])(?:${FORM_BODY_FIRST_PAIR_KEYS})=([^&\s]+)(?=&[A-Za-z_][A-Za-z0-9_.-]*=)`;
const FORM_FIRST_PAIR_KEYS_RE = new RegExp(`^(?:${FORM_BODY_FIRST_PAIR_KEYS})$`, "i");
const FORM_FIRST_PAIR_KEY_CHAR = (char) => isAlnumChar(char) || char === "-" || char === "_";
const FORM_FIRST_PAIR_TAIL_CHAR = (char) => isAlnumChar(char) || char === "_" || char === "." || char === "-";
/** The lookahead `&[A-Za-z_][A-Za-z0-9_.-]*=` is a deterministic forward check. */
function isFormPairBoundary(text, ampersand) {
	const first = text[ampersand + 1];
	if (!(first !== void 0 && (isLetterChar(first) || first === "_"))) return false;
	return text[runEndOf(text, ampersand + 2, FORM_FIRST_PAIR_TAIL_CHAR)] === "=";
}
/**
* Form first-pair rule: the value class excludes `&` and whitespace, so the greedy value
* always ends at the first `&` or whitespace and the lookahead is checked there alone;
* per-candidate value runs are shared so long runs are read once.
*/
const FORM_BODY_FIRST_PAIR_MATCHER = Object.freeze({
	source: "form-body-first-pair (linear)",
	*exec(text) {
		const valueEnd = makeCachedRunEnd(text, (char) => char !== "&" && !isJsWhitespaceChar(char));
		let searchFloor = 0;
		let separator = text.indexOf("=");
		while (separator !== -1) {
			let keyStart = separator;
			while (keyStart > 0 && FORM_FIRST_PAIR_KEY_CHAR(text[keyStart - 1])) keyStart -= 1;
			const key = text.slice(keyStart, separator);
			if (separator >= searchFloor && key.length > 0 && FORM_FIRST_PAIR_KEYS_RE.test(key) && (keyStart === 0 || text[keyStart - 1] === "," || text[keyStart - 1] === ";" || isJsWhitespaceAt(text, keyStart - 1))) {
				const start = separator + 1;
				const end = valueEnd(start);
				if (end > start && end < text.length && text[end] === "&" && isFormPairBoundary(text, end)) {
					const matchStart = keyStart > 0 ? keyStart - 1 : 0;
					yield {
						match: text.slice(matchStart, end),
						groups: [keyStart > 0 ? text[keyStart - 1] : "", text.slice(start, end)],
						input: text,
						offset: matchStart
					};
					searchFloor = end;
					separator = text.indexOf("=", end);
					continue;
				}
			}
			separator = text.indexOf("=", separator + 1);
		}
	}
});
const STANDALONE_QUOTED_PREFIX_SOURCE = String.raw`(^|[\s,;({\["])(?:${STANDALONE_ASSIGNMENT_SECRET_KEYS})=(["'\x60])`;
const CONFIG_QUOTED_PREFIX_SOURCE = String.raw`(^|[\s,{])(?:(?:${CONFIG_QUOTED_ASSIGNMENT_SECRET_KEYS})(?:\s*:\s*|\s+=\s*|=\s*)|[a-z0-9][a-z0-9._-]{0,79}[-_](?:${CONFIG_PREFIXED_PASSWORD_ASSIGNMENT_SECRET_KEYS})\s*[:=]\s*|[a-z0-9_.-]{1,80}\.(?:${CONFIG_ASSIGNMENT_SECRET_KEYS})\s*[:=]\s*)(["'\x60])`;
/**
* Shared matcher for the two quoted-value rules. The tempered dot `((?:(?!\2)[^\r\n])+)`
* forced a per-character lookahead and one backtrack stack entry per value character; the
* value here is a linear scan to the first closing quote or line end, and failed scans are
* memoized per quote character because later candidates share their tail regions.
*/
function makeQuotedAssignmentMatcher(source, prefixSource, flags) {
	const prefixRegexPool = [];
	function* exec(text) {
		const prefixRe = prefixRegexPool.pop() ?? new RegExp(prefixSource, flags);
		const failedScans = /* @__PURE__ */ new Map();
		let searchFrom = 0;
		try {
			for (;;) {
				prefixRe.lastIndex = searchFrom;
				const prefix = prefixRe.exec(text);
				if (!prefix) return;
				const start = prefix.index;
				const quote = prefix[2];
				const valueStart = start + prefix[0].length;
				const memo = failedScans.get(quote);
				let closing = -1;
				if (!(memo !== void 0 && valueStart >= memo.start && valueStart < memo.end)) {
					closing = valueStart;
					while (closing < text.length) {
						const char = text[closing];
						if (char === quote || char === "\r" || char === "\n") break;
						closing += 1;
					}
				}
				if (closing > valueStart && text[closing] === quote) {
					const end = closing + 1;
					yield {
						match: text.slice(start, end),
						groups: [
							prefix[1] ?? "",
							quote,
							text.slice(valueStart, closing)
						],
						input: text,
						offset: start
					};
					searchFrom = end;
				} else {
					if (closing !== -1) failedScans.set(quote, {
						start: valueStart,
						end: closing
					});
					searchFrom = start + 1;
				}
			}
		} finally {
			prefixRe.lastIndex = 0;
			prefixRegexPool.push(prefixRe);
		}
	}
	return Object.freeze({
		source,
		exec
	});
}
const STANDALONE_QUOTED_MATCHER = makeQuotedAssignmentMatcher("standalone-quoted-assignment (linear)", STANDALONE_QUOTED_PREFIX_SOURCE, "gi");
const CONFIG_QUOTED_MATCHER = makeQuotedAssignmentMatcher("config-quoted-assignment (linear)", CONFIG_QUOTED_PREFIX_SOURCE, "g");
/** Default rule sources whose compilation routes to the linear matchers above. */
const LINEAR_MATCHER_SOURCES = /* @__PURE__ */ new Map([
	[base64SafeToken(String.raw`gAAAA[A-Za-z0-9_=-]{20,}`), GAAAA_TOKEN_MATCHER],
	[base64SafeToken(String.raw`ATCTT3xFfG[A-Za-z0-9+/=_-]+=[A-Za-z0-9]{8}`), ATCTT_TOKEN_MATCHER],
	[base64SafeToken(String.raw`ATATT[A-Za-z0-9+/=_-]+=[A-Za-z0-9]{8}`), ATATT_TOKEN_MATCHER],
	[base64SafeToken(String.raw`ATBB[A-Za-z0-9_=.-]{16,}`), ATBB_TOKEN_MATCHER],
	[base64SafeToken(String.raw`dapi[0-9a-f]{32}(?:-\d)?`), DAPI_TOKEN_MATCHER],
	[base64SafeToken(String.raw`AKIA[A-Z0-9]{16}`), AKIA_TOKEN_MATCHER],
	[base64SafeToken(String.raw`ASIA[A-Z0-9]{16}`), ASIA_TOKEN_MATCHER],
	[JWT_REDACT_PATTERN, JWT_MATCHER],
	[URL_USERINFO_REDACT_PATTERN, URL_USERINFO_MATCHER],
	[CONNECTION_STRING_REDACT_PATTERN, CONNECTION_STRING_MATCHER],
	[FORM_BODY_FIRST_PAIR_REDACT_PATTERN, FORM_BODY_FIRST_PAIR_MATCHER],
	[STANDALONE_ASSIGNMENT_QUOTED_REDACT_PATTERN, STANDALONE_QUOTED_MATCHER],
	[CONFIG_QUOTED_ASSIGNMENT_REDACT_PATTERN, CONFIG_QUOTED_MATCHER]
]);
//#endregion
//#region src/logging/redact-mask.ts
const DEFAULT_REDACT_MIN_LENGTH = 18;
const DEFAULT_REDACT_KEEP_START = 6;
const SECRET_VALUE_TRAILING_DELIMITER_RE = /(["'`,;)}\]]+)$/u;
const SECRET_VALUE_SUFFIX_RE = /^["'`,;)}\]]*$/u;
const SECRET_VALUE_QUOTE_CHARS = /* @__PURE__ */ new Set([
	"\"",
	"'",
	"`"
]);
function maskToken(token) {
	if (token.length < DEFAULT_REDACT_MIN_LENGTH) return "***";
	return `${sliceUtf16Safe(token, 0, DEFAULT_REDACT_KEEP_START)}…${sliceUtf16Safe(token, -4)}`;
}
function splitSecretValueForMask(token) {
	const openingQuote = token[0] ?? "";
	const contentStart = SECRET_VALUE_QUOTE_CHARS.has(openingQuote) ? 1 : 0;
	if (contentStart) {
		const closingQuoteIndex = token.lastIndexOf(openingQuote);
		if (closingQuoteIndex > 0) {
			const suffix = token.slice(closingQuoteIndex + 1);
			if (SECRET_VALUE_SUFFIX_RE.test(suffix)) return {
				maskable: token.slice(1, closingQuoteIndex),
				suffix,
				maskStart: 0,
				maskEnd: closingQuoteIndex + 1
			};
		}
	}
	const content = token.slice(contentStart);
	const trailingDelimiter = content.match(SECRET_VALUE_TRAILING_DELIMITER_RE)?.[1] ?? "";
	const maskable = trailingDelimiter && trailingDelimiter.length < content.length ? content.slice(0, -trailingDelimiter.length) : content;
	return {
		maskable,
		suffix: maskable === content ? "" : trailingDelimiter,
		maskStart: 0,
		maskEnd: contentStart + maskable.length
	};
}
function splitFormAwareCredentialValue(token) {
	const pairBoundary = token.search(/&[A-Za-z_][A-Za-z0-9_.-]*=/u);
	return pairBoundary < 0 ? {
		secret: token,
		suffix: ""
	} : {
		secret: token.slice(0, pairBoundary),
		suffix: token.slice(pairBoundary)
	};
}
function maskSecretValue(token, options) {
	const { maskable, suffix } = splitSecretValueForMask(token);
	return `${options?.hinted ? maskToken(maskable) : "***"}${suffix}`;
}
//#endregion
//#region src/logging/redact-performance.ts
const redactionPerformance = channel("openclaw.redaction");
/** The capture subscriber receives counts and synchronous CPU time, never text or patterns. */
function startRedactionMeasurement(operation) {
	if (!redactionPerformance.hasSubscribers) return;
	const startedAt = performance.now();
	const cpu = process.threadCpuUsage();
	return (outcome, inputChars, patternCount) => {
		const elapsedMs = performance.now() - startedAt;
		const used = process.threadCpuUsage(cpu);
		redactionPerformance.publish({
			operation,
			outcome,
			pid: process.pid,
			threadId,
			isMainThread,
			elapsedMs,
			threadCpuMs: (used.user + used.system) / 1e3,
			...inputChars === void 0 ? {} : { inputChars },
			...patternCount === void 0 ? {} : { patternCount }
		});
	};
}
//#endregion
//#region src/logging/redact-prefilter.ts
const FIELD_KEY_PREFILTER_RE = /KEY|TOKEN|SECRET|PASSWORD|PASSWD|AUTH|COOKIE|SIGNATURE|CREDENTIAL|CARD|CVC|CVV|PAYMENT|security[-_]?code|\bpass\s*[=:]|\bpassphrase\s*[=:]|_(?:password|pass|passphrase|passwd)\s*[=:]|jwt\s*[=:]|session=|code=|\bsig\s*=/iu;
const SEPARATED_VENDOR_PREFILTER_RE = /sk-|GOCSPX-|gh[opsur]_|github_pat_|glpat-|gloas-|gldt-|glcbt-|glptt-|glft-|glimt-|glagent-|glwt-|glsoat-|glffct-|glrt-|glrtr-|_gitlab_session=|xox[baprs]-|xapp-|gsk_|pplx-|fal_|fc-|bb_live_|[sr]k_(?:live|test)_|npm_|pypi-|do[opr]_v1_|dckr_|bkua_|CCIPAT_|sbp_|dd[pw]_|glsa_|nfp_|CFPAT-|BBDC-|HRKU-|pat-(?:eu|na)1-|apify_api_|fio-u-|tvly-|exa_|syt_|retaindb_|mem0_|brv_|xai-|fw-|fw_|fpk_|(?:^|[^A-Za-z0-9_])(?:am_|sk_)|hf_|api_org_|r8_/iu;
const DEFAULT_REDACT_PREFILTER_RE = /PRIVATE KEY|\bBearer\s+|GR1348941|hooks\.slack\.com|discord|AIza|ya29\.|1\/\/0|eyJ|gAAAA|SG\.|dp\.(?:ct|pt|sa|st|scim|audit)\.|dapi[0-9a-f]|ATCTT3|ATATT|ATBB|FlyV1|A[KS]IA[A-Z0-9]|AKID|LTAI|\bbot\d{6,}:|\b\d{6,}:[A-Za-z0-9_-]{20,}/iu;
const OBFUSCATED_KEY_CHAR_RE = new RegExp(`[${FORM_BODY_KEY_INVISIBLE_CHARS}+%]`, "u");
const FULL_CONTEXT_REDACT_EXTRA_TRIGGERS_RE = /JWT|Bearer\s+|am_|sk_|(?<!\d)\d{6,}:[A-Za-z0-9_-]{20,}/i;
function couldMatchDefaultRedactPatterns(text) {
	return FIELD_KEY_PREFILTER_RE.test(text) || (text.includes("-") || text.includes("_")) && SEPARATED_VENDOR_PREFILTER_RE.test(text) || DEFAULT_REDACT_PREFILTER_RE.test(text) || text.includes("://") && text.includes("@") || text.includes("=") && OBFUSCATED_KEY_CHAR_RE.test(text) || AWS_SECRET_ACCESS_KEY_MATCHER.couldMatch(text);
}
function couldMatchDefaultFullContextPatterns(text) {
	return couldMatchDefaultRedactPatterns(text) || FULL_CONTEXT_REDACT_EXTRA_TRIGGERS_RE.test(text);
}
function couldMatchVendorRedactPatterns(text) {
	return (text.includes("-") || text.includes("_")) && SEPARATED_VENDOR_PREFILTER_RE.test(text) || DEFAULT_REDACT_PREFILTER_RE.test(text) || FULL_CONTEXT_REDACT_EXTRA_TRIGGERS_RE.test(text);
}
function createRedactPrefilter(probe) {
	const matches = /* @__PURE__ */ new Map();
	return (text) => {
		const cached = matches.get(text);
		if (cached !== void 0) return cached;
		const result = probe(text);
		matches.set(text, result);
		return result;
	};
}
//#endregion
//#region src/shared/assistant-error-format.ts
const HTTP_STATUS_DELIMITER_RE = /(?:\s*:\s*|\s+)/;
new RegExp(`^(?:http\\s*)?(\\d{3})${HTTP_STATUS_DELIMITER_RE.source}(.+)$`, "i");
new RegExp(`^(?:http\\s*)?(\\d{3})(?:${HTTP_STATUS_DELIMITER_RE.source}([\\s\\S]+))?$`, "i");
const CONNECTION_FAILED_MESSAGE = "Couldn't connect to the AI service. Check your connection, then try again. For details, open Settings → Logs in the Control UI or run `openclaw logs --follow`.";
const TRANSPORT_ERRORS = [
	{
		code: /\beconnrefused\b/i,
		phrases: /connection refused|actively refused/i,
		message: CONNECTION_FAILED_MESSAGE
	},
	{
		code: /\beconnreset\b|\beconnaborted\b|\benetreset\b|\bepipe\b/i,
		phrases: /socket hang up|connection reset|connection aborted/i,
		message: "Lost the connection to the AI service. Check the conversation before trying again. For details, open Settings → Logs in the Control UI or run `openclaw logs --follow`."
	},
	{
		code: /\benotfound\b|\beai_again\b|\benetunreach\b|\behostunreach\b|\behostdown\b/i,
		phrases: /getaddrinfo|no such host|\bdns\b|network is unreachable|host is unreachable|fetch failed|connection error|network request failed/i,
		message: CONNECTION_FAILED_MESSAGE
	}
];
function isKnownTransportErrorCode(value) {
	return TRANSPORT_ERRORS.some(({ code }) => code.exec(value)?.[0] === value);
}
//#endregion
//#region src/logging/structured-authorization-code.ts
function shouldRedactStructuredAuthorizationCode(normalizedKey, path, transportCode) {
	if (normalizedKey !== "code") return false;
	const normalizedPath = path.map((part) => part.toLowerCase());
	if (normalizedPath.length === 1 || normalizedPath.at(-1) === "code" && [
		"error",
		"nodeerror",
		"status",
		"details",
		"warnings"
	].includes(normalizedPath.at(-2) ?? "")) return false;
	return !(transportCode !== void 0 && normalizedPath.length > 1 && normalizedPath.slice(0, -1).every((part) => part === "cause") && isKnownTransportErrorCode(transportCode));
}
//#endregion
//#region src/logging/redact.ts
const DEFAULT_REDACT_MODE = "tools";
const backendPatterns = createBackendRedactPatterns();
const shellReferencePreservingPatterns = /* @__PURE__ */ new WeakSet();
const builtInResolvedPatterns = /* @__PURE__ */ new WeakSet();
const vendorResolvedPatterns = /* @__PURE__ */ new WeakMap();
const vendorSources = new Set(VENDOR_TOKEN_REDACT_PATTERNS);
const chunkUnsafePatterns = /* @__PURE__ */ new WeakSet();
const canonicalBuiltInSources = new Set([...DEFAULT_REDACT_PATTERNS, ...backendPatterns.toolPayload].filter((entry) => typeof entry === "string"));
const formAwareEqualsAssignmentPatterns = /* @__PURE__ */ new WeakSet();
const sourceAssignmentPatterns = /* @__PURE__ */ new WeakSet();
let defaultResolvedPatterns;
let toolPayloadResolvedPatterns;
const FORM_BODY_KEY_OBFUSCATION_RE = new RegExp(String.raw`[${FORM_BODY_KEY_INVISIBLE_CHARS}+]`, "gu");
const FORM_BODY_KEY_SEPARATOR_RE = /[\p{C}\p{Z}\u115F\u1160\u3164\uFFA0+]/gu;
const FORM_BODY_PERCENT_ESCAPE_RE = /%[0-9A-Fa-f]{2}/u;
const FORM_BODY_KEY = String.raw`[${FORM_BODY_KEY_INVISIBLE_CHARS}+]*(?:[A-Za-z_]|%[0-9A-Fa-f]{2})(?:[A-Za-z0-9_.-]|%[0-9A-Fa-f]{2}|[${FORM_BODY_KEY_INVISIBLE_CHARS}+])*`;
const FORM_BODY_VALUE = "[^&\\s<>]*";
const URL_QUERY_VALUE = "[^&#\\s<>]*";
const FORM_BODY_PAIR = String.raw`${FORM_BODY_KEY}=${FORM_BODY_VALUE}`;
const FORM_BODY_RE = new RegExp(String.raw`^${FORM_BODY_PAIR}(?:&${FORM_BODY_PAIR})+$`, "u");
const FORM_BODY_SUBSTRING_RE = new RegExp(String.raw`(^|[\s:({\[,="'` + "`" + String.raw`])(${FORM_BODY_PAIR}(?:&${FORM_BODY_PAIR})+)`, "gu");
const ENCODED_FORM_PAIR_RE = new RegExp(String.raw`(^|[\s:({\[,="'` + "`" + String.raw`&])(${FORM_BODY_KEY})=(${FORM_BODY_VALUE})`, "gu");
const FORM_BODY_CONTEXT_SINGLE_PAIR_RE = new RegExp(String.raw`(\b(?:body|form(?:[-_\s]?body)?)\s*[:=]\s*(["'\x60]?))(${FORM_BODY_KEY})=(${FORM_BODY_VALUE})(["'\x60]?)`, "giu");
const URL_QUERY_PAIR_RE = new RegExp(String.raw`([?&])(${FORM_BODY_KEY})=(${URL_QUERY_VALUE})`, "gu");
const FORM_BODY_LINE_BREAK_SPLIT_RE = /(\r\n|\r|\n)/u;
const FORM_BODY_LINE_BREAK_SEGMENT_RE = /^(?:\r\n|\r|\n)$/u;
const STRUCTURED_SECRET_FIELD_RE = new RegExp(String.raw`^(?:api[-_]?key|apiKey|api[-_]?token|apiToken|bearer[-_]?token|bearerToken|token|secret|password|passwd|${AWS_SECRET_ACCESS_KEY_FIELD_KEYS}|credential|authorization|private[-_]?key|privateKey|access[-_]?token|accessToken|refresh[-_]?token|refreshToken|id[-_]?token|idToken|auth[-_]?token|authToken|client[-_]?secret|clientSecret|app[-_]?secret|appSecret|secret[-_]?value|secretValue|raw[-_]?secret|rawSecret|secret[-_]?input|secretInput|key|key[-_]?material|keyMaterial|jwt|session|signature|cookie|set[-_]?cookie|${PAYMENT_CREDENTIAL_QUERY_KEYS}|${PAYMENT_CREDENTIAL_JSON_KEYS})$`, "i");
const STRUCTURED_INTERNAL_SOURCE_PATH_VALUE_RE = /^\$WORKSPACE_DIR\/[A-Za-z0-9._/-]+\.jsonl$/u;
const STRUCTURED_APP_PASSWORD_FIELD_RE = /^(?:apple|icloud|app[-_]?specific[-_]?password|appSpecificPassword|application[-_]?password|text|content|message|error|errorMessage|detail|details|reason)$/i;
const APP_SPECIFIC_PASSWORD_RE = /\b([a-z]{4}-[a-z]{4}-[a-z]{4}-[a-z]{4})\b/g;
const BENIGN_APP_PASSWORD_WORDS = /* @__PURE__ */ new Set([
	"case",
	"claw",
	"demo",
	"file",
	"main",
	"name",
	"open",
	"path",
	"slug",
	"test"
]);
const STRUCTURED_SECRET_ENV_FIELD_RE = new RegExp(String.raw`^(?:(?:[A-Z0-9]+[_-])+(?:KEY|TOKEN|SECRET|PASSWORD|PASSWD)|API[_-]?KEY|TOKEN|SECRET|PASSWORD|PASSWD|${PAYMENT_CREDENTIAL_ENV_KEYS})$`, "i");
function parsePattern(raw) {
	if (raw === PEM_REDACT_PATTERN_SOURCE) return PEM_REDACT_MATCHER;
	if (typeof raw !== "string" && !(raw instanceof RegExp)) {
		if (backendPatterns.ambiguousMatchers.has(raw)) sourceAssignmentPatterns.add(raw);
		return raw;
	}
	let pattern = typeof raw === "string" ? LINEAR_MATCHER_SOURCES.get(raw) ?? null : null;
	let canPrefilter = true;
	if (raw instanceof RegExp) {
		if (raw.flags.includes("g")) pattern = raw;
		else pattern = new RegExp(raw.source, `${raw.flags}g`);
	} else if (!pattern && raw.trim()) {
		const [source, flags] = parseRedactPatternSource(raw);
		const optimized = canonicalBuiltInSources.has(raw) ? rewriteOpenEndedRepeats(source, flags) : source;
		pattern = compileConfigRegex(optimized, flags)?.regex ?? null;
		canPrefilter = pattern !== null && pattern.source === optimized && pattern.flags === flags && pattern.exec === RegExp.prototype.exec && pattern[Symbol.replace] === RegExp.prototype[Symbol.replace];
	}
	if (pattern && typeof raw === "string" && backendPatterns.shellReferencePreserving.has(raw)) shellReferencePreservingPatterns.add(pattern);
	if (pattern && typeof raw === "string" && backendPatterns.ambiguousAssignments.has(raw)) sourceAssignmentPatterns.add(pattern);
	if (pattern && typeof raw === "string" && backendPatterns.formAware.has(raw)) formAwareEqualsAssignmentPatterns.add(pattern);
	if (pattern && typeof raw === "string" && (raw.startsWith(IDENTIFIER_SAFE_TOKEN_BOUNDARY) || backendPatterns.chunkUnsafe.has(raw))) chunkUnsafePatterns.add(pattern);
	if (pattern && typeof raw === "string") {
		if (canonicalBuiltInSources.has(raw)) builtInResolvedPatterns.add(pattern);
		if (canPrefilter && vendorSources.has(raw) && !vendorResolvedPatterns.has(pattern)) {
			const source = pattern.source;
			const exec = pattern.exec;
			const flags = pattern instanceof RegExp ? pattern.flags : void 0;
			const replace = pattern instanceof RegExp ? pattern[Symbol.replace] : void 0;
			vendorResolvedPatterns.set(pattern, () => pattern.source === source && pattern.exec === exec && (!(pattern instanceof RegExp) || pattern.flags === flags && pattern[Symbol.replace] === replace));
		}
		const probe = backendPatterns.prefilters.get(raw);
		if (canPrefilter && probe) setRedactPatternPrefilter(pattern, probe);
	}
	return pattern;
}
function resolvePatterns(value) {
	if (value === backendPatterns.toolPayload) {
		toolPayloadResolvedPatterns ??= backendPatterns.toolPayload.map(parsePattern).filter((re) => Boolean(re));
		return toolPayloadResolvedPatterns;
	}
	if (!value?.length || value === DEFAULT_REDACT_PATTERNS) {
		defaultResolvedPatterns ??= DEFAULT_REDACT_PATTERNS.map(parsePattern).filter((re) => Boolean(re));
		return defaultResolvedPatterns;
	}
	const seen = /* @__PURE__ */ new Set();
	const combined = [];
	for (const raw of value) {
		const resolved = parsePattern(raw);
		if (resolved !== null && !seen.has(resolved)) {
			seen.add(resolved);
			combined.push(resolved);
		}
	}
	if (!seen.has(AWS_SECRET_ACCESS_KEY_MATCHER)) combined.push(AWS_SECRET_ACCESS_KEY_MATCHER);
	return combined;
}
function usesBuiltInRedactPatterns(value) {
	return !value?.length || value === DEFAULT_REDACT_PATTERNS || value === backendPatterns.toolPayload;
}
function normalizeSensitiveKeyName(value) {
	const stripped = value.replace(FORM_BODY_KEY_SEPARATOR_RE, "");
	try {
		return decodeURIComponent(stripped).replace(FORM_BODY_KEY_SEPARATOR_RE, "").toLowerCase().replaceAll("-", "_");
	} catch {
		return stripped.toLowerCase().replaceAll("-", "_");
	}
}
function isSensitiveBodyKey(key) {
	return isSensitiveUrlQueryParamName(key) || backendPatterns.bodySecretKeys.has(normalizeSensitiveKeyName(key));
}
function hasEncodedOrInvisibleFormKey(key) {
	return FORM_BODY_PERCENT_ESCAPE_RE.test(key) || key.replace(FORM_BODY_KEY_OBFUSCATION_RE, "") !== key;
}
function visitSensitiveAssignments(text, kind, visit) {
	if (!text || kind === "url" && !text.includes("?")) return;
	if (kind === "encoded" && !text.includes("%") && text.replace(FORM_BODY_KEY_OBFUSCATION_RE, "") === text) return;
	if (kind === "context" && !/[=:]/u.test(text)) return;
	const visitPair = (key, token, valueOffset) => {
		if (kind === "encoded" && !hasEncodedOrInvisibleFormKey(key)) return;
		if (!isSensitiveBodyKey(key)) return;
		const { maskable, maskStart, maskEnd } = splitSecretValueForMask(token);
		visit(valueOffset + maskStart, valueOffset + maskEnd, maskable);
	};
	if (kind === "form") {
		let cursor = 0;
		for (const pair of text.split("&")) {
			const equalsIndex = pair.indexOf("=");
			if (equalsIndex >= 0) visitPair(pair.slice(0, equalsIndex), pair.slice(equalsIndex + 1), cursor + equalsIndex + 1);
			cursor += pair.length + 1;
		}
		return;
	}
	const pattern = kind === "url" ? URL_QUERY_PAIR_RE : kind === "encoded" ? ENCODED_FORM_PAIR_RE : FORM_BODY_CONTEXT_SINGLE_PAIR_RE;
	const keyIndex = kind === "context" ? 3 : 2;
	pattern.lastIndex = 0;
	try {
		for (let match = pattern.exec(text); match; match = pattern.exec(text)) {
			const prefix = match[1] ?? "";
			const key = match[keyIndex] ?? "";
			visitPair(key, match[keyIndex + 1] ?? "", match.index + prefix.length + key.length + 1);
		}
	} finally {
		pattern.lastIndex = 0;
	}
}
function redactAssignmentValues(text, kind, onEdits) {
	const parts = [];
	const edits = [];
	let cursor = 0;
	visitSensitiveAssignments(text, kind, (start, end, maskable) => {
		const replacement = kind === "url" ? maskToken(maskable) : "***";
		parts.push(text.slice(cursor, start), replacement);
		if (onEdits) edits.push({
			start,
			end,
			replacement
		});
		cursor = end;
	});
	if (edits.length > 0) onEdits?.(edits);
	return parts.length > 0 ? parts.join("") + text.slice(cursor) : text;
}
function redactFormBodyLine(text, onEdits) {
	if (!text.includes("=")) return text;
	const contextRedacted = redactAssignmentValues(redactAssignmentValues(text, "encoded", onEdits), "context", onEdits);
	if (!contextRedacted.includes("&")) return contextRedacted;
	if (FORM_BODY_RE.test(contextRedacted)) return redactAssignmentValues(contextRedacted, "form", onEdits);
	const substringEdits = [];
	const redacted = contextRedacted.replace(FORM_BODY_SUBSTRING_RE, (match, prefix, body, offset) => {
		const redactedBody = redactAssignmentValues(body, "form", onEdits ? (edits) => {
			for (const edit of edits) substringEdits.push({
				...edit,
				start: offset + prefix.length + edit.start,
				end: offset + prefix.length + edit.end
			});
		} : void 0);
		return redactedBody === body ? match : `${prefix}${redactedBody}`;
	});
	if (substringEdits.length > 0) onEdits?.(substringEdits);
	return redactAssignmentValues(redactAssignmentValues(redacted, "encoded", onEdits), "context", onEdits);
}
function redactFormBody(text, onEdits) {
	if (!text.includes("=")) return text;
	if (FORM_BODY_LINE_BREAK_SPLIT_RE.test(text)) {
		let offset = 0;
		return text.split(FORM_BODY_LINE_BREAK_SPLIT_RE).map((segment) => {
			const result = FORM_BODY_LINE_BREAK_SEGMENT_RE.test(segment) ? segment : redactFormBodyLine(segment, onEdits ? (edits) => onEdits(edits.map((edit) => ({
				...edit,
				start: offset + edit.start,
				end: offset + edit.end
			}))) : void 0);
			offset += result.length;
			return result;
		}).join("");
	}
	return redactFormBodyLine(text, onEdits);
}
function isShellReferenceToKey(key, value) {
	if (!/^[A-Z_][A-Z0-9_]*$/.test(key)) return false;
	const bare = value.match(/^\$([A-Z_][A-Z0-9_]*)$/);
	if (bare) return bare[1] === key;
	return value.match(/^\$\{([A-Z_][A-Z0-9_]*)(?::[-=?+])?\}$/)?.[1] === key;
}
function maskSecretFieldValue(key, value) {
	const expansion = value.match(/^\$\{([A-Z_][A-Z0-9_]*):[-=?+][^{}]+\}(?![\s\S])/);
	if (expansion && expansion[1] === key) return `${value.slice(0, value.indexOf(":") + 2)}***}`;
	return "***";
}
function shouldPreserveShellReferenceMatch(match, token) {
	const key = match.match(/\b([A-Z_][A-Z0-9_]*)\b\s*[=:]/)?.[1];
	return key ? isShellReferenceToKey(key, token) : false;
}
function isEmptyShellParameterExpansionTail(token) {
	return /^[-=?+]\}$/.test(token);
}
function prepareRedactionCapture({ match, groups, input, offset, replacement: policyReplacement }, pattern, preserveSourceAssignment) {
	if (match.includes("PRIVATE KEY-----")) return {
		start: offset,
		end: offset + match.length,
		value: match,
		redact: (target) => ({
			start: target.start,
			end: target.end,
			value: target.value,
			replacement: policyReplacement ?? redactPemBlock(target.value, "…redacted…")
		})
	};
	const selected = selectSecretCapture(match, groups);
	if (selected.value === "***") return;
	const tokenIndex = selected.value === match ? 0 : getSecretCaptureStart(pattern, input, match, offset, selected);
	if (tokenIndex < 0) return;
	const start = offset + tokenIndex;
	return {
		start,
		end: start + selected.value.length,
		value: selected.value,
		redact: (target) => {
			const token = target.value;
			if (sourceAssignmentPatterns.has(pattern) && preserveSourceAssignment?.(input, offset + getSecretCaptureStart(pattern, input, match, offset, selected))) return;
			const formAwareValue = formAwareEqualsAssignmentPatterns.has(pattern) ? splitFormAwareCredentialValue(token) : {
				secret: token,
				suffix: ""
			};
			if (splitSecretValueForMask(formAwareValue.secret).maskable === "***") return;
			const isShellReferencePattern = shellReferencePreservingPatterns.has(pattern);
			if (isShellReferencePattern && (shouldPreserveShellReferenceMatch(match, token) || isEmptyShellParameterExpansionTail(token))) return;
			const masked = policyReplacement ?? (preserveSourceAssignment && sourceAssignmentPatterns.has(pattern) ? maskSecretValue(token) : isShellReferencePattern ? maskToken(token) : `${maskSecretValue(formAwareValue.secret, { hinted: true })}${formAwareValue.suffix}`);
			return {
				start: target.start,
				end: target.end,
				replacement: masked
			};
		}
	};
}
function redactMatch(match, pattern, preserveSourceAssignment) {
	const capture = prepareRedactionCapture(match, pattern, preserveSourceAssignment);
	const edit = capture?.redact(capture);
	return edit ? match.match.slice(0, edit.start - match.offset) + edit.replacement + match.match.slice(edit.end - match.offset) : match.match;
}
function redactText(text, patterns, options) {
	const finishMeasurement = startRedactionMeasurement("text");
	let outcome = "error";
	try {
		let next = redactFormBody(redactAssignmentValues(redactStructuredAuthHeaders(text, "***"), "url"));
		let pattern;
		const replace = (match) => redactMatch(match, pattern, options?.preserveSourceAssignment);
		const replaceRegex = (...args) => replace(readRedactMatch(args));
		let vendorInput;
		let vendorMatch = false;
		for (pattern of patterns) {
			if (vendorResolvedPatterns.get(pattern)?.()) {
				if (vendorInput !== next) {
					vendorInput = next;
					vendorMatch = couldMatchVendorRedactPatterns(next);
				}
				if (!vendorMatch) {
					if (pattern instanceof RegExp) pattern.lastIndex = 0;
					continue;
				}
			}
			next = pattern instanceof RegExp && !options?.fullContext && !builtInResolvedPatterns.has(pattern) && !chunkUnsafePatterns.has(pattern) ? replacePatternBounded(next, pattern, replaceRegex) : replaceRedactPattern(next, pattern, replace, replaceRegex);
		}
		outcome = "ok";
		return next;
	} finally {
		finishMeasurement?.(outcome, text.length, patterns.length);
	}
}
function looksLikeAppSpecificPassword(candidate) {
	return candidate.split("-").every((part) => !BENIGN_APP_PASSWORD_WORDS.has(part.toLowerCase()));
}
function resolveConfigRedaction() {
	const cfg = readLoggingConfig();
	return {
		mode: DEFAULT_REDACT_MODE,
		patterns: cfg?.redactPatterns
	};
}
function resolveRedactOptions(options) {
	const resolved = options ?? resolveConfigRedaction();
	const mode = resolved.mode === "off" ? "off" : DEFAULT_REDACT_MODE;
	return {
		mode,
		patterns: mode === "off" ? [] : resolvePatterns(resolved.patterns)
	};
}
function redactSensitiveText(text, options) {
	if (!text) return text;
	return redactSensitiveTextWithOptions(redactRegisteredSecretValues(text, maskToken), options ?? resolveConfigRedaction());
}
function redactSensitiveTextWithOptions(exactRedacted, resolvedOptions) {
	if (resolvedOptions.mode === "off") return exactRedacted;
	if (usesBuiltInRedactPatterns(resolvedOptions.patterns) && !couldMatchDefaultRedactPatterns(exactRedacted)) return exactRedacted;
	return redactText(exactRedacted, resolveRedactOptions(resolvedOptions).patterns);
}
function isSensitiveFieldKey(key) {
	return STRUCTURED_SECRET_FIELD_RE.test(key) || STRUCTURED_SECRET_ENV_FIELD_RE.test(key);
}
function isPublicShareIdPath(path) {
	if (path.at(-1)?.toLowerCase() !== "id") return false;
	return path.at(-2)?.toLowerCase().replaceAll("-", "").replaceAll("_", "") === "publicshare";
}
function shouldRedactStructuredPrimitiveField(key, path) {
	const normalizedKey = key.toLowerCase();
	return isPublicShareIdPath(path) || shouldRedactStructuredAuthorizationCode(normalizedKey, path) || isSensitiveFieldKey(key);
}
function isPlainRedactableObject(value) {
	const prototype = Object.getPrototypeOf(value);
	return prototype === Object.prototype || prototype === null;
}
function preservesStructuredReference(key, value) {
	return key.toLowerCase() === "session" && STRUCTURED_INTERNAL_SOURCE_PATH_VALUE_RE.test(value) || isShellReferenceToKey(key, value);
}
function shouldRedactStructuredStringField(key, value, path, objectPath) {
	return shouldRedactStructuredAuthorizationCode(key.toLowerCase(), path, objectPath ? value : void 0) || isSensitiveFieldKey(key) && !preservesStructuredReference(key, value);
}
function classifyLogFieldProtection(key, path, objectPath, value) {
	return (value === void 0 ? shouldRedactStructuredPrimitiveField(key, path) : isPublicShareIdPath(path) || shouldRedactStructuredStringField(key, value, path, objectPath)) ? "legacy" : backendPatterns.credentialHeaderField.test(key) ? "header" : void 0;
}
function getFieldRecordEdits(field, mode) {
	const { key, value, path, objectPath } = field;
	if (mode === "off" || !field.string && value === "null" || !classifyLogFieldProtection(key, path, objectPath, field.string ? value : void 0)) return [];
	return [{
		start: 0,
		end: value.length,
		replacement: maskSecretFieldValue(key, value)
	}];
}
function getTextRecordEdits(field, mode, fileFields) {
	const { value } = field;
	if (field.isKey || fileFields && !field.origin.structured) return [];
	if (fileFields && field.origin.primitiveMask) return [{
		start: 0,
		end: value.length,
		replacement: "***"
	}];
	if (!field.string) return [];
	const edits = [];
	const registered = redactRegisteredSecretValues(value, (secret, start) => {
		const replacement = maskToken(secret);
		edits.push({
			start,
			end: start + secret.length,
			replacement
		});
		return replacement;
	});
	if (fileFields && isPublicShareIdPath(field.path)) return [{
		start: 0,
		end: value.length,
		replacement: maskToken(registered)
	}];
	if (mode === "off") return edits;
	let combined = edits;
	const receive = (added) => {
		combined = composeRedactionEdits(value.length, combined, added);
	};
	const headers = findStructuredAuthParamRanges(registered).map(({ start, end }) => ({
		start,
		end,
		replacement: "***"
	}));
	receive(headers);
	redactFormBody(redactAssignmentValues(applyRedactionEdits(registered, headers), "url", receive), receive);
	return combined;
}
function getLegacyFieldRecordEdits(field, value, beforeConversion = false) {
	const { key, value: original, path, objectPath } = field;
	if (field.isKey || !field.origin.structured || !field.string) return [];
	if (isPublicShareIdPath(path)) return [];
	const edits = [];
	if (value !== original || STRUCTURED_APP_PASSWORD_FIELD_RE.test(key)) {
		for (const match of value.matchAll(APP_SPECIFIC_PASSWORD_RE)) if (looksLikeAppSpecificPassword(match[0])) edits.push({
			start: match.index,
			end: match.index + match[0].length,
			replacement: maskToken(match[0])
		});
	}
	if (edits.length > 0 || value !== original) return edits;
	const protection = classifyLogFieldProtection(key, path, objectPath, value);
	return protection === "legacy" || beforeConversion && protection === "header" ? [{
		start: 0,
		end: value.length,
		replacement: maskToken(value)
	}] : [];
}
function* redactionEditMatches(input, edits) {
	for (const edit of edits) yield {
		match: input.slice(edit.start, edit.end),
		groups: [],
		input,
		offset: edit.start,
		replacement: edit.replacement
	};
}
const preparationPatterns = [
	{
		source: "registered secret values",
		*exec(input) {
			const matches = [];
			redactRegisteredSecretValues(input, (secret, offset) => {
				matches.push({
					match: secret,
					groups: [],
					input,
					offset,
					replacement: maskToken(secret)
				});
				return secret;
			});
			yield* matches;
		}
	},
	{
		source: "structured authorization parameters",
		*exec(input) {
			for (const { start, end } of findStructuredAuthParamRanges(input)) yield {
				match: input.slice(start, end),
				groups: [],
				input,
				offset: start,
				replacement: "***"
			};
		}
	},
	{
		source: "URL query assignments",
		*exec(input) {
			const edits = [];
			redactAssignmentValues(input, "url", (added) => edits.push(...added));
			yield* redactionEditMatches(input, edits);
		}
	},
	{
		source: "form bodies",
		*exec(input) {
			let edits = [];
			redactFormBody(input, (added) => {
				edits = composeRedactionEdits(input.length, edits, added);
			});
			yield* redactionEditMatches(input, edits);
		}
	}
];
const CONSOLE_STRUCTURAL_FIELDS = /* @__PURE__ */ new Set(["time", "level"]);
function prepareFileToJsonReceivers(record, patterns) {
	const decoded = /* @__PURE__ */ new WeakSet();
	const active = /* @__PURE__ */ new WeakSet();
	const visit = (value, key, path, objectPath, decode) => {
		if (typeof value === "string" && decode) {
			const field = {
				key,
				path,
				objectPath,
				value,
				isKey: false,
				string: true,
				origin: {
					structured: true,
					primitiveMask: false
				}
			};
			let current = applyRedactionEdits(value, getTextRecordEdits(field, "tools", true));
			if (!isPublicShareIdPath(path)) for (const pattern of patterns) current = applyRedactionEdits(current, getPatternRedactionEdits(current, pattern, prepareRedactionCapture));
			return applyRedactionEdits(current, getLegacyFieldRecordEdits(field, current, true));
		}
		if (value === null || typeof value !== "object") return decode && [
			"number",
			"boolean",
			"bigint"
		].includes(typeof value) && classifyLogFieldProtection(key, path, objectPath, void 0) ? "***" : value;
		if (!Array.isArray(value) && !isPlainRedactableObject(value)) return value;
		if (active.has(value)) return "[Circular]";
		active.add(value);
		let clone;
		let decodeFields = decode;
		if (Array.isArray(value)) clone = value.map((entry) => visit(entry, key, path, false, decodeFields));
		else {
			const entries = Object.entries(value);
			decodeFields ||= entries.some(([name, entry]) => name === "toJSON" && typeof entry === "function");
			clone = Object.fromEntries(entries.map(([name, entry]) => [name, visit(entry, name, [...path, name], objectPath, decodeFields)]));
		}
		if (decodeFields) decoded.add(clone);
		active.delete(value);
		return clone;
	};
	return {
		record: visit(record, "", [], true, false),
		decoded
	};
}
/** Converts native values once and applies configured and structural protection before output. */
function redactLogRecord(record, options, finish) {
	const finishMeasurement = startRedactionMeasurement("log-record");
	let outcome = "error";
	let inputChars;
	try {
		const resolved = resolveRedactOptions();
		const prepared = options.format === "console" ? {
			record,
			decoded: /* @__PURE__ */ new WeakSet()
		} : prepareFileToJsonReceivers(record, options.decodedOptions?.patterns ?? resolved.patterns);
		const ordinary = {
			structured: true,
			primitiveMask: false
		};
		const origins = {
			value: ordinary,
			children: /* @__PURE__ */ new Map()
		};
		const ancestors = [];
		const json = JSON.stringify(prepared.record, function(key, value) {
			while (ancestors.length > 0 && ancestors.at(-1)?.value !== this) ancestors.pop();
			const parent = ancestors.at(-1);
			const array = Array.isArray(this);
			const fieldKey = array && parent ? parent.key : key;
			const path = array && parent ? parent.path : parent ? [...parent.path, key] : [];
			const source = !parent ? prepared.record : parent.structured && options.format !== "console" ? Reflect.get(this, key) : value;
			const structured = (parent?.structured ?? true) && (source === null || typeof source !== "object" || !prepared.decoded.has(source) && source === value && (Array.isArray(source) || isPlainRedactableObject(source)));
			const primitiveMask = structured && [
				"number",
				"boolean",
				"bigint"
			].includes(typeof source) && classifyLogFieldProtection(fieldKey, path, !array, void 0) === "legacy";
			const circular = value !== null && typeof value === "object" && ancestors.some((frame) => frame.value === value);
			const emitted = circular ? "[Circular]" : typeof value === "bigint" ? String(value) : value;
			const container = emitted !== null && typeof emitted === "object";
			let node = origins;
			if (parent && parent.structured && (container || !structured || primitiveMask || circular)) {
				node = {
					value: {
						structured: structured && !circular,
						primitiveMask
					},
					children: /* @__PURE__ */ new Map()
				};
				parent.origins.children.set(key, node);
			} else if (parent) node = parent.origins;
			else origins.value = {
				structured,
				primitiveMask
			};
			if (container) ancestors.push({
				value: emitted,
				path,
				key: fieldKey,
				origins: node,
				structured
			});
			return emitted;
		});
		inputChars = json.length;
		let materialized = JSON.parse(json);
		const message = options.deriveMessage?.(materialized);
		if (message) {
			origins.children.set("message", {
				value: ordinary,
				children: /* @__PURE__ */ new Map()
			});
			if (Object.hasOwn(materialized, "message")) materialized.message = message.text;
			else {
				const entries = Object.entries(materialized);
				entries.splice(entries.findIndex(([key]) => key === "hostname") + 1, 0, ["message", message.text]);
				materialized = Object.fromEntries(entries);
			}
		}
		const serialized = message ? JSON.stringify(materialized) : json;
		const decodedPatterns = options.decodedOptions?.patterns ?? resolved.patterns;
		const couldMatch = createRedactPrefilter(couldMatchDefaultFullContextPatterns);
		const result = finish(redactJsonRecord(serialized, origins, [[{ patterns: decodedPatterns }], [{ patterns: preparationPatterns }, {
			patterns: resolved.patterns,
			...resolved.patterns === defaultResolvedPatterns ? { couldMatch } : {}
		}]], prepareRedactionCapture, options.format === "console" ? () => [] : getLegacyFieldRecordEdits, (field) => getFieldRecordEdits(field, resolved.mode), (field) => getTextRecordEdits(field, resolved.mode, options.format !== "console"), (field, currentValue) => (options.format === "console" ? field.path.length === 1 && CONSOLE_STRUCTURAL_FIELDS.has(field.key) : !field.origin.structured || field.origin.primitiveMask || isPublicShareIdPath(field.path)) || decodedPatterns === defaultResolvedPatterns && !couldMatch(currentValue), message), message ? serialized : void 0);
		outcome = "ok";
		return result;
	} finally {
		finishMeasurement?.(outcome, inputChars);
	}
}
function redactLogRecordForTransport(record, options = {}) {
	return redactLogRecord(record, options, (redacted) => JSON.parse(redacted));
}
function serializeRedactedFileLogRecord(record, options = {}) {
	return redactLogRecord(record, options, (redacted, canonical) => redacted === canonical ? redacted : JSON.stringify(JSON.parse(redacted)));
}
//#endregion
//#region src/logging/timestamps.ts
const validTimeZoneCache = /* @__PURE__ */ new Map();
const timestampFormatterCache = /* @__PURE__ */ new Map();
let hostTimeZone;
let lastTimestampParts;
function isValidTimeZone(tz) {
	const cached = validTimeZoneCache.get(tz);
	if (cached !== void 0) return cached;
	let valid;
	try {
		new Intl.DateTimeFormat("en", { timeZone: tz }).format();
		valid = true;
	} catch {
		valid = false;
	}
	validTimeZoneCache.set(tz, valid);
	return valid;
}
function resolveEffectiveTimeZone(timeZone) {
	const explicit = timeZone ?? process.env.TZ;
	return explicit && isValidTimeZone(explicit) ? explicit : hostTimeZone ??= Intl.DateTimeFormat().resolvedOptions().timeZone;
}
function formatOffset(offsetRaw) {
	return offsetRaw === "GMT" ? "+00:00" : offsetRaw.slice(3);
}
function getTimestampParts(date, timeZone) {
	const effectiveTimeZone = resolveEffectiveTimeZone(timeZone);
	const second = Math.floor(date.getTime() / 1e3);
	if (lastTimestampParts?.second === second && lastTimestampParts.timeZone === effectiveTimeZone) return lastTimestampParts.parts;
	let fmt = timestampFormatterCache.get(effectiveTimeZone);
	if (!fmt) {
		fmt = new Intl.DateTimeFormat("en", {
			timeZone: effectiveTimeZone,
			year: "numeric",
			month: "2-digit",
			day: "2-digit",
			hour: "2-digit",
			minute: "2-digit",
			second: "2-digit",
			hour12: false,
			fractionalSecondDigits: 3,
			timeZoneName: "longOffset"
		});
		timestampFormatterCache.set(effectiveTimeZone, fmt);
	}
	const parts = {};
	for (const part of fmt.formatToParts(date)) parts[part.type] = part.value;
	lastTimestampParts = {
		second,
		timeZone: effectiveTimeZone,
		parts
	};
	return parts;
}
function formatTimestamp(date, options) {
	const style = options?.style ?? "medium";
	const parts = getTimestampParts(date, options?.timeZone);
	const offset = formatOffset(parts.timeZoneName ?? "GMT");
	const milliseconds = String(date.getUTCMilliseconds()).padStart(3, "0");
	switch (style) {
		case "short": return `${parts.hour}:${parts.minute}:${parts.second}${offset}`;
		case "medium": return `${parts.hour}:${parts.minute}:${parts.second}.${milliseconds}${offset}`;
		case "long": return `${parts.year}-${parts.month}-${parts.day}T${parts.hour}:${parts.minute}:${parts.second}.${milliseconds}${offset}`;
	}
	throw new Error("Unsupported timestamp style");
}
//#endregion
//#region src/logging/json-console-line.ts
function formatJsonConsoleLine(params) {
	const envelope = {
		...params.meta,
		time: formatTimestamp(/* @__PURE__ */ new Date(), { style: "long" }),
		level: params.level,
		...params.subsystem ? { subsystem: params.subsystem } : {},
		message: params.message
	};
	return JSON.stringify(redactLogRecordForTransport(envelope, { format: "console" }));
}
/** Formats diagnostics that must bypass console capture without bypassing JSON console style. */
function formatConsoleDiagnosticLine(params) {
	return (loggingState.overrideSettings?.consoleStyle ?? readLoggingConfig()?.consoleStyle) === "json" ? formatJsonConsoleLine(params) : params.message;
}
//#endregion
//#region src/logging/logger-file-transport.ts
const DEFAULT_MAX_QUEUED_RECORDS = 4096;
const MAX_APPEND_BATCH_BYTES = 65536;
const MAX_ROTATED_LOG_FILES = 5;
const MAX_TRACKED_APPEND_FAILURE_FILES = 64;
let queue = [];
let queueStart = 0;
let activeBatch = null;
let activeIndex = 0;
let droppedCount = 0;
let droppedTarget = null;
let maxQueuedRecords = DEFAULT_MAX_QUEUED_RECORDS;
let scheduledFlush = null;
let flushPromise = null;
let drainGeneration = 0;
let processExiting = false;
let processHooksInstalled = false;
let appendFile = appendRegularFile;
const warnedRotationFiles = /* @__PURE__ */ new Map();
const warnedAppendFiles = /* @__PURE__ */ new Set();
let appendFailureTrackingSaturated = false;
function rotatedLogPath(file, index) {
	const ext = path.extname(file);
	return `${file.slice(0, file.length - ext.length)}.${index}${ext}`;
}
/**
* Creates a log directory and its missing parents with a bounded number of mkdir calls.
* Node's recursive mkdirSync retries forever when a parent exists but refuses new
* children (a deleted directory that is still mounted, such as a stale systemd
* PrivateTmp), which would block the event loop inside the logger.
*/
function ensureLogDirectorySync(dir, createParents = true) {
	try {
		fs$1.mkdirSync(dir);
	} catch (error) {
		const code = extractErrorCode(error);
		if (code === "EEXIST" && fs$1.statSync(dir).isDirectory()) return;
		const parent = path.dirname(dir);
		if (!createParents || code !== "ENOENT" || parent === dir) throw error;
		ensureLogDirectorySync(parent);
		ensureLogDirectorySync(dir, false);
	}
}
function rotateLogFile(file) {
	try {
		ensureLogDirectorySync(path.dirname(file));
		fs$1.rmSync(rotatedLogPath(file, MAX_ROTATED_LOG_FILES), { force: true });
		for (let index = 4; index >= 1; index -= 1) {
			const from = rotatedLogPath(file, index);
			if (fs$1.existsSync(from)) fs$1.renameSync(from, rotatedLogPath(file, index + 1));
		}
		if (fs$1.existsSync(file)) fs$1.renameSync(file, rotatedLogPath(file, 1));
		return true;
	} catch {
		return false;
	}
}
async function getCurrentLogFileBytes(file) {
	try {
		return (await fs.stat(file)).size;
	} catch {
		return 0;
	}
}
function getCurrentLogFileBytesSync(file) {
	try {
		return fs$1.statSync(file).size;
	} catch {
		return 0;
	}
}
function buildDroppedMarker(target, count) {
	const date = /* @__PURE__ */ new Date();
	const message = `[openclaw] file log queue overflow; dropped ${count} oldest record${count === 1 ? "" : "s"}`;
	const record = {
		0: message,
		_meta: {
			date,
			hostname: target.hostname,
			logLevelName: "WARN",
			name: "openclaw"
		},
		time: formatTimestamp(date, { style: "long" }),
		hostname: target.hostname,
		message,
		dropped: count
	};
	return {
		...target,
		payload: `${serializeRedactedFileLogRecord(record)}\n`
	};
}
function writeFileTransportWarning(message, synchronous) {
	try {
		const line = `${formatConsoleDiagnosticLine({
			level: "warn",
			message: redactSensitiveText(message)
		})}\n`;
		if (synchronous) fs$1.writeSync(process.stderr.fd, line);
		else process.stderr.write(line);
		return true;
	} catch {
		return false;
	}
}
function warnAboutRotationFailure(entry, synchronous) {
	if (warnedRotationFiles.get(entry.file) === entry.maxFileBytes) return;
	warnedRotationFiles.set(entry.file, entry.maxFileBytes);
	writeFileTransportWarning(`[openclaw] log file rotation failed; continuing writes file=${entry.file} maxFileBytes=${entry.maxFileBytes}`, synchronous);
}
function warnAboutAppendFailure(entry, synchronous) {
	if (warnedAppendFiles.has(entry.file)) return;
	const saturated = warnedAppendFiles.size >= MAX_TRACKED_APPEND_FAILURE_FILES;
	if (saturated && appendFailureTrackingSaturated) return;
	if (!writeFileTransportWarning(saturated ? "[openclaw] log file append failure diagnostics saturated; suppressing new file targets" : `[openclaw] log file append failed; records dropped; check that the path is a writable regular file; file=${entry.file}`, synchronous)) return;
	if (saturated) appendFailureTrackingSaturated = true;
	else warnedAppendFiles.add(entry.file);
}
function clearAppendFailure(entry) {
	if (warnedAppendFiles.delete(entry.file)) appendFailureTrackingSaturated = false;
}
function claimQueuedEntries() {
	const entries = queueStart === 0 ? queue : [...queue.slice(queueStart), ...queue.slice(0, queueStart)];
	queue = [];
	queueStart = 0;
	if (droppedCount > 0 && droppedTarget) entries.unshift(buildDroppedMarker(droppedTarget, droppedCount));
	droppedCount = 0;
	droppedTarget = null;
	return entries;
}
function prepareWrite(entry, cursor, synchronous, entries, index) {
	let payloadBytes = Buffer.byteLength(entry.payload, "utf8");
	if (cursor.bytes > 0 && cursor.bytes + payloadBytes > entry.maxFileBytes) {
		if (rotateLogFile(entry.file)) {
			cursor.bytes = 0;
			warnedRotationFiles.delete(entry.file);
		} else warnAboutRotationFailure(entry, synchronous);
	}
	let nextIndex = index + 1;
	if (synchronous) return {
		payload: entry.payload,
		payloadBytes,
		nextIndex
	};
	const payloads = [entry.payload];
	for (; nextIndex < entries.length; nextIndex += 1) {
		const next = entries[nextIndex];
		if (!next || next.file !== entry.file || next.maxFileBytes !== entry.maxFileBytes) break;
		const nextBytes = Buffer.byteLength(next.payload, "utf8");
		if (payloadBytes + nextBytes > MAX_APPEND_BATCH_BYTES || cursor.bytes + payloadBytes + nextBytes > entry.maxFileBytes) break;
		payloads.push(next.payload);
		payloadBytes += nextBytes;
	}
	return {
		payload: payloads.join(""),
		payloadBytes,
		nextIndex
	};
}
async function writeEntries(entries, generation) {
	const cursors = /* @__PURE__ */ new Map();
	for (let index = 0; index < entries.length;) {
		if (generation !== drainGeneration || processExiting) return;
		const entry = entries[index];
		if (!entry) return;
		let cursor = cursors.get(entry.file);
		if (!cursor) {
			cursor = { bytes: await getCurrentLogFileBytes(entry.file) };
			if (generation !== drainGeneration || processExiting) return;
			cursors.set(entry.file, cursor);
		}
		const batch = prepareWrite(entry, cursor, false, entries, index);
		activeIndex = batch.nextIndex;
		try {
			await appendFile({
				filePath: entry.file,
				content: batch.payload
			});
			cursor.bytes += batch.payloadBytes;
			clearAppendFailure(entry);
		} catch {
			warnAboutAppendFailure(entry, false);
		} finally {
			for (const written of entries.slice(index, batch.nextIndex)) written.payload = "";
			index = batch.nextIndex;
		}
	}
}
function writeEntriesSync(entries) {
	const cursors = /* @__PURE__ */ new Map();
	for (let index = 0; index < entries.length;) {
		const entry = entries[index];
		if (!entry) return;
		let cursor = cursors.get(entry.file);
		if (!cursor) {
			cursor = { bytes: getCurrentLogFileBytesSync(entry.file) };
			cursors.set(entry.file, cursor);
		}
		const batch = prepareWrite(entry, cursor, true, entries, index);
		try {
			appendRegularFileSync({
				filePath: entry.file,
				content: batch.payload
			});
			cursor.bytes += batch.payloadBytes;
			clearAppendFailure(entry);
		} catch {
			warnAboutAppendFailure(entry, true);
		}
		entry.payload = "";
		index = batch.nextIndex;
	}
}
async function runFlushLoop() {
	const generation = drainGeneration;
	for (;;) {
		if (generation !== drainGeneration || processExiting) return;
		const entries = claimQueuedEntries();
		if (entries.length === 0) return;
		activeBatch = entries;
		activeIndex = 0;
		await writeEntries(entries, generation);
		if (generation !== drainGeneration) return;
		activeBatch = null;
		activeIndex = 0;
	}
}
function startFlush() {
	if (flushPromise || processExiting) return;
	const running = runFlushLoop().catch(() => void 0);
	flushPromise = running;
	running.then(() => {
		if (flushPromise === running) flushPromise = null;
		if (queue.length > 0 || droppedCount > 0) scheduleFlush();
	});
}
function scheduleFlush() {
	if (scheduledFlush || flushPromise || processExiting) return;
	scheduledFlush = setImmediate(() => {
		scheduledFlush = null;
		startFlush();
	});
}
function handleProcessBeforeExit() {
	flushFileLogQueue();
}
function handleProcessExit() {
	processExiting = true;
	drainFileLogQueueSync();
}
function installProcessHooks() {
	if (processHooksInstalled) return;
	processHooksInstalled = true;
	process.on("beforeExit", handleProcessBeforeExit);
	process.on("exit", handleProcessExit);
}
function removeProcessHooks() {
	if (!processHooksInstalled) return;
	process.removeListener("beforeExit", handleProcessBeforeExit);
	process.removeListener("exit", handleProcessExit);
	processHooksInstalled = false;
}
/** Enqueues one serialized record without waiting for filesystem I/O. */
function enqueueFileLog(entry) {
	if (processExiting) {
		writeEntriesSync([entry]);
		return;
	}
	installProcessHooks();
	if (queue.length >= maxQueuedRecords) {
		const dropped = queue[queueStart];
		if (dropped) {
			dropped.payload = "";
			droppedTarget ??= dropped;
			droppedCount += 1;
		}
		queue[queueStart] = entry;
		queueStart = (queueStart + 1) % queue.length;
	} else queue.push(entry);
	scheduleFlush();
}
/** Waits until every record currently queued for the async transport has settled. */
async function flushFileLogQueue() {
	for (;;) {
		if (scheduledFlush) {
			clearImmediate(scheduledFlush);
			scheduledFlush = null;
		}
		if (!flushPromise && (queue.length > 0 || droppedCount > 0)) startFlush();
		const running = flushPromise;
		if (!running) return;
		await running;
	}
}
/** Synchronously rescues pending records for process.exit() and crash-adjacent paths. */
function drainFileLogQueueSync() {
	if (scheduledFlush) {
		clearImmediate(scheduledFlush);
		scheduledFlush = null;
	}
	drainGeneration += 1;
	const entries = activeBatch ? activeBatch.slice(activeIndex) : [];
	activeBatch = null;
	activeIndex = 0;
	entries.push(...claimQueuedEntries());
	writeEntriesSync(entries);
}
function setFileLogQueueMaxRecordsForTests(value) {
	maxQueuedRecords = Math.max(1, value ?? DEFAULT_MAX_QUEUED_RECORDS);
}
function setFileLogAppenderForTests(value) {
	appendFile = value ?? appendRegularFile;
}
function resetFileLogTransportForTests() {
	drainFileLogQueueSync();
	removeProcessHooks();
	processExiting = false;
	appendFile = appendRegularFile;
	maxQueuedRecords = DEFAULT_MAX_QUEUED_RECORDS;
	warnedRotationFiles.clear();
	warnedAppendFiles.clear();
	appendFailureTrackingSaturated = false;
}
resolveGlobalSingleton(Symbol.for("openclaw.fileLogTransport"), () => {
	if (process.env.VITEST !== "true") installProcessHooks();
	return {
		drainSync: drainFileLogQueueSync,
		enqueue: enqueueFileLog,
		flush: flushFileLogQueue,
		resetForTests: resetFileLogTransportForTests,
		setAppenderForTests: setFileLogAppenderForTests,
		setMaxQueuedRecordsForTests: setFileLogQueueMaxRecordsForTests
	};
});
theme.success;
theme.warn;
theme.info;
theme.error;
resolveGlobalSingleton(Symbol.for("openclaw.signalExitOwner"), () => ({}));
resolveGlobalSet(Symbol.for("openclaw.signalExitBarriers"), "close-and-restart");
resolveGlobalSet(Symbol.for("openclaw.signalExitGates"), "close-and-restart");
resolveGlobalSet(Symbol.for("openclaw.signalExitFinalizers"), "close-and-restart");
`${process.release.name}${process.pid}`;
//#endregion
//#region src/runtime.ts
function shouldEmitRuntimeLog(env = process.env) {
	if (env.VITEST !== "true") return true;
	if (env.OPENCLAW_TEST_RUNTIME_LOG === "1") return true;
	return typeof console.log.mock === "object";
}
function shouldEmitRuntimeStdout(env = process.env) {
	if (env.VITEST !== "true") return true;
	if (env.OPENCLAW_TEST_RUNTIME_LOG === "1") return true;
	return typeof process.stdout.write.mock === "object";
}
function isPipeClosedError(err) {
	const code = err?.code;
	return code === "EPIPE" || code === "EIO";
}
function writeStdout(value) {
	if (!shouldEmitRuntimeStdout()) return;
	const line = value.endsWith("\n") ? value : `${value}\n`;
	try {
		process.stdout.write(line);
	} catch (err) {
		if (isPipeClosedError(err)) return;
		throw err;
	}
}
function createRuntimeIo() {
	return {
		log: (...args) => {
			if (!shouldEmitRuntimeLog()) return;
			console.log(...args);
		},
		error: (...args) => {
			console.error(...args);
		},
		writeStdout,
		writeJson: (value, space = 2) => {
			writeStdout(JSON.stringify(value, void 0, space > 0 ? space : void 0));
		}
	};
}
({ ...createRuntimeIo() });
(() => {
	try {
		const inspect = process.getBuiltinModule?.("util").inspect;
		return typeof inspect === "function" ? inspect : null;
	} catch {
		return null;
	}
})();
//#endregion
//#region src/shared/detached-async-context.ts
const asyncHooks = typeof process === "undefined" ? void 0 : process.getBuiltinModule("node:async_hooks");
asyncHooks && resolveGlobalSingleton(Symbol.for("openclaw.detachedAsyncContext"), () => new asyncHooks.AsyncResource("openclaw.detached-async-context"));
resolveGlobalSingleton(Symbol.for("openclaw.asyncWorkScope"), () => new AsyncLocalStorage());
resolveGlobalSingleton(Symbol.for("openclaw.asyncWorkScopeAncestry"), () => new AsyncLocalStorage());
resolveGlobalSingleton(Symbol.for("openclaw.pluginSourceRecoveryUnavailableError"), () => class SourceRecoveryUnavailableError extends Error {
	constructor(cause) {
		super("Captured plugin source is missing; its previous code cannot be recovered.", { cause });
		this.name = "PluginSourceRecoveryUnavailableError";
	}
});
resolveGlobalSingleton(Symbol.for("openclaw.pluginInstanceDrainTimeoutError"), () => class DrainTimeoutError extends Error {
	constructor(message, settled, options, forcedRetirement) {
		super(message, options);
		this.settled = settled;
		this.forcedRetirement = forcedRetirement;
		this.name = "PluginInstanceDrainTimeoutError";
	}
});
//#endregion
//#region src/plugins/plugin-instance-invocation.ts
var InvocationFrame = class InvocationFrame {
	constructor(scopes, invocation = scopes.invocation) {
		this.invocation = invocation;
		this.metadataScope = scopes.metadataScope;
		this.cacheScope = scopes.cacheScope;
		this.sourceCaptureStorage = scopes.sourceCaptureStorage;
	}
	withScopes(scopes) {
		return new InvocationFrame(scopes);
	}
};
/** Copy core scopes through the current owner so runtime-only fields survive. */
function createPluginExecutionFrame(scopes, current) {
	return current ? current.withScopes(scopes) : new InvocationFrame(scopes);
}
const pluginExecutionContext = resolveGlobalSingleton(Symbol.for("openclaw.pluginInstanceInvocation"), () => {
	const frames = new AsyncLocalStorage();
	return {
		invocation: {
			getStore() {
				return frames.getStore()?.invocation;
			},
			run(invocation, run) {
				const current = frames.getStore();
				return frames.run(current?.invocation === invocation ? current : createPluginExecutionFrame({
					...current,
					invocation
				}, current), run);
			},
			exit(run) {
				const current = frames.getStore();
				return current?.invocation ? frames.run(current.withScopes({
					...current,
					invocation: void 0
				}), run) : run();
			}
		},
		getFrame() {
			return frames.getStore();
		},
		runFrame(frame, run) {
			return frames.run(frame, run);
		}
	};
});
pluginExecutionContext.invocation;
pluginExecutionContext.getFrame;
pluginExecutionContext.runFrame;
resolveGlobalSingleton(Symbol.for("openclaw.pluginCache"), () => ({
	snapshotOwners: /* @__PURE__ */ new WeakMap(),
	retirements: []
}));
resolveGlobalSingleton(Symbol.for("openclaw.pluginCacheRetainers"), () => /* @__PURE__ */ new WeakMap());
resolveGlobalSingleton(Symbol.for("openclaw.pluginInstanceCacheOwners"), () => /* @__PURE__ */ new WeakMap());
path.join(os.tmpdir(), "openclaw-empty-bundled-plugins");
[
	{ entries: [
		{
			"name": "@agentmail/agentmail",
			"description": "Official AgentMail email channel and CLI-backed skill.",
			"source": "external",
			"kind": "channel",
			"openclaw": {
				"plugin": {
					"id": "agentmail",
					"label": "AgentMail"
				},
				"channel": {
					"id": "agentmail",
					"label": "AgentMail",
					"selectionLabel": "AgentMail (Email)",
					"detailLabel": "AgentMail Email",
					"docsPath": "https://www.agentmail.to/docs/integrations/openclaw",
					"docsLabel": "agentmail",
					"exposure": { "docs": false },
					"blurb": "Durable, allowlisted, reply-only email conversations through AgentMail.",
					"order": 89,
					"quickstartAllowFrom": true,
					"configuredState": { "env": { "anyOf": ["AGENTMAIL_API_KEY"] } }
				},
				"channelConfigs": { "agentmail": {
					"label": "AgentMail",
					"description": "AgentMail email channel accounts and sender allowlist.",
					"schema": {
						"type": "object",
						"additionalProperties": true
					}
				} },
				"install": {
					"clawhubSpec": "clawhub:@agentmail/agentmail@0.2.1",
					"expectedIntegrity": "sha256:155221cec38673a39bc27629f9f6ec87567ce4e37b7fa619ec4b1f7ca3d28730",
					"defaultChoice": "clawhub",
					"minHostVersion": ">=2026.8.1-beta.2",
					"allowInvalidConfigRecovery": true
				}
			}
		},
		{
			"name": "@openclaw/buzz",
			"version": "2026.10.9",
			"description": "Connect OpenClaw agents to Buzz rooms",
			"source": "official",
			"kind": "channel",
			"openclaw": {
				"channelConfigs": { "buzz": {
					"label": "Buzz",
					"description": "Connect OpenClaw agents to Buzz team rooms."
				} },
				"setupFeatures": { "configPromotion": "preserve-root" },
				"channel": {
					"id": "buzz",
					"configuredState": { "env": { "allOf": ["BUZZ_RELAY_URL", "BUZZ_PRIVATE_KEY"] } },
					"label": "Buzz",
					"selectionLabel": "Buzz",
					"docsPath": "/channels/buzz",
					"docsLabel": "buzz",
					"blurb": "Connect OpenClaw agents to Buzz team rooms.",
					"markdownCapable": true,
					"order": 56,
					"setup": { "fields": [
						{
							"key": "relayUrl",
							"kind": "string",
							"cli": {
								"flags": "--relay-url <url>",
								"description": "Buzz relay WebSocket URL"
							}
						},
						{
							"key": "privateKey",
							"kind": "string",
							"sensitive": true,
							"cli": {
								"flags": "--private-key <key>",
								"description": "Buzz bot Nostr private key"
							}
						},
						{
							"key": "useEnv",
							"kind": "boolean",
							"cli": {
								"flags": "--use-env",
								"description": "Use BUZZ_PRIVATE_KEY with the supplied relay URL"
							},
							"envVars": ["BUZZ_PRIVATE_KEY"]
						}
					] }
				},
				"install": {
					"clawhubSpec": "clawhub:@openclaw/buzz",
					"npmSpec": "@openclaw/buzz",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.7.2"
				}
			}
		},
		{
			"name": "@openclaw/clickclack",
			"version": "2026.10.9",
			"description": "OpenClaw ClickClack channel plugin",
			"source": "official",
			"kind": "channel",
			"openclaw": {
				"contracts": { "tools": ["discussion"] },
				"channelConfigs": { "clickclack": {
					"label": "ClickClack",
					"description": "ClickClack channel accounts and group activation policy."
				} },
				"channel": {
					"id": "clickclack",
					"configuredState": { "env": { "anyOf": ["CLICKCLACK_BOT_TOKEN"] } },
					"label": "ClickClack",
					"selectionLabel": "ClickClack",
					"detailLabel": "ClickClack Bot",
					"docsPath": "/channels/clickclack",
					"docsLabel": "clickclack",
					"blurb": "self-hosted chat via first-class ClickClack bot tokens.",
					"systemImage": "bubble.left.and.bubble.right",
					"markdownCapable": true,
					"preferSessionLookupForAnnounceTarget": true,
					"order": 85,
					"commands": {
						"nativeCommandsAutoEnabled": false,
						"nativeSkillsAutoEnabled": false
					},
					"setup": { "fields": [
						{
							"key": "code",
							"kind": "string",
							"sensitive": true,
							"cli": {
								"flags": "--code <code>",
								"description": "ClickClack one-time setup code or setup URL"
							}
						},
						{
							"key": "token",
							"kind": "string",
							"sensitive": true,
							"cli": {
								"flags": "--token <token>",
								"description": "ClickClack bot token"
							}
						},
						{
							"key": "tokenFile",
							"kind": "string",
							"sensitive": true,
							"cli": {
								"flags": "--token-file <path>",
								"description": "ClickClack bot token file"
							}
						},
						{
							"key": "baseUrl",
							"kind": "string",
							"cli": {
								"flags": "--base-url <url>",
								"description": "ClickClack API base URL"
							}
						},
						{
							"key": "workspace",
							"kind": "string",
							"cli": {
								"flags": "--workspace <workspace>",
								"description": "ClickClack workspace id, slug, or name"
							}
						},
						{
							"key": "defaultTo",
							"kind": "string",
							"cli": {
								"flags": "--default-to <target>",
								"description": "Default ClickClack target"
							}
						},
						{
							"key": "allowFrom",
							"kind": "string-list",
							"cli": {
								"flags": "--allow-from <ids>",
								"description": "Allowed ClickClack senders"
							}
						},
						{
							"key": "agentActivity",
							"kind": "boolean",
							"cli": {
								"flags": "--agent-activity",
								"description": "Enable ClickClack agent activity"
							}
						},
						{
							"key": "useEnv",
							"kind": "boolean",
							"cli": {
								"flags": "--use-env",
								"description": "Use CLICKCLACK_BOT_TOKEN"
							},
							"envVars": ["CLICKCLACK_BOT_TOKEN"]
						}
					] }
				},
				"install": {
					"clawhubSpec": "clawhub:@openclaw/clickclack",
					"npmSpec": "@openclaw/clickclack",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.9.8",
					"allowInvalidConfigRecovery": true
				}
			}
		},
		{
			"name": "@openclaw/discord",
			"version": "2026.10.9",
			"description": "OpenClaw Discord channel plugin for channels, DMs, commands, and app events.",
			"source": "official",
			"kind": "channel",
			"openclaw": {
				"contracts": { "transcriptSourceProviders": ["discord-voice"] },
				"channel": {
					"id": "discord",
					"configuredState": { "env": { "anyOf": ["DISCORD_BOT_TOKEN"] } },
					"approvalFlags": ["native"],
					"label": "Discord",
					"selectionLabel": "Discord (Bot API)",
					"detailLabel": "Discord Bot",
					"docsPath": "/channels/discord",
					"docsLabel": "discord",
					"blurb": "very well supported right now.",
					"systemImage": "bubble.left.and.bubble.right",
					"markdownCapable": true,
					"preferSessionLookupForAnnounceTarget": true,
					"setup": { "fields": [{
						"key": "token",
						"kind": "string",
						"sensitive": true,
						"cli": {
							"flags": "--token <token>",
							"description": "Discord bot token"
						}
					}, {
						"key": "useEnv",
						"kind": "boolean",
						"cli": {
							"flags": "--use-env",
							"description": "Use DISCORD_BOT_TOKEN"
						},
						"envVars": ["DISCORD_BOT_TOKEN"]
					}] },
					"commands": {
						"nativeCommandsAutoEnabled": true,
						"nativeSkillsAutoEnabled": true
					},
					"doctorCapabilities": {
						"dmAllowFromMode": "topOnly",
						"groupModel": "route",
						"groupAllowFromFallbackToAllowFrom": false,
						"warnOnEmptyGroupSenderAllowlist": false
					}
				},
				"install": {
					"clawhubSpec": "clawhub:@openclaw/discord",
					"npmSpec": "@openclaw/discord",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.5.26",
					"allowInvalidConfigRecovery": true
				}
			}
		},
		{
			"name": "@openclaw/feishu",
			"version": "2026.10.9",
			"description": "OpenClaw Feishu/Lark channel plugin for chats and workplace tools (community maintained by @m1heng).",
			"source": "official",
			"kind": "channel",
			"openclaw": {
				"contracts": { "tools": [
					"feishu_app_scopes",
					"feishu_bitable_create_app",
					"feishu_bitable_create_field",
					"feishu_bitable_create_record",
					"feishu_bitable_get_meta",
					"feishu_bitable_get_record",
					"feishu_bitable_list_fields",
					"feishu_bitable_list_records",
					"feishu_bitable_update_record",
					"feishu_chat",
					"feishu_doc",
					"feishu_drive",
					"feishu_perm",
					"feishu_wiki"
				] },
				"channel": {
					"id": "feishu",
					"configuredState": {
						"env": { "anyOf": [
							"FEISHU_APP_ID",
							"FEISHU_APP_SECRET",
							"FEISHU_VERIFICATION_TOKEN",
							"FEISHU_ENCRYPT_KEY"
						] },
						"specifier": "./configured-state",
						"exportName": "hasConfiguredFeishuChannelState"
					},
					"label": "Feishu",
					"selectionLabel": "Feishu/Lark (飞书)",
					"docsPath": "/channels/feishu",
					"docsLabel": "feishu",
					"blurb": "飞书/Lark enterprise messaging with doc/wiki/drive tools.",
					"aliases": ["lark"],
					"order": 35,
					"quickstartAllowFrom": true,
					"setup": { "fields": [] }
				},
				"install": {
					"clawhubSpec": "clawhub:@openclaw/feishu",
					"npmSpec": "@openclaw/feishu",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.5.29"
				}
			}
		},
		{
			"name": "@openclaw/googlechat",
			"version": "2026.10.9",
			"description": "OpenClaw Google Chat channel plugin for spaces and direct messages.",
			"source": "official",
			"kind": "channel",
			"openclaw": {
				"channel": {
					"id": "googlechat",
					"configuredState": { "env": { "anyOf": ["GOOGLE_CHAT_SERVICE_ACCOUNT", "GOOGLE_CHAT_SERVICE_ACCOUNT_FILE"] } },
					"approvalFlags": ["native"],
					"label": "Google Chat",
					"selectionLabel": "Google Chat (Chat API)",
					"detailLabel": "Google Chat",
					"docsPath": "/channels/googlechat",
					"docsLabel": "googlechat",
					"blurb": "Google Workspace Chat app with HTTP webhook.",
					"aliases": ["gchat", "google-chat"],
					"order": 55,
					"systemImage": "message.badge",
					"markdownCapable": true,
					"doctorCapabilities": {
						"dmAllowFromMode": "topOnly",
						"groupModel": "route",
						"groupAllowFromFallbackToAllowFrom": false,
						"warnOnEmptyGroupSenderAllowlist": false
					},
					"setup": { "fields": [
						{
							"key": "token",
							"kind": "string",
							"sensitive": true,
							"cli": {
								"flags": "--token <json>",
								"description": "Google Chat service account JSON"
							}
						},
						{
							"key": "tokenFile",
							"kind": "string",
							"sensitive": true,
							"cli": {
								"flags": "--token-file <path>",
								"description": "Google Chat service account file"
							}
						},
						{
							"key": "audienceType",
							"kind": "choice",
							"choices": ["app-url", "project-number"],
							"cli": {
								"flags": "--audience-type <type>",
								"description": "Google Chat audience type"
							}
						},
						{
							"key": "audience",
							"kind": "string",
							"cli": {
								"flags": "--audience <value>",
								"description": "Google Chat audience value"
							}
						},
						{
							"key": "webhookPath",
							"kind": "string",
							"cli": {
								"flags": "--webhook-path <path>",
								"description": "Google Chat webhook path"
							}
						},
						{
							"key": "webhookUrl",
							"kind": "string",
							"cli": {
								"flags": "--webhook-url <url>",
								"description": "Google Chat webhook URL"
							}
						},
						{
							"key": "useEnv",
							"kind": "boolean",
							"cli": {
								"flags": "--use-env",
								"description": "Use Google Chat environment credentials"
							},
							"envVars": ["GOOGLE_CHAT_SERVICE_ACCOUNT", "GOOGLE_CHAT_SERVICE_ACCOUNT_FILE"],
							"envVarMode": "any"
						}
					] }
				},
				"install": {
					"clawhubSpec": "clawhub:@openclaw/googlechat",
					"npmSpec": "@openclaw/googlechat",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.9.8"
				}
			}
		},
		{
			"name": "@openclaw/imessage",
			"version": "2026.10.9",
			"description": "OpenClaw iMessage channel plugin using imsg on a signed-in Mac",
			"source": "official",
			"kind": "channel",
			"openclaw": {
				"channel": {
					"id": "imessage",
					"approvalFlags": ["native"],
					"label": "iMessage",
					"selectionLabel": "iMessage (imsg)",
					"detailLabel": "iMessage",
					"docsPath": "/channels/imessage",
					"docsLabel": "imessage",
					"blurb": "Local iMessage/SMS through the imsg bridge, including private API message actions when enabled.",
					"aliases": ["imsg"],
					"systemImage": "message.fill",
					"setup": { "fields": [
						{
							"key": "cliPath",
							"kind": "string",
							"cli": {
								"flags": "--cli-path <path>",
								"description": "iMessage CLI path"
							}
						},
						{
							"key": "dbPath",
							"kind": "string",
							"cli": {
								"flags": "--db-path <path>",
								"description": "iMessage database path"
							}
						},
						{
							"key": "service",
							"kind": "choice",
							"choices": [
								"imessage",
								"sms",
								"auto"
							],
							"cli": {
								"flags": "--service <service>",
								"description": "iMessage service"
							}
						},
						{
							"key": "region",
							"kind": "string",
							"cli": {
								"flags": "--region <region>",
								"description": "SMS region"
							}
						}
					] }
				},
				"install": {
					"clawhubSpec": "clawhub:@openclaw/imessage",
					"npmSpec": "@openclaw/imessage",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.7.2",
					"allowInvalidConfigRecovery": true
				}
			}
		},
		{
			"name": "@openclaw/irc",
			"version": "2026.10.9",
			"description": "OpenClaw IRC channel plugin",
			"source": "official",
			"kind": "channel",
			"openclaw": {
				"channel": {
					"id": "irc",
					"configuredState": { "env": { "allOf": ["IRC_HOST", "IRC_NICK"] } },
					"label": "IRC",
					"selectionLabel": "IRC (Server + Nick)",
					"detailLabel": "IRC",
					"docsPath": "/channels/irc",
					"docsLabel": "irc",
					"blurb": "classic IRC networks with DM/channel routing and pairing controls.",
					"aliases": ["internet-relay-chat"],
					"systemImage": "network",
					"setup": { "fields": [
						{
							"key": "host",
							"kind": "string",
							"cli": {
								"flags": "--host <host>",
								"description": "IRC server host"
							}
						},
						{
							"key": "port",
							"kind": "string",
							"cli": {
								"flags": "--port <port>",
								"description": "IRC server port"
							}
						},
						{
							"key": "tls",
							"kind": "boolean",
							"cli": {
								"flags": "--tls",
								"description": "Use TLS for IRC"
							}
						},
						{
							"key": "nick",
							"kind": "string",
							"cli": {
								"flags": "--nick <nick>",
								"description": "IRC nickname"
							}
						},
						{
							"key": "username",
							"kind": "string",
							"cli": {
								"flags": "--username <name>",
								"description": "IRC username"
							}
						},
						{
							"key": "realname",
							"kind": "string",
							"cli": {
								"flags": "--realname <name>",
								"description": "IRC real name"
							}
						},
						{
							"key": "channels",
							"kind": "string-list",
							"cli": {
								"flags": "--channels <names>",
								"description": "IRC channels"
							}
						},
						{
							"key": "password",
							"kind": "string",
							"sensitive": true,
							"cli": {
								"flags": "--password <password>",
								"description": "IRC server password"
							}
						},
						{
							"key": "useEnv",
							"kind": "boolean",
							"cli": {
								"flags": "--use-env",
								"description": "Use IRC environment configuration"
							},
							"envVars": ["IRC_HOST", "IRC_NICK"]
						}
					] }
				},
				"install": {
					"clawhubSpec": "clawhub:@openclaw/irc",
					"npmSpec": "@openclaw/irc",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.9.8",
					"allowInvalidConfigRecovery": true
				}
			}
		},
		{
			"name": "@openclaw/line",
			"version": "2026.10.9",
			"description": "OpenClaw LINE channel plugin for LINE Bot API chats.",
			"source": "official",
			"kind": "channel",
			"openclaw": {
				"channel": {
					"id": "line",
					"configuredState": { "env": { "allOf": ["LINE_CHANNEL_ACCESS_TOKEN", "LINE_CHANNEL_SECRET"] } },
					"label": "LINE",
					"selectionLabel": "LINE (Messaging API)",
					"detailLabel": "LINE Bot",
					"docsPath": "/channels/line",
					"docsLabel": "line",
					"blurb": "LINE Messaging API webhook bot.",
					"systemImage": "message",
					"order": 75,
					"quickstartAllowFrom": true,
					"doctorCapabilities": {
						"dmAllowFromMode": "topOnly",
						"groupModel": "sender",
						"groupAllowFromFallbackToAllowFrom": false,
						"warnOnEmptyGroupSenderAllowlist": true
					},
					"setup": { "fields": [
						{
							"key": "channelAccessToken",
							"kind": "string",
							"sensitive": true,
							"cli": {
								"flags": "--channel-access-token <token>",
								"description": "LINE channel access token"
							}
						},
						{
							"key": "token",
							"kind": "string",
							"sensitive": true,
							"cli": {
								"flags": "--token <token>",
								"description": "LINE channel access token (alias)"
							}
						},
						{
							"key": "channelSecret",
							"kind": "string",
							"sensitive": true,
							"cli": {
								"flags": "--channel-secret <secret>",
								"description": "LINE channel secret"
							}
						},
						{
							"key": "tokenFile",
							"kind": "string",
							"sensitive": true,
							"cli": {
								"flags": "--token-file <path>",
								"description": "LINE access token file"
							}
						},
						{
							"key": "secretFile",
							"kind": "string",
							"sensitive": true,
							"cli": {
								"flags": "--secret-file <path>",
								"description": "LINE channel secret file"
							}
						},
						{
							"key": "useEnv",
							"kind": "boolean",
							"cli": {
								"flags": "--use-env",
								"description": "Use LINE environment credentials"
							},
							"envVars": ["LINE_CHANNEL_ACCESS_TOKEN", "LINE_CHANNEL_SECRET"]
						}
					] }
				},
				"install": {
					"clawhubSpec": "clawhub:@openclaw/line",
					"npmSpec": "@openclaw/line",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.4.10"
				}
			}
		},
		{
			"name": "@openclaw/matrix",
			"version": "2026.10.9",
			"description": "OpenClaw Matrix channel plugin for rooms and direct messages.",
			"source": "official",
			"kind": "channel",
			"openclaw": {
				"channel": {
					"id": "matrix",
					"configuredState": { "env": { "anyOf": [
						"MATRIX_HOMESERVER",
						"MATRIX_USER_ID",
						"MATRIX_ACCESS_TOKEN",
						"MATRIX_PASSWORD",
						"MATRIX_DEVICE_ID",
						"MATRIX_DEVICE_NAME",
						"MATRIX_OPS_HOMESERVER",
						"MATRIX_OPS_ACCESS_TOKEN",
						"MATRIX_OPS_DEVICE_ID",
						"MATRIX_OPS_DEVICE_NAME"
					] } },
					"approvalFlags": ["native"],
					"label": "Matrix",
					"selectionLabel": "Matrix (plugin)",
					"docsPath": "/channels/matrix",
					"docsLabel": "matrix",
					"blurb": "open protocol; install the plugin to enable.",
					"order": 70,
					"markdownCapable": true,
					"quickstartAllowFrom": true,
					"doctorCapabilities": {
						"dmAllowFromMode": "nestedOnly",
						"groupModel": "sender",
						"groupAllowFromFallbackToAllowFrom": false,
						"warnOnEmptyGroupSenderAllowlist": true
					},
					"setup": { "fields": [
						{
							"key": "homeserver",
							"kind": "string",
							"cli": {
								"flags": "--homeserver <url>",
								"description": "Matrix homeserver URL"
							}
						},
						{
							"key": "userId",
							"kind": "string",
							"cli": {
								"flags": "--user-id <id>",
								"description": "Matrix user id"
							}
						},
						{
							"key": "accessToken",
							"kind": "string",
							"sensitive": true,
							"cli": {
								"flags": "--access-token <token>",
								"description": "Matrix access token"
							}
						},
						{
							"key": "password",
							"kind": "string",
							"sensitive": true,
							"cli": {
								"flags": "--password <password>",
								"description": "Matrix password"
							}
						},
						{
							"key": "deviceName",
							"kind": "string",
							"cli": {
								"flags": "--device-name <name>",
								"description": "Matrix device name"
							}
						},
						{
							"key": "avatarUrl",
							"kind": "string",
							"cli": {
								"flags": "--avatar-url <url>",
								"description": "Matrix avatar URL"
							}
						},
						{
							"key": "initialSyncLimit",
							"kind": "integer",
							"cli": {
								"flags": "--initial-sync-limit <n>",
								"description": "Matrix initial sync room limit"
							}
						},
						{
							"key": "proxy",
							"kind": "string",
							"cli": {
								"flags": "--proxy <url>",
								"description": "Matrix proxy URL"
							}
						},
						{
							"key": "dangerouslyAllowPrivateNetwork",
							"kind": "boolean",
							"cli": {
								"flags": "--dangerously-allow-private-network",
								"description": "Allow private-network Matrix homeservers"
							}
						},
						{
							"key": "useEnv",
							"kind": "boolean",
							"cli": {
								"flags": "--use-env",
								"description": "Use Matrix environment credentials"
							}
						}
					] },
					"persistedAuthState": {
						"specifier": "./auth-presence",
						"exportName": "hasAnyMatrixAuth",
						"backingStore": "plugin-state"
					}
				},
				"install": {
					"clawhubSpec": "clawhub:@openclaw/matrix",
					"npmSpec": "@openclaw/matrix",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.4.10",
					"allowInvalidConfigRecovery": true
				}
			}
		},
		{
			"name": "@openclaw/mattermost",
			"version": "2026.10.9",
			"description": "OpenClaw Mattermost channel plugin",
			"source": "official",
			"kind": "channel",
			"openclaw": {
				"channel": {
					"id": "mattermost",
					"configuredState": { "env": { "allOf": ["MATTERMOST_BOT_TOKEN", "MATTERMOST_URL"] } },
					"label": "Mattermost",
					"selectionLabel": "Mattermost (plugin)",
					"docsPath": "/channels/mattermost",
					"docsLabel": "mattermost",
					"blurb": "self-hosted Slack-style chat; install the plugin to enable.",
					"order": 65,
					"setup": { "fields": [
						{
							"key": "token",
							"kind": "string",
							"sensitive": true,
							"cli": {
								"flags": "--token <token>",
								"description": "Mattermost bot token"
							}
						},
						{
							"key": "botToken",
							"kind": "string",
							"sensitive": true,
							"cli": {
								"flags": "--bot-token <token>",
								"description": "Mattermost bot token"
							}
						},
						{
							"key": "httpUrl",
							"kind": "string",
							"cli": {
								"flags": "--http-url <url>",
								"description": "Mattermost server URL"
							}
						},
						{
							"key": "useEnv",
							"kind": "boolean",
							"cli": {
								"flags": "--use-env",
								"description": "Use Mattermost environment credentials"
							},
							"envVars": ["MATTERMOST_BOT_TOKEN", "MATTERMOST_URL"]
						}
					] }
				},
				"install": {
					"clawhubSpec": "clawhub:@openclaw/mattermost",
					"npmSpec": "@openclaw/mattermost",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.6.9",
					"allowInvalidConfigRecovery": true
				}
			}
		},
		{
			"name": "@openclaw/msteams",
			"version": "2026.10.9",
			"description": "OpenClaw Microsoft Teams channel plugin for bot conversations.",
			"source": "official",
			"kind": "channel",
			"openclaw": {
				"channel": {
					"id": "msteams",
					"configuredState": {
						"env": { "anyOf": [
							"MSTEAMS_APP_ID",
							"MSTEAMS_APP_PASSWORD",
							"MSTEAMS_TENANT_ID"
						] },
						"specifier": "./configured-state",
						"exportName": "hasConfiguredMSTeamsChannelState"
					},
					"label": "Microsoft Teams",
					"selectionLabel": "Microsoft Teams (Teams SDK)",
					"docsPath": "/channels/msteams",
					"docsLabel": "msteams",
					"blurb": "Teams SDK; enterprise support.",
					"aliases": ["teams"],
					"order": 60,
					"doctorCapabilities": {
						"dmAllowFromMode": "topOnly",
						"groupModel": "hybrid",
						"groupAllowFromFallbackToAllowFrom": true,
						"warnOnEmptyGroupSenderAllowlist": true
					},
					"setup": { "fields": [] }
				},
				"install": {
					"clawhubSpec": "clawhub:@openclaw/msteams",
					"npmSpec": "@openclaw/msteams",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.4.10"
				}
			}
		},
		{
			"name": "@openclaw/nextcloud-talk",
			"version": "2026.10.9",
			"description": "OpenClaw Nextcloud Talk channel plugin for conversations.",
			"source": "official",
			"kind": "channel",
			"openclaw": {
				"channel": {
					"id": "nextcloud-talk",
					"configuredState": {
						"env": { "anyOf": ["NEXTCLOUD_TALK_BOT_SECRET", "NEXTCLOUD_TALK_API_PASSWORD"] },
						"specifier": "./configured-state",
						"exportName": "hasConfiguredNextcloudTalkChannelState"
					},
					"label": "Nextcloud Talk",
					"selectionLabel": "Nextcloud Talk (self-hosted)",
					"docsPath": "/channels/nextcloud-talk",
					"docsLabel": "nextcloud-talk",
					"blurb": "Self-hosted chat via Nextcloud Talk webhook bots.",
					"aliases": ["nc-talk", "nc"],
					"order": 65,
					"quickstartAllowFrom": true,
					"setup": { "fields": [
						{
							"key": "baseUrl",
							"kind": "string",
							"cli": {
								"flags": "--base-url <url>",
								"description": "Nextcloud base URL"
							}
						},
						{
							"key": "url",
							"kind": "string",
							"cli": {
								"flags": "--url <url>",
								"description": "Legacy Nextcloud base URL alias"
							}
						},
						{
							"key": "secret",
							"kind": "string",
							"sensitive": true,
							"cli": {
								"flags": "--secret <secret>",
								"description": "Nextcloud Talk bot secret"
							}
						},
						{
							"key": "token",
							"kind": "string",
							"sensitive": true,
							"cli": {
								"flags": "--token <secret>",
								"description": "Legacy Nextcloud bot secret alias"
							}
						},
						{
							"key": "password",
							"kind": "string",
							"sensitive": true,
							"cli": {
								"flags": "--password <secret>",
								"description": "Legacy Nextcloud bot secret alias"
							}
						},
						{
							"key": "secretFile",
							"kind": "string",
							"sensitive": true,
							"cli": {
								"flags": "--secret-file <path>",
								"description": "Nextcloud Talk bot secret file"
							}
						},
						{
							"key": "tokenFile",
							"kind": "string",
							"sensitive": true,
							"cli": {
								"flags": "--token-file <path>",
								"description": "Legacy Nextcloud bot secret file alias"
							}
						},
						{
							"key": "useEnv",
							"kind": "boolean",
							"cli": {
								"flags": "--use-env",
								"description": "Use Nextcloud Talk environment credentials"
							},
							"envVars": ["NEXTCLOUD_TALK_BOT_SECRET"]
						}
					] }
				},
				"install": {
					"clawhubSpec": "clawhub:@openclaw/nextcloud-talk",
					"npmSpec": "@openclaw/nextcloud-talk",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.4.10"
				}
			}
		},
		{
			"name": "@openclaw/nostr",
			"version": "2026.10.9",
			"description": "OpenClaw Nostr channel plugin for NIP-04 encrypted direct messages.",
			"source": "official",
			"kind": "channel",
			"openclaw": {
				"channel": {
					"id": "nostr",
					"configuredState": { "env": { "anyOf": ["NOSTR_PRIVATE_KEY"] } },
					"label": "Nostr",
					"selectionLabel": "Nostr (NIP-04 DMs)",
					"docsPath": "/channels/nostr",
					"docsLabel": "nostr",
					"blurb": "Decentralized protocol; encrypted DMs via NIP-04.",
					"order": 55,
					"quickstartAllowFrom": true,
					"setup": { "fields": [
						{
							"key": "privateKey",
							"kind": "string",
							"sensitive": true,
							"cli": {
								"flags": "--private-key <key>",
								"description": "Nostr private key"
							}
						},
						{
							"key": "relayUrls",
							"kind": "string",
							"cli": {
								"flags": "--relay-urls <urls>",
								"description": "Nostr relay URLs"
							}
						},
						{
							"key": "useEnv",
							"kind": "boolean",
							"cli": {
								"flags": "--use-env",
								"description": "Use NOSTR_PRIVATE_KEY"
							},
							"envVars": ["NOSTR_PRIVATE_KEY"]
						}
					] }
				},
				"install": {
					"clawhubSpec": "clawhub:@openclaw/nostr",
					"npmSpec": "@openclaw/nostr",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.4.10"
				}
			}
		},
		{
			"name": "@tencent-weixin/openclaw-weixin",
			"description": "OpenClaw Weixin channel plugin by the Tencent Weixin team.",
			"source": "external",
			"kind": "channel",
			"openclaw": {
				"plugin": {
					"id": "openclaw-weixin",
					"label": "Weixin"
				},
				"channel": {
					"id": "openclaw-weixin",
					"label": "Weixin",
					"selectionLabel": "Weixin(微信)",
					"detailLabel": "Weixin",
					"docsPath": "/channels/wechat",
					"docsLabel": "weixin",
					"blurb": "Personal WeChat messaging via QR-code login.",
					"aliases": [
						"weixin",
						"wechat",
						"微信"
					],
					"order": 75
				},
				"channelConfigs": { "openclaw-weixin": {
					"label": "Weixin",
					"description": "Personal WeChat conversation channel.",
					"schema": {
						"type": "object",
						"additionalProperties": true
					}
				} },
				"install": {
					"npmSpec": "@tencent-weixin/openclaw-weixin@2.4.8",
					"defaultChoice": "npm",
					"expectedIntegrity": "sha512-hhO9prUQwzfSpIL6XGWazRsxNs89K+Mis3iQW6a8eum4AIDxOUiTFMg4nlNVD/au0SjAx5CcJjo3KYWZqGpgQA==",
					"minHostVersion": ">=2026.5.12"
				}
			}
		},
		{
			"name": "@zalo-platforms/openclaw-zaloclawbot",
			"description": "OpenClaw Zalo ClawBot channel plugin by the Zalo Platforms team.",
			"source": "external",
			"kind": "channel",
			"openclaw": {
				"plugin": {
					"id": "openclaw-zaloclawbot",
					"label": "Zalo ClawBot"
				},
				"channel": {
					"id": "openclaw-zaloclawbot",
					"label": "Zalo ClawBot",
					"selectionLabel": "Zalo ClawBot (QR)",
					"detailLabel": "Zalo ClawBot",
					"docsPath": "/channels/zaloclawbot",
					"docsLabel": "zaloclawbot",
					"blurb": "Personal Zalo assistant bot via QR-code login — owner-bound, no setup.",
					"aliases": ["zaloclawbot", "zalo-clawbot"],
					"order": 82
				},
				"channelConfigs": { "openclaw-zaloclawbot": {
					"label": "Zalo ClawBot",
					"description": "Personal Zalo assistant — QR-onboarded, owner-bound.",
					"schema": {
						"type": "object",
						"additionalProperties": true
					}
				} },
				"install": {
					"npmSpec": "@zalo-platforms/openclaw-zaloclawbot@0.1.4",
					"defaultChoice": "npm",
					"expectedIntegrity": "sha512-5IxZriHJYACLLGqkCPPsTP9tas62kXEOFqTFAFMdunAM3SPhIJwVFRp0WvoP/m7L2PX85weD0g8LOtxM93VDYg==",
					"minHostVersion": ">=2026.4.10"
				}
			}
		},
		{
			"name": "@tencent-connect/openclaw-qqbot",
			"description": "OpenClaw QQ Bot channel plugin by the Tencent Connect team.",
			"source": "external",
			"kind": "channel",
			"openclaw": {
				"plugin": {
					"id": "openclaw-qqbot",
					"label": "QQ Bot"
				},
				"setupFeatures": { "configPromotion": "preserve-root" },
				"legacyNpmPackageNames": ["@openclaw/qqbot"],
				"contracts": { "tools": ["qqbot_platform_api", "qqbot_remind"] },
				"channel": {
					"id": "qqbot",
					"label": "QQ Bot",
					"selectionLabel": "QQ Bot (Official API)",
					"detailLabel": "QQ Bot",
					"docsPath": "/channels/qqbot",
					"docsLabel": "qqbot",
					"blurb": "connect to QQ via official QQ Bot API with group chat and direct message support.",
					"envVars": ["QQBOT_APP_ID", "QQBOT_CLIENT_SECRET"],
					"approvalFlags": ["native"],
					"doctorCapabilities": { "openDmRequiresAllowFromWildcard": false },
					"systemImage": "bubble.left.and.bubble.right"
				},
				"channelSecrets": { "fields": [{
					"field": "clientSecret",
					"activationField": "appId",
					"activationEnv": "QQBOT_APP_ID"
				}] },
				"channelHostConfig": {
					"docsSource": "official",
					"compatibilityMigration": "qqbot.tencent-2.0-compatibility",
					"schemaAllOf": [{
						"not": { "required": ["defaultAccount"] },
						"properties": {
							"allowFrom": {
								"type": "array",
								"minItems": 1,
								"items": { "allOf": [{ "not": { "const": "*" } }, { "anyOf": [{ "const": "openclaw:approval-disabled" }, {
									"type": "string",
									"pattern": "^[^a-z]*$"
								}] }] }
							},
							"accounts": {
								"type": "object",
								"not": { "required": ["default"] },
								"additionalProperties": {
									"type": "object",
									"properties": { "allowFrom": {
										"type": "array",
										"minItems": 1,
										"items": { "allOf": [{ "not": { "const": "*" } }, { "anyOf": [{ "const": "openclaw:approval-disabled" }, {
											"type": "string",
											"pattern": "^[^a-z]*$"
										}] }] }
									} },
									"required": ["allowFrom"]
								}
							}
						},
						"required": ["allowFrom"]
					}]
				},
				"channelConfigs": { "qqbot": {
					"label": "QQ Bot",
					"description": "QQ Bot API conversation channel.",
					"preferOver": ["qqbot"],
					"schema": {
						"type": "object",
						"additionalProperties": true,
						"properties": {
							"appId": { "type": "string" },
							"clientSecret": { "type": "string" }
						}
					}
				} },
				"install": {
					"npmSpec": "@tencent-connect/openclaw-qqbot@2.0.3",
					"defaultChoice": "npm",
					"expectedIntegrity": "sha512-yngu/2cPeZjJfIfHWCXWB2/6KlDHrb9vpOUjKLdQxePLSp6wCn3CFOALcBIVq/9o6jlYz9WTU9idW6nfX1xpFA=="
				}
			}
		},
		{
			"name": "@openclaw/raft",
			"version": "2026.10.9",
			"description": "OpenClaw Raft channel plugin for Raft CLI wake bridges.",
			"source": "official",
			"kind": "channel",
			"openclaw": {
				"channelConfigs": { "raft": {
					"label": "Raft",
					"description": "Raft External Agent CLI wake bridge."
				} },
				"channel": {
					"id": "raft",
					"configuredState": { "env": { "anyOf": ["RAFT_PROFILE"] } },
					"label": "Raft",
					"selectionLabel": "Raft (CLI wake bridge)",
					"docsPath": "/channels/raft",
					"docsLabel": "raft",
					"blurb": "Raft CLI wake bridge for human and agent collaboration.",
					"order": 72,
					"setup": { "fields": [{
						"key": "profile",
						"kind": "string",
						"cli": {
							"flags": "--profile <profile>",
							"description": "Raft CLI profile"
						}
					}] }
				},
				"install": {
					"clawhubSpec": "clawhub:@openclaw/raft",
					"npmSpec": "@openclaw/raft",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.6.8"
				}
			}
		},
		{
			"name": "@openclaw/signal",
			"version": "2026.10.9",
			"description": "OpenClaw Signal channel plugin",
			"source": "official",
			"kind": "channel",
			"openclaw": {
				"channel": {
					"id": "signal",
					"approvalFlags": ["native"],
					"label": "Signal",
					"selectionLabel": "Signal (signal-cli)",
					"detailLabel": "Signal REST",
					"docsPath": "/channels/signal",
					"docsLabel": "signal",
					"blurb": "signal-cli linked device with additional setup for the local REST bridge.",
					"systemImage": "antenna.radiowaves.left.and.right",
					"markdownCapable": true,
					"setup": { "fields": [
						{
							"key": "signalNumber",
							"kind": "string",
							"cli": {
								"flags": "--signal-number <e164>",
								"description": "Signal account number (E.164)"
							}
						},
						{
							"key": "signalTransport",
							"kind": "choice",
							"choices": ["external-native", "container"],
							"cli": {
								"flags": "--signal-transport <kind>",
								"description": "Signal HTTP transport (external-native or container)"
							}
						},
						{
							"key": "cliPath",
							"kind": "string",
							"cli": {
								"flags": "--cli-path <path>",
								"description": "signal-cli executable path"
							}
						},
						{
							"key": "httpUrl",
							"kind": "string",
							"cli": {
								"flags": "--http-url <url>",
								"description": "Signal HTTP service URL"
							}
						},
						{
							"key": "httpHost",
							"kind": "string",
							"cli": {
								"flags": "--http-host <host>",
								"description": "Signal HTTP daemon host"
							}
						},
						{
							"key": "httpPort",
							"kind": "string",
							"cli": {
								"flags": "--http-port <port>",
								"description": "Signal HTTP daemon port"
							}
						}
					] }
				},
				"install": {
					"clawhubSpec": "clawhub:@openclaw/signal",
					"npmSpec": "@openclaw/signal",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.6.9",
					"allowInvalidConfigRecovery": true
				}
			}
		},
		{
			"name": "@openclaw/slack",
			"version": "2026.10.9",
			"description": "OpenClaw Slack channel plugin for channels, DMs, commands, and app events.",
			"source": "official",
			"kind": "channel",
			"openclaw": {
				"channelConfigs": { "slack": {
					"label": "Slack",
					"description": "Slack channel, DM, command, and app event integration."
				} },
				"channel": {
					"id": "slack",
					"configuredState": {
						"env": { "anyOf": [
							"SLACK_BOT_TOKEN",
							"SLACK_APP_TOKEN",
							"SLACK_USER_TOKEN"
						] },
						"specifier": "./configured-state",
						"exportName": "hasConfiguredSlackChannelState"
					},
					"approvalFlags": ["native"],
					"label": "Slack",
					"selectionLabel": "Slack (Socket Mode)",
					"detailLabel": "Slack Bot",
					"docsPath": "/channels/slack",
					"docsLabel": "slack",
					"blurb": "supported (Socket Mode).",
					"systemImage": "number",
					"markdownCapable": true,
					"doctorCapabilities": {
						"dmAllowFromMode": "topOnly",
						"groupModel": "route",
						"groupAllowFromFallbackToAllowFrom": false,
						"warnOnEmptyGroupSenderAllowlist": false
					},
					"commands": {
						"nativeCommandsAutoEnabled": false,
						"nativeSkillsAutoEnabled": false
					},
					"setup": { "fields": [
						{
							"key": "botToken",
							"kind": "string",
							"sensitive": true,
							"cli": {
								"flags": "--bot-token <token>",
								"description": "Slack bot token"
							}
						},
						{
							"key": "appToken",
							"kind": "string",
							"sensitive": true,
							"cli": {
								"flags": "--app-token <token>",
								"description": "Slack app token"
							}
						},
						{
							"key": "userToken",
							"kind": "string",
							"sensitive": true,
							"cli": {
								"flags": "--user-token <token>",
								"description": "Slack user token"
							}
						},
						{
							"key": "signingSecret",
							"kind": "string",
							"sensitive": true,
							"cli": {
								"flags": "--signing-secret <secret>",
								"description": "Slack signing secret"
							}
						},
						{
							"key": "identity",
							"kind": "choice",
							"choices": ["bot", "user"],
							"cli": {
								"flags": "--identity <kind>",
								"description": "Slack identity"
							}
						},
						{
							"key": "mode",
							"kind": "choice",
							"choices": ["socket", "http"],
							"cli": {
								"flags": "--mode <mode>",
								"description": "Slack connection mode"
							}
						},
						{
							"key": "useEnv",
							"kind": "boolean",
							"cli": {
								"flags": "--use-env",
								"description": "Use Slack environment credentials"
							},
							"envVars": ["SLACK_BOT_TOKEN"]
						}
					] }
				},
				"install": {
					"clawhubSpec": "clawhub:@openclaw/slack",
					"npmSpec": "@openclaw/slack",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.9.8",
					"allowInvalidConfigRecovery": true
				}
			}
		},
		{
			"name": "@openclaw/sms",
			"version": "2026.10.9",
			"description": "OpenClaw SMS/MMS channel plugin for Twilio messages.",
			"source": "official",
			"kind": "channel",
			"openclaw": {
				"channel": {
					"id": "sms",
					"configuredState": {
						"env": { "anyOf": [
							"TWILIO_ACCOUNT_SID",
							"TWILIO_AUTH_TOKEN",
							"TWILIO_PHONE_NUMBER",
							"TWILIO_SMS_FROM",
							"TWILIO_MESSAGING_SERVICE_SID"
						] },
						"specifier": "./configured-state",
						"exportName": "hasConfiguredSmsChannelState"
					},
					"label": "SMS",
					"selectionLabel": "SMS (Twilio)",
					"detailLabel": "Twilio SMS/MMS",
					"docsPath": "/channels/sms",
					"docsLabel": "sms",
					"blurb": "Twilio-backed SMS/MMS with inbound webhooks and outbound replies.",
					"order": 88,
					"quickstartAllowFrom": true,
					"setup": { "fields": [
						{
							"key": "accountSid",
							"kind": "string",
							"sensitive": true,
							"cli": {
								"flags": "--account-sid <sid>",
								"description": "Twilio account SID"
							}
						},
						{
							"key": "authToken",
							"kind": "string",
							"sensitive": true,
							"cli": {
								"flags": "--auth-token <token>",
								"description": "Twilio auth token"
							}
						},
						{
							"key": "fromNumber",
							"kind": "string",
							"cli": {
								"flags": "--from-number <e164>",
								"description": "Twilio sender phone number"
							}
						},
						{
							"key": "messagingServiceSid",
							"kind": "string",
							"cli": {
								"flags": "--messaging-service-sid <sid>",
								"description": "Twilio Messaging Service SID"
							}
						},
						{
							"key": "defaultTo",
							"kind": "string",
							"cli": {
								"flags": "--default-to <e164>",
								"description": "Default SMS recipient"
							}
						},
						{
							"key": "webhookPath",
							"kind": "string",
							"cli": {
								"flags": "--webhook-path <path>",
								"description": "SMS webhook path"
							}
						},
						{
							"key": "publicWebhookUrl",
							"kind": "string",
							"cli": {
								"flags": "--public-webhook-url <url>",
								"description": "Public SMS webhook URL"
							}
						},
						{
							"key": "dmPolicy",
							"kind": "choice",
							"choices": [
								"pairing",
								"allowlist",
								"open",
								"disabled"
							],
							"cli": {
								"flags": "--dm-policy <policy>",
								"description": "SMS DM policy"
							}
						},
						{
							"key": "allowFrom",
							"kind": "string-list",
							"cli": {
								"flags": "--allow-from <numbers>",
								"description": "Allowed SMS senders"
							}
						}
					] }
				},
				"install": {
					"clawhubSpec": "clawhub:@openclaw/sms",
					"npmSpec": "@openclaw/sms",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.9.8",
					"allowInvalidConfigRecovery": true
				}
			}
		},
		{
			"name": "@openclaw/synology-chat",
			"version": "2026.10.9",
			"description": "Synology Chat channel plugin for OpenClaw channels and direct messages.",
			"source": "official",
			"kind": "channel",
			"openclaw": {
				"channel": {
					"id": "synology-chat",
					"configuredState": { "env": { "allOf": ["SYNOLOGY_CHAT_TOKEN", "SYNOLOGY_CHAT_INCOMING_URL"] } },
					"label": "Synology Chat",
					"selectionLabel": "Synology Chat (Webhook)",
					"docsPath": "/channels/synology-chat",
					"docsLabel": "synology-chat",
					"blurb": "Connect your Synology NAS Chat to OpenClaw with full agent capabilities.",
					"order": 90,
					"setup": { "fields": [
						{
							"key": "token",
							"kind": "string",
							"sensitive": true,
							"cli": {
								"flags": "--token <token>",
								"description": "Synology Chat token"
							}
						},
						{
							"key": "url",
							"kind": "string",
							"sensitive": true,
							"cli": {
								"flags": "--url <url>",
								"description": "Synology Chat webhook URL"
							}
						},
						{
							"key": "webhookUrl",
							"kind": "string",
							"sensitive": true,
							"cli": {
								"flags": "--webhook-url <url>",
								"description": "Public HTTPS Synology Chat callback URL used for attachments"
							}
						},
						{
							"key": "webhookPath",
							"kind": "string",
							"cli": {
								"flags": "--webhook-path <path>",
								"description": "Synology Chat webhook path"
							}
						},
						{
							"key": "useEnv",
							"kind": "boolean",
							"cli": {
								"flags": "--use-env",
								"description": "Use Synology Chat environment credentials"
							},
							"envVars": ["SYNOLOGY_CHAT_TOKEN"]
						}
					] }
				},
				"install": {
					"clawhubSpec": "clawhub:@openclaw/synology-chat",
					"npmSpec": "@openclaw/synology-chat",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.4.10"
				}
			}
		},
		{
			"name": "@openclaw/tlon",
			"version": "2026.10.9",
			"description": "OpenClaw Tlon/Urbit channel plugin for chat workflows.",
			"source": "official",
			"kind": "channel",
			"openclaw": {
				"channel": {
					"id": "tlon",
					"label": "Tlon",
					"selectionLabel": "Tlon (Urbit)",
					"docsPath": "/channels/tlon",
					"docsLabel": "tlon",
					"blurb": "decentralized messaging on Urbit; install the plugin to enable.",
					"order": 90,
					"quickstartAllowFrom": true,
					"setup": { "fields": [
						{
							"key": "ship",
							"kind": "string",
							"cli": {
								"flags": "--ship <ship>",
								"description": "Tlon ship"
							}
						},
						{
							"key": "url",
							"kind": "string",
							"cli": {
								"flags": "--url <url>",
								"description": "Tlon URL"
							}
						},
						{
							"key": "code",
							"kind": "string",
							"sensitive": true,
							"cli": {
								"flags": "--code <code>",
								"description": "Tlon login code"
							}
						},
						{
							"key": "dangerouslyAllowPrivateNetwork",
							"kind": "boolean",
							"cli": {
								"flags": "--dangerously-allow-private-network",
								"description": "Allow private-network Tlon URLs"
							}
						},
						{
							"key": "groupChannels",
							"kind": "string-list",
							"cli": {
								"flags": "--group-channels <list>",
								"description": "Tlon group channels"
							}
						},
						{
							"key": "dmAllowlist",
							"kind": "string-list",
							"cli": {
								"flags": "--dm-allowlist <list>",
								"description": "Tlon DM allowlist"
							}
						},
						{
							"key": "autoDiscoverChannels",
							"kind": "boolean",
							"cli": {
								"flags": "--auto-discover-channels",
								"negatedFlags": "--no-auto-discover-channels",
								"description": "Auto-discover Tlon group channels"
							}
						},
						{
							"key": "ownerShip",
							"kind": "string",
							"cli": {
								"flags": "--owner-ship <ship>",
								"description": "Tlon owner ship"
							}
						}
					] }
				},
				"install": {
					"clawhubSpec": "clawhub:@openclaw/tlon",
					"npmSpec": "@openclaw/tlon",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.4.10"
				}
			}
		},
		{
			"name": "@openclaw/twitch",
			"version": "2026.10.9",
			"description": "OpenClaw Twitch channel plugin for chat and moderation workflows.",
			"source": "official",
			"kind": "channel",
			"openclaw": {
				"channel": {
					"id": "twitch",
					"configuredState": { "env": { "anyOf": ["OPENCLAW_TWITCH_ACCESS_TOKEN"] } },
					"label": "Twitch",
					"selectionLabel": "Twitch (Chat)",
					"docsPath": "/channels/twitch",
					"blurb": "Twitch chat integration",
					"aliases": ["twitch-chat"],
					"setup": { "fields": [] }
				},
				"install": {
					"clawhubSpec": "clawhub:@openclaw/twitch",
					"npmSpec": "@openclaw/twitch",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.4.10"
				}
			}
		},
		{
			"name": "@wecom/wecom-openclaw-plugin",
			"description": "OpenClaw WeCom channel plugin by the Tencent WeCom team.",
			"source": "external",
			"kind": "channel",
			"openclaw": {
				"plugin": {
					"id": "wecom-openclaw-plugin",
					"label": "WeCom"
				},
				"contracts": { "tools": ["wecom_mcp"] },
				"channel": {
					"id": "wecom",
					"label": "WeCom",
					"selectionLabel": "WeCom(企业微信)",
					"detailLabel": "WeCom",
					"docsPath": "/channels/wecom",
					"docsLabel": "wecom",
					"blurb": "Enterprise messaging and documents, scheduling, task tools.",
					"aliases": [
						"qywx",
						"wework",
						"enterprise-wechat"
					],
					"order": 45
				},
				"channelConfigs": { "wecom": {
					"label": "WeCom",
					"description": "Enterprise WeChat conversation channel.",
					"schema": {
						"type": "object",
						"additionalProperties": true
					}
				} },
				"install": {
					"npmSpec": "@wecom/wecom-openclaw-plugin@2026.7.2",
					"defaultChoice": "npm",
					"expectedIntegrity": "sha512-7kqdBIOF3SgDDoBoFtO6jxnxofbYSgbKdxZDNabD0y0jg2xKcVqlXZOOJ9+XQho/QOtIFrnRH2IRnPukFEYwJg=="
				}
			}
		},
		{
			"name": "@openclaw/whatsapp",
			"version": "2026.10.9",
			"description": "OpenClaw WhatsApp channel plugin for WhatsApp Web chats.",
			"source": "official",
			"kind": "channel",
			"openclaw": {
				"contracts": { "tools": ["whatsapp_call", "whatsapp_login"] },
				"setupFeatures": { "configPromotion": "preserve-root" },
				"channel": {
					"id": "whatsapp",
					"approvalFlags": ["native"],
					"label": "WhatsApp",
					"selectionLabel": "WhatsApp (QR link)",
					"detailLabel": "WhatsApp Web",
					"docsPath": "/channels/whatsapp",
					"docsLabel": "whatsapp",
					"blurb": "works with your own number; recommend a separate phone + eSIM.",
					"systemImage": "message",
					"persistedAuthState": {
						"specifier": "./auth-presence",
						"exportName": "hasAnyWhatsAppAuth"
					},
					"setup": { "fields": [{
						"key": "authDir",
						"kind": "string",
						"cli": {
							"flags": "--auth-dir <path>",
							"description": "WhatsApp auth directory override"
						}
					}] }
				},
				"install": {
					"clawhubSpec": "clawhub:@openclaw/whatsapp",
					"npmSpec": "@openclaw/whatsapp",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.4.25"
				}
			}
		},
		{
			"name": "openclaw-plugin-yuanbao",
			"description": "OpenClaw Yuanbao channel plugin by the Tencent Yuanbao team.",
			"source": "external",
			"kind": "channel",
			"openclaw": {
				"plugin": {
					"id": "openclaw-plugin-yuanbao",
					"label": "Yuanbao"
				},
				"contracts": { "tools": [
					"query_group_info",
					"query_session_members",
					"yuanbao_remind"
				] },
				"channel": {
					"id": "yuanbao",
					"label": "Yuanbao",
					"selectionLabel": "Yuanbao (元宝)",
					"detailLabel": "Yuanbao",
					"docsPath": "/channels/yuanbao",
					"docsLabel": "yuanbao",
					"blurb": "Tencent Yuanbao AI assistant conversation channel.",
					"aliases": [
						"yuanbao",
						"yb",
						"tencent-yuanbao",
						"元宝"
					],
					"order": 85
				},
				"channelConfigs": { "yuanbao": {
					"label": "Yuanbao",
					"description": "Tencent Yuanbao AI assistant channel.",
					"schema": {
						"type": "object",
						"additionalProperties": true
					}
				} },
				"install": {
					"npmSpec": "openclaw-plugin-yuanbao@2.18.2",
					"defaultChoice": "npm",
					"expectedIntegrity": "sha512-cL85zWLePhi/GWRsXL8ogS4tejNuCE/J0V/OYhDFJzElF2TmndVCUAXaJdssgv/ULJ9sBaic88wAzRllIgZIwA=="
				}
			}
		},
		{
			"name": "@openclaw/zalo",
			"version": "2026.10.9",
			"description": "OpenClaw Zalo channel plugin for bot and webhook chats.",
			"source": "official",
			"kind": "channel",
			"openclaw": {
				"channel": {
					"id": "zalo",
					"configuredState": { "env": { "anyOf": ["ZALO_BOT_TOKEN"] } },
					"label": "Zalo",
					"selectionLabel": "Zalo (Bot API)",
					"docsPath": "/channels/zalo",
					"docsLabel": "zalo",
					"blurb": "Vietnam-focused messaging platform with Bot API.",
					"aliases": ["zl"],
					"order": 80,
					"quickstartAllowFrom": true,
					"setup": { "fields": [
						{
							"key": "token",
							"kind": "string",
							"sensitive": true,
							"cli": {
								"flags": "--token <token>",
								"description": "Zalo bot token"
							}
						},
						{
							"key": "tokenFile",
							"kind": "string",
							"sensitive": true,
							"cli": {
								"flags": "--token-file <path>",
								"description": "Zalo bot token file"
							}
						},
						{
							"key": "useEnv",
							"kind": "boolean",
							"cli": {
								"flags": "--use-env",
								"description": "Use ZALO_BOT_TOKEN"
							},
							"envVars": ["ZALO_BOT_TOKEN"]
						}
					] }
				},
				"install": {
					"clawhubSpec": "clawhub:@openclaw/zalo",
					"npmSpec": "@openclaw/zalo",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.9.8"
				}
			}
		},
		{
			"name": "@openclaw/zalouser",
			"version": "2026.10.9",
			"description": "OpenClaw Zalo Personal Account plugin via native zca-js integration.",
			"source": "official",
			"kind": "channel",
			"openclaw": {
				"contracts": { "tools": ["zalouser"] },
				"channel": {
					"id": "zalouser",
					"configuredState": { "env": { "anyOf": ["ZALOUSER_PROFILE", "ZCA_PROFILE"] } },
					"label": "Zalo Personal",
					"selectionLabel": "Zalo (Personal Account)",
					"docsPath": "/channels/zalouser",
					"docsLabel": "zalouser",
					"blurb": "Zalo personal account via QR code login.",
					"aliases": ["zlu"],
					"order": 85,
					"quickstartAllowFrom": false,
					"doctorCapabilities": {
						"dmAllowFromMode": "topOnly",
						"groupModel": "hybrid",
						"groupAllowFromFallbackToAllowFrom": false,
						"warnOnEmptyGroupSenderAllowlist": false
					},
					"setup": { "fields": [] }
				},
				"install": {
					"clawhubSpec": "clawhub:@openclaw/zalouser",
					"npmSpec": "@openclaw/zalouser",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.4.10"
				}
			}
		}
	] },
	{ entries: [
		{
			"name": "@openclaw/amazon-bedrock-provider",
			"description": "OpenClaw Amazon Bedrock provider plugin",
			"source": "official",
			"kind": "provider",
			"openclaw": {
				"plugin": {
					"id": "amazon-bedrock",
					"label": "Amazon Bedrock"
				},
				"providers": [{
					"id": "amazon-bedrock",
					"name": "Amazon Bedrock",
					"docs": "/providers/bedrock",
					"categories": ["cloud", "llm"]
				}],
				"install": {
					"clawhubSpec": "clawhub:@openclaw/amazon-bedrock-provider",
					"npmSpec": "@openclaw/amazon-bedrock-provider",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.5.12-beta.1"
				}
			}
		},
		{
			"name": "@openclaw/amazon-bedrock-mantle-provider",
			"description": "OpenClaw Amazon Bedrock Mantle provider plugin",
			"source": "official",
			"kind": "provider",
			"openclaw": {
				"plugin": {
					"id": "amazon-bedrock-mantle",
					"label": "Amazon Bedrock Mantle"
				},
				"providers": [{
					"id": "amazon-bedrock-mantle",
					"name": "Amazon Bedrock Mantle",
					"docs": "/providers/bedrock-mantle",
					"categories": ["cloud", "llm"]
				}],
				"install": {
					"clawhubSpec": "clawhub:@openclaw/amazon-bedrock-mantle-provider",
					"npmSpec": "@openclaw/amazon-bedrock-mantle-provider",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.5.12-beta.1"
				}
			}
		},
		{
			"name": "@openclaw/anthropic-vertex-provider",
			"description": "OpenClaw Anthropic Vertex provider plugin",
			"source": "official",
			"kind": "provider",
			"openclaw": {
				"plugin": {
					"id": "anthropic-vertex",
					"label": "Anthropic Vertex"
				},
				"providers": [{
					"id": "anthropic-vertex",
					"name": "Anthropic Vertex",
					"docs": "/providers/models",
					"categories": ["cloud", "llm"]
				}],
				"install": {
					"clawhubSpec": "clawhub:@openclaw/anthropic-vertex-provider",
					"npmSpec": "@openclaw/anthropic-vertex-provider",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.5.12-beta.1"
				}
			}
		},
		{
			"name": "@openclaw/arcee-provider",
			"description": "OpenClaw Arcee provider plugin.",
			"source": "official",
			"kind": "provider",
			"openclaw": {
				"plugin": {
					"id": "arcee",
					"label": "Arcee AI"
				},
				"providers": [{
					"id": "arcee",
					"name": "Arcee AI",
					"docs": "/providers/arcee",
					"categories": ["cloud", "llm"],
					"envVars": ["ARCEEAI_API_KEY"],
					"authChoices": [{
						"method": "arcee-platform",
						"choiceId": "arceeai-api-key",
						"choiceLabel": "Arcee AI API key",
						"choiceHint": "Direct (chat.arcee.ai)",
						"groupId": "arcee",
						"groupLabel": "Arcee AI",
						"groupHint": "Direct API or OpenRouter",
						"optionKey": "arceeaiApiKey",
						"cliFlag": "--arceeai-api-key",
						"cliOption": "--arceeai-api-key <key>",
						"cliDescription": "Arcee AI API key",
						"onboardingScopes": ["text-inference"]
					}, {
						"method": "openrouter",
						"choiceId": "arceeai-openrouter",
						"choiceLabel": "OpenRouter API key",
						"choiceHint": "Via OpenRouter (openrouter.ai)",
						"groupId": "arcee",
						"groupLabel": "Arcee AI",
						"groupHint": "Direct API or OpenRouter",
						"optionKey": "openrouterApiKey",
						"onboardingScopes": ["text-inference"]
					}]
				}],
				"install": {
					"clawhubSpec": "clawhub:@openclaw/arcee-provider",
					"npmSpec": "@openclaw/arcee-provider",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.6.8"
				}
			}
		},
		{
			"name": "@openclaw/baseten-provider",
			"description": "OpenClaw Baseten provider plugin.",
			"source": "official",
			"kind": "provider",
			"openclaw": {
				"plugin": {
					"id": "baseten",
					"label": "Baseten"
				},
				"providers": [{
					"id": "baseten",
					"name": "Baseten",
					"docs": "/providers/baseten",
					"categories": ["cloud", "llm"],
					"envVars": ["BASETEN_API_KEY"],
					"authChoices": [{
						"method": "api-key",
						"choiceId": "baseten-api-key",
						"choiceLabel": "Baseten API key",
						"groupId": "baseten",
						"groupLabel": "Baseten",
						"groupHint": "Hosted Model APIs, including Inkling",
						"optionKey": "basetenApiKey",
						"cliFlag": "--baseten-api-key",
						"cliOption": "--baseten-api-key <key>",
						"cliDescription": "Baseten API key",
						"onboardingScopes": ["text-inference"]
					}]
				}],
				"install": {
					"clawhubSpec": "clawhub:@openclaw/baseten-provider",
					"npmSpec": "@openclaw/baseten-provider",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.7.2"
				}
			}
		},
		{
			"name": "@openclaw/byteplus-provider",
			"description": "OpenClaw BytePlus provider plugin.",
			"source": "official",
			"kind": "provider",
			"openclaw": {
				"plugin": {
					"id": "byteplus",
					"label": "BytePlus"
				},
				"providers": [{
					"id": "byteplus",
					"aliases": ["byteplus-plan"],
					"name": "BytePlus",
					"docs": "/concepts/model-providers#byteplus-international",
					"categories": ["cloud", "llm"],
					"envVars": ["BYTEPLUS_API_KEY"],
					"authChoices": [{
						"method": "api-key",
						"choiceId": "byteplus-api-key",
						"choiceLabel": "BytePlus API key",
						"groupId": "byteplus",
						"groupLabel": "BytePlus",
						"groupHint": "API key",
						"optionKey": "byteplusApiKey",
						"cliFlag": "--byteplus-api-key",
						"cliOption": "--byteplus-api-key <key>",
						"cliDescription": "BytePlus API key",
						"onboardingScopes": ["text-inference"]
					}]
				}],
				"contracts": { "videoGenerationProviders": ["byteplus"] },
				"install": {
					"clawhubSpec": "clawhub:@openclaw/byteplus-provider",
					"npmSpec": "@openclaw/byteplus-provider",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.7.2"
				}
			}
		},
		{
			"name": "@openclaw/cerebras-provider",
			"description": "OpenClaw Cerebras provider plugin.",
			"source": "official",
			"kind": "provider",
			"openclaw": {
				"plugin": {
					"id": "cerebras",
					"label": "Cerebras"
				},
				"providerEndpoints": [{
					"endpointClass": "cerebras-native",
					"hosts": ["api.cerebras.ai"]
				}],
				"providers": [{
					"id": "cerebras",
					"name": "Cerebras",
					"docs": "/providers/cerebras",
					"categories": ["cloud", "llm"],
					"envVars": ["CEREBRAS_API_KEY"],
					"authChoices": [{
						"method": "api-key",
						"choiceId": "cerebras-api-key",
						"choiceLabel": "Cerebras API key",
						"groupId": "cerebras",
						"groupLabel": "Cerebras",
						"groupHint": "Fast OpenAI-compatible inference",
						"optionKey": "cerebrasApiKey",
						"cliFlag": "--cerebras-api-key",
						"cliOption": "--cerebras-api-key <key>",
						"cliDescription": "Cerebras API key",
						"onboardingScopes": ["text-inference"]
					}]
				}],
				"install": {
					"clawhubSpec": "clawhub:@openclaw/cerebras-provider",
					"npmSpec": "@openclaw/cerebras-provider",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.6.8"
				}
			}
		},
		{
			"name": "@openclaw/chutes-provider",
			"description": "OpenClaw Chutes.ai provider plugin.",
			"source": "official",
			"kind": "provider",
			"openclaw": {
				"plugin": {
					"id": "chutes",
					"label": "Chutes"
				},
				"providerEndpoints": [{
					"endpointClass": "chutes-native",
					"hosts": ["llm.chutes.ai"]
				}],
				"providers": [{
					"id": "chutes",
					"name": "Chutes",
					"docs": "/providers/chutes",
					"categories": ["cloud", "llm"],
					"envVars": ["CHUTES_API_KEY", "CHUTES_OAUTH_TOKEN"],
					"authChoices": [{
						"method": "oauth",
						"choiceId": "chutes",
						"choiceLabel": "Chutes (OAuth)",
						"choiceHint": "Browser sign-in",
						"groupId": "chutes",
						"groupLabel": "Chutes",
						"groupHint": "OAuth + API key",
						"onboardingScopes": ["text-inference"]
					}, {
						"method": "api-key",
						"choiceId": "chutes-api-key",
						"choiceLabel": "Chutes API key",
						"choiceHint": "Open-source models including Llama, DeepSeek, and more",
						"groupId": "chutes",
						"groupLabel": "Chutes",
						"groupHint": "OAuth + API key",
						"optionKey": "chutesApiKey",
						"cliFlag": "--chutes-api-key",
						"cliOption": "--chutes-api-key <key>",
						"cliDescription": "Chutes API key",
						"onboardingScopes": ["text-inference"]
					}]
				}],
				"install": {
					"clawhubSpec": "clawhub:@openclaw/chutes-provider",
					"npmSpec": "@openclaw/chutes-provider",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.6.8"
				}
			}
		},
		{
			"name": "@openclaw/cohere-provider",
			"description": "OpenClaw Cohere provider plugin",
			"source": "official",
			"kind": "provider",
			"openclaw": {
				"plugin": {
					"id": "cohere",
					"label": "Cohere"
				},
				"providers": [{
					"id": "cohere",
					"name": "Cohere",
					"docs": "/providers/cohere",
					"categories": ["cloud", "llm"],
					"authChoices": [{
						"method": "api-key",
						"choiceId": "cohere-api-key",
						"choiceLabel": "Cohere API key",
						"groupId": "cohere",
						"groupLabel": "Cohere",
						"groupHint": "OpenAI-compatible inference",
						"optionKey": "cohereApiKey",
						"cliFlag": "--cohere-api-key",
						"cliOption": "--cohere-api-key <key>",
						"cliDescription": "Cohere API key",
						"onboardingScopes": ["text-inference"]
					}]
				}],
				"install": {
					"clawhubSpec": "clawhub:@openclaw/cohere-provider",
					"npmSpec": "@openclaw/cohere-provider",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.6.8"
				}
			}
		},
		{
			"name": "@openclaw/cloudflare-ai-gateway-provider",
			"description": "OpenClaw Cloudflare AI Gateway provider plugin.",
			"source": "official",
			"kind": "provider",
			"openclaw": {
				"plugin": {
					"id": "cloudflare-ai-gateway",
					"label": "Cloudflare AI Gateway"
				},
				"providers": [{
					"id": "cloudflare-ai-gateway",
					"name": "Cloudflare AI Gateway",
					"docs": "/providers/cloudflare-ai-gateway",
					"categories": ["cloud", "llm"],
					"envVars": ["CLOUDFLARE_AI_GATEWAY_API_KEY"],
					"authChoices": [{
						"method": "api-key",
						"choiceId": "cloudflare-ai-gateway-api-key",
						"choiceLabel": "Cloudflare AI Gateway",
						"choiceHint": "Account ID + Gateway ID + API key",
						"groupId": "cloudflare-ai-gateway",
						"groupLabel": "Cloudflare AI Gateway",
						"groupHint": "Account ID + Gateway ID + API key",
						"optionKey": "cloudflareAiGatewayApiKey",
						"cliFlag": "--cloudflare-ai-gateway-api-key",
						"cliOption": "--cloudflare-ai-gateway-api-key <key>",
						"cliDescription": "Cloudflare AI Gateway API key",
						"onboardingScopes": ["text-inference"]
					}]
				}],
				"install": {
					"clawhubSpec": "clawhub:@openclaw/cloudflare-ai-gateway-provider",
					"npmSpec": "@openclaw/cloudflare-ai-gateway-provider",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.6.8"
				}
			}
		},
		{
			"name": "@openclaw/comfy-provider",
			"description": "OpenClaw ComfyUI provider plugin.",
			"source": "official",
			"kind": "provider",
			"openclaw": {
				"plugin": {
					"id": "comfy",
					"label": "ComfyUI"
				},
				"providers": [{
					"id": "comfy",
					"name": "ComfyUI",
					"docs": "/providers/comfy",
					"categories": [
						"local",
						"cloud",
						"media"
					],
					"envVars": ["COMFY_API_KEY", "COMFY_CLOUD_API_KEY"],
					"authChoices": [{
						"method": "cloud-api-key",
						"choiceId": "comfy-cloud-api-key",
						"choiceLabel": "Comfy Cloud API key",
						"choiceHint": "Required for cloud workflows",
						"groupId": "comfy",
						"groupLabel": "ComfyUI",
						"groupHint": "Local or cloud workflows",
						"optionKey": "comfyApiKey",
						"cliFlag": "--comfy-api-key",
						"cliOption": "--comfy-api-key <key>",
						"cliDescription": "Comfy Cloud API key",
						"onboardingScopes": ["image-generation"]
					}]
				}],
				"contracts": {
					"imageGenerationProviders": ["comfy"],
					"musicGenerationProviders": ["comfy"],
					"videoGenerationProviders": ["comfy"]
				},
				"install": {
					"clawhubSpec": "clawhub:@openclaw/comfy-provider",
					"npmSpec": "@openclaw/comfy-provider",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.7.2"
				}
			}
		},
		{
			"name": "@openclaw/deepinfra-provider",
			"description": "OpenClaw DeepInfra provider plugin.",
			"source": "official",
			"kind": "provider",
			"openclaw": {
				"plugin": {
					"id": "deepinfra",
					"label": "DeepInfra"
				},
				"providerEndpoints": [{
					"endpointClass": "deepinfra-native",
					"hosts": ["api.deepinfra.com"]
				}],
				"providers": [{
					"id": "deepinfra",
					"name": "DeepInfra",
					"docs": "/providers/deepinfra",
					"categories": ["cloud", "llm"],
					"envVars": ["DEEPINFRA_API_KEY"],
					"authChoices": [{
						"method": "api-key",
						"choiceId": "deepinfra-api-key",
						"choiceLabel": "DeepInfra API key",
						"choiceHint": "Unified API for open source models",
						"groupId": "deepinfra",
						"groupLabel": "DeepInfra",
						"groupHint": "Unified API for open source models",
						"optionKey": "deepinfraApiKey",
						"cliFlag": "--deepinfra-api-key",
						"cliOption": "--deepinfra-api-key <key>",
						"cliDescription": "DeepInfra API key",
						"onboardingScopes": ["text-inference"]
					}]
				}],
				"contracts": {
					"mediaUnderstandingProviders": ["deepinfra"],
					"embeddingProviders": ["deepinfra"],
					"imageGenerationProviders": ["deepinfra"],
					"speechProviders": ["deepinfra"],
					"videoGenerationProviders": ["deepinfra"]
				},
				"install": {
					"clawhubSpec": "clawhub:@openclaw/deepinfra-provider",
					"npmSpec": "@openclaw/deepinfra-provider",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.6.8"
				}
			}
		},
		{
			"name": "@openclaw/deepseek-provider",
			"description": "OpenClaw DeepSeek provider plugin.",
			"source": "official",
			"kind": "provider",
			"openclaw": {
				"plugin": {
					"id": "deepseek",
					"label": "DeepSeek"
				},
				"providerEndpoints": [{
					"endpointClass": "deepseek-native",
					"hosts": ["api.deepseek.com"]
				}],
				"providers": [{
					"id": "deepseek",
					"name": "DeepSeek",
					"docs": "/providers/deepseek",
					"categories": ["cloud", "llm"],
					"envVars": ["DEEPSEEK_API_KEY"],
					"authChoices": [{
						"method": "api-key",
						"choiceId": "deepseek-api-key",
						"choiceLabel": "DeepSeek API key",
						"groupId": "deepseek",
						"groupLabel": "DeepSeek",
						"groupHint": "API key",
						"optionKey": "deepseekApiKey",
						"cliFlag": "--deepseek-api-key",
						"cliOption": "--deepseek-api-key <key>",
						"cliDescription": "DeepSeek API key",
						"onboardingScopes": ["text-inference"]
					}]
				}],
				"install": {
					"clawhubSpec": "clawhub:@openclaw/deepseek-provider",
					"npmSpec": "@openclaw/deepseek-provider",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.6.8"
				}
			}
		},
		{
			"name": "@openclaw/featherless-provider",
			"description": "OpenClaw Featherless AI provider plugin.",
			"source": "official",
			"kind": "provider",
			"openclaw": {
				"plugin": {
					"id": "featherless",
					"label": "Featherless AI"
				},
				"providers": [{
					"id": "featherless",
					"name": "Featherless AI",
					"docs": "/providers/featherless",
					"categories": ["cloud", "llm"],
					"envVars": ["FEATHERLESS_API_KEY"],
					"authChoices": [{
						"method": "api-key",
						"choiceId": "featherless-api-key",
						"choiceLabel": "Featherless AI API key",
						"choiceHint": "OpenAI-compatible access to open models",
						"groupId": "featherless",
						"groupLabel": "Featherless AI",
						"groupHint": "OpenAI-compatible access to open models",
						"optionKey": "featherlessApiKey",
						"cliFlag": "--featherless-api-key",
						"cliOption": "--featherless-api-key <key>",
						"cliDescription": "Featherless AI API key",
						"onboardingScopes": ["text-inference"]
					}]
				}],
				"install": {
					"clawhubSpec": "clawhub:@openclaw/featherless-provider",
					"npmSpec": "@openclaw/featherless-provider",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.6.11"
				}
			}
		},
		{
			"name": "@openclaw/gmi-provider",
			"description": "OpenClaw GMI Cloud provider plugin",
			"source": "official",
			"kind": "provider",
			"openclaw": {
				"plugin": {
					"id": "gmi",
					"label": "GMI Cloud"
				},
				"providerEndpoints": [{
					"endpointClass": "gmi-native",
					"hosts": ["api.gmi-serving.com"]
				}],
				"providers": [{
					"id": "gmi",
					"aliases": ["gmi-cloud", "gmicloud"],
					"name": "GMI Cloud",
					"docs": "/providers/gmi",
					"categories": ["cloud", "llm"],
					"authChoices": [{
						"method": "api-key",
						"choiceId": "gmi-api-key",
						"choiceLabel": "GMI Cloud API key",
						"choiceHint": "OpenAI-compatible GMI Cloud endpoint.",
						"groupId": "gmi",
						"groupLabel": "GMI Cloud",
						"groupHint": "OpenAI-compatible GMI Cloud endpoint",
						"optionKey": "gmiApiKey",
						"cliFlag": "--gmi-api-key",
						"cliOption": "--gmi-api-key <key>",
						"cliDescription": "GMI Cloud API key",
						"onboardingScopes": ["text-inference"]
					}]
				}],
				"install": {
					"clawhubSpec": "clawhub:@openclaw/gmi-provider",
					"npmSpec": "@openclaw/gmi-provider",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.6.8"
				}
			}
		},
		{
			"name": "@openclaw/longcat-provider",
			"description": "OpenClaw LongCat provider plugin",
			"source": "official",
			"kind": "provider",
			"openclaw": {
				"plugin": {
					"id": "longcat",
					"label": "LongCat"
				},
				"providers": [{
					"id": "longcat",
					"aliases": ["meituan-longcat"],
					"name": "LongCat",
					"docs": "/providers/longcat",
					"categories": ["cloud", "llm"],
					"envVars": ["LONGCAT_API_KEY"],
					"authChoices": [{
						"method": "api-key",
						"choiceId": "longcat-api-key",
						"choiceLabel": "LongCat API key",
						"groupId": "longcat",
						"groupLabel": "LongCat",
						"groupHint": "OpenAI-compatible LongCat API",
						"optionKey": "longcatApiKey",
						"cliFlag": "--longcat-api-key",
						"cliOption": "--longcat-api-key <key>",
						"cliDescription": "LongCat API key",
						"onboardingScopes": ["text-inference"]
					}]
				}],
				"install": {
					"clawhubSpec": "clawhub:@openclaw/longcat-provider",
					"npmSpec": "@openclaw/longcat-provider",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.6.8"
				}
			}
		},
		{
			"name": "@openclaw/meta-provider",
			"description": "OpenClaw Meta provider plugin.",
			"source": "official",
			"kind": "provider",
			"openclaw": {
				"plugin": {
					"id": "meta",
					"label": "Meta"
				},
				"providerEndpoints": [{
					"endpointClass": "meta-native",
					"hosts": ["api.meta.ai"]
				}],
				"providers": [{
					"id": "meta",
					"name": "Meta",
					"docs": "/providers/meta",
					"categories": ["cloud", "llm"],
					"envVars": ["MODEL_API_KEY"],
					"authChoices": [{
						"method": "api-key",
						"choiceId": "meta-api-key",
						"choiceLabel": "Meta API key",
						"choiceHint": "Meta (Responses API)",
						"groupId": "meta",
						"groupLabel": "Meta",
						"groupHint": "Meta (Responses API)",
						"optionKey": "metaApiKey",
						"cliFlag": "--meta-api-key",
						"cliOption": "--meta-api-key <key>",
						"cliDescription": "Meta API key",
						"onboardingScopes": ["text-inference"]
					}]
				}],
				"install": {
					"clawhubSpec": "clawhub:@openclaw/meta-provider",
					"npmSpec": "@openclaw/meta-provider",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.6.11"
				}
			}
		},
		{
			"name": "@openclaw/mistral-provider",
			"description": "OpenClaw Mistral provider plugin",
			"source": "official",
			"kind": "provider",
			"openclaw": {
				"plugin": {
					"id": "mistral",
					"label": "Mistral"
				},
				"providerEndpoints": [{
					"endpointClass": "mistral-public",
					"hosts": ["api.mistral.ai"]
				}],
				"providers": [{
					"id": "mistral",
					"name": "Mistral",
					"docs": "/providers/mistral",
					"categories": ["cloud", "llm"],
					"envVars": ["MISTRAL_API_KEY"],
					"authChoices": [{
						"method": "api-key",
						"choiceId": "mistral-api-key",
						"choiceLabel": "Mistral API key",
						"groupId": "mistral",
						"groupLabel": "Mistral AI",
						"groupHint": "API key",
						"optionKey": "mistralApiKey",
						"cliFlag": "--mistral-api-key",
						"cliOption": "--mistral-api-key <key>",
						"cliDescription": "Mistral API key",
						"onboardingScopes": ["text-inference"]
					}]
				}],
				"contracts": {
					"embeddingProviders": ["mistral"],
					"mediaUnderstandingProviders": ["mistral"],
					"realtimeTranscriptionProviders": ["mistral"]
				},
				"install": {
					"clawhubSpec": "clawhub:@openclaw/mistral-provider",
					"npmSpec": "@openclaw/mistral-provider",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.7.2"
				}
			}
		},
		{
			"name": "@openclaw/novita-provider",
			"description": "OpenClaw NovitaAI provider plugin",
			"source": "official",
			"kind": "provider",
			"openclaw": {
				"plugin": {
					"id": "novita",
					"label": "NovitaAI"
				},
				"providerEndpoints": [{
					"endpointClass": "novita-native",
					"hosts": ["api.novita.ai"]
				}],
				"providers": [{
					"id": "novita",
					"aliases": ["novita-ai", "novitaai"],
					"name": "NovitaAI",
					"docs": "/providers/novita",
					"categories": ["cloud", "llm"],
					"envVars": ["NOVITA_API_KEY"],
					"authChoices": [{
						"method": "api-key",
						"choiceId": "novita-api-key",
						"choiceLabel": "NovitaAI API key",
						"choiceHint": "OpenAI-compatible NovitaAI endpoint",
						"groupId": "novita",
						"groupLabel": "NovitaAI",
						"groupHint": "OpenAI-compatible NovitaAI endpoint",
						"optionKey": "novitaApiKey",
						"cliFlag": "--novita-api-key",
						"cliOption": "--novita-api-key <key>",
						"cliDescription": "NovitaAI API key",
						"onboardingScopes": ["text-inference"]
					}]
				}],
				"install": {
					"clawhubSpec": "clawhub:@openclaw/novita-provider",
					"npmSpec": "@openclaw/novita-provider",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.7.2"
				}
			}
		},
		{
			"name": "@openclaw/opencode-provider",
			"description": "OpenClaw OpenCode Zen provider plugin",
			"source": "official",
			"kind": "provider",
			"openclaw": {
				"plugin": {
					"id": "opencode",
					"label": "OpenCode Zen"
				},
				"providerEndpoints": [{
					"endpointClass": "opencode-native",
					"baseUrls": ["https://opencode.ai/zen", "https://opencode.ai/zen/v1"]
				}],
				"providers": [{
					"id": "opencode",
					"name": "OpenCode Zen",
					"docs": "/providers/opencode",
					"categories": ["cloud", "llm"],
					"envVars": ["OPENCODE_API_KEY", "OPENCODE_ZEN_API_KEY"],
					"authChoices": [{
						"method": "api-key",
						"choiceId": "opencode-zen",
						"choiceLabel": "OpenCode Zen catalog",
						"groupId": "opencode",
						"groupLabel": "OpenCode",
						"groupHint": "Shared API key for Zen + Go catalogs",
						"optionKey": "opencodeZenApiKey",
						"cliFlag": "--opencode-zen-api-key",
						"cliOption": "--opencode-zen-api-key <key>",
						"cliDescription": "OpenCode API key (Zen catalog)",
						"onboardingScopes": ["text-inference"]
					}]
				}],
				"contracts": { "mediaUnderstandingProviders": ["opencode"] },
				"install": {
					"clawhubSpec": "clawhub:@openclaw/opencode-provider",
					"npmSpec": "@openclaw/opencode-provider",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.7.2"
				}
			}
		},
		{
			"name": "@openclaw/groq-provider",
			"description": "OpenClaw Groq media-understanding provider.",
			"source": "official",
			"kind": "provider",
			"openclaw": {
				"plugin": {
					"id": "groq",
					"label": "Groq"
				},
				"providerEndpoints": [{
					"endpointClass": "groq-native",
					"hosts": ["api.groq.com"]
				}],
				"providers": [{
					"id": "groq",
					"name": "Groq",
					"docs": "/providers/groq",
					"categories": ["cloud", "llm"],
					"envVars": ["GROQ_API_KEY"],
					"authChoices": [{
						"method": "api-key",
						"choiceId": "groq-api-key",
						"choiceLabel": "Groq API key",
						"groupId": "groq",
						"groupLabel": "Groq",
						"groupHint": "Fast OpenAI-compatible inference",
						"optionKey": "groqApiKey",
						"cliFlag": "--groq-api-key",
						"cliOption": "--groq-api-key <key>",
						"cliDescription": "Groq API key",
						"onboardingScopes": ["text-inference"]
					}]
				}],
				"contracts": { "mediaUnderstandingProviders": ["groq"] },
				"install": {
					"clawhubSpec": "clawhub:@openclaw/groq-provider",
					"npmSpec": "@openclaw/groq-provider",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.6.8"
				}
			}
		},
		{
			"name": "@openclaw/kilocode-provider",
			"description": "OpenClaw Kilo Gateway provider plugin.",
			"source": "official",
			"kind": "provider",
			"openclaw": {
				"plugin": {
					"id": "kilocode",
					"label": "Kilo Gateway"
				},
				"providers": [{
					"id": "kilocode",
					"name": "Kilo Gateway",
					"docs": "/providers/kilocode",
					"categories": ["cloud", "llm"],
					"envVars": ["KILOCODE_API_KEY"],
					"authChoices": [{
						"method": "api-key",
						"choiceId": "kilocode-api-key",
						"choiceLabel": "Kilo Gateway API key",
						"choiceHint": "API key (OpenRouter-compatible)",
						"groupId": "kilocode",
						"groupLabel": "Kilo Gateway",
						"groupHint": "API key (OpenRouter-compatible)",
						"optionKey": "kilocodeApiKey",
						"cliFlag": "--kilocode-api-key",
						"cliOption": "--kilocode-api-key <key>",
						"cliDescription": "Kilo Gateway API key",
						"onboardingScopes": ["text-inference"]
					}]
				}],
				"install": {
					"clawhubSpec": "clawhub:@openclaw/kilocode-provider",
					"npmSpec": "@openclaw/kilocode-provider",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.6.8"
				}
			}
		},
		{
			"name": "@openclaw/kimi-provider",
			"description": "OpenClaw Kimi provider plugin.",
			"source": "official",
			"kind": "provider",
			"openclaw": {
				"plugin": {
					"id": "kimi",
					"label": "Kimi Coding"
				},
				"providers": [{
					"id": "kimi",
					"aliases": ["kimi-coding"],
					"name": "Kimi Coding",
					"docs": "/providers/moonshot",
					"categories": ["cloud", "llm"],
					"envVars": ["KIMI_API_KEY", "KIMICODE_API_KEY"],
					"authChoices": [{
						"method": "api-key",
						"choiceId": "kimi-code-api-key",
						"choiceLabel": "Kimi Code API key (subscription)",
						"groupId": "moonshot",
						"groupLabel": "Moonshot AI (Kimi K2.6)",
						"groupHint": "Kimi K2.6",
						"optionKey": "kimiCodeApiKey",
						"cliFlag": "--kimi-code-api-key",
						"cliOption": "--kimi-code-api-key <key>",
						"cliDescription": "Kimi Code API key (subscription)",
						"onboardingScopes": ["text-inference"]
					}]
				}],
				"install": {
					"clawhubSpec": "clawhub:@openclaw/kimi-provider",
					"npmSpec": "@openclaw/kimi-provider",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.6.8"
				}
			}
		},
		{
			"name": "@openclaw/opencode-go-provider",
			"description": "OpenClaw OpenCode Go provider plugin",
			"source": "official",
			"kind": "provider",
			"openclaw": {
				"plugin": {
					"id": "opencode-go",
					"label": "OpenCode Go"
				},
				"providerEndpoints": [{
					"endpointClass": "opencode-go-native",
					"baseUrls": ["https://opencode.ai/zen/go", "https://opencode.ai/zen/go/v1"]
				}],
				"providers": [{
					"id": "opencode-go",
					"name": "OpenCode Go",
					"docs": "/providers/opencode-go",
					"categories": ["cloud", "llm"],
					"envVars": ["OPENCODE_API_KEY", "OPENCODE_ZEN_API_KEY"],
					"authChoices": [{
						"method": "api-key",
						"choiceId": "opencode-go",
						"choiceLabel": "OpenCode Go catalog",
						"groupId": "opencode",
						"groupLabel": "OpenCode",
						"groupHint": "Shared API key for Zen + Go catalogs",
						"optionKey": "opencodeGoApiKey",
						"cliFlag": "--opencode-go-api-key",
						"cliOption": "--opencode-go-api-key <key>",
						"cliDescription": "OpenCode API key (Go catalog)",
						"onboardingScopes": ["text-inference"]
					}]
				}],
				"contracts": { "mediaUnderstandingProviders": ["opencode-go"] },
				"install": {
					"clawhubSpec": "clawhub:@openclaw/opencode-go-provider",
					"npmSpec": "@openclaw/opencode-go-provider",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.7.2"
				}
			}
		},
		{
			"name": "@openclaw/pixverse-provider",
			"description": "OpenClaw PixVerse video provider plugin",
			"source": "official",
			"kind": "provider",
			"openclaw": {
				"plugin": {
					"id": "pixverse",
					"label": "PixVerse"
				},
				"providers": [{
					"id": "pixverse",
					"name": "PixVerse",
					"docs": "/providers/pixverse",
					"categories": ["cloud", "video"],
					"authChoices": [{
						"method": "api-key",
						"choiceId": "pixverse-api-key",
						"choiceLabel": "PixVerse API key",
						"choiceHint": "Wizard prompts for International or CN endpoint.",
						"groupId": "pixverse",
						"groupLabel": "PixVerse",
						"groupHint": "Video generation",
						"optionKey": "pixverseApiKey",
						"cliFlag": "--pixverse-api-key",
						"cliOption": "--pixverse-api-key <key>",
						"cliDescription": "PixVerse API key",
						"onboardingScopes": ["image-generation"]
					}]
				}],
				"install": {
					"clawhubSpec": "clawhub:@openclaw/pixverse-provider",
					"npmSpec": "@openclaw/pixverse-provider",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.5.26"
				}
			}
		},
		{
			"name": "@openclaw/qianfan-provider",
			"description": "OpenClaw Qianfan provider plugin.",
			"source": "official",
			"kind": "provider",
			"openclaw": {
				"plugin": {
					"id": "qianfan",
					"label": "Qianfan"
				},
				"providers": [{
					"id": "qianfan",
					"name": "Qianfan",
					"docs": "/providers/qianfan",
					"categories": ["cloud", "llm"],
					"envVars": ["QIANFAN_API_KEY"],
					"authChoices": [{
						"method": "api-key",
						"choiceId": "qianfan-api-key",
						"choiceLabel": "Qianfan API key",
						"groupId": "qianfan",
						"groupLabel": "Qianfan",
						"groupHint": "API key",
						"optionKey": "qianfanApiKey",
						"cliFlag": "--qianfan-api-key",
						"cliOption": "--qianfan-api-key <key>",
						"cliDescription": "QIANFAN API key",
						"onboardingScopes": ["text-inference"]
					}]
				}],
				"install": {
					"clawhubSpec": "clawhub:@openclaw/qianfan-provider",
					"npmSpec": "@openclaw/qianfan-provider",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.6.8"
				}
			}
		},
		{
			"name": "@openclaw/qwen-provider",
			"description": "OpenClaw Qwen Cloud provider plugin.",
			"source": "official",
			"kind": "provider",
			"openclaw": {
				"plugin": {
					"id": "qwen",
					"label": "Qwen Cloud"
				},
				"providerEndpoints": [{
					"endpointClass": "modelstudio-native",
					"baseUrls": [
						"https://coding-intl.dashscope.aliyuncs.com/v1",
						"https://coding.dashscope.aliyuncs.com/v1",
						"https://dashscope.aliyuncs.com/compatible-mode/v1",
						"https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
						"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1",
						"https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1"
					]
				}],
				"providers": [
					{
						"id": "qwen",
						"aliases": [
							"qwencloud",
							"modelstudio",
							"dashscope"
						],
						"name": "Qwen Cloud",
						"docs": "/providers/qwen",
						"categories": ["cloud", "llm"],
						"envVars": [
							"QWEN_API_KEY",
							"MODELSTUDIO_API_KEY",
							"DASHSCOPE_API_KEY"
						],
						"authChoices": [
							{
								"method": "standard-api-key-cn",
								"choiceId": "qwen-standard-api-key-cn",
								"deprecatedChoiceIds": ["modelstudio-standard-api-key-cn"],
								"choiceLabel": "Standard API Key for China (pay-as-you-go)",
								"choiceHint": "Endpoint: dashscope.aliyuncs.com",
								"groupId": "qwen",
								"groupLabel": "Qwen Cloud",
								"groupHint": "Standard / Coding Plan (CN / Global) + multimodal roadmap",
								"optionKey": "modelstudioStandardApiKeyCn",
								"cliFlag": "--modelstudio-standard-api-key-cn",
								"cliOption": "--modelstudio-standard-api-key-cn <key>",
								"cliDescription": "Qwen Cloud standard API key (China)",
								"onboardingScopes": ["text-inference"]
							},
							{
								"method": "standard-api-key",
								"choiceId": "qwen-standard-api-key",
								"deprecatedChoiceIds": ["modelstudio-standard-api-key"],
								"choiceLabel": "Standard API Key for Global/Intl (pay-as-you-go)",
								"choiceHint": "Endpoint: dashscope-intl.aliyuncs.com",
								"groupId": "qwen",
								"groupLabel": "Qwen Cloud",
								"groupHint": "Standard / Coding Plan (CN / Global) + multimodal roadmap",
								"optionKey": "modelstudioStandardApiKey",
								"cliFlag": "--modelstudio-standard-api-key",
								"cliOption": "--modelstudio-standard-api-key <key>",
								"cliDescription": "Qwen Cloud standard API key (Global/Intl)",
								"onboardingScopes": ["text-inference"]
							},
							{
								"method": "api-key-cn",
								"choiceId": "qwen-api-key-cn",
								"deprecatedChoiceIds": ["modelstudio-api-key-cn"],
								"choiceLabel": "Coding Plan API Key for China (subscription)",
								"choiceHint": "Endpoint: coding.dashscope.aliyuncs.com",
								"groupId": "qwen",
								"groupLabel": "Qwen Cloud",
								"groupHint": "Standard / Coding Plan (CN / Global) + multimodal roadmap",
								"optionKey": "modelstudioApiKeyCn",
								"cliFlag": "--modelstudio-api-key-cn",
								"cliOption": "--modelstudio-api-key-cn <key>",
								"cliDescription": "Qwen Cloud Coding Plan API key (China)",
								"onboardingScopes": ["text-inference"]
							},
							{
								"method": "api-key",
								"choiceId": "qwen-api-key",
								"deprecatedChoiceIds": ["modelstudio-api-key"],
								"choiceLabel": "Coding Plan API Key for Global/Intl (subscription)",
								"choiceHint": "Endpoint: coding-intl.dashscope.aliyuncs.com",
								"groupId": "qwen",
								"groupLabel": "Qwen Cloud",
								"groupHint": "Standard / Coding Plan (CN / Global) + multimodal roadmap",
								"optionKey": "modelstudioApiKey",
								"cliFlag": "--modelstudio-api-key",
								"cliOption": "--modelstudio-api-key <key>",
								"cliDescription": "Qwen Cloud Coding Plan API key (Global/Intl)",
								"onboardingScopes": ["text-inference"]
							}
						]
					},
					{
						"id": "qwen-token-plan",
						"name": "Qwen Token Plan",
						"docs": "/providers/qwen",
						"categories": ["cloud", "llm"],
						"envVars": ["QWEN_TOKEN_PLAN_API_KEY"],
						"authChoices": [{
							"method": "api-key",
							"choiceId": "qwen-token-plan",
							"choiceLabel": "Qwen Token Plan (Global/Intl)",
							"choiceHint": "Endpoint: token-plan.ap-southeast-1.maas.aliyuncs.com",
							"groupId": "qwen",
							"groupLabel": "Qwen Cloud",
							"groupHint": "Standard / Coding Plan / Token Plan",
							"optionKey": "qwenTokenPlanApiKey",
							"cliFlag": "--qwen-token-plan-api-key",
							"cliOption": "--qwen-token-plan-api-key <key>",
							"cliDescription": "Qwen Token Plan API key (Global/Intl)",
							"onboardingScopes": ["text-inference"]
						}, {
							"method": "api-key-cn",
							"choiceId": "qwen-token-plan-cn",
							"choiceLabel": "Qwen Token Plan (China)",
							"choiceHint": "Endpoint: token-plan.cn-beijing.maas.aliyuncs.com",
							"groupId": "qwen",
							"groupLabel": "Qwen Cloud",
							"groupHint": "Standard / Coding Plan / Token Plan",
							"optionKey": "qwenTokenPlanApiKeyCn",
							"cliFlag": "--qwen-token-plan-api-key-cn",
							"cliOption": "--qwen-token-plan-api-key-cn <key>",
							"cliDescription": "Qwen Token Plan API key (China)",
							"onboardingScopes": ["text-inference"]
						}]
					},
					{
						"id": "bailian-token-plan",
						"name": "Alibaba Token Plan (legacy custom config)",
						"docs": "/providers/qwen",
						"categories": ["cloud", "llm"]
					}
				],
				"contracts": {
					"mediaUnderstandingProviders": ["qwen"],
					"videoGenerationProviders": ["qwen"]
				},
				"install": {
					"clawhubSpec": "clawhub:@openclaw/qwen-provider",
					"npmSpec": "@openclaw/qwen-provider",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.6.8"
				}
			}
		},
		{
			"name": "@openclaw/radius-provider",
			"description": "OpenClaw Radius model gateway provider plugin.",
			"source": "official",
			"kind": "provider",
			"openclaw": {
				"plugin": {
					"id": "radius",
					"label": "Radius"
				},
				"providers": [{
					"id": "radius",
					"name": "Radius",
					"docs": "/providers/radius",
					"categories": ["cloud", "llm"],
					"envVars": ["RADIUS_API_KEY"],
					"authChoices": [{
						"method": "oauth",
						"choiceId": "radius",
						"choiceLabel": "Radius (browser sign-in)",
						"groupId": "radius",
						"groupLabel": "Radius",
						"groupHint": "Browser sign-in or API key",
						"onboardingScopes": ["text-inference"]
					}, {
						"method": "api-key",
						"choiceId": "radius-api-key",
						"choiceLabel": "Radius API key",
						"groupId": "radius",
						"groupLabel": "Radius",
						"groupHint": "Browser sign-in or API key",
						"optionKey": "radiusApiKey",
						"cliFlag": "--radius-api-key",
						"cliOption": "--radius-api-key <key>",
						"cliDescription": "Radius organization API key",
						"onboardingScopes": ["text-inference"]
					}]
				}],
				"install": {
					"clawhubSpec": "clawhub:@openclaw/radius-provider",
					"npmSpec": "@openclaw/radius-provider",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.9.4"
				}
			}
		},
		{
			"name": "@openclaw/fireworks-provider",
			"description": "OpenClaw Fireworks provider plugin",
			"source": "official",
			"kind": "provider",
			"openclaw": {
				"plugin": {
					"id": "fireworks",
					"label": "Fireworks"
				},
				"providers": [{
					"id": "fireworks",
					"aliases": ["fireworks-ai"],
					"name": "Fireworks",
					"docs": "/providers/fireworks",
					"categories": ["cloud", "llm"],
					"envVars": ["FIREWORKS_API_KEY"],
					"authChoices": [{
						"method": "api-key",
						"choiceId": "fireworks-api-key",
						"choiceLabel": "Fireworks API key",
						"choiceHint": "API key",
						"groupId": "fireworks",
						"groupLabel": "Fireworks",
						"groupHint": "API key",
						"optionKey": "fireworksApiKey",
						"cliFlag": "--fireworks-api-key",
						"cliOption": "--fireworks-api-key <key>",
						"cliDescription": "Fireworks API key",
						"onboardingScopes": ["text-inference"]
					}]
				}],
				"install": {
					"clawhubSpec": "clawhub:@openclaw/fireworks-provider",
					"npmSpec": "@openclaw/fireworks-provider",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.6.9"
				}
			}
		},
		{
			"name": "@openclaw/moonshot-provider",
			"description": "OpenClaw Moonshot provider plugin",
			"source": "official",
			"kind": "provider",
			"openclaw": {
				"plugin": {
					"id": "moonshot",
					"label": "Moonshot"
				},
				"providerEndpoints": [{
					"endpointClass": "moonshot-native",
					"baseUrls": ["https://api.moonshot.ai/v1", "https://api.moonshot.cn/v1"]
				}],
				"providers": [{
					"id": "moonshot",
					"aliases": ["moonshotai", "moonshot-ai"],
					"name": "Moonshot",
					"docs": "/providers/moonshot",
					"categories": ["cloud", "llm"],
					"envVars": ["MOONSHOT_API_KEY", "KIMI_API_KEY"],
					"authChoices": [{
						"method": "api-key",
						"choiceId": "moonshot-api-key",
						"choiceLabel": "Moonshot API key (.ai)",
						"choiceHint": "Kimi K2.6 + Kimi",
						"groupId": "moonshot",
						"groupLabel": "Moonshot AI (Kimi K2.6)",
						"groupHint": "Kimi K2.6",
						"optionKey": "moonshotApiKey",
						"cliFlag": "--moonshot-api-key",
						"cliOption": "--moonshot-api-key <key>",
						"cliDescription": "Moonshot API key",
						"onboardingScopes": ["text-inference"]
					}, {
						"method": "api-key-cn",
						"choiceId": "moonshot-api-key-cn",
						"choiceLabel": "Moonshot API key (.cn)",
						"choiceHint": "Kimi K2.6 + Kimi",
						"groupId": "moonshot",
						"groupLabel": "Moonshot AI (Kimi K2.6)",
						"groupHint": "Kimi K2.6",
						"optionKey": "moonshotApiKey",
						"cliFlag": "--moonshot-api-key",
						"cliOption": "--moonshot-api-key <key>",
						"cliDescription": "Moonshot API key",
						"onboardingScopes": ["text-inference"]
					}]
				}],
				"contracts": {
					"mediaUnderstandingProviders": ["moonshot"],
					"webSearchProviders": ["kimi"]
				},
				"webSearchProviders": [{
					"id": "kimi",
					"label": "Kimi (Moonshot)",
					"hint": "Requires Moonshot / Kimi API key · Moonshot web search",
					"onboardingScopes": ["text-inference"],
					"credentialLabel": "Moonshot / Kimi API key",
					"envVars": ["KIMI_API_KEY", "MOONSHOT_API_KEY"],
					"placeholder": "sk-...",
					"signupUrl": "https://platform.moonshot.cn/",
					"docsUrl": "https://docs.openclaw.ai/tools/web",
					"credentialPath": "plugins.entries.moonshot.config.webSearch.apiKey",
					"autoDetectOrder": 40
				}],
				"install": {
					"clawhubSpec": "clawhub:@openclaw/moonshot-provider",
					"npmSpec": "@openclaw/moonshot-provider",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.6.9"
				}
			}
		},
		{
			"name": "@openclaw/tencent-provider",
			"description": "OpenClaw Tencent Cloud provider plugin (TokenHub + Token Plan)",
			"source": "official",
			"kind": "provider",
			"openclaw": {
				"plugin": {
					"id": "tencent",
					"label": "Tencent Cloud"
				},
				"providers": [{
					"id": "tencent-tokenhub",
					"name": "Tencent TokenHub",
					"docs": "/providers/tencent",
					"categories": ["cloud", "llm"],
					"envVars": ["TOKENHUB_API_KEY"],
					"authChoices": [{
						"method": "api-key",
						"choiceId": "tokenhub-api-key",
						"choiceLabel": "Tencent TokenHub",
						"choiceHint": "Hy via Tencent TokenHub Gateway",
						"groupId": "tencent",
						"groupLabel": "Tencent Cloud",
						"groupHint": "Tencent TokenHub",
						"optionKey": "tokenhubApiKey",
						"cliFlag": "--tokenhub-api-key",
						"cliOption": "--tokenhub-api-key <key>",
						"cliDescription": "Tencent TokenHub API key",
						"onboardingScopes": ["text-inference"]
					}]
				}, {
					"id": "tencent-tokenplan",
					"name": "Tencent TokenPlan",
					"docs": "/providers/tencent",
					"categories": ["cloud", "llm"],
					"envVars": ["TOKENPLAN_API_KEY"],
					"authChoices": [{
						"method": "api-key",
						"choiceId": "tokenplan-api-key",
						"choiceLabel": "Tencent TokenPlan",
						"choiceHint": "Hy via Tencent TokenPlan Gateway",
						"groupId": "tencent",
						"groupLabel": "Tencent Cloud",
						"groupHint": "Tencent TokenPlan",
						"optionKey": "tokenplanApiKey",
						"cliFlag": "--tokenplan-api-key",
						"cliOption": "--tokenplan-api-key <key>",
						"cliDescription": "Tencent TokenPlan API key",
						"onboardingScopes": ["text-inference"]
					}]
				}],
				"install": {
					"clawhubSpec": "clawhub:@openclaw/tencent-provider",
					"npmSpec": "@openclaw/tencent-provider",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.6.9"
				}
			}
		},
		{
			"name": "@telnyx/openclaw-provider",
			"description": "Official Telnyx AI inference provider plugin for OpenClaw.",
			"source": "external",
			"kind": "provider",
			"openclaw": {
				"plugin": {
					"id": "telnyx",
					"label": "Telnyx"
				},
				"providers": [{
					"id": "telnyx",
					"name": "Telnyx",
					"docs": "/providers/telnyx",
					"categories": ["cloud", "llm"],
					"envVars": ["TELNYX_API_KEY"],
					"authChoices": [{
						"method": "api-key",
						"choiceId": "telnyx-api-key",
						"choiceLabel": "Telnyx API key",
						"choiceHint": "OpenAI-compatible Telnyx AI inference endpoint",
						"groupId": "telnyx",
						"groupLabel": "Telnyx",
						"groupHint": "OpenAI-compatible Telnyx AI inference endpoint",
						"optionKey": "telnyxApiKey",
						"cliFlag": "--telnyx-api-key",
						"cliOption": "--telnyx-api-key <key>",
						"cliDescription": "Telnyx API key",
						"onboardingScopes": ["text-inference"]
					}]
				}],
				"install": {
					"clawhubSpec": "clawhub:@telnyx/openclaw-provider@0.2.0",
					"npmSpec": "@telnyx/openclaw-provider@0.2.0",
					"defaultChoice": "npm",
					"expectedIntegrity": "sha512-htqOJfPx+TlLWE/nmpdJJVgrg8zDqRIX87smzY3CnKcdJPlx51Rc1kWzarvE+2hvhpm2lzD5sKkxRSIWKz2AaA==",
					"minHostVersion": ">=2026.8.1"
				}
			}
		},
		{
			"name": "@openclaw/venice-provider",
			"description": "OpenClaw Venice provider plugin",
			"source": "official",
			"kind": "provider",
			"openclaw": {
				"plugin": {
					"id": "venice",
					"label": "Venice"
				},
				"providers": [{
					"id": "venice",
					"name": "Venice",
					"docs": "/providers/venice",
					"categories": ["cloud", "llm"],
					"envVars": ["VENICE_API_KEY"],
					"authChoices": [{
						"method": "api-key",
						"choiceId": "venice-api-key",
						"choiceLabel": "Venice AI API key",
						"choiceHint": "Privacy-focused (uncensored models)",
						"groupId": "venice",
						"groupLabel": "Venice AI",
						"groupHint": "Privacy-focused (uncensored models)",
						"optionKey": "veniceApiKey",
						"cliFlag": "--venice-api-key",
						"cliOption": "--venice-api-key <key>",
						"cliDescription": "Venice API key",
						"onboardingScopes": ["text-inference"]
					}]
				}],
				"install": {
					"clawhubSpec": "clawhub:@openclaw/venice-provider",
					"npmSpec": "@openclaw/venice-provider",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.6.9"
				}
			}
		},
		{
			"name": "@openclaw/vercel-ai-gateway-provider",
			"description": "OpenClaw Vercel AI Gateway provider plugin",
			"source": "official",
			"kind": "provider",
			"openclaw": {
				"plugin": {
					"id": "vercel-ai-gateway",
					"label": "Vercel AI Gateway"
				},
				"providerEndpoints": [{
					"endpointClass": "vercel-ai-gateway",
					"hosts": ["ai-gateway.vercel.sh"]
				}],
				"providers": [{
					"id": "vercel-ai-gateway",
					"name": "Vercel AI Gateway",
					"docs": "/providers/vercel-ai-gateway",
					"categories": ["cloud", "llm"],
					"envVars": ["AI_GATEWAY_API_KEY"],
					"authChoices": [{
						"method": "api-key",
						"choiceId": "ai-gateway-api-key",
						"choiceLabel": "Vercel AI Gateway API key",
						"choiceHint": "API key",
						"groupId": "ai-gateway",
						"groupLabel": "Vercel AI Gateway",
						"groupHint": "API key",
						"optionKey": "aiGatewayApiKey",
						"cliFlag": "--ai-gateway-api-key",
						"cliOption": "--ai-gateway-api-key <key>",
						"cliDescription": "Vercel AI Gateway API key",
						"onboardingScopes": ["text-inference"]
					}]
				}],
				"install": {
					"clawhubSpec": "clawhub:@openclaw/vercel-ai-gateway-provider",
					"npmSpec": "@openclaw/vercel-ai-gateway-provider",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.6.9"
				}
			}
		},
		{
			"name": "@openclaw/vydra-provider",
			"description": "OpenClaw Vydra media provider plugin.",
			"source": "official",
			"kind": "provider",
			"openclaw": {
				"plugin": {
					"id": "vydra",
					"label": "Vydra"
				},
				"providers": [{
					"id": "vydra",
					"name": "Vydra",
					"docs": "/providers/vydra",
					"categories": ["cloud", "media"],
					"envVars": ["VYDRA_API_KEY"],
					"authChoices": [{
						"method": "api-key",
						"choiceId": "vydra-api-key",
						"choiceLabel": "Vydra API key",
						"groupId": "vydra",
						"groupLabel": "Vydra",
						"groupHint": "Image, video, and speech",
						"optionKey": "vydraApiKey",
						"cliFlag": "--vydra-api-key",
						"cliOption": "--vydra-api-key <key>",
						"cliDescription": "Vydra API key",
						"onboardingScopes": ["image-generation"]
					}]
				}],
				"contracts": {
					"speechProviders": ["vydra"],
					"imageGenerationProviders": ["vydra"],
					"videoGenerationProviders": ["vydra"]
				},
				"install": {
					"clawhubSpec": "clawhub:@openclaw/vydra-provider",
					"npmSpec": "@openclaw/vydra-provider",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.7.2"
				}
			}
		},
		{
			"name": "@openclaw/xiaomi-provider",
			"description": "OpenClaw Xiaomi provider plugin.",
			"source": "official",
			"kind": "provider",
			"openclaw": {
				"plugin": {
					"id": "xiaomi",
					"label": "Xiaomi"
				},
				"providerEndpoints": [{
					"endpointClass": "xiaomi-native",
					"hosts": [
						"api.xiaomimimo.com",
						"token-plan-ams.xiaomimimo.com",
						"token-plan-cn.xiaomimimo.com",
						"token-plan-sgp.xiaomimimo.com"
					]
				}],
				"providers": [{
					"id": "xiaomi",
					"name": "Xiaomi",
					"docs": "/providers/xiaomi",
					"categories": ["cloud", "llm"],
					"envVars": ["XIAOMI_API_KEY"],
					"authChoices": [{
						"method": "api-key",
						"choiceId": "xiaomi-api-key",
						"choiceLabel": "Xiaomi API key (Pay-as-you-go)",
						"choiceHint": "Endpoint: api.xiaomimimo.com/v1",
						"groupId": "xiaomi",
						"groupLabel": "Xiaomi",
						"groupHint": "Pay-as-you-go / Token Plan",
						"optionKey": "xiaomiApiKey",
						"cliFlag": "--xiaomi-api-key",
						"cliOption": "--xiaomi-api-key <key>",
						"cliDescription": "Xiaomi MiMo pay-as-you-go API key",
						"onboardingScopes": ["text-inference"]
					}]
				}, {
					"id": "xiaomi-token-plan",
					"name": "Xiaomi Token Plan",
					"docs": "/providers/xiaomi",
					"categories": ["cloud", "llm"],
					"envVars": ["XIAOMI_TOKEN_PLAN_API_KEY"],
					"authChoices": [
						{
							"method": "token-plan-ams",
							"choiceId": "xiaomi-token-plan-ams",
							"choiceLabel": "Xiaomi Token Plan (Europe)",
							"choiceHint": "Endpoint preset: token-plan-ams.xiaomimimo.com/v1",
							"groupId": "xiaomi",
							"groupLabel": "Xiaomi",
							"groupHint": "Pay-as-you-go / Token Plan",
							"optionKey": "xiaomiTokenPlanApiKey",
							"cliFlag": "--xiaomi-token-plan-api-key",
							"cliOption": "--xiaomi-token-plan-api-key <key>",
							"cliDescription": "Xiaomi MiMo Token Plan API key",
							"onboardingScopes": ["text-inference"]
						},
						{
							"method": "token-plan-cn",
							"choiceId": "xiaomi-token-plan-cn",
							"choiceLabel": "Xiaomi Token Plan (China)",
							"choiceHint": "Endpoint preset: token-plan-cn.xiaomimimo.com/v1",
							"groupId": "xiaomi",
							"groupLabel": "Xiaomi",
							"groupHint": "Pay-as-you-go / Token Plan",
							"optionKey": "xiaomiTokenPlanApiKey",
							"cliFlag": "--xiaomi-token-plan-api-key",
							"cliOption": "--xiaomi-token-plan-api-key <key>",
							"cliDescription": "Xiaomi MiMo Token Plan API key",
							"onboardingScopes": ["text-inference"]
						},
						{
							"method": "token-plan-sgp",
							"choiceId": "xiaomi-token-plan-sgp",
							"choiceLabel": "Xiaomi Token Plan (Singapore)",
							"choiceHint": "Endpoint preset: token-plan-sgp.xiaomimimo.com/v1",
							"groupId": "xiaomi",
							"groupLabel": "Xiaomi",
							"groupHint": "Pay-as-you-go / Token Plan",
							"optionKey": "xiaomiTokenPlanApiKey",
							"cliFlag": "--xiaomi-token-plan-api-key",
							"cliOption": "--xiaomi-token-plan-api-key <key>",
							"cliDescription": "Xiaomi MiMo Token Plan API key",
							"onboardingScopes": ["text-inference"]
						}
					]
				}],
				"contracts": {
					"speechProviders": ["xiaomi"],
					"usageProviders": ["xiaomi", "xiaomi-token-plan"]
				},
				"install": {
					"clawhubSpec": "clawhub:@openclaw/xiaomi-provider",
					"npmSpec": "@openclaw/xiaomi-provider",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.7.2"
				}
			}
		},
		{
			"name": "@openclaw/zai-provider",
			"description": "OpenClaw Z.AI provider plugin",
			"source": "official",
			"kind": "provider",
			"openclaw": {
				"plugin": {
					"id": "zai",
					"label": "Z.AI"
				},
				"providerEndpoints": [{
					"endpointClass": "zai-native",
					"hosts": ["api.z.ai", "open.bigmodel.cn"]
				}],
				"providers": [{
					"id": "zai",
					"aliases": ["z-ai", "z.ai"],
					"name": "Z.AI",
					"docs": "/providers/zai",
					"categories": ["cloud", "llm"],
					"envVars": ["ZAI_API_KEY", "Z_AI_API_KEY"],
					"authChoices": [
						{
							"method": "api-key",
							"choiceId": "zai-api-key",
							"choiceLabel": "Z.AI API key",
							"groupId": "zai",
							"groupLabel": "Z.AI",
							"groupHint": "GLM Coding Plan / Global / CN",
							"optionKey": "zaiApiKey",
							"cliFlag": "--zai-api-key",
							"cliOption": "--zai-api-key <key>",
							"cliDescription": "Z.AI API key",
							"onboardingScopes": ["text-inference"]
						},
						{
							"method": "coding-global",
							"choiceId": "zai-coding-global",
							"choiceLabel": "Coding-Plan-Global",
							"choiceHint": "GLM Coding Plan Global (api.z.ai)",
							"groupId": "zai",
							"groupLabel": "Z.AI",
							"groupHint": "GLM Coding Plan / Global / CN",
							"optionKey": "zaiApiKey",
							"cliFlag": "--zai-api-key",
							"cliOption": "--zai-api-key <key>",
							"cliDescription": "Z.AI API key",
							"onboardingScopes": ["text-inference"]
						},
						{
							"method": "coding-cn",
							"choiceId": "zai-coding-cn",
							"choiceLabel": "Coding-Plan-CN",
							"choiceHint": "GLM Coding Plan CN (open.bigmodel.cn)",
							"groupId": "zai",
							"groupLabel": "Z.AI",
							"groupHint": "GLM Coding Plan / Global / CN",
							"optionKey": "zaiApiKey",
							"cliFlag": "--zai-api-key",
							"cliOption": "--zai-api-key <key>",
							"cliDescription": "Z.AI API key",
							"onboardingScopes": ["text-inference"]
						},
						{
							"method": "global",
							"choiceId": "zai-global",
							"choiceLabel": "Global",
							"choiceHint": "Z.AI Global (api.z.ai)",
							"groupId": "zai",
							"groupLabel": "Z.AI",
							"groupHint": "GLM Coding Plan / Global / CN",
							"optionKey": "zaiApiKey",
							"cliFlag": "--zai-api-key",
							"cliOption": "--zai-api-key <key>",
							"cliDescription": "Z.AI API key",
							"onboardingScopes": ["text-inference"]
						},
						{
							"method": "cn",
							"choiceId": "zai-cn",
							"choiceLabel": "CN",
							"choiceHint": "Z.AI CN (open.bigmodel.cn)",
							"groupId": "zai",
							"groupLabel": "Z.AI",
							"groupHint": "GLM Coding Plan / Global / CN",
							"optionKey": "zaiApiKey",
							"cliFlag": "--zai-api-key",
							"cliOption": "--zai-api-key <key>",
							"cliDescription": "Z.AI API key",
							"onboardingScopes": ["text-inference"]
						}
					]
				}],
				"contracts": { "mediaUnderstandingProviders": ["zai"] },
				"install": {
					"clawhubSpec": "clawhub:@openclaw/zai-provider",
					"npmSpec": "@openclaw/zai-provider",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.6.9"
				}
			}
		},
		{
			"name": "@openclaw/synthetic-provider",
			"description": "OpenClaw Synthetic provider plugin.",
			"source": "official",
			"kind": "provider",
			"openclaw": {
				"plugin": {
					"id": "synthetic",
					"label": "Synthetic"
				},
				"providers": [{
					"id": "synthetic",
					"name": "Synthetic",
					"docs": "/providers/synthetic",
					"categories": ["cloud", "llm"],
					"envVars": ["SYNTHETIC_API_KEY"],
					"authChoices": [{
						"method": "api-key",
						"choiceId": "synthetic-api-key",
						"choiceLabel": "Synthetic API key",
						"groupId": "synthetic",
						"groupLabel": "Synthetic",
						"groupHint": "Anthropic-compatible (multi-model)",
						"optionKey": "syntheticApiKey",
						"cliFlag": "--synthetic-api-key",
						"cliOption": "--synthetic-api-key <key>",
						"cliDescription": "Synthetic API key",
						"onboardingScopes": ["text-inference"]
					}]
				}],
				"install": {
					"clawhubSpec": "clawhub:@openclaw/synthetic-provider",
					"npmSpec": "@openclaw/synthetic-provider",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.7.2"
				}
			}
		},
		{
			"name": "@openclaw/voyage-provider",
			"description": "OpenClaw Voyage embedding provider plugin.",
			"source": "official",
			"kind": "provider",
			"openclaw": {
				"plugin": {
					"id": "voyage",
					"label": "Voyage"
				},
				"providers": [{
					"id": "voyage",
					"name": "Voyage",
					"docs": "/reference/memory-config",
					"categories": ["cloud"],
					"envVars": ["VOYAGE_API_KEY"]
				}],
				"contracts": { "embeddingProviders": ["voyage"] },
				"install": {
					"clawhubSpec": "clawhub:@openclaw/voyage-provider",
					"npmSpec": "@openclaw/voyage-provider",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.7.2"
				}
			}
		},
		{
			"name": "@openclaw/volcengine-provider",
			"description": "OpenClaw Volcengine provider plugin.",
			"source": "official",
			"kind": "provider",
			"openclaw": {
				"plugin": {
					"id": "volcengine",
					"label": "Volcengine"
				},
				"providers": [{
					"id": "volcengine",
					"aliases": ["volcengine-plan"],
					"name": "Volcengine",
					"docs": "/providers/volcengine",
					"categories": ["cloud", "llm"],
					"envVars": ["VOLCANO_ENGINE_API_KEY"],
					"authChoices": [{
						"method": "api-key",
						"choiceId": "volcengine-api-key",
						"choiceLabel": "Volcano Engine API key",
						"groupId": "volcengine",
						"groupLabel": "Volcano Engine",
						"groupHint": "API key",
						"optionKey": "volcengineApiKey",
						"cliFlag": "--volcengine-api-key",
						"cliOption": "--volcengine-api-key <key>",
						"cliDescription": "Volcano Engine API key",
						"onboardingScopes": ["text-inference"]
					}]
				}, {
					"id": "volcengine-plan",
					"name": "Volcengine Plan",
					"docs": "/providers/volcengine",
					"categories": ["cloud", "llm"],
					"envVars": ["VOLCANO_ENGINE_API_KEY"]
				}],
				"contracts": { "speechProviders": ["volcengine"] },
				"install": {
					"clawhubSpec": "clawhub:@openclaw/volcengine-provider",
					"npmSpec": "@openclaw/volcengine-provider",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.7.2"
				}
			}
		},
		{
			"name": "@openclaw/stepfun-provider",
			"description": "OpenClaw StepFun provider plugin.",
			"source": "official",
			"kind": "provider",
			"openclaw": {
				"plugin": {
					"id": "stepfun",
					"label": "StepFun"
				},
				"providers": [{
					"id": "stepfun",
					"name": "StepFun",
					"docs": "/providers/stepfun",
					"categories": ["cloud", "llm"],
					"envVars": ["STEPFUN_API_KEY"],
					"authChoices": [{
						"method": "standard-api-key-cn",
						"choiceId": "stepfun-standard-api-key-cn",
						"choiceLabel": "StepFun Standard API key (China)",
						"choiceHint": "Endpoint: api.stepfun.com/v1",
						"groupId": "stepfun",
						"groupLabel": "StepFun",
						"groupHint": "Standard / Step Plan (China / Global)",
						"optionKey": "stepfunApiKey",
						"cliFlag": "--stepfun-api-key",
						"cliOption": "--stepfun-api-key <key>",
						"cliDescription": "StepFun API key",
						"onboardingScopes": ["text-inference"]
					}, {
						"method": "standard-api-key-intl",
						"choiceId": "stepfun-standard-api-key-intl",
						"choiceLabel": "StepFun Standard API key (Global/Intl)",
						"choiceHint": "Endpoint: api.stepfun.ai/v1",
						"groupId": "stepfun",
						"groupLabel": "StepFun",
						"groupHint": "Standard / Step Plan (China / Global)",
						"optionKey": "stepfunApiKey",
						"cliFlag": "--stepfun-api-key",
						"cliOption": "--stepfun-api-key <key>",
						"cliDescription": "StepFun API key",
						"onboardingScopes": ["text-inference"]
					}]
				}, {
					"id": "stepfun-plan",
					"name": "StepFun stepfun plan",
					"docs": "/providers/stepfun",
					"categories": ["cloud", "llm"],
					"envVars": ["STEPFUN_API_KEY"],
					"authChoices": [{
						"method": "plan-api-key-cn",
						"choiceId": "stepfun-plan-api-key-cn",
						"choiceLabel": "StepFun Step Plan API key (China)",
						"choiceHint": "Endpoint: api.stepfun.com/step_plan/v1",
						"groupId": "stepfun",
						"groupLabel": "StepFun",
						"groupHint": "Standard / Step Plan (China / Global)",
						"optionKey": "stepfunApiKey",
						"cliFlag": "--stepfun-api-key",
						"cliOption": "--stepfun-api-key <key>",
						"cliDescription": "StepFun API key",
						"onboardingScopes": ["text-inference"]
					}, {
						"method": "plan-api-key-intl",
						"choiceId": "stepfun-plan-api-key-intl",
						"choiceLabel": "StepFun Step Plan API key (Global/Intl)",
						"choiceHint": "Endpoint: api.stepfun.ai/step_plan/v1",
						"groupId": "stepfun",
						"groupLabel": "StepFun",
						"groupHint": "Standard / Step Plan (China / Global)",
						"optionKey": "stepfunApiKey",
						"cliFlag": "--stepfun-api-key",
						"cliOption": "--stepfun-api-key <key>",
						"cliDescription": "StepFun API key",
						"onboardingScopes": ["text-inference"]
					}]
				}],
				"install": {
					"clawhubSpec": "clawhub:@openclaw/stepfun-provider",
					"npmSpec": "@openclaw/stepfun-provider",
					"defaultChoice": "npm",
					"minHostVersion": ">=2026.6.9"
				}
			}
		}
	] },
	{
		schemaVersion: 1,
		id: "openclaw-official-external-plugins",
		generatedAt: "2026-06-22T00:00:00.000Z",
		sequence: 1,
		description: "Bundled fallback feed for official external OpenClaw plugins.",
		entries: [
			{
				"name": "@openclaw/acpx",
				"description": "OpenClaw ACP runtime backend",
				"source": "official",
				"kind": "plugin",
				"openclaw": {
					"plugin": {
						"id": "acpx",
						"label": "ACPX Runtime"
					},
					"install": {
						"clawhubSpec": "clawhub:@openclaw/acpx",
						"npmSpec": "@openclaw/acpx",
						"defaultChoice": "npm",
						"minHostVersion": ">=2026.4.25"
					}
				}
			},
			{
				"name": "@openclaw/brave-plugin",
				"description": "OpenClaw Brave plugin",
				"source": "official",
				"kind": "plugin",
				"openclaw": {
					"plugin": {
						"id": "brave",
						"label": "Brave"
					},
					"webSearchProviders": [{
						"id": "brave",
						"label": "Brave Search",
						"hint": "Brave Search web results.",
						"onboardingScopes": ["text-inference"],
						"credentialLabel": "Brave Search API key",
						"envVars": ["BRAVE_API_KEY"],
						"placeholder": "BSA...",
						"signupUrl": "https://api-dashboard.search.brave.com/app/keys",
						"docsUrl": "https://docs.openclaw.ai/tools/brave-search",
						"credentialPath": "plugins.entries.brave.config.webSearch.apiKey",
						"autoDetectOrder": 10
					}],
					"install": {
						"clawhubSpec": "clawhub:@openclaw/brave-plugin",
						"npmSpec": "@openclaw/brave-plugin",
						"defaultChoice": "npm",
						"minHostVersion": ">=2026.4.10",
						"allowInvalidConfigRecovery": true
					}
				}
			},
			{
				"name": "@openclaw/codex",
				"description": "OpenClaw Codex app-server harness and native session catalog",
				"source": "official",
				"kind": "plugin",
				"openclaw": {
					"plugin": {
						"id": "codex",
						"label": "Codex"
					},
					"contracts": { "migrationProviders": ["codex"] },
					"install": {
						"clawhubSpec": "clawhub:@openclaw/codex",
						"npmSpec": "@openclaw/codex",
						"defaultChoice": "npm",
						"minHostVersion": ">=2026.5.1-beta.1"
					},
					"setup": { "nativeSessionCatalog": {
						"label": "Codex",
						"legacyDefaultEnabled": true,
						"description": "Existing native Codex conversations on this Gateway and eligible paired nodes.",
						"nodeCommands": [
							"codex.appServer.threads.list.v1",
							"codex.appServer.thread.turns.list.v1",
							"codex.sessionCatalog.transcript.read.v1",
							"codex.terminal.resume.v1",
							"codex.cli.sessions.list"
						]
					} }
				}
			},
			{
				"name": "@openclaw/copilot",
				"description": "OpenClaw GitHub Copilot agent runtime plugin",
				"source": "official",
				"kind": "plugin",
				"openclaw": {
					"plugin": {
						"id": "copilot",
						"label": "GitHub Copilot agent runtime"
					},
					"install": {
						"clawhubSpec": "clawhub:@openclaw/copilot",
						"npmSpec": "@openclaw/copilot",
						"defaultChoice": "npm",
						"minHostVersion": ">=2026.5.28"
					}
				}
			},
			{
				"name": "@openclaw/cua-computer",
				"description": "Experimental CUA Driver computer control for macOS, Windows, and Linux node hosts",
				"source": "official",
				"kind": "plugin",
				"openclaw": {
					"plugin": {
						"id": "cua-computer",
						"label": "CUA Computer"
					},
					"install": {
						"clawhubSpec": "clawhub:@openclaw/cua-computer",
						"npmSpec": "@openclaw/cua-computer",
						"defaultChoice": "clawhub",
						"minHostVersion": ">=2026.9.6"
					}
				}
			},
			{
				"name": "@openclaw/diagnostics-otel",
				"description": "OpenClaw diagnostics OpenTelemetry exporter",
				"source": "official",
				"kind": "plugin",
				"openclaw": {
					"plugin": {
						"id": "diagnostics-otel",
						"label": "Diagnostics OpenTelemetry"
					},
					"install": {
						"clawhubSpec": "clawhub:@openclaw/diagnostics-otel",
						"npmSpec": "@openclaw/diagnostics-otel",
						"defaultChoice": "npm",
						"minHostVersion": ">=2026.4.25"
					}
				}
			},
			{
				"name": "@openclaw/diagnostics-prometheus",
				"description": "OpenClaw diagnostics Prometheus exporter",
				"source": "official",
				"kind": "plugin",
				"openclaw": {
					"plugin": {
						"id": "diagnostics-prometheus",
						"label": "Diagnostics Prometheus"
					},
					"install": {
						"clawhubSpec": "clawhub:@openclaw/diagnostics-prometheus",
						"npmSpec": "@openclaw/diagnostics-prometheus",
						"defaultChoice": "npm",
						"minHostVersion": ">=2026.4.25"
					}
				}
			},
			{
				"name": "@openclaw/diffs",
				"description": "OpenClaw diff viewer plugin",
				"source": "official",
				"kind": "plugin",
				"openclaw": {
					"plugin": {
						"id": "diffs",
						"label": "Diffs"
					},
					"catalog": {
						"featured": true,
						"order": 40
					},
					"install": {
						"clawhubSpec": "clawhub:@openclaw/diffs",
						"npmSpec": "@openclaw/diffs",
						"defaultChoice": "npm",
						"minHostVersion": ">=2026.4.30"
					}
				}
			},
			{
				"name": "@openclaw/diffs-language-pack",
				"description": "OpenClaw diffs viewer syntax highlighting language pack",
				"source": "official",
				"kind": "plugin",
				"openclaw": {
					"plugin": {
						"id": "diffs-language-pack",
						"label": "Diff Viewer Language Pack"
					},
					"install": {
						"npmSpec": "@openclaw/diffs-language-pack",
						"clawhubSpec": "clawhub:@openclaw/diffs-language-pack",
						"defaultChoice": "npm",
						"minHostVersion": ">=2026.5.27"
					}
				}
			},
			{
				"name": "@openclaw/duckduckgo-plugin",
				"description": "OpenClaw DuckDuckGo plugin.",
				"source": "official",
				"kind": "plugin",
				"openclaw": {
					"plugin": {
						"id": "duckduckgo",
						"label": "DuckDuckGo"
					},
					"contracts": { "webSearchProviders": ["duckduckgo"] },
					"webSearchProviders": [{
						"id": "duckduckgo",
						"label": "DuckDuckGo Search (experimental)",
						"hint": "Free web search fallback with no API key required",
						"onboardingScopes": ["text-inference"],
						"requiresCredential": false,
						"envVars": [],
						"placeholder": "(no key needed)",
						"signupUrl": "https://duckduckgo.com/",
						"docsUrl": "https://docs.openclaw.ai/tools/duckduckgo-search",
						"credentialPath": "",
						"autoDetectOrder": 100
					}],
					"install": {
						"clawhubSpec": "clawhub:@openclaw/duckduckgo-plugin",
						"npmSpec": "@openclaw/duckduckgo-plugin",
						"defaultChoice": "npm",
						"minHostVersion": ">=2026.7.2"
					}
				}
			},
			{
				"name": "@openclaw/exa-plugin",
				"description": "OpenClaw Exa plugin.",
				"source": "official",
				"kind": "plugin",
				"openclaw": {
					"plugin": {
						"id": "exa",
						"label": "Exa"
					},
					"contracts": { "webSearchProviders": ["exa"] },
					"webSearchProviders": [{
						"id": "exa",
						"label": "Exa Search",
						"hint": "Neural + keyword search with date filters and content extraction",
						"onboardingScopes": ["text-inference"],
						"credentialLabel": "Exa API key",
						"envVars": ["EXA_API_KEY"],
						"placeholder": "exa-...",
						"signupUrl": "https://exa.ai/",
						"docsUrl": "https://docs.openclaw.ai/tools/web",
						"credentialPath": "plugins.entries.exa.config.webSearch.apiKey",
						"autoDetectOrder": 65
					}],
					"install": {
						"clawhubSpec": "clawhub:@openclaw/exa-plugin",
						"npmSpec": "@openclaw/exa-plugin",
						"defaultChoice": "npm",
						"minHostVersion": ">=2026.6.8"
					}
				}
			},
			{
				"name": "@openclaw/facetime",
				"description": "Experimental OpenClaw FaceTime realtime voice carrier",
				"source": "official",
				"kind": "plugin",
				"openclaw": {
					"plugin": {
						"id": "facetime",
						"label": "FaceTime"
					},
					"contracts": { "tools": ["facetime_call"] },
					"install": {
						"clawhubSpec": "clawhub:@openclaw/facetime",
						"npmSpec": "@openclaw/facetime",
						"defaultChoice": "npm",
						"minHostVersion": ">=2026.9.4",
						"allowInvalidConfigRecovery": true
					}
				}
			},
			{
				"name": "@openclaw/firecrawl-plugin",
				"description": "OpenClaw Firecrawl plugin.",
				"source": "official",
				"kind": "plugin",
				"openclaw": {
					"plugin": {
						"id": "firecrawl",
						"label": "Firecrawl"
					},
					"contracts": {
						"webFetchProviders": ["firecrawl"],
						"webSearchProviders": ["firecrawl", "firecrawl-free"],
						"tools": ["firecrawl_search", "firecrawl_scrape"]
					},
					"webSearchProviders": [{
						"id": "firecrawl",
						"label": "Firecrawl Search",
						"hint": "Structured results with optional result scraping",
						"onboardingScopes": ["text-inference"],
						"credentialLabel": "Firecrawl API key",
						"envVars": ["FIRECRAWL_API_KEY"],
						"placeholder": "fc-...",
						"signupUrl": "https://www.firecrawl.dev/",
						"docsUrl": "https://docs.openclaw.ai/tools/firecrawl",
						"credentialPath": "plugins.entries.firecrawl.config.webSearch.apiKey",
						"autoDetectOrder": 60
					}, {
						"id": "firecrawl-free",
						"label": "Firecrawl Search (Free)",
						"hint": "Free web search via Firecrawl's hosted starter tier — no API key required",
						"onboardingScopes": ["text-inference"],
						"requiresCredential": false,
						"envVars": [],
						"placeholder": "(no key needed)",
						"signupUrl": "https://www.firecrawl.dev/",
						"docsUrl": "https://docs.openclaw.ai/tools/firecrawl",
						"credentialPath": ""
					}],
					"install": {
						"clawhubSpec": "clawhub:@openclaw/firecrawl-plugin",
						"npmSpec": "@openclaw/firecrawl-plugin",
						"defaultChoice": "npm",
						"minHostVersion": ">=2026.6.8"
					}
				}
			},
			{
				"name": "@openclaw/fish-audio-speech",
				"description": "OpenClaw Fish Audio speech plugin.",
				"source": "official",
				"kind": "plugin",
				"openclaw": {
					"legacyPluginIds": ["fish-audio"],
					"plugin": {
						"id": "fish-audio-speech",
						"label": "Fish Audio Speech"
					},
					"install": {
						"clawhubSpec": "clawhub:@openclaw/fish-audio-speech",
						"npmSpec": "@openclaw/fish-audio-speech",
						"defaultChoice": "npm",
						"minHostVersion": ">=2026.7.2"
					}
				}
			},
			{
				"name": "@openclaw/google-meet",
				"description": "OpenClaw Google Meet participant plugin",
				"source": "official",
				"kind": "plugin",
				"openclaw": {
					"plugin": {
						"id": "google-meet",
						"label": "Google Meet"
					},
					"install": {
						"clawhubSpec": "clawhub:@openclaw/google-meet",
						"npmSpec": "@openclaw/google-meet",
						"defaultChoice": "npm",
						"minHostVersion": ">=2026.4.20"
					}
				}
			},
			{
				"name": "@openclaw/gradium-speech",
				"description": "OpenClaw Gradium speech plugin.",
				"source": "official",
				"kind": "plugin",
				"openclaw": {
					"plugin": {
						"id": "gradium",
						"label": "Gradium"
					},
					"contracts": { "speechProviders": ["gradium"] },
					"install": {
						"clawhubSpec": "clawhub:@openclaw/gradium-speech",
						"npmSpec": "@openclaw/gradium-speech",
						"defaultChoice": "npm",
						"minHostVersion": ">=2026.6.8"
					}
				}
			},
			{
				"name": "@openclaw/inworld-speech",
				"description": "OpenClaw Inworld speech plugin.",
				"source": "official",
				"kind": "plugin",
				"openclaw": {
					"plugin": {
						"id": "inworld",
						"label": "Inworld"
					},
					"contracts": { "speechProviders": ["inworld"] },
					"install": {
						"clawhubSpec": "clawhub:@openclaw/inworld-speech",
						"npmSpec": "@openclaw/inworld-speech",
						"defaultChoice": "npm",
						"minHostVersion": ">=2026.6.8"
					}
				}
			},
			{
				"name": "@openclaw/lobster",
				"description": "Lobster workflow tool plugin (typed pipelines + resumable approvals)",
				"source": "official",
				"kind": "plugin",
				"openclaw": {
					"plugin": {
						"id": "lobster",
						"label": "Lobster"
					},
					"catalog": {
						"featured": true,
						"order": 50
					},
					"install": {
						"clawhubSpec": "clawhub:@openclaw/lobster",
						"npmSpec": "@openclaw/lobster",
						"defaultChoice": "npm",
						"minHostVersion": ">=2026.4.25"
					}
				}
			},
			{
				"name": "@openclaw/memory-lancedb",
				"description": "OpenClaw LanceDB-backed long-term memory plugin with auto-recall/capture",
				"source": "official",
				"kind": "plugin",
				"openclaw": {
					"plugin": {
						"id": "memory-lancedb",
						"label": "Memory LanceDB"
					},
					"catalog": {
						"featured": true,
						"order": 70
					},
					"install": {
						"clawhubSpec": "clawhub:@openclaw/memory-lancedb",
						"npmSpec": "@openclaw/memory-lancedb",
						"defaultChoice": "npm",
						"minHostVersion": ">=2026.5.31"
					}
				}
			},
			{
				"name": "@openclaw/llama-cpp-provider",
				"description": "OpenClaw managed llama.cpp server provider plugin",
				"source": "official",
				"kind": "plugin",
				"openclaw": {
					"plugin": {
						"id": "llama-cpp",
						"label": "llama.cpp Provider"
					},
					"contracts": { "embeddingProviders": ["local"] },
					"install": {
						"clawhubSpec": "clawhub:@openclaw/llama-cpp-provider",
						"npmSpec": "@openclaw/llama-cpp-provider",
						"defaultChoice": "npm",
						"minHostVersion": ">=2026.6.2"
					}
				}
			},
			{
				"name": "@openclaw/mxc-sandbox",
				"description": "OpenClaw MXC sandbox execution plugin for MXC-capable hosts",
				"source": "official",
				"kind": "plugin",
				"openclaw": {
					"plugin": {
						"id": "mxc",
						"label": "MXC Sandbox Execution"
					},
					"install": {
						"clawhubSpec": "clawhub:@openclaw/mxc-sandbox",
						"npmSpec": "@openclaw/mxc-sandbox",
						"defaultChoice": "npm",
						"minHostVersion": ">=2026.6.11"
					}
				}
			},
			{
				"name": "@openclaw/onnx",
				"description": "Local ONNX decision models for OpenClaw",
				"source": "official",
				"kind": "plugin",
				"openclaw": {
					"plugin": {
						"id": "onnx",
						"label": "ONNX"
					},
					"install": {
						"clawhubSpec": "clawhub:@openclaw/onnx",
						"npmSpec": "@openclaw/onnx",
						"defaultChoice": "npm",
						"minHostVersion": ">=2026.9.6"
					}
				}
			},
			{
				"name": "@openclaw/openshell-sandbox",
				"description": "OpenClaw OpenShell sandbox backend",
				"source": "official",
				"kind": "plugin",
				"openclaw": {
					"plugin": {
						"id": "openshell",
						"label": "OpenShell Sandbox"
					},
					"install": {
						"clawhubSpec": "clawhub:@openclaw/openshell-sandbox",
						"npmSpec": "@openclaw/openshell-sandbox",
						"defaultChoice": "npm",
						"minHostVersion": ">=2026.5.12-beta.1"
					}
				}
			},
			{
				"name": "@openclaw/parallel-plugin",
				"description": "OpenClaw Parallel web search plugin.",
				"source": "official",
				"kind": "plugin",
				"openclaw": {
					"plugin": {
						"id": "parallel",
						"label": "Parallel"
					},
					"contracts": { "webSearchProviders": ["parallel", "parallel-free"] },
					"webSearchProviders": [{
						"id": "parallel",
						"label": "Parallel Search",
						"hint": "LLM-optimized dense excerpts from web sources",
						"onboardingScopes": ["text-inference"],
						"credentialLabel": "Parallel API key",
						"envVars": ["PARALLEL_API_KEY"],
						"placeholder": "par-...",
						"signupUrl": "https://platform.parallel.ai",
						"docsUrl": "https://docs.openclaw.ai/tools/parallel-search",
						"credentialPath": "plugins.entries.parallel.config.webSearch.apiKey",
						"autoDetectOrder": 75
					}, {
						"id": "parallel-free",
						"label": "Parallel Search (Free)",
						"hint": "Free web search via Parallel's hosted Search MCP — no API key required",
						"onboardingScopes": ["text-inference"],
						"requiresCredential": false,
						"envVars": [],
						"placeholder": "(no key needed)",
						"signupUrl": "https://parallel.ai",
						"docsUrl": "https://docs.openclaw.ai/tools/parallel-search",
						"credentialPath": ""
					}],
					"install": {
						"clawhubSpec": "clawhub:@openclaw/parallel-plugin",
						"npmSpec": "@openclaw/parallel-plugin",
						"defaultChoice": "npm",
						"minHostVersion": ">=2026.6.8"
					}
				}
			},
			{
				"name": "@openclaw/perplexity-plugin",
				"description": "OpenClaw Perplexity plugin.",
				"source": "official",
				"kind": "plugin",
				"openclaw": {
					"plugin": {
						"id": "perplexity",
						"label": "Perplexity"
					},
					"contracts": { "webSearchProviders": ["perplexity"] },
					"webSearchProviders": [{
						"id": "perplexity",
						"label": "Perplexity Search",
						"hint": "Requires Perplexity API key or OpenRouter API key · structured results",
						"onboardingScopes": ["text-inference"],
						"credentialLabel": "Perplexity API key",
						"envVars": ["PERPLEXITY_API_KEY", "OPENROUTER_API_KEY"],
						"placeholder": "pplx-...",
						"signupUrl": "https://www.perplexity.ai/settings/api",
						"docsUrl": "https://docs.openclaw.ai/perplexity",
						"credentialPath": "plugins.entries.perplexity.config.webSearch.apiKey",
						"autoDetectOrder": 50
					}],
					"install": {
						"clawhubSpec": "clawhub:@openclaw/perplexity-plugin",
						"npmSpec": "@openclaw/perplexity-plugin",
						"defaultChoice": "npm",
						"minHostVersion": ">=2026.6.8"
					}
				}
			},
			{
				"name": "@openclaw/searxng-plugin",
				"description": "OpenClaw SearXNG plugin",
				"source": "official",
				"kind": "plugin",
				"openclaw": {
					"plugin": {
						"id": "searxng",
						"label": "SearXNG"
					},
					"contracts": { "webSearchProviders": ["searxng"] },
					"webSearchProviders": [{
						"id": "searxng",
						"label": "SearXNG Search",
						"hint": "Self-hosted meta-search with no API key required",
						"onboardingScopes": ["text-inference"],
						"requiresCredential": true,
						"credentialLabel": "SearXNG Base URL",
						"envVars": ["SEARXNG_BASE_URL"],
						"placeholder": "http://localhost:8080",
						"signupUrl": "https://docs.searxng.org/",
						"docsUrl": "https://docs.openclaw.ai/tools/searxng-search",
						"credentialPath": "plugins.entries.searxng.config.webSearch.baseUrl",
						"autoDetectOrder": 200
					}],
					"install": {
						"clawhubSpec": "clawhub:@openclaw/searxng-plugin",
						"npmSpec": "@openclaw/searxng-plugin",
						"defaultChoice": "npm",
						"minHostVersion": ">=2026.6.9",
						"allowInvalidConfigRecovery": true
					}
				}
			},
			{
				"name": "@openclaw/slack-huddles",
				"description": "OpenClaw Slack huddle browser meeting participant plugin.",
				"source": "official",
				"kind": "plugin",
				"openclaw": {
					"plugin": {
						"id": "slack-huddles",
						"label": "Slack huddles"
					},
					"contracts": {
						"tools": ["slack_huddles"],
						"transcriptSourceProviders": ["slack-huddle"]
					},
					"install": {
						"clawhubSpec": "clawhub:@openclaw/slack-huddles",
						"npmSpec": "@openclaw/slack-huddles",
						"defaultChoice": "npm",
						"minHostVersion": ">=2026.9.8"
					}
				}
			},
			{
				"name": "@openclaw/tavily-plugin",
				"description": "OpenClaw Tavily plugin",
				"source": "official",
				"kind": "plugin",
				"openclaw": {
					"plugin": {
						"id": "tavily",
						"label": "Tavily"
					},
					"contracts": {
						"webSearchProviders": ["tavily"],
						"tools": ["tavily_search", "tavily_extract"]
					},
					"webSearchProviders": [{
						"id": "tavily",
						"label": "Tavily Search",
						"hint": "Structured results with domain filters and AI answer summaries",
						"onboardingScopes": ["text-inference"],
						"credentialLabel": "Tavily API key",
						"envVars": ["TAVILY_API_KEY"],
						"placeholder": "tvly-...",
						"signupUrl": "https://tavily.com/",
						"docsUrl": "https://docs.openclaw.ai/tools/tavily",
						"credentialPath": "plugins.entries.tavily.config.webSearch.apiKey",
						"autoDetectOrder": 70
					}],
					"install": {
						"clawhubSpec": "clawhub:@openclaw/tavily-plugin",
						"npmSpec": "@openclaw/tavily-plugin",
						"defaultChoice": "npm",
						"minHostVersion": ">=2026.6.9",
						"allowInvalidConfigRecovery": true
					}
				}
			},
			{
				"name": "@openclaw/team-reports",
				"description": "OpenClaw team activity reports plugin (GitHub org + Discord activity with model summaries)",
				"source": "official",
				"kind": "plugin",
				"openclaw": {
					"plugin": {
						"id": "team-reports",
						"label": "Team Reports"
					},
					"install": {
						"clawhubSpec": "clawhub:@openclaw/team-reports",
						"npmSpec": "@openclaw/team-reports",
						"defaultChoice": "npm",
						"minHostVersion": ">=2026.9.2"
					}
				}
			},
			{
				"name": "@openclaw/teams-meetings",
				"description": "OpenClaw Microsoft Teams browser meeting participant plugin.",
				"source": "official",
				"kind": "plugin",
				"openclaw": {
					"plugin": {
						"id": "teams-meetings",
						"label": "Microsoft Teams meetings"
					},
					"contracts": {
						"tools": ["teams_meetings"],
						"transcriptSourceProviders": ["teams"]
					},
					"install": {
						"clawhubSpec": "clawhub:@openclaw/teams-meetings",
						"npmSpec": "@openclaw/teams-meetings",
						"defaultChoice": "npm",
						"minHostVersion": ">=2026.9.8"
					}
				}
			},
			{
				"name": "@openclaw/tokenjuice",
				"description": "OpenClaw tokenjuice exec output compaction plugin",
				"source": "official",
				"kind": "plugin",
				"openclaw": {
					"plugin": {
						"id": "tokenjuice",
						"label": "Tokenjuice"
					},
					"catalog": {
						"featured": true,
						"order": 60
					},
					"install": {
						"clawhubSpec": "clawhub:@openclaw/tokenjuice",
						"npmSpec": "@openclaw/tokenjuice",
						"defaultChoice": "npm",
						"minHostVersion": ">=2026.5.28"
					}
				}
			},
			{
				"name": "@openclaw/typesafe",
				"description": "OpenClaw TypeSafe typed decisions and optional evaluation tool",
				"source": "official",
				"kind": "plugin",
				"openclaw": {
					"plugin": {
						"id": "typesafe",
						"label": "TypeSafe AI"
					},
					"install": {
						"clawhubSpec": "clawhub:@openclaw/typesafe",
						"npmSpec": "@openclaw/typesafe",
						"defaultChoice": "npm",
						"minHostVersion": ">=2026.9.6"
					}
				}
			},
			{
				"name": "@openclaw/voice-call",
				"description": "OpenClaw voice-call plugin",
				"source": "official",
				"kind": "plugin",
				"openclaw": {
					"plugin": {
						"id": "voice-call",
						"label": "Voice Call"
					},
					"install": {
						"clawhubSpec": "clawhub:@openclaw/voice-call",
						"npmSpec": "@openclaw/voice-call",
						"defaultChoice": "npm",
						"minHostVersion": ">=2026.4.10"
					}
				}
			},
			{
				"name": "@openclaw/zoom-meetings",
				"description": "OpenClaw Zoom browser meeting participant plugin.",
				"source": "official",
				"kind": "plugin",
				"openclaw": {
					"plugin": {
						"id": "zoom-meetings",
						"label": "Zoom meetings"
					},
					"contracts": {
						"tools": ["zoom_meetings"],
						"transcriptSourceProviders": ["zoom"]
					},
					"install": {
						"clawhubSpec": "clawhub:@openclaw/zoom-meetings",
						"npmSpec": "@openclaw/zoom-meetings",
						"defaultChoice": "npm",
						"minHostVersion": ">=2026.9.8"
					}
				}
			}
		]
	}
].flatMap((source) => source.entries.filter((entry) => isRecord(entry)));
path.join("dist", "channel-catalog.json");
//#endregion
//#region src/channels/ids.ts
const BUNDLED_CHAT_CHANNEL_ENTRIES = Object.freeze([
	{
		channelId: "a2a",
		order: 75,
		label: "A2A"
	},
	{
		channelId: "buzz",
		order: 56,
		label: "Buzz"
	},
	{
		channelId: "clickclack",
		order: 85,
		label: "ClickClack"
	},
	{
		channelId: "discord",
		label: "Discord"
	},
	{
		channelId: "feishu",
		aliases: ["lark"],
		order: 35,
		label: "Feishu"
	},
	{
		channelId: "googlechat",
		aliases: ["gchat", "google-chat"],
		order: 55,
		label: "Google Chat"
	},
	{
		channelId: "imessage",
		aliases: ["imsg"],
		label: "iMessage"
	},
	{
		channelId: "irc",
		aliases: ["internet-relay-chat"],
		label: "IRC"
	},
	{
		channelId: "line",
		order: 75,
		label: "LINE"
	},
	{
		channelId: "matrix",
		order: 70,
		label: "Matrix"
	},
	{
		channelId: "mattermost",
		order: 65,
		label: "Mattermost"
	},
	{
		channelId: "msteams",
		aliases: ["teams"],
		order: 60,
		label: "Microsoft Teams"
	},
	{
		channelId: "nextcloud-talk",
		aliases: ["nc", "nc-talk"],
		order: 65,
		label: "Nextcloud Talk"
	},
	{
		channelId: "nostr",
		order: 55,
		label: "Nostr"
	},
	{
		channelId: "qa-channel",
		order: 999,
		configurable: false,
		label: "QA Channel"
	},
	{
		channelId: "raft",
		order: 72,
		label: "Raft"
	},
	{
		channelId: "reef",
		label: "Reef"
	},
	{
		channelId: "signal",
		label: "Signal"
	},
	{
		channelId: "slack",
		label: "Slack"
	},
	{
		channelId: "sms",
		order: 88,
		label: "SMS"
	},
	{
		channelId: "synology-chat",
		order: 90,
		label: "Synology Chat"
	},
	{
		channelId: "telegram",
		label: "Telegram"
	},
	{
		channelId: "tlon",
		order: 90,
		label: "Tlon"
	},
	{
		channelId: "twitch",
		aliases: ["twitch-chat"],
		label: "Twitch"
	},
	{
		channelId: "whatsapp",
		label: "WhatsApp"
	},
	{
		channelId: "x",
		order: 85,
		label: "X (Twitter)"
	},
	{
		channelId: "zalo",
		aliases: ["zl"],
		order: 80,
		label: "Zalo"
	},
	{
		channelId: "zalouser",
		aliases: ["zlu"],
		order: 85,
		label: "Zalo Personal"
	}
].filter((entry) => entry.configurable !== false).map((entry) => ({
	id: normalizeOptionalLowercaseString(entry.channelId) ?? entry.channelId,
	aliases: entry.aliases ?? [],
	label: entry.label?.trim() || void 0,
	order: entry.order ?? Number.MAX_SAFE_INTEGER
})).toSorted((left, right) => left.order - right.order || left.id.localeCompare(right.id, "en", { sensitivity: "base" })));
new Set(BUNDLED_CHAT_CHANNEL_ENTRIES.map((entry) => entry.id));
/**
* Stable built-in channel order derived from generated bundled channel metadata.
*/
const CHAT_CHANNEL_ORDER = Object.freeze(BUNDLED_CHAT_CHANNEL_ENTRIES.map((entry) => entry.id));
Object.freeze(Object.fromEntries(BUNDLED_CHAT_CHANNEL_ENTRIES.flatMap((entry) => entry.aliases.map((alias) => [alias, entry.id]))));
new Set(CHAT_CHANNEL_ORDER);
//#endregion
//#region extensions/telegram/src/account-selection.ts
function resolveTelegramBindingAccountId(value) {
	if (!value || typeof value !== "object") return null;
	const binding = value;
	if (normalizeLowercaseStringOrEmpty(binding.match?.channel) !== "telegram") return null;
	const accountId = typeof binding.match?.accountId === "string" ? binding.match.accountId : "";
	if (!accountId.trim() || accountId.trim() === "*") return null;
	return normalizeAccountId(accountId);
}
function hasTelegramAccountConfig(cfg, accountId) {
	const normalized = normalizeAccountId(accountId);
	if (resolveTelegramAccountConfig(cfg, normalized)) return true;
	const channel = cfg.channels?.telegram;
	if (normalized !== "default" && (Object.keys(channel?.accounts ?? {}).length > 0 || !cfg.bindings?.some((binding) => resolveTelegramBindingAccountId(binding) === normalized))) return false;
	return hasConfiguredAccountValue(channel?.botToken) || hasConfiguredAccountValue(channel?.tokenFile) || normalized === "default" && hasConfiguredAccountValue(process.env.TELEGRAM_BOT_TOKEN);
}
const { listAccountIds: listTelegramAccountIds } = createAccountListHelpers("telegram", {
	normalizeAccountId,
	additionalAccountIds: (cfg) => [...new Set((cfg.bindings ?? []).map(resolveTelegramBindingAccountId).filter((id) => id !== null))].toSorted((left, right) => left.localeCompare(right)),
	hasImplicitDefaultAccount: (cfg) => hasTelegramAccountConfig(cfg, DEFAULT_ACCOUNT_ID)
});
//#endregion
//#region src/config/gateway-public-origin.ts
function resolveGatewayPublicOrigin(config) {
	const raw = config?.gateway?.publicOrigin?.trim();
	if (!raw) return;
	try {
		const parsed = new URL(raw);
		return parsed.pathname === "/" && !parsed.search && !parsed.hash ? parsed.origin : void 0;
	} catch {
		return;
	}
}
//#endregion
//#region src/gateway/gateway-http-route-contracts.ts
const GATEWAY_PROBE_ROUTES = /* @__PURE__ */ new Map([
	["/health", "live"],
	["/healthz", "live"],
	["/ready", "ready"],
	["/readyz", "ready"],
	["/startup", "startup"],
	["/startupz", "startup"]
]);
function classifyGatewayProbePath(pathname) {
	for (const [root, status] of GATEWAY_PROBE_ROUTES) {
		if (pathname === root) return status;
		if (pathname.startsWith(`${root}/`)) return "namespace";
	}
	return "outside";
}
//#endregion
//#region src/plugins/http-path-prefix.ts
function prefixMatchPath(pathname, prefix) {
	return pathname === prefix || pathname.startsWith(`${prefix}/`) || pathname.startsWith(`${prefix}%`);
}
//#endregion
//#region src/gateway/security-path.ts
const MAX_PATH_DECODE_PASSES = 32;
function normalizePathSeparators(pathname) {
	const collapsed = pathname.replace(/\/{2,}/g, "/");
	if (collapsed.length <= 1) return collapsed;
	return collapsed.replace(/\/+$/, "");
}
function normalizePathForSecurity(pathname) {
	const resolved = URL.parse(pathname, "http://localhost")?.pathname ?? pathname;
	return normalizePathSeparators(normalizeLowercaseStringOrEmpty(resolved)) || "/";
}
function buildCanonicalPathCandidates(pathname) {
	const candidates = /* @__PURE__ */ new Set([normalizePathForSecurity(pathname)]);
	let decoded = pathname;
	let malformedEncoding = false;
	let decodePasses = 0;
	for (let pass = 0; pass < MAX_PATH_DECODE_PASSES; pass++) {
		let nextDecoded;
		try {
			nextDecoded = decodeURIComponent(decoded);
		} catch {
			malformedEncoding = true;
			break;
		}
		if (nextDecoded === decoded) break;
		decodePasses += 1;
		decoded = nextDecoded;
		candidates.add(normalizePathForSecurity(decoded));
	}
	let decodePassLimitReached = false;
	if (!malformedEncoding) try {
		decodePassLimitReached = decodeURIComponent(decoded) !== decoded;
	} catch {
		malformedEncoding = true;
	}
	return {
		candidates: [...candidates],
		decodePasses,
		decodePassLimitReached,
		malformedEncoding
	};
}
function canonicalizePathForSecurity(pathname) {
	const { candidates, decodePasses, decodePassLimitReached, malformedEncoding } = buildCanonicalPathCandidates(pathname);
	return {
		canonicalPath: candidates[candidates.length - 1] ?? "/",
		candidates,
		decodePasses,
		decodePassLimitReached,
		malformedEncoding,
		rawNormalizedPath: normalizePathSeparators(normalizeLowercaseStringOrEmpty(pathname)) || "/"
	};
}
const PROTECTED_PLUGIN_ROUTE_PREFIXES = ["/api/channels"];
//#endregion
//#region src/gateway/server/plugins-http/path-context.ts
function normalizeProtectedPrefix(prefix) {
	const collapsed = normalizeLowercaseStringOrEmpty(prefix).replace(/\/{2,}/g, "/");
	if (collapsed.length <= 1) return collapsed || "/";
	return collapsed.replace(/\/+$/, "");
}
const NORMALIZED_PROTECTED_PLUGIN_ROUTE_PREFIXES = PROTECTED_PLUGIN_ROUTE_PREFIXES.map(normalizeProtectedPrefix);
/** Returns true when any decoded path candidate targets a protected route. */
function isProtectedPluginRoutePathFromContext(context) {
	if (context.candidates.some((candidate) => NORMALIZED_PROTECTED_PLUGIN_ROUTE_PREFIXES.some((prefix) => prefixMatchPath(candidate, prefix)))) return true;
	if (context.decodePassLimitReached) return true;
	if (!context.malformedEncoding) return false;
	return NORMALIZED_PROTECTED_PLUGIN_ROUTE_PREFIXES.some((prefix) => prefixMatchPath(context.rawNormalizedPath, prefix));
}
/** Builds all security-relevant decoded path candidates for a request path. */
function resolvePluginRoutePathContext(pathname) {
	const canonical = canonicalizePathForSecurity(pathname);
	return {
		pathname,
		canonicalPath: canonical.canonicalPath,
		candidates: canonical.candidates,
		malformedEncoding: canonical.malformedEncoding,
		decodePassLimitReached: canonical.decodePassLimitReached,
		rawNormalizedPath: canonical.rawNormalizedPath
	};
}
function resolveTelegramGatewayWebhookUrl(config, path) {
	const origin = resolveGatewayPublicOrigin(config);
	return origin ? URL.parse(`${origin}${path.startsWith("/") ? path : `/${path}`}`)?.href : void 0;
}
function resolveTelegramWebhookPathConflict(path) {
	if (path === "/healthz") return {
		kind: "health",
		message: "is reserved for webhook listener health checks"
	};
	const pathname = URL.parse(path, "http://localhost")?.pathname ?? path;
	const probe = classifyGatewayProbePath(pathname);
	if (probe === "live" || probe === "ready" || probe === "startup") return {
		kind: "probe",
		message: "is reserved for Gateway checks"
	};
	if (isProtectedPluginRoutePathFromContext(resolvePluginRoutePathContext(pathname))) return {
		kind: "auth",
		message: "requires Gateway authentication"
	};
}
//#endregion
//#region extensions/telegram/src/doctor-contract.ts
const webhookListenerMigration = createLegacyWebhookListenerDoctorContract({
	channelKey: "telegram",
	defaultPort: 8787,
	defaultHost: "127.0.0.1"
});
const { historicalWebhookListener } = webhookListenerMigration;
const RETIRED_TUNING_KEYS = /* @__PURE__ */ new Set([
	"timeoutSeconds",
	"mediaGroupFlushMs",
	"pollingStallThresholdMs",
	"retry",
	"errorCooldownMs"
]);
function stripRetiredTelegramTuning(entry, scope) {
	let changed = false;
	const updated = { ...entry };
	for (const key of scope === "channel" || scope === "account" ? RETIRED_TUNING_KEYS : ["errorCooldownMs"]) if (Object.hasOwn(updated, key)) {
		delete updated[key];
		changed = true;
	}
	const maps = scope === "topic" ? [] : scope === "chat" ? ["topics"] : ["groups", "direct"];
	if (scope === "channel") maps.push("accounts");
	for (const key of maps) {
		const entries = asNullableRecord(entry[key]);
		if (!entries) continue;
		const nextEntries = { ...entries };
		for (const [id, value] of Object.entries(entries)) {
			const child = asNullableRecord(value);
			if (!child) continue;
			const next = stripRetiredTelegramTuning(child, key === "accounts" ? "account" : key === "topics" ? "topic" : "chat");
			if (next.changed) {
				nextEntries[id] = next.entry;
				updated[key] = nextEntries;
				changed = true;
			}
		}
	}
	return {
		entry: changed ? updated : entry,
		changed
	};
}
function hasRetiredTelegramGroupHistoryContextConfig(value) {
	return asNullableRecord(value)?.includeGroupHistoryContext !== void 0;
}
function removeRetiredTelegramGroupHistoryContextConfig(params) {
	if (params.entry.includeGroupHistoryContext === void 0) return {
		entry: params.entry,
		changed: false
	};
	const { includeGroupHistoryContext, ...rest } = params.entry;
	const historyLimit = includeGroupHistoryContext === "none" ? 0 : includeGroupHistoryContext === "recent" && params.preserveRecentHistoryLimit !== void 0 && params.entry.historyLimit === void 0 ? params.preserveRecentHistoryLimit : void 0;
	const updated = historyLimit === void 0 ? rest : {
		...rest,
		historyLimit
	};
	const historyLimitNote = historyLimit === void 0 ? "" : ` and set historyLimit to ${historyLimit}`;
	params.changes.push(`Removed ${params.pathPrefix}.includeGroupHistoryContext${historyLimitNote}; Telegram group history is always on for groups and bounded by historyLimit.`);
	return {
		entry: updated,
		changed: true
	};
}
const legacyConfigRules = [
	...webhookListenerMigration.legacyConfigRules,
	{
		path: ["channels", "telegram"],
		message: "channels.telegram.includeGroupHistoryContext was removed; Telegram group history is always on for groups and bounded by historyLimit. Run \"openclaw doctor --fix\".",
		match: hasRetiredTelegramGroupHistoryContextConfig
	},
	{
		path: [
			"channels",
			"telegram",
			"accounts"
		],
		message: "channels.telegram.accounts.<id>.includeGroupHistoryContext was removed; Telegram group history is always on for groups and bounded by historyLimit. Run \"openclaw doctor --fix\".",
		match: (value) => hasLegacyAccountStreamingAliases(value, hasRetiredTelegramGroupHistoryContextConfig)
	}
];
function normalizeHistoricalWebhookConfig({ cfg }) {
	const historicalWebhookAccountIds = cfg.channels?.telegram?.enabled === false ? [] : listTelegramAccountIds(cfg).filter((accountId) => {
		const account = mergeTelegramAccountConfig(cfg, accountId);
		if (account.enabled === false || !account.webhookUrl?.trim()) return false;
		const path = account.webhookPath ?? "/telegram-webhook";
		const gatewayUrl = resolveTelegramGatewayWebhookUrl(cfg, path);
		return !gatewayUrl || URL.parse(account.webhookUrl)?.href !== gatewayUrl || resolveTelegramWebhookPathConflict(path) !== void 0;
	});
	return {
		...webhookListenerMigration.normalizeCompatibilityConfig({ cfg }),
		historicalWebhookAccountIds
	};
}
function normalizeCompatibilityConfig({ cfg }) {
	const webhook = normalizeHistoricalWebhookConfig({ cfg });
	const { historicalWebhookAccountIds } = webhook;
	const changes = [...webhook.changes];
	const rawEntry = asNullableRecord(webhook.config.channels?.telegram);
	if (!rawEntry) return {
		config: cfg,
		changes: [],
		historicalWebhookAccountIds
	};
	const tuningKnobs = stripRetiredTelegramTuning(rawEntry, "channel");
	let updated = tuningKnobs.entry;
	if (tuningKnobs.changed) changes.push("Removed retired Telegram tuning knobs.");
	const rootGroupHistoryContextMode = updated.includeGroupHistoryContext;
	const rootGroupHistoryLimitBeforeMigration = typeof updated.historyLimit === "number" ? updated.historyLimit : cfg.messages?.groupChat?.historyLimit ?? 50;
	updated = removeRetiredTelegramGroupHistoryContextConfig({
		entry: updated,
		pathPrefix: "channels.telegram",
		changes
	}).entry;
	updated = normalizeChannelAccounts({
		entry: updated,
		pathPrefix: "channels.telegram",
		changes,
		normalizeAccount: ({ account, pathPrefix, changes: accountChanges }) => removeRetiredTelegramGroupHistoryContextConfig({
			entry: account,
			pathPrefix,
			changes: accountChanges,
			...rootGroupHistoryContextMode === "none" ? { preserveRecentHistoryLimit: rootGroupHistoryLimitBeforeMigration } : {}
		})
	}).entry;
	if (webhook.config === cfg && updated === rawEntry && changes.length === 0) return {
		config: cfg,
		changes: [],
		historicalWebhookAccountIds
	};
	return {
		config: {
			...webhook.config,
			channels: {
				...webhook.config.channels,
				telegram: updated
			}
		},
		changes,
		historicalWebhookAccountIds
	};
}
//#endregion
export { historicalWebhookListener, legacyConfigRules, normalizeCompatibilityConfig, normalizeHistoricalWebhookConfig };