@@ -24,11 +24,12 @@ import {
2424} from "@webstudio-is/project-build/state" ;
2525import { publicApiOperations } from "@webstudio-is/protocol" ;
2626import { contentEngineLimits } from "@webstudio-is/content-engine/limits" ;
27- import { ROOT_INSTANCE_ID } from "@webstudio-is/sdk" ;
27+ import { ROOT_INSTANCE_ID , type Pages } from "@webstudio-is/sdk" ;
2828import { updatePersistedMcpCheckpoint } from "./mcp-checkpoint" ;
2929import {
3030 createCliProjectSessionStorage ,
3131 createIssueReportFailureTracker ,
32+ createIssueReportTargetTracker ,
3233} from "../project-session" ;
3334import {
3435 __testing__ ,
@@ -40,6 +41,7 @@ import {
4041const {
4142 getMcpDownloadAsset,
4243 getIssueReportFailureFile,
44+ getIssueReportPageId,
4345 persistIssueReportFailure,
4446 restoreIssueReportFailure,
4547 assertSingleOpCallToolSupported,
@@ -511,6 +513,69 @@ test("persists sanitized issue-report failure diagnostics for the next CLI proce
511513 } ) ;
512514} ) ;
513515
516+ test ( "resolves a screenshot route to a page ID without retaining its URL" , ( ) => {
517+ const pages = {
518+ homePageId : "home" ,
519+ rootFolderId : "root" ,
520+ pages : new Map ( [
521+ [ "home" , { id : "home" , path : "" } ] ,
522+ [ "blog" , { id : "blog" , path : "/blog/:slug" } ] ,
523+ ] ) ,
524+ folders : new Map ( ) ,
525+ } as unknown as Pages ;
526+ expect (
527+ getIssueReportPageId (
528+ "screenshot" ,
529+ { path : "/blog/private-post?authToken=secret" } ,
530+ pages
531+ )
532+ ) . toBe ( "blog" ) ;
533+ expect (
534+ getIssueReportPageId (
535+ "screenshot" ,
536+ { path : "https://private.example/" } ,
537+ pages
538+ )
539+ ) . toBeUndefined ( ) ;
540+ expect (
541+ getIssueReportPageId (
542+ "screenshot" ,
543+ { path : "/blog/private-post" , url : "https://private.example/" } ,
544+ pages
545+ )
546+ ) . toBeUndefined ( ) ;
547+ expect (
548+ getIssueReportPageId ( "list-pages" , { path : "/blog/private-post" } , pages )
549+ ) . toBeUndefined ( ) ;
550+ } ) ;
551+
552+ test ( "restores a successful tool target across CLI processes without private input" , async ( ) => {
553+ const projectRoot = await mkdtemp (
554+ path . join ( tmpdir ( ) , "webstudio-mcp-target-report-" )
555+ ) ;
556+ tempDirs . push ( projectRoot ) ;
557+ const filePath = getIssueReportFailureFile ( projectRoot , "project-1" ) ;
558+ const firstTarget = createIssueReportTargetTracker ( ) ;
559+ firstTarget . record (
560+ "screenshot" ,
561+ { path : "/blog/private-post?authToken=secret" } ,
562+ "page-1"
563+ ) ;
564+ await persistIssueReportFailure ( filePath , undefined , firstTarget . snapshot ( ) ) ;
565+ const restoredTarget = createIssueReportTargetTracker ( ) ;
566+ await restoreIssueReportFailure (
567+ filePath ,
568+ createIssueReportFailureTracker ( ) ,
569+ restoredTarget
570+ ) ;
571+ expect ( restoredTarget . get ( ) ) . toEqual ( {
572+ tool : "screenshot" ,
573+ entityIds : [ { field : "pageId" , id : "page-1" } ] ,
574+ } ) ;
575+ expect ( await readFile ( filePath , "utf8" ) ) . not . toContain ( "private-post" ) ;
576+ expect ( await readFile ( filePath , "utf8" ) ) . not . toContain ( "authToken" ) ;
577+ } ) ;
578+
514579test ( "preserves a destructive confirmation failure reason without the token or plan" , async ( ) => {
515580 const projectRoot = await mkdtemp (
516581 path . join ( tmpdir ( ) , "webstudio-mcp-confirmation-report-" )
0 commit comments