


























@@ -14,16 +14,19 @@ import {
1414relativePathEscapesContainerRoot,
1515} from "./path-utils.js";
161617+type RemoteMountSource = "workspace" | "agent" | "protectedSkill";
18+1719type ResolvedRemotePath = SandboxResolvedPath & {
1820writable: boolean;
1921mountRootPath: string;
20-source: "workspace" | "agent";
22+source: RemoteMountSource;
2123};
22242325type MountInfo = {
26+localRoot: string;
2427containerRoot: string;
2528writable: boolean;
26-source: "workspace" | "agent";
29+source: RemoteMountSource;
2730};
28312932export type RemoteShellSandboxHandle = {
@@ -239,9 +242,14 @@ class RemoteShellSandboxFsBridge implements SandboxFsBridge {
239242}
240243241244private getMounts(): MountInfo[] {
245+const workspaceRoot = path.resolve(this.sandbox.workspaceDir);
246+const agentRoot = path.resolve(this.sandbox.agentWorkspaceDir);
247+const workspaceContainerRoot = normalizeContainerPath(this.runtime.remoteWorkspaceDir);
248+const agentContainerRoot = normalizeContainerPath(this.runtime.remoteAgentWorkspaceDir);
242249const mounts: MountInfo[] = [
243250{
244-containerRoot: normalizeContainerPath(this.runtime.remoteWorkspaceDir),
251+localRoot: workspaceRoot,
252+containerRoot: workspaceContainerRoot,
245253writable: this.sandbox.workspaceAccess === "rw",
246254source: "workspace",
247255},
@@ -251,19 +259,27 @@ class RemoteShellSandboxFsBridge implements SandboxFsBridge {
251259path.resolve(this.sandbox.agentWorkspaceDir) !== path.resolve(this.sandbox.workspaceDir)
252260) {
253261mounts.push({
254-containerRoot: normalizeContainerPath(this.runtime.remoteAgentWorkspaceDir),
262+localRoot: agentRoot,
263+containerRoot: agentContainerRoot,
255264writable: this.sandbox.workspaceAccess === "rw",
256265source: "agent",
257266});
258267}
268+if (this.sandbox.workspaceAccess === "rw") {
269+mounts.push(
270+ ...buildRemoteProtectedSkillMounts({
271+localRoot: agentRoot,
272+ workspaceContainerRoot,
273+ agentContainerRoot,
274+includeAgentMount: path.resolve(this.sandbox.agentWorkspaceDir) !== path.resolve(this.sandbox.workspaceDir),
275+}),
276+);
277+}
259278return mounts;
260279}
261280262281private resolveTarget(params: { filePath: string; cwd?: string }): ResolvedRemotePath {
263282const workspaceRoot = path.resolve(this.sandbox.workspaceDir);
264-const agentRoot = path.resolve(this.sandbox.agentWorkspaceDir);
265-const workspaceContainerRoot = normalizeContainerPath(this.runtime.remoteWorkspaceDir);
266-const agentContainerRoot = normalizeContainerPath(this.runtime.remoteAgentWorkspaceDir);
267283const mounts = this.getMounts();
268284const input = params.filePath.trim();
269285const inputPosix = input.replace(/\\/g, "/");
@@ -281,22 +297,14 @@ class RemoteShellSandboxFsBridge implements SandboxFsBridge {
281297const hostCandidate = path.isAbsolute(input)
282298 ? path.resolve(input)
283299 : path.resolve(hostCwd, input);
284-if (isPathInside(workspaceRoot, hostCandidate)) {
285-const relative = toPosixRelative(workspaceRoot, hostCandidate);
286-return this.toResolvedPath({
287-mount: mounts[0],
288-containerPath: relative
289- ? path.posix.join(workspaceContainerRoot, relative)
290- : workspaceContainerRoot,
291-});
292-}
293-if (mounts[1] && isPathInside(agentRoot, hostCandidate)) {
294-const relative = toPosixRelative(agentRoot, hostCandidate);
300+const hostMount = this.resolveMountByLocalPath(mounts, hostCandidate);
301+if (hostMount) {
302+const relative = toPosixRelative(hostMount.localRoot, hostCandidate);
295303return this.toResolvedPath({
296-mount: mounts[1],
304+mount: hostMount,
297305containerPath: relative
298- ? path.posix.join(agentContainerRoot, relative)
299- : agentContainerRoot,
306+ ? path.posix.join(hostMount.containerRoot, relative)
307+ : hostMount.containerRoot,
300308});
301309}
302310@@ -326,10 +334,10 @@ class RemoteShellSandboxFsBridge implements SandboxFsBridge {
326334}
327335return {
328336relativePath:
329-params.mount.source === "workspace"
337+params.mount.source === "workspace" || params.mount.source === "protectedSkill"
330338 ? relative === "."
331339 ? ""
332- : relative
340+ : path.posix.relative(this.runtime.remoteWorkspaceDir, params.containerPath)
333341 : relative === "."
334342 ? params.mount.containerRoot
335343 : `${params.mount.containerRoot}/${relative}`,
@@ -344,7 +352,7 @@ class RemoteShellSandboxFsBridge implements SandboxFsBridge {
344352mounts: MountInfo[],
345353containerPath: string,
346354): MountInfo | null {
347-const ordered = [...mounts].toSorted((a, b) => b.containerRoot.length - a.containerRoot.length);
355+const ordered = [...mounts].toSorted(compareRemoteMountsByContainerPath);
348356for (const mount of ordered) {
349357if (isPathInsideContainerRoot(mount.containerRoot, containerPath)) {
350358return mount;
@@ -353,6 +361,16 @@ class RemoteShellSandboxFsBridge implements SandboxFsBridge {
353361return null;
354362}
355363364+private resolveMountByLocalPath(mounts: MountInfo[], localPath: string): MountInfo | null {
365+const ordered = [...mounts].toSorted(compareRemoteMountsByLocalPath);
366+for (const mount of ordered) {
367+if (isPathInside(mount.localRoot, localPath)) {
368+return mount;
369+}
370+}
371+return null;
372+}
373+356374private ensureWritable(target: ResolvedRemotePath, action: string) {
357375if (this.sandbox.workspaceAccess !== "rw" || !target.writable) {
358376throw new Error(`Sandbox path is read-only; cannot ${action}: ${target.containerPath}`);
@@ -509,6 +527,63 @@ class RemoteShellSandboxFsBridge implements SandboxFsBridge {
509527}
510528}
511529530+function buildRemoteProtectedSkillMounts(params: {
531+localRoot: string;
532+workspaceContainerRoot: string;
533+agentContainerRoot: string;
534+includeAgentMount: boolean;
535+}): MountInfo[] {
536+const mounts: MountInfo[] = [
537+{
538+localRoot: path.join(params.localRoot, "skills"),
539+containerRoot: path.posix.join(params.workspaceContainerRoot, "skills"),
540+writable: false,
541+source: "protectedSkill",
542+},
543+{
544+localRoot: path.join(params.localRoot, ".agents", "skills"),
545+containerRoot: path.posix.join(params.workspaceContainerRoot, ".agents", "skills"),
546+writable: false,
547+source: "protectedSkill",
548+},
549+];
550+if (params.includeAgentMount) {
551+mounts.push(
552+{
553+localRoot: path.join(params.localRoot, "skills"),
554+containerRoot: path.posix.join(params.agentContainerRoot, "skills"),
555+writable: false,
556+source: "protectedSkill",
557+},
558+{
559+localRoot: path.join(params.localRoot, ".agents", "skills"),
560+containerRoot: path.posix.join(params.agentContainerRoot, ".agents", "skills"),
561+writable: false,
562+source: "protectedSkill",
563+},
564+);
565+}
566+return mounts;
567+}
568+569+function compareRemoteMountsByContainerPath(a: MountInfo, b: MountInfo): number {
570+return b.containerRoot.length - a.containerRoot.length || mountPriority(b) - mountPriority(a);
571+}
572+573+function compareRemoteMountsByLocalPath(a: MountInfo, b: MountInfo): number {
574+return b.localRoot.length - a.localRoot.length || mountPriority(b) - mountPriority(a);
575+}
576+577+function mountPriority(mount: MountInfo): number {
578+if (mount.source === "protectedSkill") {
579+return 2;
580+}
581+if (mount.source === "agent") {
582+return 1;
583+}
584+return 0;
585+}
586+512587function normalizeContainerPath(value: string): string {
513588const normalized = normalizeSandboxContainerPath(value.trim() || "/");
514589return normalized.startsWith("/") ? normalized : `/${normalized}`;
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。