@uemcp/server 0.3.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +75 -0
  2. package/dist/index.mjs +1630 -269
  3. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import os, { tmpdir } from "node:os";
3
3
  import path from "node:path";
4
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
+ import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
5
5
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
6
  import { randomUUID } from "node:crypto";
7
7
  import { z } from "zod";
@@ -420,6 +420,45 @@ const BRIDGE_STOP_TRACE_STATES = [
420
420
  "not_tracing",
421
421
  "unsupported"
422
422
  ];
423
+ const BRIDGE_LIST_ACTORS_REQUEST_TYPE = "list_actors";
424
+ const BRIDGE_GET_ACTOR_DETAILS_REQUEST_TYPE = "get_actor_details";
425
+ const BRIDGE_GET_CURRENT_LEVEL_REQUEST_TYPE = "get_current_level";
426
+ const BRIDGE_SPAWN_ACTOR_REQUEST_TYPE = "spawn_actor";
427
+ const BRIDGE_DESTROY_ACTOR_REQUEST_TYPE = "destroy_actor";
428
+ const BRIDGE_UNDO_REQUEST_TYPE = "undo";
429
+ const BRIDGE_REDO_REQUEST_TYPE = "redo";
430
+ const BRIDGE_LEVEL_EDIT_STATES = [
431
+ "applied",
432
+ "validated",
433
+ "unsupported"
434
+ ];
435
+ const BRIDGE_SET_ACTOR_TRANSFORM_REQUEST_TYPE = "set_actor_transform";
436
+ const BRIDGE_SET_ACTOR_PROPERTY_REQUEST_TYPE = "set_actor_property";
437
+ const BRIDGE_SELECT_ACTORS_REQUEST_TYPE = "select_actors";
438
+ const BRIDGE_PROPERTY_VALUE_MAX_CHARS = 4096;
439
+ const BRIDGE_UNDO_STATES = [
440
+ "undone",
441
+ "nothing_to_undo",
442
+ "unsupported"
443
+ ];
444
+ const BRIDGE_REDO_STATES = [
445
+ "redone",
446
+ "nothing_to_redo",
447
+ "unsupported"
448
+ ];
449
+ const BRIDGE_OPEN_LEVEL_REQUEST_TYPE = "open_level";
450
+ const BRIDGE_GET_OPEN_LEVEL_STATUS_REQUEST_TYPE = "get_open_level_status";
451
+ const BRIDGE_OPEN_LEVEL_STATES = [
452
+ "opening",
453
+ "validated",
454
+ "unsupported"
455
+ ];
456
+ const BRIDGE_OPEN_LEVEL_STATUS_STATES = [
457
+ "opening",
458
+ "succeeded",
459
+ "failed",
460
+ "unknown_job"
461
+ ];
423
462
  const requestIdSchema = z.string().trim().min(1);
424
463
  const nonEmptyStringSchema = z.string().trim().min(1);
425
464
  const boundedStringSchema = z.string().trim().max(512);
@@ -490,7 +529,11 @@ const parentClassSchema = z.string().trim().min(1).max(128);
490
529
  const moduleNameSchema = z.string().trim().min(1).max(128);
491
530
  const classPathSchema = z.string().trim().min(1).max(512);
492
531
  const sourceFilePathSchema = z.string().trim().min(1);
493
- const selectedActorArraySchema = z.array(z.string()).max(100);
532
+ const bridgeSelectedActorSchema = z.object({
533
+ actorPath: z.string(),
534
+ label: z.string()
535
+ });
536
+ const selectedActorArraySchema = z.array(bridgeSelectedActorSchema).max(100);
494
537
  const activeModeArraySchema = z.array(z.string()).max(32);
495
538
  const packageNameArraySchema = z.array(z.string()).max(100);
496
539
  const screenshotPathSchema = z.string().trim().min(1);
@@ -511,35 +554,76 @@ const stopTraceStateSchema = z.enum(BRIDGE_STOP_TRACE_STATES);
511
554
  const traceChannelSchema = z.string().trim().min(1).max(64).regex(/^[A-Za-z0-9_]+$/);
512
555
  const traceChannelArraySchema = z.array(traceChannelSchema).max(32);
513
556
  const traceFileNameSchema = z.string().trim().min(1).max(128).regex(/^[A-Za-z0-9_-]+$/);
557
+ const actorPathSchema = z.string().trim().min(1).max(512);
558
+ const levelCursorSchema = z.number().int().min(0);
559
+ const levelMaxResultsSchema = z.number().int().min(1).max(100);
560
+ const actorDetailMemberLimitSchema = z.number().int().min(1).max(200);
561
+ const actorClassFilterArraySchema = z.array(assetPathSchema).max(32);
562
+ const levelEditStateSchema = z.enum(BRIDGE_LEVEL_EDIT_STATES);
563
+ const undoStateSchema = z.enum(BRIDGE_UNDO_STATES);
564
+ const redoStateSchema = z.enum(BRIDGE_REDO_STATES);
565
+ const openLevelStateSchema = z.enum(BRIDGE_OPEN_LEVEL_STATES);
566
+ const openLevelStatusStateSchema = z.enum(BRIDGE_OPEN_LEVEL_STATUS_STATES);
567
+ const openLevelJobIdSchema = z.string().trim().min(1);
568
+ const levelPathSchema = z.string().trim().min(1).max(512);
569
+ const actorPathBatchSchema = z.array(actorPathSchema).min(1).max(64);
570
+ const actorTagArraySchema = z.array(boundedStringSchema).max(16);
571
+ const vectorInputSchema = z.object({
572
+ x: z.number().default(0),
573
+ y: z.number().default(0),
574
+ z: z.number().default(0)
575
+ });
576
+ const rotatorInputSchema = z.object({
577
+ pitch: z.number().default(0),
578
+ roll: z.number().default(0),
579
+ yaw: z.number().default(0)
580
+ });
581
+ const transformInputSchema = z.object({
582
+ location: vectorInputSchema.default({
583
+ x: 0,
584
+ y: 0,
585
+ z: 0
586
+ }),
587
+ rotation: rotatorInputSchema.default({
588
+ pitch: 0,
589
+ roll: 0,
590
+ yaw: 0
591
+ }),
592
+ scale: vectorInputSchema.default({
593
+ x: 1,
594
+ y: 1,
595
+ z: 1
596
+ })
597
+ });
514
598
  const memoryMegabytesSchema = z.number().min(0);
515
599
  const frameTimingMillisecondsSchema = z.number().min(0);
516
600
  z.object({
517
601
  requestId: requestIdSchema,
518
602
  type: z.literal(BRIDGE_PING_REQUEST_TYPE),
519
- version: z.literal(1)
603
+ version: z.literal(2)
520
604
  });
521
605
  z.object({
522
606
  requestId: requestIdSchema,
523
607
  type: z.literal(BRIDGE_START_LIVE_COMPILE_REQUEST_TYPE),
524
- version: z.literal(1)
608
+ version: z.literal(2)
525
609
  });
526
610
  z.object({
527
611
  requestId: requestIdSchema,
528
612
  type: z.literal(BRIDGE_START_HOT_RELOAD_REQUEST_TYPE),
529
- version: z.literal(1)
613
+ version: z.literal(2)
530
614
  });
531
615
  const bridgeGetCompileStatusRequestSchema = z.object({
532
616
  compileJobId: compileJobIdSchema,
533
617
  requestId: requestIdSchema,
534
618
  type: z.literal(BRIDGE_GET_COMPILE_STATUS_REQUEST_TYPE),
535
- version: z.literal(1)
619
+ version: z.literal(2)
536
620
  });
537
621
  const bridgeGetCrashCallstackRequestSchema = z.object({
538
622
  maxFrames: crashMaxFramesSchema,
539
623
  maxRecords: crashMaxRecordsSchema,
540
624
  requestId: requestIdSchema,
541
625
  type: z.literal(BRIDGE_GET_CRASH_CALLSTACK_REQUEST_TYPE),
542
- version: z.literal(1)
626
+ version: z.literal(2)
543
627
  });
544
628
  const bridgeErrorSchema = z.object({
545
629
  code: z.string().trim().min(1),
@@ -567,21 +651,21 @@ const bridgeLogSubscribeRequestSchema = z.union([
567
651
  requestId: requestIdSchema,
568
652
  source: z.literal("output_log"),
569
653
  type: z.literal(BRIDGE_SUBSCRIBE_LOGS_REQUEST_TYPE),
570
- version: z.literal(1)
654
+ version: z.literal(2)
571
655
  }),
572
656
  z.object({
573
657
  filters: bridgeMessageLogFilterSchema,
574
658
  requestId: requestIdSchema,
575
659
  source: z.literal("message_log"),
576
660
  type: z.literal(BRIDGE_SUBSCRIBE_LOGS_REQUEST_TYPE),
577
- version: z.literal(1)
661
+ version: z.literal(2)
578
662
  }),
579
663
  z.object({
580
664
  filters: bridgeDeviceOutputLogFilterSchema,
581
665
  requestId: requestIdSchema,
582
666
  source: z.literal("device_output_log"),
583
667
  type: z.literal(BRIDGE_SUBSCRIBE_LOGS_REQUEST_TYPE),
584
- version: z.literal(1)
668
+ version: z.literal(2)
585
669
  })
586
670
  ]);
587
671
  const bridgeLogDrainRequestSchema = z.object({
@@ -590,13 +674,13 @@ const bridgeLogDrainRequestSchema = z.object({
590
674
  requestId: requestIdSchema,
591
675
  subscriptionId: subscriptionIdSchema,
592
676
  type: z.literal(BRIDGE_DRAIN_LOGS_REQUEST_TYPE),
593
- version: z.literal(1)
677
+ version: z.literal(2)
594
678
  });
595
679
  z.object({
596
680
  requestId: requestIdSchema,
597
681
  subscriptionId: subscriptionIdSchema,
598
682
  type: z.literal(BRIDGE_UNSUBSCRIBE_LOGS_REQUEST_TYPE),
599
- version: z.literal(1)
683
+ version: z.literal(2)
600
684
  });
601
685
  const bridgeStartPieRequestSchema = z.object({
602
686
  map: pieMapSchema.optional(),
@@ -606,31 +690,31 @@ const bridgeStartPieRequestSchema = z.object({
606
690
  requestId: requestIdSchema,
607
691
  runUnderOneProcess: z.boolean().optional(),
608
692
  type: z.literal(BRIDGE_START_PIE_REQUEST_TYPE),
609
- version: z.literal(1)
693
+ version: z.literal(2)
610
694
  });
611
695
  const bridgeGetPieStatusRequestSchema = z.object({
612
696
  pieSessionId: pieSessionIdSchema.optional(),
613
697
  requestId: requestIdSchema,
614
698
  type: z.literal(BRIDGE_GET_PIE_STATUS_REQUEST_TYPE),
615
- version: z.literal(1)
699
+ version: z.literal(2)
616
700
  });
617
701
  z.object({
618
702
  requestId: requestIdSchema,
619
703
  type: z.literal(BRIDGE_LIST_LAUNCH_DEVICES_REQUEST_TYPE),
620
- version: z.literal(1)
704
+ version: z.literal(2)
621
705
  });
622
706
  const bridgeLaunchOnDeviceRequestSchema = z.object({
623
707
  deviceId: launchDeviceIdSchema,
624
708
  map: pieMapSchema.optional(),
625
709
  requestId: requestIdSchema,
626
710
  type: z.literal(BRIDGE_LAUNCH_ON_DEVICE_REQUEST_TYPE),
627
- version: z.literal(1)
711
+ version: z.literal(2)
628
712
  });
629
713
  const bridgeStopPieRequestSchema = z.object({
630
714
  pieSessionId: pieSessionIdSchema.optional(),
631
715
  requestId: requestIdSchema,
632
716
  type: z.literal(BRIDGE_STOP_PIE_REQUEST_TYPE),
633
- version: z.literal(1)
717
+ version: z.literal(2)
634
718
  });
635
719
  const bridgeListAutomationTestsRequestSchema = z.object({
636
720
  cursor: automationTestCursorSchema.default(0),
@@ -639,7 +723,7 @@ const bridgeListAutomationTestsRequestSchema = z.object({
639
723
  nameFilter: boundedStringSchema.optional(),
640
724
  requestId: requestIdSchema,
641
725
  type: z.literal(BRIDGE_LIST_AUTOMATION_TESTS_REQUEST_TYPE),
642
- version: z.literal(1)
726
+ version: z.literal(2)
643
727
  });
644
728
  const bridgeAutomationTestSchema = z.object({
645
729
  displayName: z.string(),
@@ -655,7 +739,7 @@ const bridgeStartAutomationRunRequestSchema = z.object({
655
739
  requestId: requestIdSchema,
656
740
  testNames: automationTestNamesSchema.default([]),
657
741
  type: z.literal(BRIDGE_START_AUTOMATION_RUN_REQUEST_TYPE),
658
- version: z.literal(1)
742
+ version: z.literal(2)
659
743
  }).refine((value) => value.testNames.length > 0 || value.nameFilter !== void 0 && value.nameFilter.length > 0, {
660
744
  message: "A start_automation_run request requires at least one of testNames or nameFilter.",
661
745
  path: ["testNames"]
@@ -666,30 +750,30 @@ const bridgeGetAutomationRunStatusRequestSchema = z.object({
666
750
  requestId: requestIdSchema,
667
751
  testRunId: testRunIdSchema,
668
752
  type: z.literal(BRIDGE_GET_AUTOMATION_RUN_STATUS_REQUEST_TYPE),
669
- version: z.literal(1)
753
+ version: z.literal(2)
670
754
  });
671
755
  const bridgeCancelAutomationRunRequestSchema = z.object({
672
756
  requestId: requestIdSchema,
673
757
  testRunId: testRunIdSchema,
674
758
  type: z.literal(BRIDGE_CANCEL_AUTOMATION_RUN_REQUEST_TYPE),
675
- version: z.literal(1)
759
+ version: z.literal(2)
676
760
  });
677
761
  z.object({
678
762
  requestId: requestIdSchema,
679
763
  type: z.literal(BRIDGE_GET_EDITOR_STATE_REQUEST_TYPE),
680
- version: z.literal(1)
764
+ version: z.literal(2)
681
765
  });
682
766
  const bridgeSaveDirtyPackagesRequestSchema = z.object({
683
767
  requestId: requestIdSchema,
684
768
  saveContentPackages: z.boolean().default(true),
685
769
  saveMapPackages: z.boolean().default(true),
686
770
  type: z.literal(BRIDGE_SAVE_DIRTY_PACKAGES_REQUEST_TYPE),
687
- version: z.literal(1)
771
+ version: z.literal(2)
688
772
  });
689
773
  z.object({
690
774
  requestId: requestIdSchema,
691
775
  type: z.literal(BRIDGE_CAPTURE_VIEWPORT_REQUEST_TYPE),
692
- version: z.literal(1)
776
+ version: z.literal(2)
693
777
  });
694
778
  const bridgeGetClassReflectionRequestSchema = z.object({
695
779
  className: reflectedTypeNameSchema,
@@ -700,7 +784,7 @@ const bridgeGetClassReflectionRequestSchema = z.object({
700
784
  propertyCursor: reflectionCursorSchema.default(0),
701
785
  requestId: requestIdSchema,
702
786
  type: z.literal(BRIDGE_GET_CLASS_REFLECTION_REQUEST_TYPE),
703
- version: z.literal(1)
787
+ version: z.literal(2)
704
788
  });
705
789
  const bridgeGetCdoDefaultsRequestSchema = z.object({
706
790
  className: reflectedTypeNameSchema,
@@ -710,30 +794,30 @@ const bridgeGetCdoDefaultsRequestSchema = z.object({
710
794
  nameFilter: boundedStringSchema.optional(),
711
795
  requestId: requestIdSchema,
712
796
  type: z.literal(BRIDGE_GET_CDO_DEFAULTS_REQUEST_TYPE),
713
- version: z.literal(1)
797
+ version: z.literal(2)
714
798
  });
715
799
  const bridgeExecConsoleCommandRequestSchema = z.object({
716
800
  command: consoleCommandSchema,
717
801
  requestId: requestIdSchema,
718
802
  type: z.literal(BRIDGE_EXEC_CONSOLE_COMMAND_REQUEST_TYPE),
719
- version: z.literal(1)
803
+ version: z.literal(2)
720
804
  });
721
805
  const bridgeStatSnapshotRequestSchema = z.object({
722
806
  requestId: requestIdSchema,
723
807
  type: z.literal(BRIDGE_STAT_SNAPSHOT_REQUEST_TYPE),
724
- version: z.literal(1)
808
+ version: z.literal(2)
725
809
  });
726
810
  const bridgeStartTraceRequestSchema = z.object({
727
811
  channels: traceChannelArraySchema.default([]),
728
812
  fileName: traceFileNameSchema.optional(),
729
813
  requestId: requestIdSchema,
730
814
  type: z.literal(BRIDGE_START_TRACE_REQUEST_TYPE),
731
- version: z.literal(1)
815
+ version: z.literal(2)
732
816
  });
733
817
  const bridgeStopTraceRequestSchema = z.object({
734
818
  requestId: requestIdSchema,
735
819
  type: z.literal(BRIDGE_STOP_TRACE_REQUEST_TYPE),
736
- version: z.literal(1)
820
+ version: z.literal(2)
737
821
  });
738
822
  const bridgeCreateCppClassRequestSchema = z.object({
739
823
  className: classNameSchema,
@@ -742,13 +826,13 @@ const bridgeCreateCppClassRequestSchema = z.object({
742
826
  parentClass: parentClassSchema,
743
827
  requestId: requestIdSchema,
744
828
  type: z.literal(BRIDGE_CREATE_CPP_CLASS_REQUEST_TYPE),
745
- version: z.literal(1)
829
+ version: z.literal(2)
746
830
  });
747
831
  const bridgeGetCreateClassStatusRequestSchema = z.object({
748
832
  createClassJobId: createClassJobIdSchema,
749
833
  requestId: requestIdSchema,
750
834
  type: z.literal(BRIDGE_GET_CREATE_CLASS_STATUS_REQUEST_TYPE),
751
- version: z.literal(1)
835
+ version: z.literal(2)
752
836
  });
753
837
  const bridgeFindAssetsRequestSchema = z.object({
754
838
  classNames: assetPathArraySchema.default([]),
@@ -760,11 +844,134 @@ const bridgeFindAssetsRequestSchema = z.object({
760
844
  recursivePaths: z.boolean().default(true),
761
845
  requestId: requestIdSchema,
762
846
  type: z.literal(BRIDGE_FIND_ASSETS_REQUEST_TYPE),
763
- version: z.literal(1)
847
+ version: z.literal(2)
764
848
  }).refine((value) => value.classNames.length > 0 || value.packagePaths.length > 0, {
765
849
  message: "A find_assets request requires at least one of classNames or packagePaths.",
766
850
  path: ["classNames"]
767
851
  });
852
+ const bridgeListActorsRequestSchema = z.object({
853
+ classNames: actorClassFilterArraySchema.default([]),
854
+ cursor: levelCursorSchema.default(0),
855
+ levelName: boundedStringSchema.optional(),
856
+ maxResults: levelMaxResultsSchema,
857
+ nameFilter: boundedStringSchema.optional(),
858
+ requestId: requestIdSchema,
859
+ tag: boundedStringSchema.optional(),
860
+ type: z.literal(BRIDGE_LIST_ACTORS_REQUEST_TYPE),
861
+ version: z.literal(2)
862
+ });
863
+ const bridgeGetActorDetailsRequestSchema = z.object({
864
+ actorPath: actorPathSchema,
865
+ componentCursor: levelCursorSchema.default(0),
866
+ includeInherited: z.boolean().default(false),
867
+ maxResults: actorDetailMemberLimitSchema,
868
+ propertyCursor: levelCursorSchema.default(0),
869
+ requestId: requestIdSchema,
870
+ type: z.literal(BRIDGE_GET_ACTOR_DETAILS_REQUEST_TYPE),
871
+ version: z.literal(2)
872
+ });
873
+ const bridgeGetCurrentLevelRequestSchema = z.object({
874
+ requestId: requestIdSchema,
875
+ type: z.literal(BRIDGE_GET_CURRENT_LEVEL_REQUEST_TYPE),
876
+ version: z.literal(2)
877
+ });
878
+ const bridgeSpawnActorEntrySchema = z.object({
879
+ classPath: assetPathSchema,
880
+ folderPath: boundedStringSchema.optional(),
881
+ label: boundedStringSchema.optional(),
882
+ tags: actorTagArraySchema.default([]),
883
+ transform: transformInputSchema.default({
884
+ location: {
885
+ x: 0,
886
+ y: 0,
887
+ z: 0
888
+ },
889
+ rotation: {
890
+ pitch: 0,
891
+ roll: 0,
892
+ yaw: 0
893
+ },
894
+ scale: {
895
+ x: 1,
896
+ y: 1,
897
+ z: 1
898
+ }
899
+ })
900
+ });
901
+ const bridgeSpawnActorRequestSchema = z.object({
902
+ dryRun: z.boolean().default(false),
903
+ requestId: requestIdSchema,
904
+ spawns: z.array(bridgeSpawnActorEntrySchema).min(1).max(64),
905
+ type: z.literal(BRIDGE_SPAWN_ACTOR_REQUEST_TYPE),
906
+ version: z.literal(2)
907
+ });
908
+ const bridgeDestroyActorRequestSchema = z.object({
909
+ actorPaths: actorPathBatchSchema,
910
+ dryRun: z.boolean().default(false),
911
+ requestId: requestIdSchema,
912
+ type: z.literal(BRIDGE_DESTROY_ACTOR_REQUEST_TYPE),
913
+ version: z.literal(2)
914
+ });
915
+ const bridgeSetActorTransformEntrySchema = z.object({
916
+ actorPath: actorPathSchema,
917
+ location: vectorInputSchema.optional(),
918
+ rotation: rotatorInputSchema.optional(),
919
+ scale: vectorInputSchema.optional()
920
+ }).refine((entry) => entry.location !== void 0 || entry.rotation !== void 0 || entry.scale !== void 0, {
921
+ message: "A set_actor_transform entry must supply at least one of location, rotation or scale.",
922
+ path: ["location"]
923
+ });
924
+ const bridgeSetActorTransformRequestSchema = z.object({
925
+ dryRun: z.boolean().default(false),
926
+ requestId: requestIdSchema,
927
+ transforms: z.array(bridgeSetActorTransformEntrySchema).min(1).max(64),
928
+ type: z.literal(BRIDGE_SET_ACTOR_TRANSFORM_REQUEST_TYPE),
929
+ version: z.literal(2)
930
+ });
931
+ const bridgeSetActorPropertyEntrySchema = z.object({
932
+ actorPath: actorPathSchema,
933
+ propertyPath: z.string().trim().min(1).max(512),
934
+ value: z.string().max(BRIDGE_PROPERTY_VALUE_MAX_CHARS)
935
+ });
936
+ const bridgeSetActorPropertyRequestSchema = z.object({
937
+ dryRun: z.boolean().default(false),
938
+ properties: z.array(bridgeSetActorPropertyEntrySchema).min(1).max(64),
939
+ requestId: requestIdSchema,
940
+ type: z.literal(BRIDGE_SET_ACTOR_PROPERTY_REQUEST_TYPE),
941
+ version: z.literal(2)
942
+ });
943
+ const bridgeSelectActorsRequestSchema = z.object({
944
+ actorPaths: z.array(actorPathSchema).max(64).default([]),
945
+ addToSelection: z.boolean().default(false),
946
+ dryRun: z.boolean().default(false),
947
+ requestId: requestIdSchema,
948
+ type: z.literal(BRIDGE_SELECT_ACTORS_REQUEST_TYPE),
949
+ version: z.literal(2)
950
+ });
951
+ const bridgeUndoRequestSchema = z.object({
952
+ requestId: requestIdSchema,
953
+ type: z.literal(BRIDGE_UNDO_REQUEST_TYPE),
954
+ version: z.literal(2)
955
+ });
956
+ const bridgeRedoRequestSchema = z.object({
957
+ requestId: requestIdSchema,
958
+ type: z.literal(BRIDGE_REDO_REQUEST_TYPE),
959
+ version: z.literal(2)
960
+ });
961
+ const bridgeOpenLevelRequestSchema = z.object({
962
+ dryRun: z.boolean().default(false),
963
+ levelPath: levelPathSchema,
964
+ requestId: requestIdSchema,
965
+ saveDirtyPackages: z.boolean().default(false),
966
+ type: z.literal(BRIDGE_OPEN_LEVEL_REQUEST_TYPE),
967
+ version: z.literal(2)
968
+ });
969
+ const bridgeGetOpenLevelStatusRequestSchema = z.object({
970
+ openLevelJobId: openLevelJobIdSchema,
971
+ requestId: requestIdSchema,
972
+ type: z.literal(BRIDGE_GET_OPEN_LEVEL_STATUS_REQUEST_TYPE),
973
+ version: z.literal(2)
974
+ });
768
975
  const bridgeGetAssetDependenciesRequestSchema = z.object({
769
976
  assetPath: assetPathSchema,
770
977
  categories: z.array(assetDependencyCategorySchema).max(BRIDGE_ASSET_DEPENDENCY_CATEGORIES.length).default(["package"]),
@@ -774,7 +981,7 @@ const bridgeGetAssetDependenciesRequestSchema = z.object({
774
981
  referencerCursor: assetCursorSchema.default(0),
775
982
  requestId: requestIdSchema,
776
983
  type: z.literal(BRIDGE_GET_ASSET_DEPENDENCIES_REQUEST_TYPE),
777
- version: z.literal(1)
984
+ version: z.literal(2)
778
985
  });
779
986
  const bridgeFindBlueprintReferencesRequestSchema = z.object({
780
987
  className: assetPathSchema,
@@ -783,7 +990,7 @@ const bridgeFindBlueprintReferencesRequestSchema = z.object({
783
990
  referenceKinds: z.array(blueprintReferenceKindSchema).max(BRIDGE_BLUEPRINT_REFERENCE_KINDS.length).default([]),
784
991
  requestId: requestIdSchema,
785
992
  type: z.literal(BRIDGE_FIND_BLUEPRINT_REFERENCES_REQUEST_TYPE),
786
- version: z.literal(1)
993
+ version: z.literal(2)
787
994
  });
788
995
  const bridgeGetBlueprintSummaryRequestSchema = z.object({
789
996
  blueprintPath: assetPathSchema,
@@ -795,7 +1002,7 @@ const bridgeGetBlueprintSummaryRequestSchema = z.object({
795
1002
  requestId: requestIdSchema,
796
1003
  type: z.literal(BRIDGE_GET_BLUEPRINT_SUMMARY_REQUEST_TYPE),
797
1004
  variableCursor: assetCursorSchema.default(0),
798
- version: z.literal(1)
1005
+ version: z.literal(2)
799
1006
  });
800
1007
  const bridgeGetBlueprintGraphRequestSchema = z.object({
801
1008
  blueprintPath: assetPathSchema,
@@ -805,7 +1012,7 @@ const bridgeGetBlueprintGraphRequestSchema = z.object({
805
1012
  nodeCursor: assetCursorSchema.default(0),
806
1013
  requestId: requestIdSchema,
807
1014
  type: z.literal(BRIDGE_GET_BLUEPRINT_GRAPH_REQUEST_TYPE),
808
- version: z.literal(1)
1015
+ version: z.literal(2)
809
1016
  });
810
1017
  const bridgeGetBlueprintCompileErrorsRequestSchema = z.object({
811
1018
  blueprintPaths: blueprintPathArraySchema,
@@ -813,13 +1020,13 @@ const bridgeGetBlueprintCompileErrorsRequestSchema = z.object({
813
1020
  maxResults: assetMaxResultsSchema,
814
1021
  requestId: requestIdSchema,
815
1022
  type: z.literal(BRIDGE_GET_BLUEPRINT_COMPILE_ERRORS_REQUEST_TYPE),
816
- version: z.literal(1)
1023
+ version: z.literal(2)
817
1024
  });
818
1025
  const bridgeCompileBlueprintRequestSchema = z.object({
819
1026
  blueprintPaths: blueprintPathArraySchema,
820
1027
  requestId: requestIdSchema,
821
1028
  type: z.literal(BRIDGE_COMPILE_BLUEPRINT_REQUEST_TYPE),
822
- version: z.literal(1)
1029
+ version: z.literal(2)
823
1030
  });
824
1031
  const bridgeGetBlueprintCompileStatusRequestSchema = z.object({
825
1032
  compileJobId: blueprintCompileJobIdSchema,
@@ -827,7 +1034,7 @@ const bridgeGetBlueprintCompileStatusRequestSchema = z.object({
827
1034
  maxResults: assetMaxResultsSchema,
828
1035
  requestId: requestIdSchema,
829
1036
  type: z.literal(BRIDGE_GET_BLUEPRINT_COMPILE_STATUS_REQUEST_TYPE),
830
- version: z.literal(1)
1037
+ version: z.literal(2)
831
1038
  });
832
1039
  const bridgeGetConfigValueRequestSchema = z.object({
833
1040
  file: configFileSchema,
@@ -835,7 +1042,7 @@ const bridgeGetConfigValueRequestSchema = z.object({
835
1042
  requestId: requestIdSchema,
836
1043
  section: configSectionSchema,
837
1044
  type: z.literal(BRIDGE_GET_CONFIG_VALUE_REQUEST_TYPE),
838
- version: z.literal(1)
1045
+ version: z.literal(2)
839
1046
  });
840
1047
  const bridgeSetConfigValueRequestSchema = z.object({
841
1048
  file: configFileSchema,
@@ -845,7 +1052,7 @@ const bridgeSetConfigValueRequestSchema = z.object({
845
1052
  section: configSectionSchema,
846
1053
  type: z.literal(BRIDGE_SET_CONFIG_VALUE_REQUEST_TYPE),
847
1054
  value: configValueSchema,
848
- version: z.literal(1)
1055
+ version: z.literal(2)
849
1056
  });
850
1057
  const bridgeListConfigSectionRequestSchema = z.object({
851
1058
  cursor: assetCursorSchema.default(0),
@@ -854,7 +1061,7 @@ const bridgeListConfigSectionRequestSchema = z.object({
854
1061
  requestId: requestIdSchema,
855
1062
  section: configSectionSchema,
856
1063
  type: z.literal(BRIDGE_LIST_CONFIG_SECTION_REQUEST_TYPE),
857
- version: z.literal(1)
1064
+ version: z.literal(2)
858
1065
  });
859
1066
  const bridgePingSuccessResponseSchema = z.object({
860
1067
  ok: z.literal(true),
@@ -864,7 +1071,7 @@ const bridgePingSuccessResponseSchema = z.object({
864
1071
  projectPath: z.string().trim().min(1),
865
1072
  requestId: requestIdSchema,
866
1073
  type: z.literal(BRIDGE_PING_REQUEST_TYPE),
867
- version: z.literal(1)
1074
+ version: z.literal(2)
868
1075
  });
869
1076
  const bridgeLogEventSchema = z.object({
870
1077
  category: z.string().trim().min(1).optional(),
@@ -884,7 +1091,7 @@ const bridgeLogSubscribeSuccessResponseSchema = z.object({
884
1091
  source: logSourceSchema,
885
1092
  subscriptionId: subscriptionIdSchema,
886
1093
  type: z.literal(BRIDGE_SUBSCRIBE_LOGS_REQUEST_TYPE),
887
- version: z.literal(1)
1094
+ version: z.literal(2)
888
1095
  });
889
1096
  const bridgeLogDrainSuccessResponseSchema = z.object({
890
1097
  droppedCount: z.number().int().min(0),
@@ -895,14 +1102,14 @@ const bridgeLogDrainSuccessResponseSchema = z.object({
895
1102
  source: logSourceSchema,
896
1103
  subscriptionId: subscriptionIdSchema,
897
1104
  type: z.literal(BRIDGE_DRAIN_LOGS_REQUEST_TYPE),
898
- version: z.literal(1)
1105
+ version: z.literal(2)
899
1106
  });
900
1107
  const bridgeLogUnsubscribeSuccessResponseSchema = z.object({
901
1108
  ok: z.literal(true),
902
1109
  requestId: requestIdSchema,
903
1110
  subscriptionId: subscriptionIdSchema,
904
1111
  type: z.literal(BRIDGE_UNSUBSCRIBE_LOGS_REQUEST_TYPE),
905
- version: z.literal(1)
1112
+ version: z.literal(2)
906
1113
  });
907
1114
  const bridgeStartPieSuccessResponseSchema = z.object({
908
1115
  message: z.string().trim().min(1).optional(),
@@ -911,7 +1118,7 @@ const bridgeStartPieSuccessResponseSchema = z.object({
911
1118
  requestId: requestIdSchema,
912
1119
  state: pieStateSchema,
913
1120
  type: z.literal(BRIDGE_START_PIE_REQUEST_TYPE),
914
- version: z.literal(1)
1121
+ version: z.literal(2)
915
1122
  });
916
1123
  const bridgeGetPieStatusSuccessResponseSchema = z.object({
917
1124
  errorCount: z.number().int().min(0).optional(),
@@ -922,7 +1129,7 @@ const bridgeGetPieStatusSuccessResponseSchema = z.object({
922
1129
  startedAt: z.string().trim().min(1).optional(),
923
1130
  state: pieStatusStateSchema,
924
1131
  type: z.literal(BRIDGE_GET_PIE_STATUS_REQUEST_TYPE),
925
- version: z.literal(1)
1132
+ version: z.literal(2)
926
1133
  });
927
1134
  const bridgeLaunchDeviceSchema = z.object({
928
1135
  deviceId: z.string(),
@@ -936,7 +1143,7 @@ const bridgeListLaunchDevicesSuccessResponseSchema = z.object({
936
1143
  ok: z.literal(true),
937
1144
  requestId: requestIdSchema,
938
1145
  type: z.literal(BRIDGE_LIST_LAUNCH_DEVICES_REQUEST_TYPE),
939
- version: z.literal(1)
1146
+ version: z.literal(2)
940
1147
  });
941
1148
  const bridgeLaunchOnDeviceSuccessResponseSchema = z.object({
942
1149
  launchSessionId: pieSessionIdSchema,
@@ -945,7 +1152,7 @@ const bridgeLaunchOnDeviceSuccessResponseSchema = z.object({
945
1152
  requestId: requestIdSchema,
946
1153
  state: launchStateSchema,
947
1154
  type: z.literal(BRIDGE_LAUNCH_ON_DEVICE_REQUEST_TYPE),
948
- version: z.literal(1)
1155
+ version: z.literal(2)
949
1156
  });
950
1157
  const bridgeStopPieSuccessResponseSchema = z.object({
951
1158
  message: z.string().trim().min(1).optional(),
@@ -954,7 +1161,7 @@ const bridgeStopPieSuccessResponseSchema = z.object({
954
1161
  state: pieStatusStateSchema,
955
1162
  stopped: z.boolean(),
956
1163
  type: z.literal(BRIDGE_STOP_PIE_REQUEST_TYPE),
957
- version: z.literal(1)
1164
+ version: z.literal(2)
958
1165
  }).refine((value) => !value.stopped || value.state === "stopped", {
959
1166
  message: "A stop_pie response with stopped=true must report state 'stopped'.",
960
1167
  path: ["state"]
@@ -966,7 +1173,7 @@ const bridgeStartLiveCompileSuccessResponseSchema = z.object({
966
1173
  requestId: requestIdSchema,
967
1174
  state: liveCompileStateSchema,
968
1175
  type: z.literal(BRIDGE_START_LIVE_COMPILE_REQUEST_TYPE),
969
- version: z.literal(1)
1176
+ version: z.literal(2)
970
1177
  });
971
1178
  const bridgeStartHotReloadSuccessResponseSchema = z.object({
972
1179
  compileJobId: z.string().trim().min(1),
@@ -975,7 +1182,7 @@ const bridgeStartHotReloadSuccessResponseSchema = z.object({
975
1182
  requestId: requestIdSchema,
976
1183
  state: hotReloadStateSchema,
977
1184
  type: z.literal(BRIDGE_START_HOT_RELOAD_REQUEST_TYPE),
978
- version: z.literal(1)
1185
+ version: z.literal(2)
979
1186
  });
980
1187
  const bridgeCompileDiagnosticSchema = z.object({
981
1188
  file: z.string().trim().min(1).optional(),
@@ -994,7 +1201,7 @@ const bridgeGetCompileStatusSuccessResponseSchema = z.object({
994
1201
  restartReason: z.string().trim().min(1).optional(),
995
1202
  state: compileStatusStateSchema,
996
1203
  type: z.literal(BRIDGE_GET_COMPILE_STATUS_REQUEST_TYPE),
997
- version: z.literal(1),
1204
+ version: z.literal(2),
998
1205
  warningCount: z.number().int().min(0),
999
1206
  warnings: z.array(bridgeCompileDiagnosticSchema)
1000
1207
  }).superRefine((value, ctx) => {
@@ -1023,7 +1230,7 @@ const bridgeListAutomationTestsSuccessResponseSchema = z.object({
1023
1230
  totalCount: z.number().int().min(0),
1024
1231
  truncated: z.boolean(),
1025
1232
  type: z.literal(BRIDGE_LIST_AUTOMATION_TESTS_REQUEST_TYPE),
1026
- version: z.literal(1)
1233
+ version: z.literal(2)
1027
1234
  });
1028
1235
  const bridgeStartAutomationRunSuccessResponseSchema = z.object({
1029
1236
  message: nonEmptyStringSchema.optional(),
@@ -1033,7 +1240,7 @@ const bridgeStartAutomationRunSuccessResponseSchema = z.object({
1033
1240
  state: automationRunStateSchema,
1034
1241
  testRunId: testRunIdSchema,
1035
1242
  type: z.literal(BRIDGE_START_AUTOMATION_RUN_REQUEST_TYPE),
1036
- version: z.literal(1)
1243
+ version: z.literal(2)
1037
1244
  });
1038
1245
  const bridgeAutomationResultEntrySchema = z.object({
1039
1246
  file: z.string().optional(),
@@ -1067,7 +1274,7 @@ const bridgeGetAutomationRunStatusSuccessResponseSchema = z.object({
1067
1274
  totalCount: z.number().int().min(0),
1068
1275
  truncated: z.boolean(),
1069
1276
  type: z.literal(BRIDGE_GET_AUTOMATION_RUN_STATUS_REQUEST_TYPE),
1070
- version: z.literal(1)
1277
+ version: z.literal(2)
1071
1278
  });
1072
1279
  const bridgeCancelAutomationRunSuccessResponseSchema = z.object({
1073
1280
  cancelled: z.boolean(),
@@ -1077,7 +1284,7 @@ const bridgeCancelAutomationRunSuccessResponseSchema = z.object({
1077
1284
  state: cancelAutomationRunStateSchema,
1078
1285
  testRunId: testRunIdSchema,
1079
1286
  type: z.literal(BRIDGE_CANCEL_AUTOMATION_RUN_REQUEST_TYPE),
1080
- version: z.literal(1)
1287
+ version: z.literal(2)
1081
1288
  }).refine((value) => value.cancelled === (value.state === "cancelled"), {
1082
1289
  message: "A cancel_automation_run response must report cancelled=true exactly when state is 'cancelled'.",
1083
1290
  path: ["state"]
@@ -1095,7 +1302,7 @@ const bridgeGetEditorStateSuccessResponseSchema = z.object({
1095
1302
  selectedActors: selectedActorArraySchema,
1096
1303
  truncated: z.boolean(),
1097
1304
  type: z.literal(BRIDGE_GET_EDITOR_STATE_REQUEST_TYPE),
1098
- version: z.literal(1)
1305
+ version: z.literal(2)
1099
1306
  });
1100
1307
  const bridgeSaveDirtyPackagesSuccessResponseSchema = z.object({
1101
1308
  failedCount: z.number().int().min(0),
@@ -1107,7 +1314,7 @@ const bridgeSaveDirtyPackagesSuccessResponseSchema = z.object({
1107
1314
  savedPackages: packageNameArraySchema,
1108
1315
  truncated: z.boolean(),
1109
1316
  type: z.literal(BRIDGE_SAVE_DIRTY_PACKAGES_REQUEST_TYPE),
1110
- version: z.literal(1)
1317
+ version: z.literal(2)
1111
1318
  });
1112
1319
  const bridgeCaptureViewportSuccessResponseSchema = z.object({
1113
1320
  height: z.number().int().min(1),
@@ -1115,7 +1322,7 @@ const bridgeCaptureViewportSuccessResponseSchema = z.object({
1115
1322
  path: screenshotPathSchema,
1116
1323
  requestId: requestIdSchema,
1117
1324
  type: z.literal(BRIDGE_CAPTURE_VIEWPORT_REQUEST_TYPE),
1118
- version: z.literal(1),
1325
+ version: z.literal(2),
1119
1326
  width: z.number().int().min(1)
1120
1327
  });
1121
1328
  const bridgeReflectionMetadataEntrySchema = z.object({
@@ -1159,7 +1366,7 @@ const bridgeGetClassReflectionSuccessResponseSchema = z.object({
1159
1366
  requestId: requestIdSchema,
1160
1367
  truncated: z.boolean(),
1161
1368
  type: z.literal(BRIDGE_GET_CLASS_REFLECTION_REQUEST_TYPE),
1162
- version: z.literal(1)
1369
+ version: z.literal(2)
1163
1370
  });
1164
1371
  const bridgeCdoPropertyValueSchema = z.object({
1165
1372
  name: z.string(),
@@ -1177,7 +1384,7 @@ const bridgeGetCdoDefaultsSuccessResponseSchema = z.object({
1177
1384
  totalCount: z.number().int().min(0),
1178
1385
  truncated: z.boolean(),
1179
1386
  type: z.literal(BRIDGE_GET_CDO_DEFAULTS_REQUEST_TYPE),
1180
- version: z.literal(1)
1387
+ version: z.literal(2)
1181
1388
  });
1182
1389
  const bridgeCrashFrameSchema = z.object({
1183
1390
  address: nonEmptyStringSchema,
@@ -1204,7 +1411,7 @@ const bridgeGetCrashCallstackSuccessResponseSchema = z.object({
1204
1411
  totalCount: z.number().int().min(0),
1205
1412
  truncated: z.boolean(),
1206
1413
  type: z.literal(BRIDGE_GET_CRASH_CALLSTACK_REQUEST_TYPE),
1207
- version: z.literal(1)
1414
+ version: z.literal(2)
1208
1415
  });
1209
1416
  const bridgeExecConsoleCommandSuccessResponseSchema = z.object({
1210
1417
  handled: z.boolean(),
@@ -1214,7 +1421,7 @@ const bridgeExecConsoleCommandSuccessResponseSchema = z.object({
1214
1421
  target: execConsoleTargetSchema,
1215
1422
  truncated: z.boolean(),
1216
1423
  type: z.literal(BRIDGE_EXEC_CONSOLE_COMMAND_REQUEST_TYPE),
1217
- version: z.literal(1)
1424
+ version: z.literal(2)
1218
1425
  });
1219
1426
  const bridgeMemorySnapshotSchema = z.object({
1220
1427
  availablePhysicalMb: memoryMegabytesSchema,
@@ -1233,7 +1440,7 @@ const bridgeStatSnapshotSuccessResponseSchema = z.object({
1233
1440
  renderThreadTimeMs: frameTimingMillisecondsSchema.optional(),
1234
1441
  requestId: requestIdSchema,
1235
1442
  type: z.literal(BRIDGE_STAT_SNAPSHOT_REQUEST_TYPE),
1236
- version: z.literal(1)
1443
+ version: z.literal(2)
1237
1444
  });
1238
1445
  const bridgeStartTraceSuccessResponseSchema = z.object({
1239
1446
  message: nonEmptyStringSchema.optional(),
@@ -1242,7 +1449,7 @@ const bridgeStartTraceSuccessResponseSchema = z.object({
1242
1449
  state: startTraceStateSchema,
1243
1450
  tracePath: nonEmptyStringSchema.optional(),
1244
1451
  type: z.literal(BRIDGE_START_TRACE_REQUEST_TYPE),
1245
- version: z.literal(1)
1452
+ version: z.literal(2)
1246
1453
  }).refine((value) => value.state !== "unsupported" || value.tracePath === void 0, {
1247
1454
  message: "A start_trace response with state 'unsupported' must not report a tracePath.",
1248
1455
  path: ["tracePath"]
@@ -1254,7 +1461,7 @@ const bridgeStopTraceSuccessResponseSchema = z.object({
1254
1461
  state: stopTraceStateSchema,
1255
1462
  tracePath: nonEmptyStringSchema.optional(),
1256
1463
  type: z.literal(BRIDGE_STOP_TRACE_REQUEST_TYPE),
1257
- version: z.literal(1)
1464
+ version: z.literal(2)
1258
1465
  }).refine((value) => value.state === "stopped" || value.tracePath === void 0, {
1259
1466
  message: "A stop_trace response may only report a tracePath when state is 'stopped'.",
1260
1467
  path: ["tracePath"]
@@ -1266,7 +1473,7 @@ const bridgeCreateCppClassSuccessResponseSchema = z.object({
1266
1473
  requestId: requestIdSchema,
1267
1474
  state: createClassStateSchema,
1268
1475
  type: z.literal(BRIDGE_CREATE_CPP_CLASS_REQUEST_TYPE),
1269
- version: z.literal(1)
1476
+ version: z.literal(2)
1270
1477
  }).refine((value) => value.state === "creating" === (value.createClassJobId !== void 0), {
1271
1478
  message: "A create_cpp_class response must carry createClassJobId exactly when state is 'creating'.",
1272
1479
  path: ["createClassJobId"]
@@ -1283,7 +1490,7 @@ const bridgeGetCreateClassStatusSuccessResponseSchema = z.object({
1283
1490
  sourcePath: sourceFilePathSchema.optional(),
1284
1491
  state: createClassStatusStateSchema,
1285
1492
  type: z.literal(BRIDGE_GET_CREATE_CLASS_STATUS_REQUEST_TYPE),
1286
- version: z.literal(1)
1493
+ version: z.literal(2)
1287
1494
  }).superRefine((value, ctx) => {
1288
1495
  if (value.state !== "succeeded" && value.reloaded !== void 0) ctx.addIssue({
1289
1496
  code: z.ZodIssueCode.custom,
@@ -1318,7 +1525,194 @@ const bridgeFindAssetsSuccessResponseSchema = z.object({
1318
1525
  totalCount: z.number().int().min(0),
1319
1526
  truncated: z.boolean(),
1320
1527
  type: z.literal(BRIDGE_FIND_ASSETS_REQUEST_TYPE),
1321
- version: z.literal(1)
1528
+ version: z.literal(2)
1529
+ });
1530
+ const bridgeActorSummarySchema = z.object({
1531
+ actorPath: z.string(),
1532
+ className: z.string(),
1533
+ folderPath: z.string(),
1534
+ label: z.string(),
1535
+ levelName: z.string()
1536
+ });
1537
+ const bridgeListActorsSuccessResponseSchema = z.object({
1538
+ actors: z.array(bridgeActorSummarySchema).max(100),
1539
+ message: z.string().trim().min(1).optional(),
1540
+ nextCursor: levelCursorSchema.optional(),
1541
+ ok: z.literal(true),
1542
+ requestId: requestIdSchema,
1543
+ totalCount: z.number().int().min(0),
1544
+ truncated: z.boolean(),
1545
+ type: z.literal(BRIDGE_LIST_ACTORS_REQUEST_TYPE),
1546
+ version: z.literal(2)
1547
+ });
1548
+ const bridgeVectorSchema = z.object({
1549
+ x: z.number(),
1550
+ y: z.number(),
1551
+ z: z.number()
1552
+ });
1553
+ const bridgeRotatorSchema = z.object({
1554
+ pitch: z.number(),
1555
+ roll: z.number(),
1556
+ yaw: z.number()
1557
+ });
1558
+ const bridgeTransformSchema = z.object({
1559
+ location: bridgeVectorSchema,
1560
+ rotation: bridgeRotatorSchema,
1561
+ scale: bridgeVectorSchema
1562
+ });
1563
+ const bridgeActorComponentSchema = z.object({
1564
+ className: z.string(),
1565
+ name: z.string(),
1566
+ parentName: z.string().optional(),
1567
+ relativeTransform: bridgeTransformSchema.optional()
1568
+ });
1569
+ const bridgeActorPropertySchema = z.object({
1570
+ isEditable: z.boolean(),
1571
+ name: z.string(),
1572
+ type: z.string(),
1573
+ value: z.string(),
1574
+ valueTruncated: z.boolean()
1575
+ });
1576
+ const bridgeGetActorDetailsSuccessResponseSchema = z.object({
1577
+ actorPath: z.string(),
1578
+ className: z.string(),
1579
+ componentCount: z.number().int().min(0),
1580
+ components: z.array(bridgeActorComponentSchema).max(200),
1581
+ componentTruncated: z.boolean(),
1582
+ folderPath: z.string(),
1583
+ label: z.string(),
1584
+ levelName: z.string(),
1585
+ nextComponentCursor: levelCursorSchema.optional(),
1586
+ nextPropertyCursor: levelCursorSchema.optional(),
1587
+ ok: z.literal(true),
1588
+ properties: z.array(bridgeActorPropertySchema).max(200),
1589
+ propertyCount: z.number().int().min(0),
1590
+ propertyTruncated: z.boolean(),
1591
+ requestId: requestIdSchema,
1592
+ transform: bridgeTransformSchema,
1593
+ type: z.literal(BRIDGE_GET_ACTOR_DETAILS_REQUEST_TYPE),
1594
+ version: z.literal(2)
1595
+ });
1596
+ const bridgeStreamingLevelSchema = z.object({
1597
+ isLoaded: z.boolean(),
1598
+ isVisible: z.boolean(),
1599
+ packageName: z.string()
1600
+ });
1601
+ const bridgeGetCurrentLevelSuccessResponseSchema = z.object({
1602
+ levelName: z.string().trim().min(1).optional(),
1603
+ levelPath: z.string().trim().min(1).optional(),
1604
+ message: z.string().trim().min(1).optional(),
1605
+ ok: z.literal(true),
1606
+ requestId: requestIdSchema,
1607
+ streamingLevelCount: z.number().int().min(0),
1608
+ streamingLevels: z.array(bridgeStreamingLevelSchema).max(100),
1609
+ truncated: z.boolean(),
1610
+ type: z.literal(BRIDGE_GET_CURRENT_LEVEL_REQUEST_TYPE),
1611
+ version: z.literal(2),
1612
+ worldPartition: z.boolean()
1613
+ });
1614
+ const bridgeLevelEditResultSchema = z.object({
1615
+ actorPath: z.string().optional(),
1616
+ error: z.string().optional(),
1617
+ index: z.number().int().min(0),
1618
+ ok: z.boolean()
1619
+ });
1620
+ const levelEditResponseFields = {
1621
+ dryRun: z.boolean(),
1622
+ failureCount: z.number().int().min(0),
1623
+ message: z.string().trim().min(1).optional(),
1624
+ ok: z.literal(true),
1625
+ requestId: requestIdSchema,
1626
+ results: z.array(bridgeLevelEditResultSchema).max(64),
1627
+ state: levelEditStateSchema,
1628
+ successCount: z.number().int().min(0),
1629
+ transactionTitle: z.string().trim().min(1).optional(),
1630
+ version: z.literal(2)
1631
+ };
1632
+ function isLevelEditStateConsistent(value) {
1633
+ if (value.state === "applied") return !value.dryRun;
1634
+ if (value.state === "validated") return value.dryRun;
1635
+ return true;
1636
+ }
1637
+ const levelEditStateRefinement = {
1638
+ message: "A mutating response must report state 'applied' only for a real run and 'validated' only for a dryRun.",
1639
+ path: ["state"]
1640
+ };
1641
+ const bridgeSpawnActorSuccessResponseSchema = z.object({
1642
+ ...levelEditResponseFields,
1643
+ type: z.literal(BRIDGE_SPAWN_ACTOR_REQUEST_TYPE)
1644
+ }).refine(isLevelEditStateConsistent, levelEditStateRefinement);
1645
+ const bridgeDestroyActorSuccessResponseSchema = z.object({
1646
+ ...levelEditResponseFields,
1647
+ type: z.literal(BRIDGE_DESTROY_ACTOR_REQUEST_TYPE)
1648
+ }).refine(isLevelEditStateConsistent, levelEditStateRefinement);
1649
+ const bridgeSetActorTransformSuccessResponseSchema = z.object({
1650
+ ...levelEditResponseFields,
1651
+ type: z.literal(BRIDGE_SET_ACTOR_TRANSFORM_REQUEST_TYPE)
1652
+ }).refine(isLevelEditStateConsistent, levelEditStateRefinement);
1653
+ const bridgeSetActorPropertySuccessResponseSchema = z.object({
1654
+ ...levelEditResponseFields,
1655
+ type: z.literal(BRIDGE_SET_ACTOR_PROPERTY_REQUEST_TYPE)
1656
+ }).refine(isLevelEditStateConsistent, levelEditStateRefinement);
1657
+ const bridgeSelectActorsSuccessResponseSchema = z.object({
1658
+ ...levelEditResponseFields,
1659
+ selectedActorCount: z.number().int().min(0),
1660
+ type: z.literal(BRIDGE_SELECT_ACTORS_REQUEST_TYPE)
1661
+ }).refine(isLevelEditStateConsistent, levelEditStateRefinement);
1662
+ const bridgeUndoSuccessResponseSchema = z.object({
1663
+ message: z.string().trim().min(1).optional(),
1664
+ ok: z.literal(true),
1665
+ requestId: requestIdSchema,
1666
+ state: undoStateSchema,
1667
+ transactionTitle: z.string().trim().min(1).optional(),
1668
+ type: z.literal(BRIDGE_UNDO_REQUEST_TYPE),
1669
+ version: z.literal(2)
1670
+ });
1671
+ const bridgeRedoSuccessResponseSchema = z.object({
1672
+ message: z.string().trim().min(1).optional(),
1673
+ ok: z.literal(true),
1674
+ requestId: requestIdSchema,
1675
+ state: redoStateSchema,
1676
+ transactionTitle: z.string().trim().min(1).optional(),
1677
+ type: z.literal(BRIDGE_REDO_REQUEST_TYPE),
1678
+ version: z.literal(2)
1679
+ });
1680
+ const bridgeOpenLevelSuccessResponseSchema = z.object({
1681
+ levelPath: levelPathSchema.optional(),
1682
+ message: nonEmptyStringSchema.optional(),
1683
+ ok: z.literal(true),
1684
+ openLevelJobId: openLevelJobIdSchema.optional(),
1685
+ requestId: requestIdSchema,
1686
+ state: openLevelStateSchema,
1687
+ type: z.literal(BRIDGE_OPEN_LEVEL_REQUEST_TYPE),
1688
+ version: z.literal(2)
1689
+ }).refine((value) => value.state === "opening" === (value.openLevelJobId !== void 0), {
1690
+ message: "An open_level response must carry openLevelJobId exactly when state is 'opening'.",
1691
+ path: ["openLevelJobId"]
1692
+ });
1693
+ const bridgeGetOpenLevelStatusSuccessResponseSchema = z.object({
1694
+ durationMs: z.number().int().min(0).optional(),
1695
+ finishedAt: nonEmptyStringSchema.optional(),
1696
+ levelPath: levelPathSchema.optional(),
1697
+ message: nonEmptyStringSchema.optional(),
1698
+ ok: z.literal(true),
1699
+ openLevelJobId: openLevelJobIdSchema,
1700
+ requestId: requestIdSchema,
1701
+ state: openLevelStatusStateSchema,
1702
+ type: z.literal(BRIDGE_GET_OPEN_LEVEL_STATUS_REQUEST_TYPE),
1703
+ version: z.literal(2)
1704
+ }).superRefine((value, ctx) => {
1705
+ if (value.state === "succeeded" || value.state === "failed") return;
1706
+ if (value.durationMs !== void 0) ctx.addIssue({
1707
+ code: z.ZodIssueCode.custom,
1708
+ message: "durationMs must be absent until the load finishes",
1709
+ path: ["durationMs"]
1710
+ });
1711
+ if (value.finishedAt !== void 0) ctx.addIssue({
1712
+ code: z.ZodIssueCode.custom,
1713
+ message: "finishedAt must be absent until the load finishes",
1714
+ path: ["finishedAt"]
1715
+ });
1322
1716
  });
1323
1717
  const bridgeAssetDependencySchema = z.object({
1324
1718
  category: assetDependencyCategorySchema,
@@ -1339,7 +1733,7 @@ const bridgeGetAssetDependenciesSuccessResponseSchema = z.object({
1339
1733
  requestId: requestIdSchema,
1340
1734
  scanning: z.boolean(),
1341
1735
  type: z.literal(BRIDGE_GET_ASSET_DEPENDENCIES_REQUEST_TYPE),
1342
- version: z.literal(1)
1736
+ version: z.literal(2)
1343
1737
  });
1344
1738
  const bridgeBlueprintReferenceSchema = z.object({
1345
1739
  assetName: z.string(),
@@ -1358,7 +1752,7 @@ const bridgeFindBlueprintReferencesSuccessResponseSchema = z.object({
1358
1752
  totalCount: z.number().int().min(0),
1359
1753
  truncated: z.boolean(),
1360
1754
  type: z.literal(BRIDGE_FIND_BLUEPRINT_REFERENCES_REQUEST_TYPE),
1361
- version: z.literal(1)
1755
+ version: z.literal(2)
1362
1756
  });
1363
1757
  const bridgeBlueprintComponentSchema = z.object({
1364
1758
  className: z.string(),
@@ -1411,7 +1805,7 @@ const bridgeGetBlueprintSummarySuccessResponseSchema = z.object({
1411
1805
  type: z.literal(BRIDGE_GET_BLUEPRINT_SUMMARY_REQUEST_TYPE),
1412
1806
  variableCount: z.number().int().min(0),
1413
1807
  variables: z.array(bridgeBlueprintVariableSchema),
1414
- version: z.literal(1)
1808
+ version: z.literal(2)
1415
1809
  });
1416
1810
  const bridgeBlueprintPinLinkSchema = z.object({
1417
1811
  nodeId: z.string(),
@@ -1445,7 +1839,7 @@ const bridgeGetBlueprintGraphSuccessResponseSchema = z.object({
1445
1839
  requestId: requestIdSchema,
1446
1840
  truncated: z.boolean(),
1447
1841
  type: z.literal(BRIDGE_GET_BLUEPRINT_GRAPH_REQUEST_TYPE),
1448
- version: z.literal(1)
1842
+ version: z.literal(2)
1449
1843
  });
1450
1844
  const bridgeBlueprintCompileDiagnosticSchema = bridgeCompileDiagnosticSchema.extend({
1451
1845
  graphName: z.string().optional(),
@@ -1470,7 +1864,7 @@ const bridgeGetBlueprintCompileErrorsSuccessResponseSchema = z.object({
1470
1864
  totalCount: z.number().int().min(0),
1471
1865
  truncated: z.boolean(),
1472
1866
  type: z.literal(BRIDGE_GET_BLUEPRINT_COMPILE_ERRORS_REQUEST_TYPE),
1473
- version: z.literal(1)
1867
+ version: z.literal(2)
1474
1868
  });
1475
1869
  const bridgeCompileBlueprintSuccessResponseSchema = z.object({
1476
1870
  compileJobId: blueprintCompileJobIdSchema.optional(),
@@ -1479,7 +1873,7 @@ const bridgeCompileBlueprintSuccessResponseSchema = z.object({
1479
1873
  requestId: requestIdSchema,
1480
1874
  state: compileBlueprintStateSchema,
1481
1875
  type: z.literal(BRIDGE_COMPILE_BLUEPRINT_REQUEST_TYPE),
1482
- version: z.literal(1)
1876
+ version: z.literal(2)
1483
1877
  }).refine((value) => value.state === "compiling" === (value.compileJobId !== void 0), {
1484
1878
  message: "A compile_blueprint response must carry compileJobId exactly when state is 'compiling'.",
1485
1879
  path: ["compileJobId"]
@@ -1496,7 +1890,7 @@ const bridgeGetBlueprintCompileStatusSuccessResponseSchema = z.object({
1496
1890
  totalCount: z.number().int().min(0),
1497
1891
  truncated: z.boolean(),
1498
1892
  type: z.literal(BRIDGE_GET_BLUEPRINT_COMPILE_STATUS_REQUEST_TYPE),
1499
- version: z.literal(1)
1893
+ version: z.literal(2)
1500
1894
  }).superRefine((value, ctx) => {
1501
1895
  if (value.state === "succeeded" || value.state === "failed") return;
1502
1896
  for (const field of ["durationMs", "finishedAt"]) if (value[field] !== void 0) ctx.addIssue({
@@ -1522,7 +1916,7 @@ const bridgeGetConfigValueSuccessResponseSchema = z.object({
1522
1916
  type: z.literal(BRIDGE_GET_CONFIG_VALUE_REQUEST_TYPE),
1523
1917
  value: configValueSchema.optional(),
1524
1918
  valueTruncated: z.boolean().optional(),
1525
- version: z.literal(1)
1919
+ version: z.literal(2)
1526
1920
  });
1527
1921
  const bridgeSetConfigValueSuccessResponseSchema = z.object({
1528
1922
  file: configFileSchema,
@@ -1537,7 +1931,7 @@ const bridgeSetConfigValueSuccessResponseSchema = z.object({
1537
1931
  section: configSectionSchema,
1538
1932
  type: z.literal(BRIDGE_SET_CONFIG_VALUE_REQUEST_TYPE),
1539
1933
  value: configValueSchema,
1540
- version: z.literal(1)
1934
+ version: z.literal(2)
1541
1935
  });
1542
1936
  const bridgeListConfigSectionSuccessResponseSchema = z.object({
1543
1937
  entries: z.array(bridgeConfigEntrySchema).max(200),
@@ -1549,13 +1943,13 @@ const bridgeListConfigSectionSuccessResponseSchema = z.object({
1549
1943
  totalCount: z.number().int().min(0),
1550
1944
  truncated: z.boolean(),
1551
1945
  type: z.literal(BRIDGE_LIST_CONFIG_SECTION_REQUEST_TYPE),
1552
- version: z.literal(1)
1946
+ version: z.literal(2)
1553
1947
  });
1554
1948
  const bridgeErrorResponseSchema = z.object({
1555
1949
  error: bridgeErrorSchema,
1556
1950
  ok: z.literal(false),
1557
1951
  requestId: requestIdSchema,
1558
- version: z.literal(1)
1952
+ version: z.literal(2)
1559
1953
  });
1560
1954
  const bridgePingResponseSchema = z.union([bridgePingSuccessResponseSchema, bridgeErrorResponseSchema]);
1561
1955
  const bridgeLogSubscribeResponseSchema = z.union([bridgeLogSubscribeSuccessResponseSchema, bridgeErrorResponseSchema]);
@@ -1585,6 +1979,18 @@ const bridgeStartTraceResponseSchema = z.union([bridgeStartTraceSuccessResponseS
1585
1979
  const bridgeStopTraceResponseSchema = z.union([bridgeStopTraceSuccessResponseSchema, bridgeErrorResponseSchema]);
1586
1980
  const bridgeCreateCppClassResponseSchema = z.union([bridgeCreateCppClassSuccessResponseSchema, bridgeErrorResponseSchema]);
1587
1981
  const bridgeGetCreateClassStatusResponseSchema = z.union([bridgeGetCreateClassStatusSuccessResponseSchema, bridgeErrorResponseSchema]);
1982
+ const bridgeListActorsResponseSchema = z.union([bridgeListActorsSuccessResponseSchema, bridgeErrorResponseSchema]);
1983
+ const bridgeGetActorDetailsResponseSchema = z.union([bridgeGetActorDetailsSuccessResponseSchema, bridgeErrorResponseSchema]);
1984
+ const bridgeGetCurrentLevelResponseSchema = z.union([bridgeGetCurrentLevelSuccessResponseSchema, bridgeErrorResponseSchema]);
1985
+ const bridgeSpawnActorResponseSchema = z.union([bridgeSpawnActorSuccessResponseSchema, bridgeErrorResponseSchema]);
1986
+ const bridgeDestroyActorResponseSchema = z.union([bridgeDestroyActorSuccessResponseSchema, bridgeErrorResponseSchema]);
1987
+ const bridgeSetActorTransformResponseSchema = z.union([bridgeSetActorTransformSuccessResponseSchema, bridgeErrorResponseSchema]);
1988
+ const bridgeSetActorPropertyResponseSchema = z.union([bridgeSetActorPropertySuccessResponseSchema, bridgeErrorResponseSchema]);
1989
+ const bridgeSelectActorsResponseSchema = z.union([bridgeSelectActorsSuccessResponseSchema, bridgeErrorResponseSchema]);
1990
+ const bridgeUndoResponseSchema = z.union([bridgeUndoSuccessResponseSchema, bridgeErrorResponseSchema]);
1991
+ const bridgeRedoResponseSchema = z.union([bridgeRedoSuccessResponseSchema, bridgeErrorResponseSchema]);
1992
+ const bridgeOpenLevelResponseSchema = z.union([bridgeOpenLevelSuccessResponseSchema, bridgeErrorResponseSchema]);
1993
+ const bridgeGetOpenLevelStatusResponseSchema = z.union([bridgeGetOpenLevelStatusSuccessResponseSchema, bridgeErrorResponseSchema]);
1588
1994
  const bridgeFindAssetsResponseSchema = z.union([bridgeFindAssetsSuccessResponseSchema, bridgeErrorResponseSchema]);
1589
1995
  const bridgeGetAssetDependenciesResponseSchema = z.union([bridgeGetAssetDependenciesSuccessResponseSchema, bridgeErrorResponseSchema]);
1590
1996
  const bridgeFindBlueprintReferencesResponseSchema = z.union([bridgeFindBlueprintReferencesSuccessResponseSchema, bridgeErrorResponseSchema]);
@@ -1607,7 +2013,117 @@ function createBridgeFindAssetsRequest(options = {}, requestId = randomUUID()) {
1607
2013
  recursivePaths: options.recursivePaths,
1608
2014
  requestId,
1609
2015
  type: BRIDGE_FIND_ASSETS_REQUEST_TYPE,
1610
- version: 1
2016
+ version: 2
2017
+ });
2018
+ }
2019
+ function createBridgeListActorsRequest(options = {}, requestId = randomUUID()) {
2020
+ return bridgeListActorsRequestSchema.parse({
2021
+ classNames: options.classNames,
2022
+ cursor: options.cursor,
2023
+ levelName: options.levelName,
2024
+ maxResults: options.maxResults ?? 50,
2025
+ nameFilter: options.nameFilter,
2026
+ requestId,
2027
+ tag: options.tag,
2028
+ type: BRIDGE_LIST_ACTORS_REQUEST_TYPE,
2029
+ version: 2
2030
+ });
2031
+ }
2032
+ function createBridgeGetActorDetailsRequest(actorPath, options = {}, requestId = randomUUID()) {
2033
+ return bridgeGetActorDetailsRequestSchema.parse({
2034
+ actorPath,
2035
+ componentCursor: options.componentCursor,
2036
+ includeInherited: options.includeInherited,
2037
+ maxResults: options.maxResults ?? 25,
2038
+ propertyCursor: options.propertyCursor,
2039
+ requestId,
2040
+ type: BRIDGE_GET_ACTOR_DETAILS_REQUEST_TYPE,
2041
+ version: 2
2042
+ });
2043
+ }
2044
+ function createBridgeGetCurrentLevelRequest(requestId = randomUUID()) {
2045
+ return bridgeGetCurrentLevelRequestSchema.parse({
2046
+ requestId,
2047
+ type: BRIDGE_GET_CURRENT_LEVEL_REQUEST_TYPE,
2048
+ version: 2
2049
+ });
2050
+ }
2051
+ function createBridgeSpawnActorRequest(spawns, options = {}, requestId = randomUUID()) {
2052
+ return bridgeSpawnActorRequestSchema.parse({
2053
+ dryRun: options.dryRun,
2054
+ requestId,
2055
+ spawns,
2056
+ type: BRIDGE_SPAWN_ACTOR_REQUEST_TYPE,
2057
+ version: 2
2058
+ });
2059
+ }
2060
+ function createBridgeDestroyActorRequest(actorPaths, options = {}, requestId = randomUUID()) {
2061
+ return bridgeDestroyActorRequestSchema.parse({
2062
+ actorPaths,
2063
+ dryRun: options.dryRun,
2064
+ requestId,
2065
+ type: BRIDGE_DESTROY_ACTOR_REQUEST_TYPE,
2066
+ version: 2
2067
+ });
2068
+ }
2069
+ function createBridgeSetActorTransformRequest(transforms, options = {}, requestId = randomUUID()) {
2070
+ return bridgeSetActorTransformRequestSchema.parse({
2071
+ dryRun: options.dryRun,
2072
+ requestId,
2073
+ transforms,
2074
+ type: BRIDGE_SET_ACTOR_TRANSFORM_REQUEST_TYPE,
2075
+ version: 2
2076
+ });
2077
+ }
2078
+ function createBridgeSetActorPropertyRequest(properties, options = {}, requestId = randomUUID()) {
2079
+ return bridgeSetActorPropertyRequestSchema.parse({
2080
+ dryRun: options.dryRun,
2081
+ properties,
2082
+ requestId,
2083
+ type: BRIDGE_SET_ACTOR_PROPERTY_REQUEST_TYPE,
2084
+ version: 2
2085
+ });
2086
+ }
2087
+ function createBridgeSelectActorsRequest(actorPaths, options = {}, requestId = randomUUID()) {
2088
+ return bridgeSelectActorsRequestSchema.parse({
2089
+ actorPaths,
2090
+ addToSelection: options.addToSelection,
2091
+ dryRun: options.dryRun,
2092
+ requestId,
2093
+ type: BRIDGE_SELECT_ACTORS_REQUEST_TYPE,
2094
+ version: 2
2095
+ });
2096
+ }
2097
+ function createBridgeUndoRequest(requestId = randomUUID()) {
2098
+ return bridgeUndoRequestSchema.parse({
2099
+ requestId,
2100
+ type: BRIDGE_UNDO_REQUEST_TYPE,
2101
+ version: 2
2102
+ });
2103
+ }
2104
+ function createBridgeRedoRequest(requestId = randomUUID()) {
2105
+ return bridgeRedoRequestSchema.parse({
2106
+ requestId,
2107
+ type: BRIDGE_REDO_REQUEST_TYPE,
2108
+ version: 2
2109
+ });
2110
+ }
2111
+ function createBridgeOpenLevelRequest(levelPath, options = {}, requestId = randomUUID()) {
2112
+ return bridgeOpenLevelRequestSchema.parse({
2113
+ dryRun: options.dryRun,
2114
+ levelPath,
2115
+ requestId,
2116
+ saveDirtyPackages: options.saveDirtyPackages,
2117
+ type: BRIDGE_OPEN_LEVEL_REQUEST_TYPE,
2118
+ version: 2
2119
+ });
2120
+ }
2121
+ function createBridgeGetOpenLevelStatusRequest(openLevelJobId, requestId = randomUUID()) {
2122
+ return bridgeGetOpenLevelStatusRequestSchema.parse({
2123
+ openLevelJobId,
2124
+ requestId,
2125
+ type: BRIDGE_GET_OPEN_LEVEL_STATUS_REQUEST_TYPE,
2126
+ version: 2
1611
2127
  });
1612
2128
  }
1613
2129
  function createBridgeGetAssetDependenciesRequest(assetPath, options = {}, requestId = randomUUID()) {
@@ -1620,7 +2136,7 @@ function createBridgeGetAssetDependenciesRequest(assetPath, options = {}, reques
1620
2136
  referencerCursor: options.referencerCursor,
1621
2137
  requestId,
1622
2138
  type: BRIDGE_GET_ASSET_DEPENDENCIES_REQUEST_TYPE,
1623
- version: 1
2139
+ version: 2
1624
2140
  });
1625
2141
  }
1626
2142
  function createBridgeFindBlueprintReferencesRequest(className, options = {}, requestId = randomUUID()) {
@@ -1631,7 +2147,7 @@ function createBridgeFindBlueprintReferencesRequest(className, options = {}, req
1631
2147
  referenceKinds: options.referenceKinds,
1632
2148
  requestId,
1633
2149
  type: BRIDGE_FIND_BLUEPRINT_REFERENCES_REQUEST_TYPE,
1634
- version: 1
2150
+ version: 2
1635
2151
  });
1636
2152
  }
1637
2153
  function createBridgeGetBlueprintSummaryRequest(blueprintPath, options = {}, requestId = randomUUID()) {
@@ -1645,7 +2161,7 @@ function createBridgeGetBlueprintSummaryRequest(blueprintPath, options = {}, req
1645
2161
  requestId,
1646
2162
  type: BRIDGE_GET_BLUEPRINT_SUMMARY_REQUEST_TYPE,
1647
2163
  variableCursor: options.variableCursor,
1648
- version: 1
2164
+ version: 2
1649
2165
  });
1650
2166
  }
1651
2167
  function createBridgeGetBlueprintGraphRequest(blueprintPath, graphName, options = {}, requestId = randomUUID()) {
@@ -1657,7 +2173,7 @@ function createBridgeGetBlueprintGraphRequest(blueprintPath, graphName, options
1657
2173
  nodeCursor: options.nodeCursor,
1658
2174
  requestId,
1659
2175
  type: BRIDGE_GET_BLUEPRINT_GRAPH_REQUEST_TYPE,
1660
- version: 1
2176
+ version: 2
1661
2177
  });
1662
2178
  }
1663
2179
  function createBridgeGetBlueprintCompileErrorsRequest(blueprintPaths, options = {}, requestId = randomUUID()) {
@@ -1667,7 +2183,7 @@ function createBridgeGetBlueprintCompileErrorsRequest(blueprintPaths, options =
1667
2183
  maxResults: options.maxResults ?? 50,
1668
2184
  requestId,
1669
2185
  type: BRIDGE_GET_BLUEPRINT_COMPILE_ERRORS_REQUEST_TYPE,
1670
- version: 1
2186
+ version: 2
1671
2187
  });
1672
2188
  }
1673
2189
  function createBridgeCompileBlueprintRequest(blueprintPaths, requestId = randomUUID()) {
@@ -1675,7 +2191,7 @@ function createBridgeCompileBlueprintRequest(blueprintPaths, requestId = randomU
1675
2191
  blueprintPaths,
1676
2192
  requestId,
1677
2193
  type: BRIDGE_COMPILE_BLUEPRINT_REQUEST_TYPE,
1678
- version: 1
2194
+ version: 2
1679
2195
  });
1680
2196
  }
1681
2197
  function createBridgeGetBlueprintCompileStatusRequest(compileJobId, options = {}, requestId = randomUUID()) {
@@ -1685,7 +2201,7 @@ function createBridgeGetBlueprintCompileStatusRequest(compileJobId, options = {}
1685
2201
  maxResults: options.maxResults ?? 50,
1686
2202
  requestId,
1687
2203
  type: BRIDGE_GET_BLUEPRINT_COMPILE_STATUS_REQUEST_TYPE,
1688
- version: 1
2204
+ version: 2
1689
2205
  });
1690
2206
  }
1691
2207
  function createBridgeGetConfigValueRequest(input, requestId = randomUUID()) {
@@ -1695,7 +2211,7 @@ function createBridgeGetConfigValueRequest(input, requestId = randomUUID()) {
1695
2211
  requestId,
1696
2212
  section: input.section,
1697
2213
  type: BRIDGE_GET_CONFIG_VALUE_REQUEST_TYPE,
1698
- version: 1
2214
+ version: 2
1699
2215
  });
1700
2216
  }
1701
2217
  function createBridgeSetConfigValueRequest(input, requestId = randomUUID()) {
@@ -1707,7 +2223,7 @@ function createBridgeSetConfigValueRequest(input, requestId = randomUUID()) {
1707
2223
  section: input.section,
1708
2224
  type: BRIDGE_SET_CONFIG_VALUE_REQUEST_TYPE,
1709
2225
  value: input.value,
1710
- version: 1
2226
+ version: 2
1711
2227
  });
1712
2228
  }
1713
2229
  function createBridgeListConfigSectionRequest(input, requestId = randomUUID()) {
@@ -1718,28 +2234,28 @@ function createBridgeListConfigSectionRequest(input, requestId = randomUUID()) {
1718
2234
  requestId,
1719
2235
  section: input.section,
1720
2236
  type: BRIDGE_LIST_CONFIG_SECTION_REQUEST_TYPE,
1721
- version: 1
2237
+ version: 2
1722
2238
  });
1723
2239
  }
1724
2240
  function createBridgePingRequest(requestId = randomUUID()) {
1725
2241
  return {
1726
2242
  requestId,
1727
2243
  type: BRIDGE_PING_REQUEST_TYPE,
1728
- version: 1
2244
+ version: 2
1729
2245
  };
1730
2246
  }
1731
2247
  function createBridgeStartLiveCompileRequest(requestId = randomUUID()) {
1732
2248
  return {
1733
2249
  requestId,
1734
2250
  type: BRIDGE_START_LIVE_COMPILE_REQUEST_TYPE,
1735
- version: 1
2251
+ version: 2
1736
2252
  };
1737
2253
  }
1738
2254
  function createBridgeStartHotReloadRequest(requestId = randomUUID()) {
1739
2255
  return {
1740
2256
  requestId,
1741
2257
  type: BRIDGE_START_HOT_RELOAD_REQUEST_TYPE,
1742
- version: 1
2258
+ version: 2
1743
2259
  };
1744
2260
  }
1745
2261
  function createBridgeGetCompileStatusRequest(compileJobId, requestId = randomUUID()) {
@@ -1747,7 +2263,7 @@ function createBridgeGetCompileStatusRequest(compileJobId, requestId = randomUUI
1747
2263
  compileJobId,
1748
2264
  requestId,
1749
2265
  type: BRIDGE_GET_COMPILE_STATUS_REQUEST_TYPE,
1750
- version: 1
2266
+ version: 2
1751
2267
  });
1752
2268
  }
1753
2269
  function createBridgeLogSubscribeRequest(source, filters, requestId = randomUUID()) {
@@ -1756,7 +2272,7 @@ function createBridgeLogSubscribeRequest(source, filters, requestId = randomUUID
1756
2272
  requestId,
1757
2273
  source,
1758
2274
  type: BRIDGE_SUBSCRIBE_LOGS_REQUEST_TYPE,
1759
- version: 1
2275
+ version: 2
1760
2276
  });
1761
2277
  }
1762
2278
  function createBridgeLogDrainRequest(subscriptionId, cursor, maxEvents, requestId = randomUUID()) {
@@ -1766,7 +2282,7 @@ function createBridgeLogDrainRequest(subscriptionId, cursor, maxEvents, requestI
1766
2282
  requestId,
1767
2283
  subscriptionId,
1768
2284
  type: BRIDGE_DRAIN_LOGS_REQUEST_TYPE,
1769
- version: 1
2285
+ version: 2
1770
2286
  });
1771
2287
  }
1772
2288
  function createBridgeStartPieRequest(mode = "new_window", map, options = {}, requestId = randomUUID()) {
@@ -1778,7 +2294,7 @@ function createBridgeStartPieRequest(mode = "new_window", map, options = {}, req
1778
2294
  requestId,
1779
2295
  runUnderOneProcess: options.runUnderOneProcess,
1780
2296
  type: BRIDGE_START_PIE_REQUEST_TYPE,
1781
- version: 1
2297
+ version: 2
1782
2298
  });
1783
2299
  }
1784
2300
  function createBridgeGetPieStatusRequest(pieSessionId, requestId = randomUUID()) {
@@ -1786,7 +2302,7 @@ function createBridgeGetPieStatusRequest(pieSessionId, requestId = randomUUID())
1786
2302
  pieSessionId,
1787
2303
  requestId,
1788
2304
  type: BRIDGE_GET_PIE_STATUS_REQUEST_TYPE,
1789
- version: 1
2305
+ version: 2
1790
2306
  });
1791
2307
  }
1792
2308
  function createBridgeStopPieRequest(pieSessionId, requestId = randomUUID()) {
@@ -1794,14 +2310,14 @@ function createBridgeStopPieRequest(pieSessionId, requestId = randomUUID()) {
1794
2310
  pieSessionId,
1795
2311
  requestId,
1796
2312
  type: BRIDGE_STOP_PIE_REQUEST_TYPE,
1797
- version: 1
2313
+ version: 2
1798
2314
  });
1799
2315
  }
1800
2316
  function createBridgeListLaunchDevicesRequest(requestId = randomUUID()) {
1801
2317
  return {
1802
2318
  requestId,
1803
2319
  type: BRIDGE_LIST_LAUNCH_DEVICES_REQUEST_TYPE,
1804
- version: 1
2320
+ version: 2
1805
2321
  };
1806
2322
  }
1807
2323
  function createBridgeLaunchOnDeviceRequest(deviceId, map, requestId = randomUUID()) {
@@ -1810,7 +2326,7 @@ function createBridgeLaunchOnDeviceRequest(deviceId, map, requestId = randomUUID
1810
2326
  map,
1811
2327
  requestId,
1812
2328
  type: BRIDGE_LAUNCH_ON_DEVICE_REQUEST_TYPE,
1813
- version: 1
2329
+ version: 2
1814
2330
  });
1815
2331
  }
1816
2332
  function createBridgeListAutomationTestsRequest(options = {}, requestId = randomUUID()) {
@@ -1821,7 +2337,7 @@ function createBridgeListAutomationTestsRequest(options = {}, requestId = random
1821
2337
  nameFilter: options.nameFilter,
1822
2338
  requestId,
1823
2339
  type: BRIDGE_LIST_AUTOMATION_TESTS_REQUEST_TYPE,
1824
- version: 1
2340
+ version: 2
1825
2341
  });
1826
2342
  }
1827
2343
  function createBridgeStartAutomationRunRequest(selectors, requestId = randomUUID()) {
@@ -1831,7 +2347,7 @@ function createBridgeStartAutomationRunRequest(selectors, requestId = randomUUID
1831
2347
  requestId,
1832
2348
  testNames: selectors.testNames ?? [],
1833
2349
  type: BRIDGE_START_AUTOMATION_RUN_REQUEST_TYPE,
1834
- version: 1
2350
+ version: 2
1835
2351
  });
1836
2352
  }
1837
2353
  function createBridgeGetAutomationRunStatusRequest(testRunId, options = {}, requestId = randomUUID()) {
@@ -1841,7 +2357,7 @@ function createBridgeGetAutomationRunStatusRequest(testRunId, options = {}, requ
1841
2357
  requestId,
1842
2358
  testRunId,
1843
2359
  type: BRIDGE_GET_AUTOMATION_RUN_STATUS_REQUEST_TYPE,
1844
- version: 1
2360
+ version: 2
1845
2361
  });
1846
2362
  }
1847
2363
  function createBridgeCancelAutomationRunRequest(testRunId, requestId = randomUUID()) {
@@ -1849,14 +2365,14 @@ function createBridgeCancelAutomationRunRequest(testRunId, requestId = randomUUI
1849
2365
  requestId,
1850
2366
  testRunId,
1851
2367
  type: BRIDGE_CANCEL_AUTOMATION_RUN_REQUEST_TYPE,
1852
- version: 1
2368
+ version: 2
1853
2369
  });
1854
2370
  }
1855
2371
  function createBridgeGetEditorStateRequest(requestId = randomUUID()) {
1856
2372
  return {
1857
2373
  requestId,
1858
2374
  type: BRIDGE_GET_EDITOR_STATE_REQUEST_TYPE,
1859
- version: 1
2375
+ version: 2
1860
2376
  };
1861
2377
  }
1862
2378
  function createBridgeExecConsoleCommandRequest(command, requestId = randomUUID()) {
@@ -1864,14 +2380,14 @@ function createBridgeExecConsoleCommandRequest(command, requestId = randomUUID()
1864
2380
  command,
1865
2381
  requestId,
1866
2382
  type: BRIDGE_EXEC_CONSOLE_COMMAND_REQUEST_TYPE,
1867
- version: 1
2383
+ version: 2
1868
2384
  });
1869
2385
  }
1870
2386
  function createBridgeStatSnapshotRequest(requestId = randomUUID()) {
1871
2387
  return bridgeStatSnapshotRequestSchema.parse({
1872
2388
  requestId,
1873
2389
  type: BRIDGE_STAT_SNAPSHOT_REQUEST_TYPE,
1874
- version: 1
2390
+ version: 2
1875
2391
  });
1876
2392
  }
1877
2393
  function createBridgeSaveDirtyPackagesRequest(options = {}, requestId = randomUUID()) {
@@ -1880,7 +2396,7 @@ function createBridgeSaveDirtyPackagesRequest(options = {}, requestId = randomUU
1880
2396
  saveContentPackages: options.saveContentPackages,
1881
2397
  saveMapPackages: options.saveMapPackages,
1882
2398
  type: BRIDGE_SAVE_DIRTY_PACKAGES_REQUEST_TYPE,
1883
- version: 1
2399
+ version: 2
1884
2400
  });
1885
2401
  }
1886
2402
  function createBridgeStartTraceRequest(options = {}, requestId = randomUUID()) {
@@ -1889,7 +2405,7 @@ function createBridgeStartTraceRequest(options = {}, requestId = randomUUID()) {
1889
2405
  fileName: options.fileName,
1890
2406
  requestId,
1891
2407
  type: BRIDGE_START_TRACE_REQUEST_TYPE,
1892
- version: 1
2408
+ version: 2
1893
2409
  });
1894
2410
  }
1895
2411
  function createBridgeGetClassReflectionRequest(className, options = {}, requestId = randomUUID()) {
@@ -1902,14 +2418,14 @@ function createBridgeGetClassReflectionRequest(className, options = {}, requestI
1902
2418
  propertyCursor: options.propertyCursor,
1903
2419
  requestId,
1904
2420
  type: BRIDGE_GET_CLASS_REFLECTION_REQUEST_TYPE,
1905
- version: 1
2421
+ version: 2
1906
2422
  });
1907
2423
  }
1908
2424
  function createBridgeCaptureViewportRequest(requestId = randomUUID()) {
1909
2425
  return {
1910
2426
  requestId,
1911
2427
  type: BRIDGE_CAPTURE_VIEWPORT_REQUEST_TYPE,
1912
- version: 1
2428
+ version: 2
1913
2429
  };
1914
2430
  }
1915
2431
  function createBridgeGetCdoDefaultsRequest(className, options = {}, requestId = randomUUID()) {
@@ -1921,7 +2437,7 @@ function createBridgeGetCdoDefaultsRequest(className, options = {}, requestId =
1921
2437
  nameFilter: options.nameFilter,
1922
2438
  requestId,
1923
2439
  type: BRIDGE_GET_CDO_DEFAULTS_REQUEST_TYPE,
1924
- version: 1
2440
+ version: 2
1925
2441
  });
1926
2442
  }
1927
2443
  function createBridgeGetCrashCallstackRequest(maxRecords = 1, maxFrames = 32, requestId = randomUUID()) {
@@ -1930,14 +2446,14 @@ function createBridgeGetCrashCallstackRequest(maxRecords = 1, maxFrames = 32, re
1930
2446
  maxRecords,
1931
2447
  requestId,
1932
2448
  type: BRIDGE_GET_CRASH_CALLSTACK_REQUEST_TYPE,
1933
- version: 1
2449
+ version: 2
1934
2450
  });
1935
2451
  }
1936
2452
  function createBridgeStopTraceRequest(requestId = randomUUID()) {
1937
2453
  return bridgeStopTraceRequestSchema.parse({
1938
2454
  requestId,
1939
2455
  type: BRIDGE_STOP_TRACE_REQUEST_TYPE,
1940
- version: 1
2456
+ version: 2
1941
2457
  });
1942
2458
  }
1943
2459
  function createBridgeCreateCppClassRequest(className, parentClass, options = {}, requestId = randomUUID()) {
@@ -1948,7 +2464,7 @@ function createBridgeCreateCppClassRequest(className, parentClass, options = {},
1948
2464
  parentClass,
1949
2465
  requestId,
1950
2466
  type: BRIDGE_CREATE_CPP_CLASS_REQUEST_TYPE,
1951
- version: 1
2467
+ version: 2
1952
2468
  });
1953
2469
  }
1954
2470
  function createBridgeGetCreateClassStatusRequest(createClassJobId, requestId = randomUUID()) {
@@ -1956,7 +2472,7 @@ function createBridgeGetCreateClassStatusRequest(createClassJobId, requestId = r
1956
2472
  createClassJobId,
1957
2473
  requestId,
1958
2474
  type: BRIDGE_GET_CREATE_CLASS_STATUS_REQUEST_TYPE,
1959
- version: 1
2475
+ version: 2
1960
2476
  });
1961
2477
  }
1962
2478
  //#endregion
@@ -2056,6 +2572,42 @@ async function sendBridgeStopTraceRequest(request, options) {
2056
2572
  async function sendBridgeFindAssetsRequest(request, options) {
2057
2573
  return parseBridgeResponse(await exchangeBridgeMessage(encodeBridgeFrame(request), options), bridgeFindAssetsResponseSchema, request.requestId);
2058
2574
  }
2575
+ async function sendBridgeListActorsRequest(request, options) {
2576
+ return parseBridgeResponse(await exchangeBridgeMessage(encodeBridgeFrame(request), options), bridgeListActorsResponseSchema, request.requestId);
2577
+ }
2578
+ async function sendBridgeGetActorDetailsRequest(request, options) {
2579
+ return parseBridgeResponse(await exchangeBridgeMessage(encodeBridgeFrame(request), options), bridgeGetActorDetailsResponseSchema, request.requestId);
2580
+ }
2581
+ async function sendBridgeGetCurrentLevelRequest(request, options) {
2582
+ return parseBridgeResponse(await exchangeBridgeMessage(encodeBridgeFrame(request), options), bridgeGetCurrentLevelResponseSchema, request.requestId);
2583
+ }
2584
+ async function sendBridgeSpawnActorRequest(request, options) {
2585
+ return parseBridgeResponse(await exchangeBridgeMessage(encodeBridgeFrame(request), options), bridgeSpawnActorResponseSchema, request.requestId);
2586
+ }
2587
+ async function sendBridgeDestroyActorRequest(request, options) {
2588
+ return parseBridgeResponse(await exchangeBridgeMessage(encodeBridgeFrame(request), options), bridgeDestroyActorResponseSchema, request.requestId);
2589
+ }
2590
+ async function sendBridgeSetActorTransformRequest(request, options) {
2591
+ return parseBridgeResponse(await exchangeBridgeMessage(encodeBridgeFrame(request), options), bridgeSetActorTransformResponseSchema, request.requestId);
2592
+ }
2593
+ async function sendBridgeSetActorPropertyRequest(request, options) {
2594
+ return parseBridgeResponse(await exchangeBridgeMessage(encodeBridgeFrame(request), options), bridgeSetActorPropertyResponseSchema, request.requestId);
2595
+ }
2596
+ async function sendBridgeSelectActorsRequest(request, options) {
2597
+ return parseBridgeResponse(await exchangeBridgeMessage(encodeBridgeFrame(request), options), bridgeSelectActorsResponseSchema, request.requestId);
2598
+ }
2599
+ async function sendBridgeUndoRequest(request, options) {
2600
+ return parseBridgeResponse(await exchangeBridgeMessage(encodeBridgeFrame(request), options), bridgeUndoResponseSchema, request.requestId);
2601
+ }
2602
+ async function sendBridgeRedoRequest(request, options) {
2603
+ return parseBridgeResponse(await exchangeBridgeMessage(encodeBridgeFrame(request), options), bridgeRedoResponseSchema, request.requestId);
2604
+ }
2605
+ async function sendBridgeOpenLevelRequest(request, options) {
2606
+ return parseBridgeResponse(await exchangeBridgeMessage(encodeBridgeFrame(request), options), bridgeOpenLevelResponseSchema, request.requestId);
2607
+ }
2608
+ async function sendBridgeGetOpenLevelStatusRequest(request, options) {
2609
+ return parseBridgeResponse(await exchangeBridgeMessage(encodeBridgeFrame(request), options), bridgeGetOpenLevelStatusResponseSchema, request.requestId);
2610
+ }
2059
2611
  async function sendBridgeGetAssetDependenciesRequest(request, options) {
2060
2612
  return parseBridgeResponse(await exchangeBridgeMessage(encodeBridgeFrame(request), options), bridgeGetAssetDependenciesResponseSchema, request.requestId);
2061
2613
  }
@@ -2429,6 +2981,13 @@ var RegistryWriteQueue = class {
2429
2981
  function createHttpApi(options) {
2430
2982
  const app = Fastify({ logger: false });
2431
2983
  const registryWriteQueue = options.registryWriteQueue ?? new RegistryWriteQueue();
2984
+ const notifyRegistryChanged = () => {
2985
+ try {
2986
+ options.onRegistryChanged?.();
2987
+ } catch (error) {
2988
+ options.logger.warn({ error }, "Failed to notify listeners that the registry changed");
2989
+ }
2990
+ };
2432
2991
  app.setErrorHandler((error, _request, reply) => {
2433
2992
  if (error instanceof HttpError) {
2434
2993
  reply.code(error.statusCode).send({
@@ -2513,6 +3072,7 @@ function createHttpApi(options) {
2513
3072
  projectName: entry.projectName,
2514
3073
  projectPath: entry.projectPath
2515
3074
  }, "Upserted Unreal Editor project registration");
3075
+ notifyRegistryChanged();
2516
3076
  return {
2517
3077
  projectId: entry.projectId,
2518
3078
  stored: true
@@ -2543,6 +3103,7 @@ function createHttpApi(options) {
2543
3103
  existed,
2544
3104
  projectId
2545
3105
  }, "Removed Unreal Editor project registration");
3106
+ if (existed) notifyRegistryChanged();
2546
3107
  return {
2547
3108
  projectId,
2548
3109
  removed: true
@@ -2972,144 +3533,6 @@ function isLogLevel(value) {
2972
3533
  return value === "fatal" || value === "error" || value === "warn" || value === "info" || value === "debug" || value === "trace" || value === "silent";
2973
3534
  }
2974
3535
  //#endregion
2975
- //#region src/buildDiagnostics.ts
2976
- const CLANG_DIAGNOSTIC_PATTERN = /^(?<file>.+?):(?<line>\d+):(?:(?<column>\d+):)?\s+(?<severity>fatal error|error|warning):\s+(?<message>.*)$/u;
2977
- const MSVC_DIAGNOSTIC_PATTERN = /^(?<file>.+?)\((?<line>\d+)(?:,\d+)?\):\s+(?<severity>fatal error|error|warning)\s+(?<code>[A-Z]+\d+):\s+(?<message>.*)$/u;
2978
- const CLANG_TIDY_PATTERN = /^(?<file>.+?):(?<line>\d+):(?<column>\d+):\s+(?<severity>error|warning):\s+(?<message>.*?)(?:\s+\[(?<check>[^\]]+)\])?$/u;
2979
- const COOK_LINE_PREFIX_PATTERN = /^(?:\[[^\]]*\]\s*)*(?:[A-Za-z]+Helper:\s*[^:]*:\s*)?/u;
2980
- const COOK_DIAGNOSTIC_PATTERN = /^(?<category>[A-Za-z_][A-Za-z0-9_]*):\s+(?<severity>Error|Warning)(?::)?\s+(?<message>.+)$/u;
2981
- const UAT_DIAGNOSTIC_PATTERN = /^(?<severity>ERROR|WARNING):\s+(?<message>.+)$/u;
2982
- function diagnosticKey(diagnostic) {
2983
- return `${diagnostic.file ?? ""}|${diagnostic.line ?? ""}|${diagnostic.message}`;
2984
- }
2985
- function matchCookDiagnostic(rawLine) {
2986
- const line = rawLine.trim().replace(COOK_LINE_PREFIX_PATTERN, "").trim();
2987
- if (line.length === 0) return null;
2988
- const categoryMatch = COOK_DIAGNOSTIC_PATTERN.exec(line);
2989
- if (categoryMatch?.groups) {
2990
- const { category, message, severity } = categoryMatch.groups;
2991
- if (category === void 0 || message === void 0 || severity === void 0) return null;
2992
- return {
2993
- isError: severity === "Error",
2994
- message: `${category}: ${message.trim()}`
2995
- };
2996
- }
2997
- const uatMatch = UAT_DIAGNOSTIC_PATTERN.exec(line);
2998
- if (!uatMatch?.groups) return null;
2999
- const { message, severity } = uatMatch.groups;
3000
- if (message === void 0 || severity === void 0) return null;
3001
- return {
3002
- isError: severity === "ERROR",
3003
- message: message.trim()
3004
- };
3005
- }
3006
- var CookDiagnosticCollector = class {
3007
- maxEntriesPerSeverity;
3008
- errors = [];
3009
- warnings = [];
3010
- seenErrors = /* @__PURE__ */ new Set();
3011
- seenWarnings = /* @__PURE__ */ new Set();
3012
- pending = "";
3013
- truncatedFlag = false;
3014
- constructor(maxEntriesPerSeverity) {
3015
- this.maxEntriesPerSeverity = maxEntriesPerSeverity;
3016
- }
3017
- push(chunk) {
3018
- const lines = (this.pending + chunk).split(/\r?\n/u);
3019
- this.pending = lines.pop() ?? "";
3020
- for (const line of lines) this.pushLine(line);
3021
- }
3022
- flush() {
3023
- if (this.pending.length === 0) return;
3024
- const remaining = this.pending;
3025
- this.pending = "";
3026
- this.pushLine(remaining);
3027
- }
3028
- snapshot() {
3029
- return {
3030
- errorCount: this.errors.length,
3031
- errors: [...this.errors],
3032
- truncated: this.truncatedFlag,
3033
- warningCount: this.warnings.length,
3034
- warnings: [...this.warnings]
3035
- };
3036
- }
3037
- pushLine(rawLine) {
3038
- const matched = matchCookDiagnostic(rawLine);
3039
- if (!matched) return;
3040
- const diagnostic = { message: matched.message };
3041
- const target = matched.isError ? this.errors : this.warnings;
3042
- const seen = matched.isError ? this.seenErrors : this.seenWarnings;
3043
- const key = diagnosticKey(diagnostic);
3044
- if (seen.has(key)) return;
3045
- seen.add(key);
3046
- if (target.length >= this.maxEntriesPerSeverity) {
3047
- this.truncatedFlag = true;
3048
- return;
3049
- }
3050
- target.push(diagnostic);
3051
- }
3052
- };
3053
- function parseBuildDiagnostics(output) {
3054
- const errors = [];
3055
- const warnings = [];
3056
- const seenErrors = /* @__PURE__ */ new Set();
3057
- const seenWarnings = /* @__PURE__ */ new Set();
3058
- for (const rawLine of output.split(/\r?\n/u)) {
3059
- const line = rawLine.trim();
3060
- if (line.length === 0) continue;
3061
- const match = CLANG_DIAGNOSTIC_PATTERN.exec(line) ?? MSVC_DIAGNOSTIC_PATTERN.exec(line);
3062
- if (!match?.groups) continue;
3063
- const groups = match.groups;
3064
- const { file, line: lineText, message, severity } = groups;
3065
- if (file === void 0 || lineText === void 0 || message === void 0 || severity === void 0) continue;
3066
- const code = typeof groups.code === "string" ? `${groups.code}: ` : "";
3067
- const diagnostic = {
3068
- file,
3069
- line: Number.parseInt(lineText, 10),
3070
- message: `${code}${message}`.trim()
3071
- };
3072
- const isError = severity === "error" || severity === "fatal error";
3073
- const target = isError ? errors : warnings;
3074
- const seen = isError ? seenErrors : seenWarnings;
3075
- const key = diagnosticKey(diagnostic);
3076
- if (seen.has(key)) continue;
3077
- seen.add(key);
3078
- target.push(diagnostic);
3079
- }
3080
- return {
3081
- errorCount: errors.length,
3082
- errors,
3083
- warningCount: warnings.length,
3084
- warnings
3085
- };
3086
- }
3087
- function parseClangTidyFindings(output) {
3088
- const findings = [];
3089
- const seen = /* @__PURE__ */ new Set();
3090
- for (const rawLine of output.split(/\r?\n/u)) {
3091
- const match = CLANG_TIDY_PATTERN.exec(rawLine.trim());
3092
- if (!match?.groups) continue;
3093
- const groups = match.groups;
3094
- const { file, line, message } = groups;
3095
- if (file === void 0 || line === void 0 || message === void 0) continue;
3096
- const severity = groups.severity === "error" ? "error" : "warning";
3097
- const finding = {
3098
- check: groups.check ?? "unknown",
3099
- file,
3100
- line: Number.parseInt(line, 10),
3101
- message: message.trim(),
3102
- severity
3103
- };
3104
- if (typeof groups.column === "string") finding.column = Number.parseInt(groups.column, 10);
3105
- const key = `${finding.file}|${finding.line}|${finding.column ?? ""}|${finding.check}|${finding.message}`;
3106
- if (seen.has(key)) continue;
3107
- seen.add(key);
3108
- findings.push(finding);
3109
- }
3110
- return findings;
3111
- }
3112
- //#endregion
3113
3536
  //#region src/engineToolchain.ts
3114
3537
  const UNREAL_BUILD_CONFIGURATIONS = [
3115
3538
  "Debug",
@@ -3140,6 +3563,7 @@ function cookCommandletTargetPlatform(platform) {
3140
3563
  const DEFAULT_PROCESS_TIMEOUT_MS = 18e5;
3141
3564
  const MAX_PROCESS_TIMEOUT_MS = 72e5;
3142
3565
  const DEFAULT_PACKAGE_TIMEOUT_MS = 54e5;
3566
+ const ENGINE_INSTALL_DIRECTORY_PATTERN = /^UE_(\d+(?:\.\d+)*)$/u;
3143
3567
  async function defaultFileExists(filePath) {
3144
3568
  try {
3145
3569
  await access(filePath);
@@ -3357,6 +3781,47 @@ async function resolveEngine(options) {
3357
3781
  unrealBuildTool
3358
3782
  };
3359
3783
  }
3784
+ async function listEngineInstalls(context) {
3785
+ const ueInstallsDir = context.env.UE_INSTALLS_DIR?.trim();
3786
+ if (!ueInstallsDir) throw new EngineToolchainError("UE_INSTALLS_DIR is not set; define it in .env (see .env.example).");
3787
+ let entries;
3788
+ try {
3789
+ entries = await context.listDirectory(ueInstallsDir);
3790
+ } catch (error) {
3791
+ throw new EngineToolchainError(`Failed to read UE_INSTALLS_DIR ${ueInstallsDir}: ${error instanceof Error ? error.message : String(error)}`, { cause: error instanceof Error ? error : void 0 });
3792
+ }
3793
+ const installs = [];
3794
+ for (const entry of entries) {
3795
+ const engineVersion = ENGINE_INSTALL_DIRECTORY_PATTERN.exec(entry)?.[1];
3796
+ if (engineVersion === void 0) continue;
3797
+ const engineRoot = path.join(ueInstallsDir, entry);
3798
+ installs.push({
3799
+ engineRoot,
3800
+ engineVersion,
3801
+ unrealBuildToolResolved: await isUnrealBuildToolResolvable(engineRoot, context)
3802
+ });
3803
+ }
3804
+ return installs.sort((left, right) => compareEngineVersions(left.engineVersion, right.engineVersion));
3805
+ }
3806
+ async function isUnrealBuildToolResolvable(engineRoot, context) {
3807
+ try {
3808
+ await resolveUnrealBuildTool(engineRoot, context);
3809
+ return true;
3810
+ } catch (error) {
3811
+ if (error instanceof EngineToolchainError) return false;
3812
+ throw error;
3813
+ }
3814
+ }
3815
+ function compareEngineVersions(left, right) {
3816
+ const leftSegments = left.split(".");
3817
+ const rightSegments = right.split(".");
3818
+ const segmentCount = Math.max(leftSegments.length, rightSegments.length);
3819
+ for (let index = 0; index < segmentCount; index += 1) {
3820
+ const difference = Number(leftSegments[index] ?? 0) - Number(rightSegments[index] ?? 0);
3821
+ if (difference !== 0) return difference;
3822
+ }
3823
+ return 0;
3824
+ }
3360
3825
  async function resolveUnrealBuildTool(engineRoot, context) {
3361
3826
  if (context.platform === "win32") {
3362
3827
  const ubtExe = path.join(engineRoot, "Engine", "Binaries", "DotNET", "UnrealBuildTool", "UnrealBuildTool.exe");
@@ -3460,12 +3925,431 @@ async function resolveBundledDotnet(engineRoot, context) {
3460
3925
  const candidate = path.join(dotnetRoot, versionDir, platformDir, executableName);
3461
3926
  if (await context.fileExists(candidate)) return candidate;
3462
3927
  }
3463
- }
3464
- function buildUnrealBuildToolInvocation(engine, extraArgs) {
3465
- return {
3466
- args: [...engine.unrealBuildTool.args, ...extraArgs],
3467
- command: engine.unrealBuildTool.command
3468
- };
3928
+ }
3929
+ function buildUnrealBuildToolInvocation(engine, extraArgs) {
3930
+ return {
3931
+ args: [...engine.unrealBuildTool.args, ...extraArgs],
3932
+ command: engine.unrealBuildTool.command
3933
+ };
3934
+ }
3935
+ const RESOURCE_MIME_TYPE = "application/json";
3936
+ const REGISTRY_RESOURCE_URI = "uemcp://registry";
3937
+ const REGISTRY_PROJECT_RESOURCE_URI_TEMPLATE = "uemcp://registry/{projectId}";
3938
+ const ENGINES_RESOURCE_URI = "uemcp://engines";
3939
+ const WORKSPACE_PROJECT_RESOURCE_URI = "uemcp://workspace/project";
3940
+ function readRegistryResource(dependencies) {
3941
+ const entries = dependencies.registry.snapshot();
3942
+ return clampResourceList(entries, (visibleEntries, truncated) => ({
3943
+ entries: visibleEntries,
3944
+ totalCount: entries.length,
3945
+ truncated
3946
+ }));
3947
+ }
3948
+ function readRegistryProjectResource(projectId, dependencies) {
3949
+ const parsedProjectId = projectIdSchema.safeParse(projectId);
3950
+ if (!parsedProjectId.success) return {
3951
+ entry: null,
3952
+ found: false,
3953
+ projectId,
3954
+ reason: "The requested projectId is not a canonical 32-character Unreal ProjectID.",
3955
+ truncated: false
3956
+ };
3957
+ const entry = dependencies.registry.getProject(parsedProjectId.data);
3958
+ if (!entry) return {
3959
+ entry: null,
3960
+ found: false,
3961
+ projectId: parsedProjectId.data,
3962
+ reason: "No Unreal Editor is registered with this server for that projectId.",
3963
+ truncated: false
3964
+ };
3965
+ return clampResourcePayload({
3966
+ entry,
3967
+ found: true,
3968
+ projectId: entry.projectId,
3969
+ truncated: false
3970
+ }, () => ({
3971
+ entry: null,
3972
+ found: true,
3973
+ projectId: parsedProjectId.data,
3974
+ reason: "The registry entry exceeded the resource payload budget.",
3975
+ truncated: true
3976
+ }));
3977
+ }
3978
+ async function readEnginesResource(dependencies) {
3979
+ const context = dependencies.engineToolchainContext ?? createEngineToolchainContext();
3980
+ const installsDirectory = context.env.UE_INSTALLS_DIR?.trim() ?? null;
3981
+ let engineInstalls;
3982
+ try {
3983
+ engineInstalls = await listEngineInstalls(context);
3984
+ } catch (error) {
3985
+ if (!(error instanceof EngineToolchainError)) throw error;
3986
+ dependencies.logger.warn({ error }, "Failed to enumerate installed Unreal engines for the engines resource");
3987
+ return {
3988
+ engineInstalls: [],
3989
+ installsDirectory: installsDirectory && installsDirectory.length > 0 ? installsDirectory : null,
3990
+ reason: error.message,
3991
+ totalCount: 0,
3992
+ truncated: false
3993
+ };
3994
+ }
3995
+ return clampResourceList(engineInstalls, (visibleInstalls, truncated) => ({
3996
+ engineInstalls: visibleInstalls,
3997
+ installsDirectory: installsDirectory && installsDirectory.length > 0 ? installsDirectory : null,
3998
+ totalCount: engineInstalls.length,
3999
+ truncated
4000
+ }));
4001
+ }
4002
+ async function readWorkspaceProjectResource(dependencies) {
4003
+ const workingDirectory = dependencies.workingDirectory ?? process.cwd();
4004
+ try {
4005
+ const resolved = await dependencies.projectIdResolver.resolveProjectIdFromProjectPath(workingDirectory);
4006
+ return clampResourcePayload({
4007
+ configPath: resolved.configPath,
4008
+ projectId: resolved.projectId,
4009
+ projectPath: resolved.projectPath,
4010
+ resolved: true,
4011
+ truncated: false,
4012
+ workingDirectory
4013
+ }, () => ({
4014
+ reason: "The resolved workspace project exceeded the resource payload budget.",
4015
+ resolved: false,
4016
+ truncated: true,
4017
+ workingDirectory
4018
+ }));
4019
+ } catch (error) {
4020
+ if (!(error instanceof ProjectIdResolutionError)) throw error;
4021
+ return {
4022
+ configPath: error.filePath,
4023
+ reason: error.reason,
4024
+ resolved: false,
4025
+ truncated: false,
4026
+ workingDirectory
4027
+ };
4028
+ }
4029
+ }
4030
+ function registerMcpResources(server, dependencies) {
4031
+ server.registerResource("registry", REGISTRY_RESOURCE_URI, {
4032
+ description: "Every Unreal Editor currently registered with this server, straight from the in-memory registry. Attach this instead of calling list_registered_projects when you only need to know which editors are live and which projectId to pass to the tools. Reads no files and never contacts an editor.",
4033
+ mimeType: RESOURCE_MIME_TYPE,
4034
+ title: "Registered Unreal Editors"
4035
+ }, (uri) => buildResourceContents(uri, readRegistryResource(dependencies)));
4036
+ server.registerResource("registry-project", new ResourceTemplate(REGISTRY_PROJECT_RESOURCE_URI_TEMPLATE, {
4037
+ complete: { projectId: (value) => completeRegisteredProjectId(value, dependencies) },
4038
+ list: () => listRegistryProjectResources(dependencies)
4039
+ }), {
4040
+ description: "The registration for a single Unreal Editor, addressed by canonical 32-character ProjectID. Reports found: false with a reason rather than failing when the projectId is unknown or malformed.",
4041
+ mimeType: RESOURCE_MIME_TYPE,
4042
+ title: "Registered Unreal Editor"
4043
+ }, (uri, variables) => buildResourceContents(uri, readRegistryProjectResource(readTemplateVariable(variables.projectId), dependencies)));
4044
+ server.registerResource("engines", ENGINES_RESOURCE_URI, {
4045
+ description: "Unreal Engine installs discovered under UE_INSTALLS_DIR, each with its version, engine root, and whether UnrealBuildTool resolved inside it. Source builds and GUID engine associations are excluded because the build tools cannot resolve them either.",
4046
+ mimeType: RESOURCE_MIME_TYPE,
4047
+ title: "Installed Unreal Engines"
4048
+ }, async (uri) => buildResourceContents(uri, await readEnginesResource(dependencies)));
4049
+ server.registerResource("workspace-project", WORKSPACE_PROJECT_RESOURCE_URI, {
4050
+ description: "The Unreal project identity of the server's working directory, resolved from Config/DefaultGame.ini. Reports resolved: false with a reason when the working directory is not an Unreal project.",
4051
+ mimeType: RESOURCE_MIME_TYPE,
4052
+ title: "Workspace Unreal Project"
4053
+ }, async (uri) => buildResourceContents(uri, await readWorkspaceProjectResource(dependencies)));
4054
+ }
4055
+ function listRegistryProjectResources(dependencies) {
4056
+ return { resources: dependencies.registry.snapshot().map((entry) => ({
4057
+ description: `Registration for ${entry.projectName} at ${entry.projectPath}`,
4058
+ mimeType: RESOURCE_MIME_TYPE,
4059
+ name: entry.projectId,
4060
+ title: entry.projectName,
4061
+ uri: buildRegistryProjectUri(entry.projectId)
4062
+ })) };
4063
+ }
4064
+ function completeRegisteredProjectId(value, dependencies) {
4065
+ const prefix = value.trim().toLowerCase();
4066
+ return dependencies.registry.snapshot().map((entry) => entry.projectId).filter((projectId) => projectId.startsWith(prefix));
4067
+ }
4068
+ function buildRegistryProjectUri(projectId) {
4069
+ return `${REGISTRY_RESOURCE_URI}/${projectId}`;
4070
+ }
4071
+ function buildResourceContents(uri, payload) {
4072
+ return { contents: [{
4073
+ mimeType: RESOURCE_MIME_TYPE,
4074
+ text: serializeResourcePayload(payload),
4075
+ uri: uri.href
4076
+ }] };
4077
+ }
4078
+ function readTemplateVariable(value) {
4079
+ if (Array.isArray(value)) return value[0] ?? "";
4080
+ return value ?? "";
4081
+ }
4082
+ function clampResourceList(items, build) {
4083
+ const fullPayload = build(items, false);
4084
+ if (measureResourcePayloadBytes(fullPayload) <= 65536) return fullPayload;
4085
+ let smallestRejected = items.length;
4086
+ let largestAccepted = 0;
4087
+ while (largestAccepted + 1 < smallestRejected) {
4088
+ const candidateCount = Math.floor((largestAccepted + smallestRejected) / 2);
4089
+ if (measureResourcePayloadBytes(build(items.slice(0, candidateCount), true)) <= 65536) {
4090
+ largestAccepted = candidateCount;
4091
+ continue;
4092
+ }
4093
+ smallestRejected = candidateCount;
4094
+ }
4095
+ return build(items.slice(0, largestAccepted), true);
4096
+ }
4097
+ function clampResourcePayload(payload, buildMinimal) {
4098
+ if (measureResourcePayloadBytes(payload) <= 65536) return payload;
4099
+ return buildMinimal();
4100
+ }
4101
+ function measureResourcePayloadBytes(payload) {
4102
+ return Buffer$1.byteLength(serializeResourcePayload(payload), "utf8");
4103
+ }
4104
+ function serializeResourcePayload(payload) {
4105
+ return JSON.stringify(payload, null, 2);
4106
+ }
4107
+ //#endregion
4108
+ //#region src/mcpPrompts.ts
4109
+ const projectIdPromptArgument = projectIdSchema.describe("Canonical 32-character Unreal ProjectID of the target editor. Read uemcp://registry to find one.");
4110
+ const diagnoseCrashPromptArgs = { projectId: projectIdPromptArgument };
4111
+ const diagnoseBridgeDisconnectPromptArgs = { projectPath: z.string().trim().min(1).optional().describe("Filesystem path of the Unreal project directory or .uproject, when it is known.") };
4112
+ const smokeTestBridgePromptArgs = { projectId: projectIdPromptArgument };
4113
+ const addGameplayFeaturePromptArgs = {
4114
+ className: bridgeCreateCppClassRequestSchema.shape.className.describe("Name of the C++ class to create, without the type prefix (for example PatrolComponent)."),
4115
+ moduleName: bridgeCreateCppClassRequestSchema.shape.moduleName.describe("Target module for the new class. Omitted means the project’s primary game module."),
4116
+ parentClass: bridgeCreateCppClassRequestSchema.shape.parentClass.describe("Parent class to derive from (for example Actor, ActorComponent, or a project class)."),
4117
+ projectId: projectIdPromptArgument
4118
+ };
4119
+ function buildDiagnoseCrashPrompt(args) {
4120
+ return buildPromptResult("Investigate the most recent Unreal Editor crash for a registered project.", [
4121
+ `Investigate the most recent crash for Unreal project ${args.projectId}.`,
4122
+ "",
4123
+ "Work in this order and stop as soon as the cause is established:",
4124
+ `1. Read ${buildRegistryProjectUri(args.projectId)}, then call ping_project. A registration that is present but unreachable is the signature of a crash: deregistration only runs on a clean shutdown, so a killed process leaves its entry behind. An absent entry proves nothing on its own — the editor may never have registered, or a prior health check may already have evicted it.`,
4125
+ "2. Call get_crash_callstack for the project. Read the newest record first. Note the crash type, the assert or exception text, and the topmost project frames — engine frames below them are usually the victim, not the cause.",
4126
+ "3. Call get_editor_state to see what the editor was doing (PIE state, loaded level, current selection). A crash during PIE and a crash while idle have different suspects.",
4127
+ "4. Call stream_output_log with maxEvents around 50 and minVerbosity of warning. The first call establishes the subscription and returns no events; call it again with the returned cursor to drain. Look for the warnings immediately preceding the crash.",
4128
+ "5. If the callstack names a Blueprint, call get_blueprint_compile_errors for it; if it names a C++ symbol, call get_compile_status to check whether the running binary matches the current source.",
4129
+ "",
4130
+ "Report the crash type, the failing frame, the evidence that supports it, and the smallest change that would address it. Say so plainly if the callstack is inconclusive rather than guessing."
4131
+ ].join("\n"));
4132
+ }
4133
+ function buildDiagnoseBridgeDisconnectPrompt(args) {
4134
+ const projectPathStep = args.projectPath ? `2. Call get_project_id with projectPath ${args.projectPath} to get the canonical ProjectID, then compare it to the registry entries from step 1. A mismatch means the editor and this server are reading different Config/DefaultGame.ini files.` : "2. Ask for the project path if you need one, then call get_project_id with it and compare the result to the registry entries from step 1. A mismatch means the editor and this server are reading different Config/DefaultGame.ini files.";
4135
+ return buildPromptResult("Diagnose why this server cannot reach a running Unreal Editor over the MCPBridge.", [
4136
+ "The MCP server cannot reach a running Unreal Editor. Find which leg of the connection is broken before changing anything.",
4137
+ "",
4138
+ "The two processes meet in two places: the editor registers itself over this server’s HTTP API, and this server opens a TCP bridge socket to the port the editor recorded in that registration.",
4139
+ "",
4140
+ `1. Read ${REGISTRY_RESOURCE_URI}. No entry at all means registration never landed or was evicted. An entry with a stale lastSeenAt means registration landed but health checks are failing.`,
4141
+ projectPathStep,
4142
+ "3. Call ping_project with the registered projectId. reachable: false with registrationRemoved: true means the editor’s TCP listener is gone — the editor died, or the plugin lost its port. Note that this call evicts a stale registration, so read the registry resource before calling it, not after.",
4143
+ "4. If the registration looks healthy but one specific tool fails, call stream_output_log and drain with the returned cursor. Plugin-side faults are logged under the LogMCPBridgeRuntime category: failures resolving the ProjectID, failures binding the bridge socket, and rejected registrations all name themselves there.",
4144
+ "",
4145
+ "Common causes, in rough order of likelihood: the editor was closed or crashed and the registration is stale; the editor is open on a different project than the one being asked about; the project directory was copied, so two directories share one ProjectID and fight over one registration; the plugin is pointed at a different server address.",
4146
+ "",
4147
+ "Report which leg is broken and the single smallest action that fixes it. Do not propose code changes until the diagnosis is complete — this failure is usually configuration, not a bug."
4148
+ ].join("\n"));
4149
+ }
4150
+ function buildSmokeTestBridgePrompt(args) {
4151
+ return buildPromptResult("Prove the MCPBridge works end to end for a registered project.", [
4152
+ `Verify the MCPBridge is healthy for Unreal project ${args.projectId}. This is green-path verification, not fault diagnosis.`,
4153
+ "",
4154
+ `1. Read ${buildRegistryProjectUri(args.projectId)}. Expect a registration whose lastSeenAt is recent and whose port is in the 7000-7100 range.`,
4155
+ "2. Call ping_project. Expect reachable: true, registrationRemoved: false, and attemptsUsed: 1.",
4156
+ "3. Call get_editor_state. Expect the loaded level and PIE state of the editor actually open in front of the user.",
4157
+ "4. Exercise log streaming, which is the one path that covers subscribe, emit, drain, and unsubscribe together:",
4158
+ " a. Call stream_output_log with categoryAllow of [\"LogTemp\"], textContains of \"smoke-bridge-output\", and maxEvents of 10. The first call returns an empty events array and a cursor. That is correct — the subscription has nothing buffered yet.",
4159
+ " b. Call exec_console_command with the command: log LogTemp Warning smoke-bridge-output ping",
4160
+ " c. Call stream_output_log again with the same arguments plus the cursor from step 4a. Expect at least one event whose message contains smoke-bridge-output, and a nextCursor that has advanced.",
4161
+ "",
4162
+ "Report each step as pass or fail with the value that proves it. If any step fails, stop and say which one — the diagnose_bridge_disconnect prompt takes it from there. Treat an empty first drain as expected, never as a failure."
4163
+ ].join("\n"));
4164
+ }
4165
+ function buildAddGameplayFeaturePrompt(args) {
4166
+ const moduleClause = args.moduleName ? ` in module ${args.moduleName}` : "";
4167
+ return buildPromptResult("Add a C++ gameplay class to a live Unreal project and take it through compile and test.", [
4168
+ `Add a C++ class ${args.className} deriving from ${args.parentClass}${moduleClause} to Unreal project ${args.projectId}, then get it compiling and tested.`,
4169
+ "",
4170
+ "Every step here is asynchronous. Each start returns a job id; do not assume success from the start call alone.",
4171
+ "",
4172
+ `1. Call create_cpp_class with className ${args.className}, parentClass ${args.parentClass}${args.moduleName ? `, moduleName ${args.moduleName}` : ""}. It returns a createClassJobId immediately.`,
4173
+ "2. Wait for it with wait_for_job using kind create_class, or poll get_create_class_status. Do not edit the generated files until it reports completion — the editor is still writing them.",
4174
+ "3. Read the generated header and source, then write the actual implementation. A generated class compiles but does nothing.",
4175
+ "4. Call trigger_live_compile, then wait_for_job with kind compile. On failure, read the diagnostics from get_compile_status, fix the source, and compile again. Do not proceed past a failing compile.",
4176
+ "5. Call list_automation_tests to find the tests covering this area, then run_automation_tests, then wait_for_job with kind test_run. Report which tests ran and which failed.",
4177
+ "6. Call save_dirty_packages once the work is done so nothing is left unsaved in the editor.",
4178
+ "",
4179
+ "If the project has no automation tests covering the new class, say so rather than reporting the step as passed."
4180
+ ].join("\n"));
4181
+ }
4182
+ function registerMcpPrompts(server) {
4183
+ server.registerPrompt("diagnose_crash", {
4184
+ argsSchema: diagnoseCrashPromptArgs,
4185
+ description: "Walk the most recent Unreal Editor crash for a project back to its cause using the crash callstack, editor state, and output log.",
4186
+ title: "Diagnose Unreal Crash"
4187
+ }, (args) => buildDiagnoseCrashPrompt(args));
4188
+ server.registerPrompt("diagnose_bridge_disconnect", {
4189
+ argsSchema: diagnoseBridgeDisconnectPromptArgs,
4190
+ description: "Find which leg of the MCPBridge is broken when this server cannot reach a running Unreal Editor — missing registration, ProjectID mismatch, dead listener, or a plugin-side failure.",
4191
+ title: "Diagnose Bridge Disconnect"
4192
+ }, (args) => buildDiagnoseBridgeDisconnectPrompt(args));
4193
+ server.registerPrompt("smoke_test_bridge", {
4194
+ argsSchema: smokeTestBridgePromptArgs,
4195
+ description: "Prove the MCPBridge round-trips end to end for a project: registration, ping, editor state, and a full log subscribe-emit-drain cycle.",
4196
+ title: "Smoke-Test the Bridge"
4197
+ }, (args) => buildSmokeTestBridgePrompt(args));
4198
+ server.registerPrompt("add_gameplay_feature", {
4199
+ argsSchema: addGameplayFeaturePromptArgs,
4200
+ description: "Add a C++ gameplay class to a live Unreal project and drive it through class creation, implementation, live compile, and automation tests.",
4201
+ title: "Add Gameplay Feature"
4202
+ }, (args) => buildAddGameplayFeaturePrompt(args));
4203
+ }
4204
+ function buildPromptResult(description, text) {
4205
+ return {
4206
+ description,
4207
+ messages: [{
4208
+ content: {
4209
+ text,
4210
+ type: "text"
4211
+ },
4212
+ role: "user"
4213
+ }]
4214
+ };
4215
+ }
4216
+ //#endregion
4217
+ //#region src/buildDiagnostics.ts
4218
+ const CLANG_DIAGNOSTIC_PATTERN = /^(?<file>.+?):(?<line>\d+):(?:(?<column>\d+):)?\s+(?<severity>fatal error|error|warning):\s+(?<message>.*)$/u;
4219
+ const MSVC_DIAGNOSTIC_PATTERN = /^(?<file>.+?)\((?<line>\d+)(?:,\d+)?\):\s+(?<severity>fatal error|error|warning)\s+(?<code>[A-Z]+\d+):\s+(?<message>.*)$/u;
4220
+ const CLANG_TIDY_PATTERN = /^(?<file>.+?):(?<line>\d+):(?<column>\d+):\s+(?<severity>error|warning):\s+(?<message>.*?)(?:\s+\[(?<check>[^\]]+)\])?$/u;
4221
+ const COOK_LINE_PREFIX_PATTERN = /^(?:\[[^\]]*\]\s*)*(?:[A-Za-z]+Helper:\s*[^:]*:\s*)?/u;
4222
+ const COOK_DIAGNOSTIC_PATTERN = /^(?<category>[A-Za-z_][A-Za-z0-9_]*):\s+(?<severity>Error|Warning)(?::)?\s+(?<message>.+)$/u;
4223
+ const UAT_DIAGNOSTIC_PATTERN = /^(?<severity>ERROR|WARNING):\s+(?<message>.+)$/u;
4224
+ function diagnosticKey(diagnostic) {
4225
+ return `${diagnostic.file ?? ""}|${diagnostic.line ?? ""}|${diagnostic.message}`;
4226
+ }
4227
+ function matchCookDiagnostic(rawLine) {
4228
+ const line = rawLine.trim().replace(COOK_LINE_PREFIX_PATTERN, "").trim();
4229
+ if (line.length === 0) return null;
4230
+ const categoryMatch = COOK_DIAGNOSTIC_PATTERN.exec(line);
4231
+ if (categoryMatch?.groups) {
4232
+ const { category, message, severity } = categoryMatch.groups;
4233
+ if (category === void 0 || message === void 0 || severity === void 0) return null;
4234
+ return {
4235
+ isError: severity === "Error",
4236
+ message: `${category}: ${message.trim()}`
4237
+ };
4238
+ }
4239
+ const uatMatch = UAT_DIAGNOSTIC_PATTERN.exec(line);
4240
+ if (!uatMatch?.groups) return null;
4241
+ const { message, severity } = uatMatch.groups;
4242
+ if (message === void 0 || severity === void 0) return null;
4243
+ return {
4244
+ isError: severity === "ERROR",
4245
+ message: message.trim()
4246
+ };
4247
+ }
4248
+ var CookDiagnosticCollector = class {
4249
+ maxEntriesPerSeverity;
4250
+ errors = [];
4251
+ warnings = [];
4252
+ seenErrors = /* @__PURE__ */ new Set();
4253
+ seenWarnings = /* @__PURE__ */ new Set();
4254
+ pending = "";
4255
+ truncatedFlag = false;
4256
+ constructor(maxEntriesPerSeverity) {
4257
+ this.maxEntriesPerSeverity = maxEntriesPerSeverity;
4258
+ }
4259
+ push(chunk) {
4260
+ const lines = (this.pending + chunk).split(/\r?\n/u);
4261
+ this.pending = lines.pop() ?? "";
4262
+ for (const line of lines) this.pushLine(line);
4263
+ }
4264
+ flush() {
4265
+ if (this.pending.length === 0) return;
4266
+ const remaining = this.pending;
4267
+ this.pending = "";
4268
+ this.pushLine(remaining);
4269
+ }
4270
+ snapshot() {
4271
+ return {
4272
+ errorCount: this.errors.length,
4273
+ errors: [...this.errors],
4274
+ truncated: this.truncatedFlag,
4275
+ warningCount: this.warnings.length,
4276
+ warnings: [...this.warnings]
4277
+ };
4278
+ }
4279
+ pushLine(rawLine) {
4280
+ const matched = matchCookDiagnostic(rawLine);
4281
+ if (!matched) return;
4282
+ const diagnostic = { message: matched.message };
4283
+ const target = matched.isError ? this.errors : this.warnings;
4284
+ const seen = matched.isError ? this.seenErrors : this.seenWarnings;
4285
+ const key = diagnosticKey(diagnostic);
4286
+ if (seen.has(key)) return;
4287
+ seen.add(key);
4288
+ if (target.length >= this.maxEntriesPerSeverity) {
4289
+ this.truncatedFlag = true;
4290
+ return;
4291
+ }
4292
+ target.push(diagnostic);
4293
+ }
4294
+ };
4295
+ function parseBuildDiagnostics(output) {
4296
+ const errors = [];
4297
+ const warnings = [];
4298
+ const seenErrors = /* @__PURE__ */ new Set();
4299
+ const seenWarnings = /* @__PURE__ */ new Set();
4300
+ for (const rawLine of output.split(/\r?\n/u)) {
4301
+ const line = rawLine.trim();
4302
+ if (line.length === 0) continue;
4303
+ const match = CLANG_DIAGNOSTIC_PATTERN.exec(line) ?? MSVC_DIAGNOSTIC_PATTERN.exec(line);
4304
+ if (!match?.groups) continue;
4305
+ const groups = match.groups;
4306
+ const { file, line: lineText, message, severity } = groups;
4307
+ if (file === void 0 || lineText === void 0 || message === void 0 || severity === void 0) continue;
4308
+ const code = typeof groups.code === "string" ? `${groups.code}: ` : "";
4309
+ const diagnostic = {
4310
+ file,
4311
+ line: Number.parseInt(lineText, 10),
4312
+ message: `${code}${message}`.trim()
4313
+ };
4314
+ const isError = severity === "error" || severity === "fatal error";
4315
+ const target = isError ? errors : warnings;
4316
+ const seen = isError ? seenErrors : seenWarnings;
4317
+ const key = diagnosticKey(diagnostic);
4318
+ if (seen.has(key)) continue;
4319
+ seen.add(key);
4320
+ target.push(diagnostic);
4321
+ }
4322
+ return {
4323
+ errorCount: errors.length,
4324
+ errors,
4325
+ warningCount: warnings.length,
4326
+ warnings
4327
+ };
4328
+ }
4329
+ function parseClangTidyFindings(output) {
4330
+ const findings = [];
4331
+ const seen = /* @__PURE__ */ new Set();
4332
+ for (const rawLine of output.split(/\r?\n/u)) {
4333
+ const match = CLANG_TIDY_PATTERN.exec(rawLine.trim());
4334
+ if (!match?.groups) continue;
4335
+ const groups = match.groups;
4336
+ const { file, line, message } = groups;
4337
+ if (file === void 0 || line === void 0 || message === void 0) continue;
4338
+ const severity = groups.severity === "error" ? "error" : "warning";
4339
+ const finding = {
4340
+ check: groups.check ?? "unknown",
4341
+ file,
4342
+ line: Number.parseInt(line, 10),
4343
+ message: message.trim(),
4344
+ severity
4345
+ };
4346
+ if (typeof groups.column === "string") finding.column = Number.parseInt(groups.column, 10);
4347
+ const key = `${finding.file}|${finding.line}|${finding.column ?? ""}|${finding.check}|${finding.message}`;
4348
+ if (seen.has(key)) continue;
4349
+ seen.add(key);
4350
+ findings.push(finding);
4351
+ }
4352
+ return findings;
3469
4353
  }
3470
4354
  //#endregion
3471
4355
  //#region src/moduleScaffold.ts
@@ -4863,6 +5747,62 @@ const findAssetsInputSchema = {
4863
5747
  recursiveClasses: bridgeFindAssetsRequestSchema.shape.recursiveClasses.describe("Whether classNames also match subclasses (default true)."),
4864
5748
  recursivePaths: bridgeFindAssetsRequestSchema.shape.recursivePaths.describe("Whether packagePaths also match sub-folders (default true).")
4865
5749
  };
5750
+ const listActorsInputSchema = {
5751
+ classNames: bridgeListActorsRequestSchema.shape.classNames.describe("Actor class names to match, either short (StaticMeshActor, PointLight) or full class paths (/Script/Engine.StaticMeshActor, /Game/BP/BP_Foo.BP_Foo_C). Subclasses match too. Empty means every class."),
5752
+ cursor: bridgeListActorsRequestSchema.shape.cursor.describe("Offset into the stable-ordered matched list (default 0). Pass the previous response nextCursor to fetch the next page."),
5753
+ levelName: bridgeListActorsRequestSchema.shape.levelName.describe("Restrict the walk to a single level, by package name (/Game/Maps/MyLevel) or short name (MyLevel). Omitted means every level loaded in the editor world."),
5754
+ maxResults: bridgeListActorsRequestSchema.shape.maxResults.default(50).describe("Maximum number of actors to return per page (1-100, default 50). totalCount still reports the unbounded match count."),
5755
+ nameFilter: bridgeListActorsRequestSchema.shape.nameFilter.describe("Optional case-insensitive substring matched against each actor label and path."),
5756
+ projectId: projectIdSchema.describe("Canonical Unreal ProjectID whose registered editor should list its actors."),
5757
+ tag: bridgeListActorsRequestSchema.shape.tag.describe("Optional exact-match actor tag (AActor::Tags) to filter by.")
5758
+ };
5759
+ const getActorDetailsInputSchema = {
5760
+ actorPath: bridgeGetActorDetailsRequestSchema.shape.actorPath.describe("Full path name of the actor, as returned by list_actors or get_editor_state (e.g. /Game/Maps/Test.Test:PersistentLevel.StaticMeshActor_3)."),
5761
+ componentCursor: bridgeGetActorDetailsRequestSchema.shape.componentCursor.describe("Offset into the component list (default 0). Pass the previous response nextComponentCursor to fetch the next page."),
5762
+ includeInherited: bridgeGetActorDetailsRequestSchema.shape.includeInherited.describe("Whether to include properties inherited from parent classes (default false). Inherited properties are the bulk of any AActor subclass."),
5763
+ maxResults: bridgeGetActorDetailsRequestSchema.shape.maxResults.default(25).describe("Maximum entries returned per page for each list (1-200, default 25)."),
5764
+ projectId: projectIdSchema.describe("Canonical Unreal ProjectID whose registered editor holds the actor."),
5765
+ propertyCursor: bridgeGetActorDetailsRequestSchema.shape.propertyCursor.describe("Offset into the property list (default 0). Pass the previous response nextPropertyCursor to fetch the next page.")
5766
+ };
5767
+ const getCurrentLevelInputSchema = { projectId: projectIdSchema.describe("Canonical Unreal ProjectID whose registered editor should report its loaded level.") };
5768
+ const spawnActorInputSchema = {
5769
+ dryRun: bridgeSpawnActorRequestSchema.shape.dryRun.describe("When true, validate every entry and report what would be spawned without opening a transaction or touching the world (default false)."),
5770
+ projectId: projectIdSchema.describe("Canonical Unreal ProjectID whose registered editor should spawn the actors."),
5771
+ spawns: bridgeSpawnActorRequestSchema.shape.spawns.describe("Actors to spawn (1-64). Each entry needs a classPath — a native class path (/Script/Engine.StaticMeshActor) or a Blueprint generated class (/Game/BP/BP_Foo.BP_Foo_C) — plus an optional transform, label, outliner folderPath, and tags. The whole batch spawns inside one transaction, so Ctrl+Z removes all of it as a single step.")
5772
+ };
5773
+ const destroyActorInputSchema = {
5774
+ actorPaths: bridgeDestroyActorRequestSchema.shape.actorPaths.describe("Full path names of the actors to destroy (1-64), as returned by list_actors. The whole batch is destroyed inside one transaction, so Ctrl+Z restores all of it as a single step."),
5775
+ dryRun: bridgeDestroyActorRequestSchema.shape.dryRun.describe("When true, check that every actorPath resolves and report what would be destroyed without opening a transaction or touching the world (default false)."),
5776
+ projectId: projectIdSchema.describe("Canonical Unreal ProjectID whose registered editor holds the actors.")
5777
+ };
5778
+ const setActorTransformInputSchema = {
5779
+ dryRun: bridgeSetActorTransformRequestSchema.shape.dryRun.describe("When true, check that every actorPath resolves and report what would move without opening a transaction (default false)."),
5780
+ projectId: projectIdSchema.describe("Canonical Unreal ProjectID whose registered editor holds the actors."),
5781
+ transforms: bridgeSetActorTransformRequestSchema.shape.transforms.describe("Transform edits to apply (1-64). Each entry needs an actorPath plus any of location, rotation, and scale — an omitted component keeps its current value, so you can move an actor without restating its rotation. The whole batch runs in one transaction.")
5782
+ };
5783
+ const setActorPropertyInputSchema = {
5784
+ dryRun: bridgeSetActorPropertyRequestSchema.shape.dryRun.describe("When true, resolve every property path and check the value coerces, without opening a transaction or writing anything (default false)."),
5785
+ projectId: projectIdSchema.describe("Canonical Unreal ProjectID whose registered editor holds the actors."),
5786
+ properties: bridgeSetActorPropertyRequestSchema.shape.properties.describe("Property writes to apply (1-64). Each entry needs an actorPath, a propertyPath — 'bHidden' for an actor property or 'StaticMeshComponent.RelativeScale3D' for a component's — and a value in the same ExportText format get_actor_details and get_cdo_defaults emit. Non-editable (EditConst) properties are rejected with an error rather than silently ignored.")
5787
+ };
5788
+ const selectActorsInputSchema = {
5789
+ actorPaths: bridgeSelectActorsRequestSchema.shape.actorPaths.describe("Actor paths to select (up to 64). An empty list clears the selection. These are the same actorPath values list_actors and get_editor_state report."),
5790
+ addToSelection: bridgeSelectActorsRequestSchema.shape.addToSelection.describe("When true, add the listed actors to the current selection instead of replacing it (default false)."),
5791
+ dryRun: bridgeSelectActorsRequestSchema.shape.dryRun.describe("When true, check that every actorPath resolves and report what would be selected without changing the selection (default false)."),
5792
+ projectId: projectIdSchema.describe("Canonical Unreal ProjectID whose registered editor holds the actors.")
5793
+ };
5794
+ const undoInputSchema = { projectId: projectIdSchema.describe("Canonical Unreal ProjectID whose registered editor should undo its last transaction.") };
5795
+ const redoInputSchema = { projectId: projectIdSchema.describe("Canonical Unreal ProjectID whose registered editor should redo its last undone transaction.") };
5796
+ const openLevelInputSchema = {
5797
+ dryRun: bridgeOpenLevelRequestSchema.shape.dryRun.describe("When true, check that the level package exists and report what would be opened without loading it (default false)."),
5798
+ levelPath: bridgeOpenLevelRequestSchema.shape.levelPath.describe("Package name of the level to open (e.g. /Game/Maps/MyLevel), as get_current_level and find_assets report it."),
5799
+ projectId: projectIdSchema.describe("Canonical Unreal ProjectID whose registered editor should open the level."),
5800
+ saveDirtyPackages: bridgeOpenLevelRequestSchema.shape.saveDirtyPackages.describe("When true, save dirty packages before tearing down the current world; otherwise unsaved changes are discarded (default false).")
5801
+ };
5802
+ const getOpenLevelStatusInputSchema = {
5803
+ openLevelJobId: bridgeGetOpenLevelStatusRequestSchema.shape.openLevelJobId.describe("openLevelJobId returned by open_level identifying the load job to poll."),
5804
+ projectId: projectIdSchema
5805
+ };
4866
5806
  const getAssetDependenciesInputSchema = {
4867
5807
  assetPath: bridgeGetAssetDependenciesRequestSchema.shape.assetPath.describe("Package name or object path of the asset to inspect (e.g. /Game/BP/BP_Player or /Game/BP/BP_Player.BP_Player)."),
4868
5808
  categories: bridgeGetAssetDependenciesRequestSchema.shape.categories.describe("Dependency categories to include: 'package' (default), 'searchable_name', 'manage'."),
@@ -5253,7 +6193,7 @@ function registerMcpTools(server, dependencies) {
5253
6193
  }, dependencies));
5254
6194
  });
5255
6195
  server.registerTool("get_editor_state", {
5256
- description: "Report the registered Unreal Editor's current state for a project: the open map name and package path, the labels of the selected actors, the active editor modes, and whether Play-in-Editor is running. Selected actors are capped per response; selectedActorCount reports the full selection size. activeModes is empty until the level editor has finished initializing, so an empty list right after the editor starts means 'not ready yet', not 'no mode'.",
6196
+ description: "Report the registered Unreal Editor's current state for a project: the open map name and package path, the selected actors as { actorPath, label } objects whose actorPath the level tools accept directly, the active editor modes, and whether Play-in-Editor is running. Selected actors are capped per response; selectedActorCount reports the full selection size. activeModes is empty until the level editor has finished initializing, so an empty list right after the editor starts means 'not ready yet', not 'no mode'.",
5257
6197
  inputSchema: getEditorStateInputSchema,
5258
6198
  title: "Get Editor State"
5259
6199
  }, async ({ projectId }) => {
@@ -5380,6 +6320,134 @@ function registerMcpTools(server, dependencies) {
5380
6320
  projectId
5381
6321
  }, dependencies));
5382
6322
  });
6323
+ server.registerTool("list_actors", {
6324
+ description: "Enumerate actors in the editor's currently loaded level(s), optionally filtered by class, name substring, tag, or level. Returns each actor's canonical actorPath (the identifier every other level tool takes), display label, class, level, and World Outliner folder. Results are paged: pass the previous response nextCursor to fetch the next page, and totalCount reports the full match count even when a page truncates. Use this to find an actor before calling get_actor_details.",
6325
+ inputSchema: listActorsInputSchema,
6326
+ title: "List Actors"
6327
+ }, async ({ classNames, cursor, levelName, maxResults, nameFilter, projectId, tag }) => {
6328
+ return toToolSuccess(await listActors({
6329
+ classNames,
6330
+ cursor,
6331
+ levelName,
6332
+ maxResults,
6333
+ nameFilter,
6334
+ projectId,
6335
+ tag
6336
+ }, dependencies));
6337
+ });
6338
+ server.registerTool("get_actor_details", {
6339
+ description: "Inspect one actor in the loaded level by its actorPath: world transform, attached component hierarchy (each component's name, class, attachment parent, and relative transform), and reflected UPROPERTY values. Property values use the same ExportText format get_cdo_defaults emits, so a value read here can be written back verbatim. Components and properties page independently — pass nextComponentCursor / nextPropertyCursor to continue each list. Inherited properties are excluded unless includeInherited is true.",
6340
+ inputSchema: getActorDetailsInputSchema,
6341
+ title: "Get Actor Details"
6342
+ }, async ({ actorPath, componentCursor, includeInherited, maxResults, projectId, propertyCursor }) => {
6343
+ return toToolSuccess(await getActorDetails({
6344
+ actorPath,
6345
+ componentCursor,
6346
+ includeInherited,
6347
+ maxResults,
6348
+ projectId,
6349
+ propertyCursor
6350
+ }, dependencies));
6351
+ });
6352
+ server.registerTool("get_current_level", {
6353
+ description: "Report the level the editor currently has open — its short name and package path — plus every streaming level registered on that world with its loaded and visible state. worldPartition=true means the map streams cells through World Partition rather than through the listed ULevelStreaming entries, so the streamingLevels list will usually be empty for such a map. Returns no level fields when the editor has no world loaded.",
6354
+ inputSchema: getCurrentLevelInputSchema,
6355
+ title: "Get Current Level"
6356
+ }, async ({ projectId }) => {
6357
+ return toToolSuccess(await getCurrentLevel({ projectId }, dependencies));
6358
+ });
6359
+ server.registerTool("spawn_actor", {
6360
+ description: "Spawn one or more actors into the editor's loaded level. Each entry takes a classPath (native or Blueprint generated class) and an optional transform, label, outliner folder, and tags. The whole batch runs inside a single editor transaction, so a human pressing Ctrl+Z removes every actor the call spawned as one step, and save_dirty_packages will report the level afterwards. Returns a per-entry result carrying the new actorPath or the reason that entry failed; a failed entry does not abort the rest of the batch. Pass dryRun: true to validate the batch and see what would be spawned without changing anything.",
6361
+ inputSchema: spawnActorInputSchema,
6362
+ title: "Spawn Actor"
6363
+ }, async ({ dryRun, projectId, spawns }) => {
6364
+ return toToolSuccess(await spawnActor({
6365
+ dryRun,
6366
+ projectId,
6367
+ spawns
6368
+ }, dependencies));
6369
+ });
6370
+ server.registerTool("destroy_actor", {
6371
+ description: "Destroy one or more actors in the loaded level, addressed by the actorPath list_actors returns. The whole batch runs inside a single editor transaction, so Ctrl+Z restores every actor the call destroyed as one step. Returns a per-entry result; an actorPath that no longer resolves fails only its own entry. Pass dryRun: true to check the paths and see what would be destroyed without changing anything.",
6372
+ inputSchema: destroyActorInputSchema,
6373
+ title: "Destroy Actor"
6374
+ }, async ({ actorPaths, dryRun, projectId }) => {
6375
+ return toToolSuccess(await destroyActor({
6376
+ actorPaths,
6377
+ dryRun,
6378
+ projectId
6379
+ }, dependencies));
6380
+ });
6381
+ server.registerTool("set_actor_transform", {
6382
+ description: "Move, rotate, and/or scale one or more actors in the loaded level. Each entry addresses an actor by actorPath and supplies any of location, rotation, and scale — an omitted component keeps its current value. The whole batch runs inside a single editor transaction, so Ctrl+Z restores every original transform as one step. Returns a per-entry result; pass dryRun: true to check the paths without moving anything.",
6383
+ inputSchema: setActorTransformInputSchema,
6384
+ title: "Set Actor Transform"
6385
+ }, async ({ dryRun, projectId, transforms }) => {
6386
+ return toToolSuccess(await setActorTransform({
6387
+ dryRun,
6388
+ projectId,
6389
+ transforms
6390
+ }, dependencies));
6391
+ });
6392
+ server.registerTool("set_actor_property", {
6393
+ description: "Write reflected UPROPERTY values on actors or their components in the loaded level. Each entry takes an actorPath, a propertyPath ('bHidden' on the actor, 'StaticMeshComponent.RelativeScale3D' on a component), and a value in the same ExportText format get_actor_details and get_cdo_defaults emit — so you can read a value, edit it, and write it back without translating. Properties marked EditConst or not exposed for editing are rejected with an explicit error rather than silently ignored. The whole batch runs in one transaction; pass dryRun: true to resolve paths and check coercion without writing.",
6394
+ inputSchema: setActorPropertyInputSchema,
6395
+ title: "Set Actor Property"
6396
+ }, async ({ dryRun, projectId, properties }) => {
6397
+ return toToolSuccess(await setActorProperty({
6398
+ dryRun,
6399
+ projectId,
6400
+ properties
6401
+ }, dependencies));
6402
+ });
6403
+ server.registerTool("select_actors", {
6404
+ description: "Set the editor's actor selection — the write-side counterpart to get_editor_state, using the same actorPath identifiers list_actors returns. An empty actorPaths list clears the selection; addToSelection adds to it instead of replacing it. Runs inside a transaction, so Ctrl+Z restores the previous selection. Returns the resulting selectedActorCount plus a per-entry result for any path that no longer resolves.",
6405
+ inputSchema: selectActorsInputSchema,
6406
+ title: "Select Actors"
6407
+ }, async ({ actorPaths, addToSelection, dryRun, projectId }) => {
6408
+ return toToolSuccess(await selectActors({
6409
+ actorPaths,
6410
+ addToSelection,
6411
+ dryRun,
6412
+ projectId
6413
+ }, dependencies));
6414
+ });
6415
+ server.registerTool("undo", {
6416
+ description: "Undo the editor's most recent transaction — the same action as pressing Ctrl+Z in the editor — and report the title of the transaction that was undone. Use this to roll back your own level edits without a human in the loop. Returns state 'nothing_to_undo' when the transaction buffer is empty. Note this undoes whatever is at the head of the buffer, which may be a human's edit rather than yours, so check the returned transactionTitle.",
6417
+ inputSchema: undoInputSchema,
6418
+ title: "Undo"
6419
+ }, async ({ projectId }) => {
6420
+ return toToolSuccess(await undo({ projectId }, dependencies));
6421
+ });
6422
+ server.registerTool("redo", {
6423
+ description: "Redo the editor's most recently undone transaction — the same action as pressing Ctrl+Y in the editor — and report the title of the transaction that was redone. Returns state 'nothing_to_redo' when there is nothing to reapply.",
6424
+ inputSchema: redoInputSchema,
6425
+ title: "Redo"
6426
+ }, async ({ projectId }) => {
6427
+ return toToolSuccess(await redo({ projectId }, dependencies));
6428
+ });
6429
+ server.registerTool("open_level", {
6430
+ description: "Open a level in the editor by package name. Loading a map tears down and rebuilds the world, which takes far longer than a bridge request can wait, so this only accepts the job: it returns state 'opening' with an openLevelJobId to poll with get_open_level_status. Unsaved changes in the current world are discarded unless saveDirtyPackages is true. This is NOT undoable — loading a map clears the editor's transaction buffer, so undo cannot bring the previous level back.",
6431
+ inputSchema: openLevelInputSchema,
6432
+ title: "Open Level"
6433
+ }, async ({ dryRun, levelPath, projectId, saveDirtyPackages }) => {
6434
+ return toToolSuccess(await openLevel({
6435
+ dryRun,
6436
+ levelPath,
6437
+ projectId,
6438
+ saveDirtyPackages
6439
+ }, dependencies));
6440
+ });
6441
+ server.registerTool("get_open_level_status", {
6442
+ description: "Poll a level load started by open_level for a projectId; returns state (opening, succeeded, failed, unknown_job), the loaded levelPath, and timing once finished. Poll until the state leaves 'opening' before issuing further level tools — actor paths from the previous world do not survive the load.",
6443
+ inputSchema: getOpenLevelStatusInputSchema,
6444
+ title: "Get Open Level Status"
6445
+ }, async ({ openLevelJobId, projectId }) => {
6446
+ return toToolSuccess(await getOpenLevelStatus({
6447
+ openLevelJobId,
6448
+ projectId
6449
+ }, dependencies));
6450
+ });
5383
6451
  server.registerTool("find_assets", {
5384
6452
  description: "Query a project's Unreal Asset Registry for assets by class and/or content path, without loading them; returns each match's name, class, package name, package path, and object path. Requires at least one of classNames or packagePaths — an unfiltered query would stall the editor enumerating every asset. Results are paged: pass the previous response nextCursor to fetch the next page (totalCount reports the full match count). scanning=true means the registry is still indexing, so an empty result does not prove the asset is absent.",
5385
6453
  inputSchema: findAssetsInputSchema,
@@ -5489,6 +6557,285 @@ function registerMcpTools(server, dependencies) {
5489
6557
  }, dependencies));
5490
6558
  });
5491
6559
  }
6560
+ async function listActors(input, dependencies) {
6561
+ const normalizedProjectId = projectIdSchema.parse(input.projectId);
6562
+ const entry = dependencies.registry.getProject(normalizedProjectId);
6563
+ if (!entry) throw new Error(`No registered Unreal engine was found for projectId '${normalizedProjectId}'.`);
6564
+ const response = await sendBridgeListActorsRequest(createBridgeListActorsRequest({
6565
+ classNames: input.classNames,
6566
+ cursor: input.cursor,
6567
+ levelName: input.levelName,
6568
+ maxResults: input.maxResults,
6569
+ nameFilter: input.nameFilter,
6570
+ tag: input.tag
6571
+ }), {
6572
+ connectTimeoutMs: deriveBridgeConnectTimeoutMs$1(dependencies.pingTimeoutMs),
6573
+ host: "127.0.0.1",
6574
+ port: entry.port,
6575
+ readTimeoutMs: deriveEditorWorkTimeoutMs(dependencies.pingTimeoutMs)
6576
+ });
6577
+ if (!response.ok) throw new Error(`Bridge list_actors failed (${response.error.code}): ${response.error.message}`);
6578
+ return {
6579
+ actors: response.actors,
6580
+ message: response.message,
6581
+ nextCursor: response.nextCursor,
6582
+ projectId: entry.projectId,
6583
+ totalCount: response.totalCount,
6584
+ truncated: response.truncated
6585
+ };
6586
+ }
6587
+ async function getActorDetails(input, dependencies) {
6588
+ const normalizedProjectId = projectIdSchema.parse(input.projectId);
6589
+ const entry = dependencies.registry.getProject(normalizedProjectId);
6590
+ if (!entry) throw new Error(`No registered Unreal engine was found for projectId '${normalizedProjectId}'.`);
6591
+ const response = await sendBridgeGetActorDetailsRequest(createBridgeGetActorDetailsRequest(input.actorPath, {
6592
+ componentCursor: input.componentCursor,
6593
+ includeInherited: input.includeInherited,
6594
+ maxResults: input.maxResults,
6595
+ propertyCursor: input.propertyCursor
6596
+ }), {
6597
+ connectTimeoutMs: deriveBridgeConnectTimeoutMs$1(dependencies.pingTimeoutMs),
6598
+ host: "127.0.0.1",
6599
+ port: entry.port,
6600
+ readTimeoutMs: deriveEditorWorkTimeoutMs(dependencies.pingTimeoutMs)
6601
+ });
6602
+ if (!response.ok) throw new Error(`Bridge get_actor_details failed (${response.error.code}): ${response.error.message}`);
6603
+ return {
6604
+ actorPath: response.actorPath,
6605
+ className: response.className,
6606
+ componentCount: response.componentCount,
6607
+ components: response.components,
6608
+ componentTruncated: response.componentTruncated,
6609
+ folderPath: response.folderPath,
6610
+ label: response.label,
6611
+ levelName: response.levelName,
6612
+ nextComponentCursor: response.nextComponentCursor,
6613
+ nextPropertyCursor: response.nextPropertyCursor,
6614
+ projectId: entry.projectId,
6615
+ properties: response.properties,
6616
+ propertyCount: response.propertyCount,
6617
+ propertyTruncated: response.propertyTruncated,
6618
+ transform: response.transform
6619
+ };
6620
+ }
6621
+ async function getCurrentLevel(input, dependencies) {
6622
+ const normalizedProjectId = projectIdSchema.parse(input.projectId);
6623
+ const entry = dependencies.registry.getProject(normalizedProjectId);
6624
+ if (!entry) throw new Error(`No registered Unreal engine was found for projectId '${normalizedProjectId}'.`);
6625
+ const response = await sendBridgeGetCurrentLevelRequest(createBridgeGetCurrentLevelRequest(), {
6626
+ connectTimeoutMs: deriveBridgeConnectTimeoutMs$1(dependencies.pingTimeoutMs),
6627
+ host: "127.0.0.1",
6628
+ port: entry.port,
6629
+ readTimeoutMs: deriveEditorWorkTimeoutMs(dependencies.pingTimeoutMs)
6630
+ });
6631
+ if (!response.ok) throw new Error(`Bridge get_current_level failed (${response.error.code}): ${response.error.message}`);
6632
+ return {
6633
+ levelName: response.levelName,
6634
+ levelPath: response.levelPath,
6635
+ message: response.message,
6636
+ projectId: entry.projectId,
6637
+ streamingLevelCount: response.streamingLevelCount,
6638
+ streamingLevels: response.streamingLevels,
6639
+ truncated: response.truncated,
6640
+ worldPartition: response.worldPartition
6641
+ };
6642
+ }
6643
+ async function spawnActor(input, dependencies) {
6644
+ const normalizedProjectId = projectIdSchema.parse(input.projectId);
6645
+ const entry = dependencies.registry.getProject(normalizedProjectId);
6646
+ if (!entry) throw new Error(`No registered Unreal engine was found for projectId '${normalizedProjectId}'.`);
6647
+ const response = await sendBridgeSpawnActorRequest(createBridgeSpawnActorRequest(input.spawns, { dryRun: input.dryRun }), {
6648
+ connectTimeoutMs: deriveBridgeConnectTimeoutMs$1(dependencies.pingTimeoutMs),
6649
+ host: "127.0.0.1",
6650
+ port: entry.port,
6651
+ readTimeoutMs: deriveEditorWorkTimeoutMs(dependencies.pingTimeoutMs)
6652
+ });
6653
+ if (!response.ok) throw new Error(`Bridge spawn_actor failed (${response.error.code}): ${response.error.message}`);
6654
+ return {
6655
+ dryRun: response.dryRun,
6656
+ failureCount: response.failureCount,
6657
+ message: response.message,
6658
+ projectId: entry.projectId,
6659
+ results: response.results,
6660
+ state: response.state,
6661
+ successCount: response.successCount,
6662
+ transactionTitle: response.transactionTitle
6663
+ };
6664
+ }
6665
+ async function destroyActor(input, dependencies) {
6666
+ const normalizedProjectId = projectIdSchema.parse(input.projectId);
6667
+ const entry = dependencies.registry.getProject(normalizedProjectId);
6668
+ if (!entry) throw new Error(`No registered Unreal engine was found for projectId '${normalizedProjectId}'.`);
6669
+ const response = await sendBridgeDestroyActorRequest(createBridgeDestroyActorRequest(input.actorPaths, { dryRun: input.dryRun }), {
6670
+ connectTimeoutMs: deriveBridgeConnectTimeoutMs$1(dependencies.pingTimeoutMs),
6671
+ host: "127.0.0.1",
6672
+ port: entry.port,
6673
+ readTimeoutMs: deriveEditorWorkTimeoutMs(dependencies.pingTimeoutMs)
6674
+ });
6675
+ if (!response.ok) throw new Error(`Bridge destroy_actor failed (${response.error.code}): ${response.error.message}`);
6676
+ return {
6677
+ dryRun: response.dryRun,
6678
+ failureCount: response.failureCount,
6679
+ message: response.message,
6680
+ projectId: entry.projectId,
6681
+ results: response.results,
6682
+ state: response.state,
6683
+ successCount: response.successCount,
6684
+ transactionTitle: response.transactionTitle
6685
+ };
6686
+ }
6687
+ async function setActorTransform(input, dependencies) {
6688
+ const normalizedProjectId = projectIdSchema.parse(input.projectId);
6689
+ const entry = dependencies.registry.getProject(normalizedProjectId);
6690
+ if (!entry) throw new Error(`No registered Unreal engine was found for projectId '${normalizedProjectId}'.`);
6691
+ const response = await sendBridgeSetActorTransformRequest(createBridgeSetActorTransformRequest(input.transforms, { dryRun: input.dryRun }), {
6692
+ connectTimeoutMs: deriveBridgeConnectTimeoutMs$1(dependencies.pingTimeoutMs),
6693
+ host: "127.0.0.1",
6694
+ port: entry.port,
6695
+ readTimeoutMs: deriveEditorWorkTimeoutMs(dependencies.pingTimeoutMs)
6696
+ });
6697
+ if (!response.ok) throw new Error(`Bridge set_actor_transform failed (${response.error.code}): ${response.error.message}`);
6698
+ return {
6699
+ dryRun: response.dryRun,
6700
+ failureCount: response.failureCount,
6701
+ message: response.message,
6702
+ projectId: entry.projectId,
6703
+ results: response.results,
6704
+ state: response.state,
6705
+ successCount: response.successCount,
6706
+ transactionTitle: response.transactionTitle
6707
+ };
6708
+ }
6709
+ async function setActorProperty(input, dependencies) {
6710
+ const normalizedProjectId = projectIdSchema.parse(input.projectId);
6711
+ const entry = dependencies.registry.getProject(normalizedProjectId);
6712
+ if (!entry) throw new Error(`No registered Unreal engine was found for projectId '${normalizedProjectId}'.`);
6713
+ const response = await sendBridgeSetActorPropertyRequest(createBridgeSetActorPropertyRequest(input.properties, { dryRun: input.dryRun }), {
6714
+ connectTimeoutMs: deriveBridgeConnectTimeoutMs$1(dependencies.pingTimeoutMs),
6715
+ host: "127.0.0.1",
6716
+ port: entry.port,
6717
+ readTimeoutMs: deriveEditorWorkTimeoutMs(dependencies.pingTimeoutMs)
6718
+ });
6719
+ if (!response.ok) throw new Error(`Bridge set_actor_property failed (${response.error.code}): ${response.error.message}`);
6720
+ return {
6721
+ dryRun: response.dryRun,
6722
+ failureCount: response.failureCount,
6723
+ message: response.message,
6724
+ projectId: entry.projectId,
6725
+ results: response.results,
6726
+ state: response.state,
6727
+ successCount: response.successCount,
6728
+ transactionTitle: response.transactionTitle
6729
+ };
6730
+ }
6731
+ async function selectActors(input, dependencies) {
6732
+ const normalizedProjectId = projectIdSchema.parse(input.projectId);
6733
+ const entry = dependencies.registry.getProject(normalizedProjectId);
6734
+ if (!entry) throw new Error(`No registered Unreal engine was found for projectId '${normalizedProjectId}'.`);
6735
+ const response = await sendBridgeSelectActorsRequest(createBridgeSelectActorsRequest(input.actorPaths, {
6736
+ addToSelection: input.addToSelection,
6737
+ dryRun: input.dryRun
6738
+ }), {
6739
+ connectTimeoutMs: deriveBridgeConnectTimeoutMs$1(dependencies.pingTimeoutMs),
6740
+ host: "127.0.0.1",
6741
+ port: entry.port,
6742
+ readTimeoutMs: deriveEditorWorkTimeoutMs(dependencies.pingTimeoutMs)
6743
+ });
6744
+ if (!response.ok) throw new Error(`Bridge select_actors failed (${response.error.code}): ${response.error.message}`);
6745
+ return {
6746
+ dryRun: response.dryRun,
6747
+ failureCount: response.failureCount,
6748
+ message: response.message,
6749
+ projectId: entry.projectId,
6750
+ results: response.results,
6751
+ selectedActorCount: response.selectedActorCount,
6752
+ state: response.state,
6753
+ successCount: response.successCount,
6754
+ transactionTitle: response.transactionTitle
6755
+ };
6756
+ }
6757
+ async function undo(input, dependencies) {
6758
+ const normalizedProjectId = projectIdSchema.parse(input.projectId);
6759
+ const entry = dependencies.registry.getProject(normalizedProjectId);
6760
+ if (!entry) throw new Error(`No registered Unreal engine was found for projectId '${normalizedProjectId}'.`);
6761
+ const response = await sendBridgeUndoRequest(createBridgeUndoRequest(), {
6762
+ connectTimeoutMs: deriveBridgeConnectTimeoutMs$1(dependencies.pingTimeoutMs),
6763
+ host: "127.0.0.1",
6764
+ port: entry.port,
6765
+ readTimeoutMs: deriveEditorWorkTimeoutMs(dependencies.pingTimeoutMs)
6766
+ });
6767
+ if (!response.ok) throw new Error(`Bridge undo failed (${response.error.code}): ${response.error.message}`);
6768
+ return {
6769
+ message: response.message,
6770
+ projectId: entry.projectId,
6771
+ state: response.state,
6772
+ transactionTitle: response.transactionTitle
6773
+ };
6774
+ }
6775
+ async function redo(input, dependencies) {
6776
+ const normalizedProjectId = projectIdSchema.parse(input.projectId);
6777
+ const entry = dependencies.registry.getProject(normalizedProjectId);
6778
+ if (!entry) throw new Error(`No registered Unreal engine was found for projectId '${normalizedProjectId}'.`);
6779
+ const response = await sendBridgeRedoRequest(createBridgeRedoRequest(), {
6780
+ connectTimeoutMs: deriveBridgeConnectTimeoutMs$1(dependencies.pingTimeoutMs),
6781
+ host: "127.0.0.1",
6782
+ port: entry.port,
6783
+ readTimeoutMs: deriveEditorWorkTimeoutMs(dependencies.pingTimeoutMs)
6784
+ });
6785
+ if (!response.ok) throw new Error(`Bridge redo failed (${response.error.code}): ${response.error.message}`);
6786
+ return {
6787
+ message: response.message,
6788
+ projectId: entry.projectId,
6789
+ state: response.state,
6790
+ transactionTitle: response.transactionTitle
6791
+ };
6792
+ }
6793
+ async function openLevel(input, dependencies) {
6794
+ const normalizedProjectId = projectIdSchema.parse(input.projectId);
6795
+ const entry = dependencies.registry.getProject(normalizedProjectId);
6796
+ if (!entry) throw new Error(`No registered Unreal engine was found for projectId '${normalizedProjectId}'.`);
6797
+ const request = createBridgeOpenLevelRequest(input.levelPath, {
6798
+ dryRun: input.dryRun,
6799
+ saveDirtyPackages: input.saveDirtyPackages
6800
+ });
6801
+ const response = await sendBridgeOpenLevelRequest(request, {
6802
+ connectTimeoutMs: deriveBridgeConnectTimeoutMs$1(dependencies.pingTimeoutMs),
6803
+ host: "127.0.0.1",
6804
+ port: entry.port,
6805
+ readTimeoutMs: deriveEditorWorkTimeoutMs(dependencies.pingTimeoutMs)
6806
+ });
6807
+ if (!response.ok) throw new Error(`Bridge open_level failed (${response.error.code}): ${response.error.message}`);
6808
+ const result = {
6809
+ levelPath: response.levelPath ?? request.levelPath,
6810
+ projectId: entry.projectId,
6811
+ state: response.state
6812
+ };
6813
+ if (response.openLevelJobId !== void 0) result.openLevelJobId = response.openLevelJobId;
6814
+ if (response.message !== void 0) result.message = response.message;
6815
+ return result;
6816
+ }
6817
+ async function getOpenLevelStatus(input, dependencies) {
6818
+ const normalizedProjectId = projectIdSchema.parse(input.projectId);
6819
+ const entry = dependencies.registry.getProject(normalizedProjectId);
6820
+ if (!entry) throw new Error(`No registered Unreal engine was found for projectId '${normalizedProjectId}'.`);
6821
+ const response = await sendBridgeGetOpenLevelStatusRequest(createBridgeGetOpenLevelStatusRequest(input.openLevelJobId), {
6822
+ connectTimeoutMs: deriveBridgeConnectTimeoutMs$1(dependencies.pingTimeoutMs),
6823
+ host: "127.0.0.1",
6824
+ port: entry.port,
6825
+ readTimeoutMs: deriveEditorWorkTimeoutMs(dependencies.pingTimeoutMs)
6826
+ });
6827
+ if (!response.ok) throw new Error(`Bridge get_open_level_status failed (${response.error.code}): ${response.error.message}`);
6828
+ const result = {
6829
+ openLevelJobId: response.openLevelJobId,
6830
+ projectId: normalizedProjectId,
6831
+ state: response.state
6832
+ };
6833
+ if (response.levelPath !== void 0) result.levelPath = response.levelPath;
6834
+ if (response.message !== void 0) result.message = response.message;
6835
+ if (response.finishedAt !== void 0) result.finishedAt = response.finishedAt;
6836
+ if (response.durationMs !== void 0) result.durationMs = response.durationMs;
6837
+ return result;
6838
+ }
5492
6839
  async function findAssets(input, dependencies) {
5493
6840
  const normalizedProjectId = projectIdSchema.parse(input.projectId);
5494
6841
  const entry = dependencies.registry.getProject(normalizedProjectId);
@@ -6882,7 +8229,7 @@ function isMissingFileError(error) {
6882
8229
  //#endregion
6883
8230
  //#region src/server.ts
6884
8231
  const SERVER_NAME = "ue-mcp-server";
6885
- const SERVER_VERSION = "0.3.0";
8232
+ const SERVER_VERSION = "1.0.0";
6886
8233
  const logger = createModuleLogger("bootstrap");
6887
8234
  const enginePingLogger = createModuleLogger("engine-ping");
6888
8235
  const httpApiLogger = createModuleLogger("http-api");
@@ -6901,8 +8248,12 @@ async function startServer(options) {
6901
8248
  readTimeoutMs: options.config.startupPingTimeoutMs
6902
8249
  });
6903
8250
  await pruneRegistryEntries(registry, store, options.config);
8251
+ let notifyRegistryChanged = () => {};
6904
8252
  const httpApi = createHttpApi({
6905
8253
  logger: httpApiLogger,
8254
+ onRegistryChanged: () => {
8255
+ notifyRegistryChanged();
8256
+ },
6906
8257
  projectIdResolver,
6907
8258
  registry,
6908
8259
  registryWriteQueue,
@@ -6915,7 +8266,7 @@ async function startServer(options) {
6915
8266
  const listeningHttpPort = getListeningHttpPort(httpApi, options.config.httpPort);
6916
8267
  let mcpServer;
6917
8268
  try {
6918
- mcpServer = createMcpServer({
8269
+ const createdMcpServer = createMcpServer({
6919
8270
  logger: mcpToolLogger,
6920
8271
  logSessionManager,
6921
8272
  pingTimeoutMs: options.config.startupPingTimeoutMs,
@@ -6924,6 +8275,10 @@ async function startServer(options) {
6924
8275
  registryWriteQueue,
6925
8276
  store
6926
8277
  });
8278
+ mcpServer = createdMcpServer;
8279
+ notifyRegistryChanged = () => {
8280
+ createdMcpServer.sendResourceListChanged();
8281
+ };
6927
8282
  if (options.connectMcpTransport !== false) await mcpServer.connect(new StdioServerTransport());
6928
8283
  } catch (error) {
6929
8284
  try {
@@ -6977,8 +8332,14 @@ function createMcpServer(dependencies) {
6977
8332
  const server = new McpServer({
6978
8333
  name: SERVER_NAME,
6979
8334
  version: SERVER_VERSION
6980
- }, { capabilities: { tools: {} } });
8335
+ }, { capabilities: {
8336
+ prompts: {},
8337
+ resources: {},
8338
+ tools: {}
8339
+ } });
6981
8340
  registerMcpTools(server, dependencies);
8341
+ registerMcpResources(server, dependencies);
8342
+ registerMcpPrompts(server);
6982
8343
  return server;
6983
8344
  }
6984
8345
  async function pruneRegistryEntries(registry, store, config) {