import { l as normalizeOptionalString } from "./string-coerce-CIXf7egm.js";
import "./src-BntaCZM-.js";
import { t as stableStringify } from "./stable-stringify-DoZ6Yalc.js";
import { f as normalizeTrimmedStringList } from "./string-normalization-e_fvmxMf.js";
import { t as pruneMapToMaxSize } from "./map-size-DAGm21RM.js";
import { a as isPathInside } from "./path-D138yf8v.js";
import { c as resolveUserPath } from "./home-dir-BFvskzn8.js";
import "./path-guards-CQoZeoCG.js";
import { t as CONFIG_DIR } from "./utils-B2aozS-T.js";
import { o as isDefaultStateDir } from "./paths-BBSTUjD5.js";
import { t as createSubsystemLogger } from "./subsystem-CT-8ly4J.js";
import { t as shouldRejectHardlinkedPluginFiles } from "./hardlink-policy-FqitPjqc.js";
import { S as tryResolveAmbientOwnerAgentId } from "./agent-scope-config-adSj-fCz.js";
import { n as normalizeAgentId } from "./agent-id-CeT3w4ap.js";
import { a as normalizeSkillFilter, n as resolveEffectiveAgentSkillFilter, t as isSessionSkillEnabled } from "./agent-filter-CMeuwcnF.js";
import { t as canonicalizePath } from "./paths-Bf0MEhmU.js";
import { o as createSyntheticSourceInfo } from "./skill-contract-6Z2EHE_Q.js";
import { i as resolveSkillManifestMetadata, n as resolveSkillInvocationPolicy, r as resolveSkillKey, t as parseSkillFrontmatter } from "./frontmatter-BUnBwW_N.js";
import { r as resolveSkillTelemetrySourceValue } from "./source-0ivX3dtK.js";
import { l as shouldIncludeSkill, o as resolveBundledAllowlist } from "./config-BqxLa0vN.js";
import { n as resolveAllowedSkillSymlinkTargetRealPaths, r as tryRealpath } from "./symlink-targets-BNzIErMO.js";
import { a as resolveSkillDiscoveryLimits, c as resolveSkillsUserHomeDir, n as discoverPluginSkills, r as discoverSkillCandidates, s as compactSkillPath, t as canonicalSkillDirForSource } from "./skill-root-discovery-ClBne3N3.js";
import { r as getSkillsSnapshotVersion, t as bumpSkillsSnapshotVersion } from "./refresh-state-DHnXO3IV.js";
import { r as resolveNodeIdFromNodeList } from "./node-resolve-BHSmCNRY.js";
import { n as redactConfigObject } from "./redact-snapshot-3egyAnXn.js";
import { t as resolveBundledSkillsDir } from "./bundled-dir-Rim1koSz.js";
import { n as loadSkillsFromDirSafe, r as readSkillFrontmatterSafe } from "./local-loader-_UXEOwQ3.js";
import { n as resolvePluginSkillRootsFromMetadata, t as resolvePluginSkillRoots } from "./plugin-skills-DOLQgxv3.js";
import fs from "node:fs";
import path from "node:path";
import crypto from "node:crypto";
//#region src/skills/runtime/remote-skills.ts
const remoteSkillNodes = /* @__PURE__ */ new Map();
const log = createSubsystemLogger("gateway/skills-remote");
let reconcileRemoteSkillConnections = null;
function remoteConnectionKey(nodeId, connId) {
	return `${nodeId}\0${connId}`;
}
/** Installs the gateway-owned persistent-generation reconciliation boundary. */
function setRemoteSkillConnectionReconciler(reconcile) {
	reconcileRemoteSkillConnections = reconcile;
}
function prepareNodeSkills(nodeId, skills) {
	const prepared = [];
	for (const skill of skills) try {
		const frontmatter = parseSkillFrontmatter(skill.content);
		if (frontmatter.name?.trim() !== skill.name || frontmatter.description?.trim() !== skill.description) {
			log.warn(`dropped node skill with mismatched frontmatter: ${nodeId}/${skill.name}`);
			continue;
		}
		prepared.push({
			...skill,
			frontmatter
		});
	} catch (error) {
		const filePath = `node://${encodeURIComponent(nodeId)}/skills/${skill.name}/SKILL.md`;
		log.warn(`dropped node skill with invalid frontmatter (${filePath}): ${String(error)}`);
	}
	return prepared;
}
function sameSkills(left, right) {
	return left.length === right.length && left.every((skill, index) => skill.name === right[index]?.name && skill.description === right[index]?.description && skill.content === right[index]?.content);
}
function recordRemoteSkillNodeInfo(node) {
	const existing = remoteSkillNodes.get(node.nodeId);
	const connectionChanged = Boolean(node.connId && existing?.connId !== node.connId);
	const displayChanged = existing?.displayName !== node.displayName;
	const canExec = node.commands?.includes("system.run") ?? existing?.canExec ?? false;
	const executionChanged = existing?.canExec !== canExec;
	remoteSkillNodes.set(node.nodeId, {
		nodeId: node.nodeId,
		connId: node.connId ?? existing?.connId,
		displayName: node.displayName,
		connected: true,
		canExec,
		skills: connectionChanged ? [] : existing?.skills ?? []
	});
	if ((connectionChanged || displayChanged || executionChanged) && (existing?.skills.length ?? 0) > 0) bumpSkillsSnapshotVersion({ reason: "remote-node" });
}
function replaceRemoteNodeSkills(params) {
	const nextSkills = prepareNodeSkills(params.nodeId, params.skills);
	const existing = remoteSkillNodes.get(params.nodeId);
	const changed = !existing?.connected || existing.displayName !== params.displayName || !sameSkills(existing.skills, nextSkills);
	remoteSkillNodes.set(params.nodeId, {
		nodeId: params.nodeId,
		connId: existing?.connId,
		displayName: params.displayName ?? existing?.displayName,
		connected: true,
		canExec: existing?.canExec ?? false,
		skills: nextSkills
	});
	if (changed) bumpSkillsSnapshotVersion({ reason: "remote-node" });
}
function removeRemoteNodeSkills(nodeId) {
	const existing = remoteSkillNodes.get(nodeId);
	remoteSkillNodes.delete(nodeId);
	if (existing?.skills.length) bumpSkillsSnapshotVersion({ reason: "remote-node" });
}
function sanitizeSkillNameFragment(value) {
	return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 24).replace(/-+$/g, "") || "node";
}
function prefixedSkillName(params) {
	const prefix = `${sanitizeSkillNameFragment(params.nodeId)}-`;
	for (let index = 0; index < 100; index += 1) {
		const suffix = index === 0 ? "" : `-${index + 1}`;
		const availableBaseLength = Math.max(1, 64 - prefix.length - suffix.length);
		const candidate = `${prefix}${params.baseName.slice(0, availableBaseLength).replace(/-+$/g, "") || "skill"}${suffix}`;
		if (!params.usedNames.has(candidate)) return candidate;
	}
	return null;
}
function remoteSkillLocation(nodeId, name) {
	return `node://${encodeURIComponent(nodeId)}/skills/${name}/SKILL.md`;
}
function locatorNote(node, skillName) {
	const label = node.displayName?.trim() || node.nodeId;
	const cwd = remoteSkillLocation(node.nodeId, skillName).slice(0, -9);
	return `Node-hosted on ${label} (${node.nodeId}). Read this SKILL.md with the normal read tool at its exact node:// location; do not use file_fetch, which only accepts approved absolute node paths. If read is unavailable, use exec host=node node=${node.nodeId} with workdir=${cwd} to run cat SKILL.md. Run referenced files and bins with the same exec target and workdir; the node host resolves that locator to the node-local skill directory.`;
}
function mergeRemoteNodeSkillEntries(localEntries, options) {
	if (options?.canExec !== true) return [...localEntries];
	const currentConnections = reconcileRemoteSkillConnections?.();
	const connectedNodes = [...remoteSkillNodes.values()].filter((node) => node.connected && node.canExec && (!currentConnections || node.connId !== void 0 && currentConnections.has(remoteConnectionKey(node.nodeId, node.connId))));
	let boundNodeId;
	if (options.node) try {
		boundNodeId = resolveNodeIdFromNodeList(connectedNodes, options.node);
	} catch {
		return [...localEntries];
	}
	const remote = connectedNodes.filter((node) => !boundNodeId || node.nodeId === boundNodeId).flatMap((node) => node.skills.map((skill) => ({
		node,
		skill
	}))).toSorted((left, right) => left.skill.name.localeCompare(right.skill.name, "en") || left.node.nodeId.localeCompare(right.node.nodeId, "en"));
	if (remote.length === 0) return [...localEntries];
	const remoteNameCounts = /* @__PURE__ */ new Map();
	for (const { skill } of remote) remoteNameCounts.set(skill.name, (remoteNameCounts.get(skill.name) ?? 0) + 1);
	const localNames = new Set(localEntries.map((entry) => entry.skill.name));
	const usedNames = new Set(localNames);
	const remoteEntries = [];
	for (const { node, skill } of remote) {
		const exposedName = usedNames.has(skill.name) || (remoteNameCounts.get(skill.name) ?? 0) > 1 ? prefixedSkillName({
			nodeId: node.nodeId,
			baseName: skill.name,
			usedNames
		}) : skill.name;
		if (!exposedName || usedNames.has(exposedName)) {
			log.warn(`dropped node skill with unresolved name collision: ${node.nodeId}/${skill.name}`);
			continue;
		}
		usedNames.add(exposedName);
		const filePath = remoteSkillLocation(node.nodeId, skill.name);
		const invocation = resolveSkillInvocationPolicy(skill.frontmatter);
		remoteEntries.push({
			skill: {
				name: exposedName,
				description: skill.description,
				locationNote: locatorNote(node, skill.name),
				readContent: skill.content,
				filePath,
				baseDir: filePath.slice(0, -9),
				source: "openclaw-node",
				sourceInfo: createSyntheticSourceInfo(filePath, {
					source: "openclaw-node",
					scope: "temporary",
					origin: "top-level",
					baseDir: filePath.slice(0, -9)
				}),
				disableModelInvocation: invocation.disableModelInvocation
			},
			frontmatter: skill.frontmatter,
			invocation,
			disableCommandDispatch: true,
			exposure: {
				includeInRuntimeRegistry: true,
				includeInAvailableSkillsPrompt: !invocation.disableModelInvocation,
				userInvocable: invocation.userInvocable
			}
		});
	}
	return [...localEntries, ...remoteEntries].toSorted((left, right) => left.skill.name.localeCompare(right.skill.name, "en"));
}
function resetRemoteNodeSkillsForTests() {
	remoteSkillNodes.clear();
	reconcileRemoteSkillConnections = null;
}
if (process.env.VITEST || false) globalThis[Symbol.for("openclaw.remoteNodeSkillsTestApi")] = { resetRemoteNodeSkillsForTests };
//#endregion
//#region src/skills/runtime/snapshot-config-fingerprint.ts
let configFingerprints = /* @__PURE__ */ new WeakMap();
function fingerprintSkillSnapshotConfig(config) {
	const cached = configFingerprints.get(config);
	if (cached) return cached;
	const fingerprint = crypto.createHash("sha256").update(stableStringify(redactConfigObject(config))).digest("hex");
	configFingerprints.set(config, fingerprint);
	return fingerprint;
}
function resetSkillSnapshotConfigFingerprintCache() {
	configFingerprints = /* @__PURE__ */ new WeakMap();
}
//#endregion
//#region src/skills/loading/workspace-skill-loader.ts
const skillsLogger = createSubsystemLogger("skills");
const CUSTODIAN_SKILLS_DIR_NAME = "custodian-skills";
const SKILL_SOURCE_ORIGIN_RELATIVE_PATH = path.join(".openclaw", "source-origin.json");
const MAX_SKILL_SOURCE_ORIGIN_BYTES = 16 * 1024;
const MAX_SKILL_ENTRY_CACHE_SIZE = 64;
const skillEntryCache = /* @__PURE__ */ new Map();
const reportedSkillCollisions = /* @__PURE__ */ new Map();
function normalizeWorkspaceSkillRoots(roots) {
	const agentWorkspaceDir = path.resolve(roots.agentWorkspaceDir);
	const executionSkillsDir = roots.executionSkillsDir ? path.resolve(roots.executionSkillsDir) : void 0;
	return executionSkillsDir && executionSkillsDir !== path.join(agentWorkspaceDir, "skills") ? {
		agentWorkspaceDir,
		executionSkillsDir
	} : { agentWorkspaceDir };
}
function warnInvalidSkill(source, diagnostic) {
	skillsLogger.warn("Skipping invalid skill.", {
		source,
		filePath: diagnostic.path,
		error: diagnostic.message,
		consoleMessage: `Skipping invalid skill: file=${compactSkillPath(diagnostic.path)} error=${diagnostic.message}`
	});
}
function warnSkillPrecedenceCollision(winner, loser, workspaceDir) {
	const collisionKey = JSON.stringify([
		workspaceDir,
		getSkillsSnapshotVersion(workspaceDir),
		winner.name,
		winner.source,
		winner.filePath,
		loser.source,
		loser.filePath
	]);
	if (reportedSkillCollisions.has(collisionKey)) return;
	if (canonicalizePath(winner.filePath) === canonicalizePath(loser.filePath)) return;
	reportedSkillCollisions.set(collisionKey, true);
	pruneMapToMaxSize(reportedSkillCollisions, MAX_SKILL_ENTRY_CACHE_SIZE * 4);
	const collisionName = winner.name.slice(0, 128);
	skillsLogger.warn("Skill precedence collision resolved.", {
		skill: collisionName,
		winnerSource: winner.source,
		loserSource: loser.source,
		winnerPath: winner.filePath,
		loserPath: loser.filePath,
		consoleMessage: `Skill precedence collision: skill="${collisionName}" winner=${winner.source}:${compactSkillPath(winner.filePath)} loser=${loser.source}:${compactSkillPath(loser.filePath)}`
	});
}
function filterSkillEntries(entries, config, skillFilter, skillOverrides, eligibility) {
	const bundledAllowlist = resolveBundledAllowlist(config);
	let filtered = entries.filter((entry) => shouldIncludeSkill({
		entry,
		config,
		bundledAllowlist,
		eligibility
	}));
	if (skillFilter !== void 0 || skillOverrides !== void 0) {
		const normalized = normalizeSkillFilter(skillFilter) ?? [];
		const label = normalized.length > 0 ? normalized.join(", ") : "(none)";
		skillsLogger.debug(`Applying skill filter: ${label}`);
		const resolvedFilter = skillFilter === void 0 ? void 0 : normalized;
		filtered = filtered.filter((entry) => isSessionSkillEnabled(entry.skill.name, resolvedFilter, skillOverrides, resolveSkillKey(entry.skill, entry)));
		skillsLogger.debug(`After skill filter: ${filtered.map((entry) => entry.skill.name).join(", ") || "(none)"}`);
	}
	return filtered;
}
function loadContainedSkillRecords(params) {
	const expectedBaseDir = path.resolve(params.skillDir);
	const loaded = loadSkillsFromDirSafe({
		dir: params.skillDir,
		source: params.source,
		maxBytes: params.maxSkillFileBytes,
		rejectHardlinks: params.rejectHardlinks,
		onDiagnostic: (diagnostic) => warnInvalidSkill(params.source, diagnostic)
	});
	const records = loaded.skills.map((skill) => ({
		skill,
		frontmatter: loaded.frontmatterByFilePath.get(skill.filePath),
		rejectHardlinks: params.rejectHardlinks
	})).filter((record) => path.resolve(record.skill.baseDir) === expectedBaseDir);
	const canonicalSkillDir = params.canonicalSkillDir;
	return canonicalSkillDir ? records.map((record) => canonicalizeLoadedSkillRecord(record, canonicalSkillDir)) : records;
}
function readSourceInstallSkillKey(skillDir) {
	try {
		const sourceOriginPath = path.join(skillDir, SKILL_SOURCE_ORIGIN_RELATIVE_PATH);
		const stat = fs.lstatSync(sourceOriginPath);
		if (!stat.isFile() || stat.isSymbolicLink() || stat.size > MAX_SKILL_SOURCE_ORIGIN_BYTES) return;
		const skillDirRealPath = tryRealpath(skillDir);
		const sourceOriginRealPath = tryRealpath(sourceOriginPath);
		if (!skillDirRealPath || !sourceOriginRealPath || !isPathInside(skillDirRealPath, sourceOriginRealPath)) return;
		const raw = fs.readFileSync(sourceOriginPath, "utf8");
		return normalizeOptionalString(JSON.parse(raw).slug);
	} catch {
		return;
	}
}
function resolveSkillEntryMetadata(params) {
	const metadata = resolveSkillManifestMetadata(params.frontmatter);
	if (metadata?.skillKey) return metadata;
	const sourceInstallSkillKey = readSourceInstallSkillKey(params.skillDir);
	if (!sourceInstallSkillKey) return metadata;
	return {
		...metadata,
		skillKey: sourceInstallSkillKey
	};
}
function canonicalizeLoadedSkillRecord(record, canonicalSkillDir) {
	const originalBaseDir = path.resolve(record.skill.baseDir);
	const canonicalBaseDir = path.resolve(canonicalSkillDir);
	if (originalBaseDir === canonicalBaseDir) return record;
	const filePath = path.join(canonicalBaseDir, path.relative(originalBaseDir, record.skill.filePath));
	return {
		...record,
		syncSourceDir: canonicalBaseDir,
		syncDirName: path.basename(originalBaseDir),
		skill: {
			...record.skill,
			filePath,
			baseDir: canonicalBaseDir,
			sourceInfo: record.skill.sourceInfo ? {
				...record.skill.sourceInfo,
				path: filePath,
				baseDir: canonicalBaseDir
			} : record.skill.sourceInfo
		}
	};
}
function setSyncSourceForPluginSkill(record, syncSourceDir) {
	return {
		...record,
		syncSourceDir,
		syncDirName: path.basename(record.skill.baseDir)
	};
}
function loadDiscoveredSkillRecords(params) {
	const discovered = discoverSkillCandidates(params);
	const maxSkillsLoadedPerSource = Math.max(0, params.limits.maxSkillsLoadedPerSource);
	const loadCandidate = (candidate) => loadContainedSkillRecords({
		skillDir: candidate.skillDir,
		source: params.source,
		maxSkillFileBytes: params.limits.maxSkillFileBytes,
		canonicalSkillDir: canonicalSkillDirForSource(params.source, candidate.skillDirRealPath),
		rejectHardlinks: params.rejectHardlinks
	});
	if (discovered.configuredRootCandidate) {
		const rootRecords = loadCandidate(discovered.configuredRootCandidate);
		if (rootRecords.length > 0) return rootRecords;
	}
	const loadedSkills = [];
	for (const candidate of discovered.candidates) {
		if (!discovered.rootIsSkill && loadedSkills.length >= maxSkillsLoadedPerSource) break;
		loadedSkills.push(...loadCandidate(candidate));
	}
	if (loadedSkills.length > maxSkillsLoadedPerSource && !discovered.rootIsSkill) return loadedSkills.toSorted((a, b) => a.skill.name.localeCompare(b.skill.name, "en")).slice(0, maxSkillsLoadedPerSource);
	return loadedSkills;
}
function loadGeneratedPluginSkillRecords(params) {
	const candidates = discoverPluginSkills({
		...params,
		pluginSkillDirs: params.pluginSkillRoots.map((root) => root.dir)
	});
	const maxSkillsLoadedPerSource = Math.max(0, params.limits.maxSkillsLoadedPerSource);
	const loadedSkills = [];
	for (const candidate of candidates) {
		const pluginRoot = params.pluginSkillRoots.find((root) => {
			const rootRealPath = tryRealpath(root.dir);
			return rootRealPath !== null && isPathInside(rootRealPath, candidate.skillDirRealPath);
		});
		const loadedRecords = loadContainedSkillRecords({
			skillDir: candidate.skillDir,
			source: params.source,
			maxSkillFileBytes: params.limits.maxSkillFileBytes,
			rejectHardlinks: pluginRoot?.rejectHardlinks ?? true
		});
		loadedSkills.push(...loadedRecords.map((record) => setSyncSourceForPluginSkill(record, candidate.skillDirRealPath)));
		if (loadedSkills.length >= maxSkillsLoadedPerSource) break;
	}
	if (loadedSkills.length > maxSkillsLoadedPerSource) return loadedSkills.toSorted((a, b) => a.skill.name.localeCompare(b.skill.name, "en")).slice(0, maxSkillsLoadedPerSource);
	return loadedSkills;
}
function loadSkillEntries(workspaceDir, opts) {
	const workspaceOnly = opts?.workspaceOnly === true;
	const workspaceSkillsDir = opts?.workspaceSkillsDir ?? path.resolve(workspaceDir, "skills");
	const configuredCustodianAgentId = opts?.config ? tryResolveAmbientOwnerAgentId(opts.config) : void 0;
	const custodianAgentId = !workspaceOnly && opts?.agentId && configuredCustodianAgentId && normalizeAgentId(opts.agentId) === configuredCustodianAgentId ? configuredCustodianAgentId : void 0;
	const osHomeDir = resolveSkillsUserHomeDir();
	const cacheKey = JSON.stringify([
		workspaceDir,
		workspaceSkillsDir,
		workspaceOnly,
		custodianAgentId,
		opts?.managedSkillsDir,
		opts?.bundledSkillsDir,
		opts?.pluginSkillsDir,
		opts?.config ? fingerprintSkillSnapshotConfig(opts.config) : void 0,
		osHomeDir,
		process.env.OPENCLAW_STATE_DIR,
		getSkillsSnapshotVersion(workspaceDir)
	]);
	const cachedEntries = skillEntryCache.get(cacheKey);
	if (cachedEntries) return cachedEntries;
	const limits = resolveSkillDiscoveryLimits(opts?.config);
	const allowedSymlinkTargetRealPaths = resolveAllowedSkillSymlinkTargetRealPaths(opts?.config);
	const loadSkills = (params) => loadDiscoveredSkillRecords({
		...params,
		limits,
		allowedSymlinkTargetRealPaths,
		rejectHardlinks: params.rejectHardlinks ?? shouldRejectHardlinkedPluginFiles({
			origin: resolveSkillTelemetrySourceValue(params.source) === "bundled" ? "bundled" : "workspace",
			rootDir: params.dir
		})
	});
	const managedSkillsDir = opts?.managedSkillsDir ?? path.join(CONFIG_DIR, "skills");
	const bundledSkillsDir = workspaceOnly ? void 0 : opts?.bundledSkillsDir ?? resolveBundledSkillsDir();
	const pluginSkillsDir = opts?.pluginSkillsDir ?? path.join(CONFIG_DIR, "plugin-skills");
	const extraDirs = normalizeTrimmedStringList(workspaceOnly ? [] : opts?.config?.skills?.load?.extraDirs ?? []);
	const pluginSkillRoots = workspaceOnly ? [] : opts?.pluginMetadataSnapshot ? resolvePluginSkillRootsFromMetadata({
		workspaceDir,
		config: opts.config,
		pluginSkillsDir,
		metadataSnapshot: opts.pluginMetadataSnapshot
	}) : resolvePluginSkillRoots({
		workspaceDir,
		config: opts?.config,
		pluginSkillsDir
	});
	const bundledSkills = bundledSkillsDir ? loadSkills({
		dir: bundledSkillsDir,
		source: "openclaw-bundled"
	}) : [];
	const custodianSkillsDir = bundledSkillsDir && custodianAgentId ? path.join(path.dirname(bundledSkillsDir), CUSTODIAN_SKILLS_DIR_NAME) : void 0;
	const custodianSkills = custodianSkillsDir ? loadSkills({
		dir: custodianSkillsDir,
		source: "openclaw-custodian"
	}) : [];
	const extraSkills = [
		...extraDirs.flatMap((dir) => loadSkills({
			dir: resolveUserPath(dir),
			source: "openclaw-extra"
		})),
		...pluginSkillRoots.flatMap((root) => loadSkills({
			dir: root.dir,
			source: "openclaw-extra",
			rejectHardlinks: root.rejectHardlinks
		})),
		...loadGeneratedPluginSkillRecords({
			pluginSkillsDir,
			pluginSkillRoots,
			source: "openclaw-extra",
			limits
		})
	];
	const managedSkills = workspaceOnly ? [] : loadSkills({
		dir: managedSkillsDir,
		source: "openclaw-managed"
	});
	const personalAgentsSkillsDir = osHomeDir ? path.resolve(osHomeDir, ".agents", "skills") : path.resolve(".agents", "skills");
	const personalAgentsSkills = workspaceOnly || !isDefaultStateDir() ? [] : loadSkills({
		dir: personalAgentsSkillsDir,
		source: "agents-skills-personal"
	});
	const projectAgentsSkillsDir = path.resolve(workspaceDir, ".agents", "skills");
	const projectAgentsSkills = workspaceOnly ? [] : loadSkills({
		dir: projectAgentsSkillsDir,
		source: "agents-skills-project"
	});
	const workspaceSkills = loadSkills({
		dir: workspaceSkillsDir,
		source: "openclaw-workspace"
	});
	const merged = /* @__PURE__ */ new Map();
	const mergeRecord = (record) => {
		const replaced = merged.get(record.skill.name);
		if (replaced) warnSkillPrecedenceCollision(record.skill, replaced.skill, workspaceDir);
		merged.set(record.skill.name, record);
	};
	for (const record of extraSkills) mergeRecord(record);
	const bundledTierSkills = [...bundledSkills, ...custodianSkills].toSorted((left, right) => left.skill.name.localeCompare(right.skill.name, "en") || left.skill.source.localeCompare(right.skill.source, "en"));
	for (const record of bundledTierSkills) mergeRecord(record);
	for (const record of managedSkills) mergeRecord(record);
	for (const record of personalAgentsSkills) mergeRecord(record);
	for (const record of projectAgentsSkills) mergeRecord(record);
	for (const record of workspaceSkills) mergeRecord(record);
	const entries = Array.from(merged.values()).toSorted((a, b) => a.skill.name.localeCompare(b.skill.name, "en")).map((record) => {
		const skill = record.skill;
		const frontmatter = record.frontmatter ?? readSkillFrontmatterSafe({
			rootDir: skill.baseDir,
			filePath: skill.filePath,
			maxBytes: limits.maxSkillFileBytes,
			rejectHardlinks: record.rejectHardlinks
		}) ?? {};
		const invocation = resolveSkillInvocationPolicy(frontmatter);
		const entry = {
			skill,
			frontmatter,
			metadata: resolveSkillEntryMetadata({
				frontmatter,
				skillDir: skill.baseDir
			}),
			invocation,
			exposure: {
				includeInRuntimeRegistry: true,
				includeInAvailableSkillsPrompt: !invocation.disableModelInvocation,
				userInvocable: invocation.userInvocable ?? true
			}
		};
		if (record.syncSourceDir !== void 0) entry.syncSourceDir = record.syncSourceDir;
		if (record.syncDirName !== void 0) entry.syncDirName = record.syncDirName;
		return entry;
	});
	skillEntryCache.set(cacheKey, entries);
	pruneMapToMaxSize(skillEntryCache, MAX_SKILL_ENTRY_CACHE_SIZE);
	return entries;
}
function resolveEffectiveWorkspaceSkillFilter(opts) {
	if (opts?.skillFilter !== void 0) return normalizeSkillFilter(opts.skillFilter);
	if (opts?.agentSkillFilter === "ignore" || !opts?.config || !opts.agentId) return;
	return resolveEffectiveAgentSkillFilter(opts.config, opts.agentId);
}
function resolveWorkspaceSkillPromptEntries(workspaceDir, opts) {
	const skillFilter = resolveEffectiveWorkspaceSkillFilter(opts);
	return {
		eligible: filterSkillEntries(opts?.entries ?? mergeRemoteNodeSkillEntries(loadSkillEntries(workspaceDir, opts), {
			canExec: opts?.eligibility?.nodeSkills?.canExec,
			node: opts?.eligibility?.nodeSkills?.node
		}), opts?.config, skillFilter, opts?.skillOverrides, opts?.eligibility),
		skillFilter
	};
}
function loadWorkspaceSkills(workspaceDir, opts) {
	const entries = mergeRemoteNodeSkillEntries(loadSkillEntries(workspaceDir, opts), {
		canExec: opts?.eligibility?.nodeSkills?.canExec,
		node: opts?.eligibility?.nodeSkills?.node
	});
	const effectiveSkillFilter = resolveEffectiveWorkspaceSkillFilter(opts);
	if (effectiveSkillFilter === void 0 && opts?.skillOverrides === void 0 && opts?.eligibility === void 0) return entries;
	return filterSkillEntries(entries, opts?.config, effectiveSkillFilter, opts?.skillOverrides, opts?.eligibility);
}
/** Loads agent-workspace skills first, then execution-directory OpenClaw skills. */
function loadMergedWorkspaceSkills(params) {
	const { agentWorkspaceDir, executionSkillsDir } = normalizeWorkspaceSkillRoots(params);
	if (!executionSkillsDir) return loadWorkspaceSkills(agentWorkspaceDir, params);
	const agentEntries = mergeRemoteNodeSkillEntries(loadSkillEntries(agentWorkspaceDir, params), {
		canExec: params.eligibility?.nodeSkills?.canExec,
		node: params.eligibility?.nodeSkills?.node
	});
	const agentEntriesByName = new Map(agentEntries.map((entry) => [entry.skill.name, entry]));
	const executionEntries = loadSkillEntries(agentWorkspaceDir, {
		...params,
		workspaceOnly: true,
		workspaceSkillsDir: executionSkillsDir
	}).filter((entry) => {
		const agentEntry = agentEntriesByName.get(entry.skill.name);
		if (!agentEntry) return true;
		warnSkillPrecedenceCollision(agentEntry.skill, entry.skill, agentWorkspaceDir);
		return false;
	});
	const effectiveSkillFilter = resolveEffectiveWorkspaceSkillFilter(params);
	return filterSkillEntries([...agentEntries, ...executionEntries], params.config, effectiveSkillFilter, params.skillOverrides, params.eligibility);
}
function loadVisibleSkills(workspaceDir, opts) {
	const entries = mergeRemoteNodeSkillEntries(loadSkillEntries(workspaceDir, opts), {
		canExec: opts?.eligibility?.nodeSkills?.canExec,
		node: opts?.eligibility?.nodeSkills?.node
	});
	const effectiveSkillFilter = resolveEffectiveWorkspaceSkillFilter(opts);
	return filterSkillEntries(entries, opts?.config, effectiveSkillFilter, opts?.skillOverrides, opts?.eligibility);
}
function filterWorkspaceSkills(entries, opts) {
	return filterSkillEntries(entries, opts?.config, opts?.skillFilter, opts?.skillOverrides, opts?.eligibility);
}
//#endregion
export { normalizeWorkspaceSkillRoots as a, resetSkillSnapshotConfigFingerprintCache as c, removeRemoteNodeSkills as d, replaceRemoteNodeSkills as f, loadWorkspaceSkills as i, mergeRemoteNodeSkillEntries as l, loadMergedWorkspaceSkills as n, resolveWorkspaceSkillPromptEntries as o, setRemoteSkillConnectionReconciler as p, loadVisibleSkills as r, fingerprintSkillSnapshotConfig as s, filterWorkspaceSkills as t, recordRemoteSkillNodeInfo as u };
