11// Tests system prompt command output and bundled prompt section selection.
2+ import fs from "node:fs/promises" ;
3+ import os from "node:os" ;
4+ import path from "node:path" ;
25import { beforeEach , describe , expect , it , vi } from "vitest" ;
36import { resolveSessionAgentIds } from "../../agents/agent-scope.js" ;
47import { createOpenClawCodingTools } from "../../agents/agent-tools.js" ;
58import { resolveBootstrapContextForRun } from "../../agents/bootstrap-files.js" ;
6- import { resolveSandboxRuntimeStatus } from "../../agents/sandbox.js" ;
9+ import {
10+ ensureSandboxWorkspaceForSession ,
11+ resolveSandboxRuntimeStatus ,
12+ } from "../../agents/sandbox.js" ;
713import { buildAgentSystemPrompt } from "../../agents/system-prompt.js" ;
14+ import { resolveReusableWorkspaceSkillSnapshot } from "../../skills/runtime/session-snapshot.js" ;
815import { resolveCommandsSystemPromptBundle } from "./commands-system-prompt.js" ;
916import type { HandleCommandsParams } from "./commands-types.js" ;
1017
@@ -20,6 +27,7 @@ vi.mock("../../agents/bootstrap-files.js", () => ({
2027} ) ) ;
2128
2229vi . mock ( "../../agents/sandbox.js" , ( ) => ( {
30+ ensureSandboxWorkspaceForSession : vi . fn ( async ( ) => null ) ,
2331 resolveSandboxRuntimeStatus : vi . fn ( ( ) => ( { sandboxed : false , mode : "off" } ) ) ,
2432} ) ) ;
2533
@@ -130,6 +138,12 @@ describe("resolveCommandsSystemPromptBundle", () => {
130138 vi . clearAllMocks ( ) ;
131139 createOpenClawCodingToolsMock . mockClear ( ) ;
132140 createOpenClawCodingToolsMock . mockReturnValue ( [ ] ) ;
141+ vi . mocked ( ensureSandboxWorkspaceForSession ) . mockResolvedValue ( null ) ;
142+ vi . mocked ( resolveReusableWorkspaceSkillSnapshot ) . mockReturnValue ( {
143+ snapshot : { prompt : "" , skills : [ ] , resolvedSkills : [ ] } ,
144+ shouldRefresh : false ,
145+ snapshotVersion : "test-snapshot" ,
146+ } as never ) ;
133147 } ) ;
134148
135149 it ( "opts command tool builds into gateway subagent binding" , async ( ) => {
@@ -254,6 +268,69 @@ describe("resolveCommandsSystemPromptBundle", () => {
254268 expect ( sandboxInfo ?. elevated ?. fullAccessBlockedReason ) . toBe ( "host-policy" ) ;
255269 } ) ;
256270
271+ it ( "uses materialized sandbox skill paths for sandbox command prompts" , async ( ) => {
272+ const root = await fs . mkdtemp ( path . join ( os . tmpdir ( ) , "openclaw-command-sandbox-skills-" ) ) ;
273+ try {
274+ const workspaceDir = path . join ( root , "workspace" ) ;
275+ const skillsWorkspaceDir = path . join ( root , "state" , "sandbox-skills" ) ;
276+ const skillDir = path . join ( skillsWorkspaceDir , "skills" , "gog" ) ;
277+ await fs . mkdir ( skillDir , { recursive : true } ) ;
278+ await fs . writeFile (
279+ path . join ( skillDir , "SKILL.md" ) ,
280+ [ "---" , "name: gog" , "description: Gog skill" , "---" , "# Gog" , "" ] . join ( "\n" ) ,
281+ "utf8" ,
282+ ) ;
283+ const params = makeParams ( ) ;
284+ params . workspaceDir = workspaceDir ;
285+ vi . mocked ( resolveSandboxRuntimeStatus ) . mockReturnValue ( {
286+ sandboxed : true ,
287+ mode : "workspace-write" ,
288+ } as never ) ;
289+ vi . mocked ( ensureSandboxWorkspaceForSession ) . mockResolvedValue ( {
290+ workspaceDir,
291+ containerWorkdir : "/workspace" ,
292+ skillsWorkspaceDir,
293+ skillsEligibility : {
294+ remote : {
295+ platforms : [ "linux" ] ,
296+ hasBin : ( ) => true ,
297+ hasAnyBin : ( ) => true ,
298+ note : "sandbox" ,
299+ } ,
300+ } ,
301+ workspaceAccess : "rw" ,
302+ } as never ) ;
303+ vi . mocked ( resolveReusableWorkspaceSkillSnapshot ) . mockReturnValue ( {
304+ snapshot : {
305+ prompt :
306+ "<available_skills>~/.npm-global/lib/node_modules/openclaw/skills/gog/SKILL.md</available_skills>" ,
307+ skills : [ ] ,
308+ resolvedSkills : [ ] ,
309+ } ,
310+ shouldRefresh : false ,
311+ snapshotVersion : "host-snapshot" ,
312+ } as never ) ;
313+
314+ const result = await resolveCommandsSystemPromptBundle ( params ) ;
315+
316+ expect ( result . skillsPrompt ) . toContain (
317+ "/workspace/.openclaw/sandbox-skills/skills/gog/SKILL.md" ,
318+ ) ;
319+ expect ( result . skillsPrompt ) . not . toContain ( "~/.npm-global" ) ;
320+ expect ( vi . mocked ( resolveReusableWorkspaceSkillSnapshot ) ) . not . toHaveBeenCalled ( ) ;
321+ const promptParams = requireFirstArg (
322+ vi . mocked ( buildAgentSystemPrompt ) ,
323+ "buildAgentSystemPrompt" ,
324+ ) ;
325+ expect ( promptParams . skillsPrompt ) . toContain (
326+ "/workspace/.openclaw/sandbox-skills/skills/gog/SKILL.md" ,
327+ ) ;
328+ expect ( String ( promptParams . skillsPrompt ) ) . not . toContain ( "~/.npm-global" ) ;
329+ } finally {
330+ await fs . rm ( root , { recursive : true , force : true } ) ;
331+ }
332+ } ) ;
333+
257334 it ( "uses config-backed prompt settings for the target agent" , async ( ) => {
258335 vi . mocked ( resolveSandboxRuntimeStatus ) . mockReturnValue ( {
259336 sandboxed : false ,
0 commit comments