



















@@ -404,6 +404,53 @@ function isPathInsideAnyRoot(rootRealPaths: readonly string[], candidateRealPath
404404return rootRealPaths.some((rootRealPath) => isPathInside(rootRealPath, candidateRealPath));
405405}
406406407+function shouldEnforceConfiguredSkillRootContainment(source: string): boolean {
408+return source !== "openclaw-managed" && source !== "agents-skills-personal";
409+}
410+411+function shouldUseConfiguredSymlinkTargets(source: string): boolean {
412+return (
413+source === "openclaw-workspace" ||
414+source === "openclaw-extra" ||
415+source === "agents-skills-project"
416+);
417+}
418+419+function resolveSkillRootCandidatePath(params: {
420+source: string;
421+rootDir: string;
422+rootRealPath: string;
423+candidatePath: string;
424+allowedSymlinkTargetRealPaths: readonly string[];
425+}): string | null {
426+if (!shouldEnforceConfiguredSkillRootContainment(params.source)) {
427+return tryRealpath(params.candidatePath);
428+}
429+return resolveContainedSkillPath({
430+source: params.source,
431+rootDir: params.rootDir,
432+rootRealPath: params.rootRealPath,
433+candidatePath: params.candidatePath,
434+allowedSymlinkTargetRealPaths: shouldUseConfiguredSymlinkTargets(params.source)
435+ ? params.allowedSymlinkTargetRealPaths
436+ : [],
437+});
438+}
439+440+function resolveSkillFilePath(params: {
441+source: string;
442+skillDir: string;
443+skillDirRealPath: string;
444+candidatePath: string;
445+}): string | null {
446+return resolveContainedSkillPath({
447+source: params.source,
448+rootDir: params.skillDir,
449+rootRealPath: params.skillDirRealPath,
450+candidatePath: params.candidatePath,
451+});
452+}
453+407454function resolvePluginSkillRootRealPaths(pluginSkillDirs: readonly string[]): string[] {
408455return pluginSkillDirs
409456.map((dir) => tryRealpath(dir))
@@ -535,7 +582,7 @@ function loadSkillEntries(
535582maxEntriesToScan: limits.maxCandidatesPerRoot,
536583});
537584const baseDir = resolved.baseDir;
538-const baseDirRealPath = resolveContainedSkillPath({
585+const baseDirRealPath = resolveSkillRootCandidatePath({
539586source: params.source,
540587 rootDir,
541588 rootRealPath,
@@ -549,12 +596,11 @@ function loadSkillEntries(
549596// If the root itself is a skill directory, just load it directly (but enforce size cap).
550597const rootSkillMd = path.join(baseDir, "SKILL.md");
551598if (fs.existsSync(rootSkillMd)) {
552-const rootSkillRealPath = resolveContainedSkillPath({
599+const rootSkillRealPath = resolveSkillFilePath({
553600source: params.source,
554-rootDir,
555-rootRealPath: baseDirRealPath,
601+skillDir: baseDir,
602+skillDirRealPath: baseDirRealPath,
556603candidatePath: rootSkillMd,
557- allowedSymlinkTargetRealPaths,
558604});
559605if (!rootSkillRealPath) {
560606return [];
@@ -642,7 +688,7 @@ function loadSkillEntries(
642688// skill directories (e.g. ~/.openclaw/skills/coze/koze-retrieval/SKILL.md).
643689for (const name of limitedChildren) {
644690const skillDir = path.join(baseDir, name);
645-const skillDirRealPath = resolveContainedSkillPath({
691+const skillDirRealPath = resolveSkillRootCandidatePath({
646692source: params.source,
647693 rootDir,
648694rootRealPath: baseDirRealPath,
@@ -654,12 +700,11 @@ function loadSkillEntries(
654700}
655701const skillMd = path.join(skillDir, "SKILL.md");
656702if (fs.existsSync(skillMd)) {
657-const skillMdRealPath = resolveContainedSkillPath({
703+const skillMdRealPath = resolveSkillFilePath({
658704source: params.source,
659-rootDir,
660-rootRealPath: baseDirRealPath,
705+skillDir,
706+skillDirRealPath,
661707candidatePath: skillMd,
662- allowedSymlinkTargetRealPaths,
663708});
664709if (skillMdRealPath) {
665710loadCandidateSkill({ skillDir, name, skillMdRealPath });
@@ -701,20 +746,21 @@ function loadSkillEntries(
701746const nestedDir = path.join(skillDir, nestedName);
702747const nestedSkillMd = path.join(nestedDir, "SKILL.md");
703748if (fs.existsSync(nestedSkillMd)) {
704-const nestedDirRealPath = resolveContainedSkillPath({
749+const nestedDirRealPath = resolveSkillRootCandidatePath({
705750source: params.source,
706751 rootDir,
707752rootRealPath: baseDirRealPath,
708753candidatePath: nestedDir,
709754 allowedSymlinkTargetRealPaths,
710755});
711-const nestedSkillMdRealPath = resolveContainedSkillPath({
712-source: params.source,
713- rootDir,
714-rootRealPath: baseDirRealPath,
715-candidatePath: nestedSkillMd,
716- allowedSymlinkTargetRealPaths,
717-});
756+const nestedSkillMdRealPath = nestedDirRealPath
757+ ? resolveSkillFilePath({
758+source: params.source,
759+skillDir: nestedDir,
760+skillDirRealPath: nestedDirRealPath,
761+candidatePath: nestedSkillMd,
762+})
763+ : null;
718764if (nestedDirRealPath && nestedSkillMdRealPath) {
719765loadCandidateSkill({
720766skillDir: nestedDir,
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。