@chrrxs/robloxstudio-mcp 2.15.0 → 2.15.2

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.
@@ -105,7 +105,9 @@ local MemoryHandlers = TS.import(script, script.Parent, "handlers", "MemoryHandl
105
105
  local SceneAnalysisHandlers = TS.import(script, script.Parent, "handlers", "SceneAnalysisHandlers")
106
106
  local CaptureHandlers = TS.import(script, script.Parent, "handlers", "CaptureHandlers")
107
107
  local InputHandlers = TS.import(script, script.Parent, "handlers", "InputHandlers")
108
+ local EvalRuntimeHandlers = TS.import(script, script.Parent, "handlers", "EvalRuntimeHandlers")
108
109
  local LuauExec = TS.import(script, script.Parent, "LuauExec")
110
+ local State = TS.import(script, script.Parent, "State")
109
111
  local StudioTestService = game:GetService("StudioTestService")
110
112
  -- Mirror of Communication.computeInstanceId() — duplicated here because the
111
113
  -- client broker runs in the play-server DM where it can't easily import from
@@ -166,6 +168,7 @@ local BROKER_OWNER_ATTRIBUTE = "__MCPBrokerOwner"
166
168
  -- cache / etc. lives there) so the server peer alone can't satisfy them.
167
169
  local CLIENT_BROKER_ALLOWED_ENDPOINTS = {
168
170
  ["/api/execute-luau"] = true,
171
+ ["/api/eval-runtime"] = true,
169
172
  ["/api/get-runtime-logs"] = true,
170
173
  ["/api/get-memory-breakdown"] = true,
171
174
  ["/api/get-scene-analysis"] = true,
@@ -201,6 +204,8 @@ local function reRegisterProxy(proxyId, role)
201
204
  placeName = resolvePlaceName(),
202
205
  dataModelName = game.Name,
203
206
  isRunning = RunService:IsRunning(),
207
+ pluginVersion = State.CURRENT_VERSION,
208
+ pluginVariant = State.PLUGIN_VARIANT,
204
209
  })
205
210
  end)
206
211
  end
@@ -244,8 +249,8 @@ local function handleGetRuntimeLogs(data)
244
249
  local since = d.since
245
250
  local tail = d.tail
246
251
  local filter = d.filter
247
- -- "client" is the generic peer tag; MCP-side aggregator overrides with
248
- -- the specific role (e.g. "client-1") on target=all fan-out.
252
+ -- "client" is the generic capture tag; MCP-side aggregation overrides it
253
+ -- with the specific role (e.g. "client-1") for capturedBy.
249
254
  return RuntimeLogBuffer.query({
250
255
  since = since,
251
256
  tail = tail,
@@ -363,6 +368,9 @@ local function setupClientBroker()
363
368
  if payload and payload.endpoint == "/api/execute-luau" then
364
369
  return handleExecuteLuau(payload.data)
365
370
  end
371
+ if payload and payload.endpoint == "/api/eval-runtime" then
372
+ return EvalRuntimeHandlers.evalRuntime(payload.data or {})
373
+ end
366
374
  -- Legacy: raw execute-luau payload at the top level.
367
375
  return handleExecuteLuau(payload)
368
376
  end
@@ -456,6 +464,8 @@ local function registerProxy(player, rf)
456
464
  placeName = resolvePlaceName(),
457
465
  dataModelName = game.Name,
458
466
  isRunning = RunService:IsRunning(),
467
+ pluginVersion = State.CURRENT_VERSION,
468
+ pluginVariant = State.PLUGIN_VARIANT,
459
469
  })
460
470
  if not ok or not res or not res.Success then
461
471
  warn(`[robloxstudio-mcp] proxy register failed for {player.Name}`)
@@ -557,6 +567,7 @@ local LogHandlers = TS.import(script, script.Parent, "handlers", "LogHandlers")
557
567
  local SerializationHandlers = TS.import(script, script.Parent, "handlers", "SerializationHandlers")
558
568
  local MemoryHandlers = TS.import(script, script.Parent, "handlers", "MemoryHandlers")
559
569
  local SceneAnalysisHandlers = TS.import(script, script.Parent, "handlers", "SceneAnalysisHandlers")
570
+ local EvalRuntimeHandlers = TS.import(script, script.Parent, "handlers", "EvalRuntimeHandlers")
560
571
  -- Per-plugin-load random GUID. Used as the /poll URL param so the server
561
572
  -- can tell our polls apart from any other plugin's polls. Not user-facing —
562
573
  -- MCP tools and the LLM operate on instanceId (the place identifier).
@@ -584,6 +595,8 @@ end
584
595
  local instanceId = computeInstanceId()
585
596
  local assignedRole
586
597
  local duplicateInstanceRole = false
598
+ local hasVersionMismatch = false
599
+ local lastVersionMismatchWarningKey
587
600
  -- Cache the published place name from MarketplaceService:GetProductInfo so
588
601
  -- /ready can carry a friendly identifier (e.g. "Natural Disasters") distinct
589
602
  -- from game.Name (the DataModel name, often "Place1" in edit). We only fetch
@@ -661,6 +674,7 @@ local routeMap = {
661
674
  ["/api/get-tagged"] = MetadataHandlers.getTagged,
662
675
  ["/api/get-selection"] = MetadataHandlers.getSelection,
663
676
  ["/api/execute-luau"] = MetadataHandlers.executeLuau,
677
+ ["/api/eval-runtime"] = EvalRuntimeHandlers.evalRuntime,
664
678
  ["/api/undo"] = MetadataHandlers.undo,
665
679
  ["/api/redo"] = MetadataHandlers.redo,
666
680
  ["/api/bulk-set-attributes"] = MetadataHandlers.bulkSetAttributes,
@@ -783,6 +797,8 @@ function sendReady(conn)
783
797
  placeName = resolvePlaceName(),
784
798
  dataModelName = game.Name,
785
799
  isRunning = RunService:IsRunning(),
800
+ pluginVersion = State.CURRENT_VERSION,
801
+ pluginVariant = State.PLUGIN_VARIANT,
786
802
  pluginReady = true,
787
803
  timestamp = tick(),
788
804
  }),
@@ -848,6 +864,23 @@ local function pollForRequests(connIndex)
848
864
  local mcpConnected = data.mcpConnected == true
849
865
  conn.lastHttpOk = true
850
866
  conn.lastMcpOk = mcpConnected
867
+ local _condition = data.serverVersion
868
+ if _condition == nil then
869
+ _condition = "unknown"
870
+ end
871
+ local serverVersion = _condition
872
+ if data.versionMismatch == true then
873
+ hasVersionMismatch = true
874
+ local warningKey = `{State.CURRENT_VERSION}:{serverVersion}`
875
+ if lastVersionMismatchWarningKey ~= warningKey then
876
+ lastVersionMismatchWarningKey = warningKey
877
+ warn(`[MCPPlugin] Version mismatch: Studio plugin v{State.CURRENT_VERSION} / MCP v{serverVersion}. Run npx -y @chrrxs/robloxstudio-mcp@latest --auto-install-plugin and restart Studio.`)
878
+ end
879
+ UI.showBanner("version-mismatch", `Plugin v{State.CURRENT_VERSION} / MCP v{serverVersion} mismatch`)
880
+ elseif hasVersionMismatch then
881
+ hasVersionMismatch = false
882
+ UI.hideBanner("version-mismatch")
883
+ end
851
884
  -- Server tells us when its in-memory instances map doesn't have us
852
885
  -- (e.g. after an MCP process restart). Re-issue /ready immediately so
853
886
  -- target=server/client-N start routing again. The throttle inside
@@ -860,12 +893,12 @@ local function pollForRequests(connIndex)
860
893
  local el = ui
861
894
  el.step1Dot.BackgroundColor3 = Color3.fromRGB(34, 197, 94)
862
895
  el.step1Label.Text = "HTTP server (OK)"
863
- local _condition = mcpConnected
864
- if _condition then
896
+ local _condition_1 = mcpConnected
897
+ if _condition_1 then
865
898
  local _value = (string.find(el.statusLabel.Text, "Connected"))
866
- _condition = not (_value ~= 0 and _value == _value and _value)
899
+ _condition_1 = not (_value ~= 0 and _value == _value and _value)
867
900
  end
868
- if _condition then
901
+ if _condition_1 then
869
902
  el.statusLabel.Text = "Connected"
870
903
  el.statusLabel.TextColor3 = Color3.fromRGB(34, 197, 94)
871
904
  el.statusIndicator.BackgroundColor3 = Color3.fromRGB(34, 197, 94)
@@ -896,11 +929,11 @@ local function pollForRequests(connIndex)
896
929
  conn.mcpWaitStartTime = tick()
897
930
  end
898
931
  local _exp = tick()
899
- local _condition_1 = conn.mcpWaitStartTime
900
- if _condition_1 == nil then
901
- _condition_1 = tick()
932
+ local _condition_2 = conn.mcpWaitStartTime
933
+ if _condition_2 == nil then
934
+ _condition_2 = tick()
902
935
  end
903
- local elapsed = _exp - _condition_1
936
+ local elapsed = _exp - _condition_2
904
937
  el.troubleshootLabel.Visible = elapsed > 8
905
938
  UI.startPulseAnimation()
906
939
  end
@@ -1109,11 +1142,9 @@ local function checkForUpdates()
1109
1142
  if _condition ~= "" and _condition then
1110
1143
  local latestVersion = data.version
1111
1144
  if Utils.compareVersions(State.CURRENT_VERSION, latestVersion) < 0 then
1112
- local ui = UI.getElements()
1113
- ui.updateBannerText.Text = `v{latestVersion} available - github.com/chrrxs/robloxstudio-mcp`
1114
- ui.updateBanner.Visible = true
1115
- ui.contentFrame.Position = UDim2.new(0, 8, 0, 92)
1116
- ui.contentFrame.Size = UDim2.new(1, -16, 1, -100)
1145
+ if not hasVersionMismatch then
1146
+ UI.showBanner("update", `v{latestVersion} available - github.com/chrrxs/robloxstudio-mcp`)
1147
+ end
1117
1148
  end
1118
1149
  end
1119
1150
  end
@@ -1218,9 +1249,10 @@ bf.Archivable = false\
1218
1249
  bf.Parent = ServerScriptService\
1219
1250
  bf.OnInvoke = function(payload)\
1220
1251
  if typeof(payload) ~= "Instance" or not payload:IsA("ModuleScript") then\
1221
- return false, "payload must be a ModuleScript instance"\
1252
+ return \{ ok = false, value = "payload must be a ModuleScript instance" \}\
1222
1253
  end\
1223
- return pcall(require, payload)\
1254
+ local ok, value = pcall(require, payload)\
1255
+ return \{ ok = ok, value = value \}\
1224
1256
  end\
1225
1257
  `
1226
1258
  local CLIENT_BRIDGE_SOURCE = `\
@@ -1244,9 +1276,10 @@ bf.Archivable = false\
1244
1276
  bf.Parent = ReplicatedStorage\
1245
1277
  bf.OnInvoke = function(payload)\
1246
1278
  if typeof(payload) ~= "Instance" or not payload:IsA("ModuleScript") then\
1247
- return false, "payload must be a ModuleScript instance"\
1279
+ return \{ ok = false, value = "payload must be a ModuleScript instance" \}\
1248
1280
  end\
1249
- return pcall(require, payload)\
1281
+ local ok, value = pcall(require, payload)\
1282
+ return \{ ok = ok, value = value \}\
1250
1283
  end\
1251
1284
  `
1252
1285
  -- Stamp written onto each installed bridge Script so we can tell whether the
@@ -1263,9 +1296,9 @@ local function computeBridgeStamp()
1263
1296
  for i = 1, #combined do
1264
1297
  h = (h * 33 + (string.byte(combined, i))) % 2147483647
1265
1298
  end
1266
- -- "2.15.0" is replaced with the package version at package time
1299
+ -- "2.15.2" is replaced with the package version at package time
1267
1300
  -- (scripts/build-plugin.mjs injectVersion), so a release bump also restamps.
1268
- return `{tostring(h)}-2.15.0`
1301
+ return `{tostring(h)}-2.15.2`
1269
1302
  end
1270
1303
  local BRIDGE_STAMP = computeBridgeStamp()
1271
1304
  local function setSource(scriptInst, source)
@@ -2442,6 +2475,132 @@ return {
2442
2475
  </Properties>
2443
2476
  </Item>
2444
2477
  <Item class="ModuleScript" referent="9">
2478
+ <Properties>
2479
+ <string name="Name">EvalRuntimeHandlers</string>
2480
+ <string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
2481
+ local TS = require(script.Parent.Parent.Parent.include.RuntimeLib)
2482
+ local _services = TS.import(script, script.Parent.Parent.Parent, "node_modules", "@rbxts", "services")
2483
+ local LogService = _services.LogService
2484
+ local ReplicatedStorage = _services.ReplicatedStorage
2485
+ local RunService = _services.RunService
2486
+ local ServerScriptService = _services.ServerScriptService
2487
+ local BRIDGE_NAMES = TS.import(script, script.Parent.Parent, "EvalBridges").BRIDGE_NAMES
2488
+ local LuauExec = TS.import(script, script.Parent.Parent, "LuauExec")
2489
+ local PAYLOAD_INSTANCE_NAME = "__MCPEvalPayload"
2490
+ local function getBridgeConfig()
2491
+ if not RunService:IsRunning() then
2492
+ return {
2493
+ error = "eval_*_runtime requires a running playtest.",
2494
+ }
2495
+ end
2496
+ if RunService:IsServer() then
2497
+ return {
2498
+ service = ServerScriptService,
2499
+ bridgeName = BRIDGE_NAMES.serverLocal,
2500
+ missingError = "ServerEvalBridge not found. The bridge runs inside the play DM, so a playtest must be running. The bridge installs automatically (including for manually-started playtests); if a playtest is running and you still see this, reconnect the plugin in the edit window so the bridge reinstalls, then start the playtest again.",
2501
+ }
2502
+ end
2503
+ return {
2504
+ service = ReplicatedStorage,
2505
+ bridgeName = BRIDGE_NAMES.clientLocal,
2506
+ missingError = "ClientEvalBridge not found. The bridge runs inside the play DM, so a playtest must be running. The bridge installs automatically (including for manually-started playtests); if a playtest is running and you still see this, reconnect the plugin in the edit window so the bridge reinstalls, then start the playtest again.",
2507
+ }
2508
+ end
2509
+ local function evalRuntime(requestData)
2510
+ local code = requestData.code
2511
+ if not (code ~= "" and code) or code == "" then
2512
+ return {
2513
+ error = "Code is required",
2514
+ }
2515
+ end
2516
+ local config = getBridgeConfig()
2517
+ if config.error ~= nil then
2518
+ return {
2519
+ bridge = "missing",
2520
+ error = config.error,
2521
+ }
2522
+ end
2523
+ local bridge = config.service:FindFirstChild(config.bridgeName)
2524
+ if not bridge or not bridge:IsA("BindableFunction") then
2525
+ return {
2526
+ bridge = "missing",
2527
+ error = config.missingError,
2528
+ }
2529
+ end
2530
+ local m = Instance.new("ModuleScript")
2531
+ m.Name = PAYLOAD_INSTANCE_NAME
2532
+ local userLines = LuauExec.countLines(code)
2533
+ local wrapped = LuauExec.buildWrapper(code, PAYLOAD_INSTANCE_NAME)
2534
+ local okSet, setErr = pcall(function()
2535
+ m.Source = wrapped
2536
+ end)
2537
+ if not okSet then
2538
+ m:Destroy()
2539
+ return {
2540
+ bridge = "ok",
2541
+ ok = false,
2542
+ error = `ModuleScript Source set failed: {tostring(setErr)}`,
2543
+ }
2544
+ end
2545
+ m.Parent = game:GetService("Workspace")
2546
+ local historyStart = #LogService:GetLogHistory()
2547
+ local invokeOk, invokeResult = pcall(function()
2548
+ return bridge:Invoke(m)
2549
+ end)
2550
+ m:Destroy()
2551
+ if not invokeOk then
2552
+ return {
2553
+ bridge = "ok",
2554
+ ok = false,
2555
+ error = tostring(invokeResult),
2556
+ }
2557
+ end
2558
+ if not (type(invokeResult) == "table") then
2559
+ return {
2560
+ bridge = "ok",
2561
+ ok = false,
2562
+ error = `Eval bridge returned invalid result: {tostring(invokeResult)}`,
2563
+ }
2564
+ end
2565
+ local bridgeResult = invokeResult
2566
+ if bridgeResult.ok ~= true then
2567
+ return {
2568
+ bridge = "ok",
2569
+ ok = false,
2570
+ error = LuauExec.recoverPayloadRequireError(bridgeResult.value, userLines, PAYLOAD_INSTANCE_NAME, historyStart),
2571
+ }
2572
+ end
2573
+ local inner = bridgeResult.value
2574
+ if not (type(inner) == "table") then
2575
+ return {
2576
+ bridge = "ok",
2577
+ ok = true,
2578
+ result = if inner == nil then nil else LuauExec.formatReturnValue(inner),
2579
+ }
2580
+ end
2581
+ local r = inner
2582
+ local ok = r.ok == true
2583
+ local _object = {
2584
+ bridge = "ok",
2585
+ ok = ok,
2586
+ result = if ok and r.value ~= nil then LuauExec.formatReturnValue(r.value) else nil,
2587
+ error = if not ok then tostring(r.value) else nil,
2588
+ }
2589
+ local _left = "output"
2590
+ local _condition = r.output
2591
+ if _condition == nil then
2592
+ _condition = {}
2593
+ end
2594
+ _object[_left] = _condition
2595
+ return _object
2596
+ end
2597
+ return {
2598
+ evalRuntime = evalRuntime,
2599
+ }
2600
+ ]]></string>
2601
+ </Properties>
2602
+ </Item>
2603
+ <Item class="ModuleScript" referent="10">
2445
2604
  <Properties>
2446
2605
  <string name="Name">InputHandlers</string>
2447
2606
  <string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
@@ -2641,7 +2800,7 @@ return {
2641
2800
  ]]></string>
2642
2801
  </Properties>
2643
2802
  </Item>
2644
- <Item class="ModuleScript" referent="10">
2803
+ <Item class="ModuleScript" referent="11">
2645
2804
  <Properties>
2646
2805
  <string name="Name">InstanceHandlers</string>
2647
2806
  <string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
@@ -3148,7 +3307,7 @@ return {
3148
3307
  ]]></string>
3149
3308
  </Properties>
3150
3309
  </Item>
3151
- <Item class="ModuleScript" referent="11">
3310
+ <Item class="ModuleScript" referent="12">
3152
3311
  <Properties>
3153
3312
  <string name="Name">LogHandlers</string>
3154
3313
  <string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
@@ -3158,16 +3317,15 @@ local function getRuntimeLogs(requestData)
3158
3317
  local since = requestData.since
3159
3318
  local tail = requestData.tail
3160
3319
  local filter = requestData.filter
3161
- -- Plugin-side peer tag is generic ("edit"|"server"|"client"). The MCP-side
3162
- -- aggregator overrides it with the specific instance role (e.g. "client-1")
3163
- -- during fan-out for target=all, so this value is only authoritative for
3164
- -- the single-peer query path.
3165
- local peer = RuntimeLogBuffer.detectPeer()
3320
+ -- This is the buffer that captured the LogService event, not necessarily
3321
+ -- the script-origin peer. Ordinary playtests share/reflect logs across
3322
+ -- edit/server/client LogService buffers.
3323
+ local capturedBy = RuntimeLogBuffer.detectPeer()
3166
3324
  return RuntimeLogBuffer.query({
3167
3325
  since = since,
3168
3326
  tail = tail,
3169
3327
  filter = filter,
3170
- }, peer)
3328
+ }, capturedBy)
3171
3329
  end
3172
3330
  return {
3173
3331
  getRuntimeLogs = getRuntimeLogs,
@@ -3175,7 +3333,7 @@ return {
3175
3333
  ]]></string>
3176
3334
  </Properties>
3177
3335
  </Item>
3178
- <Item class="ModuleScript" referent="12">
3336
+ <Item class="ModuleScript" referent="13">
3179
3337
  <Properties>
3180
3338
  <string name="Name">MemoryHandlers</string>
3181
3339
  <string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
@@ -3243,7 +3401,7 @@ return {
3243
3401
  ]]></string>
3244
3402
  </Properties>
3245
3403
  </Item>
3246
- <Item class="ModuleScript" referent="13">
3404
+ <Item class="ModuleScript" referent="14">
3247
3405
  <Properties>
3248
3406
  <string name="Name">MetadataHandlers</string>
3249
3407
  <string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
@@ -3782,7 +3940,7 @@ return {
3782
3940
  ]]></string>
3783
3941
  </Properties>
3784
3942
  </Item>
3785
- <Item class="ModuleScript" referent="14">
3943
+ <Item class="ModuleScript" referent="15">
3786
3944
  <Properties>
3787
3945
  <string name="Name">PropertyHandlers</string>
3788
3946
  <string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
@@ -4034,7 +4192,7 @@ return {
4034
4192
  ]]></string>
4035
4193
  </Properties>
4036
4194
  </Item>
4037
- <Item class="ModuleScript" referent="15">
4195
+ <Item class="ModuleScript" referent="16">
4038
4196
  <Properties>
4039
4197
  <string name="Name">QueryHandlers</string>
4040
4198
  <string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
@@ -5076,7 +5234,7 @@ return {
5076
5234
  ]]></string>
5077
5235
  </Properties>
5078
5236
  </Item>
5079
- <Item class="ModuleScript" referent="16">
5237
+ <Item class="ModuleScript" referent="17">
5080
5238
  <Properties>
5081
5239
  <string name="Name">SceneAnalysisHandlers</string>
5082
5240
  <string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
@@ -5325,7 +5483,7 @@ return {
5325
5483
  ]]></string>
5326
5484
  </Properties>
5327
5485
  </Item>
5328
- <Item class="ModuleScript" referent="17">
5486
+ <Item class="ModuleScript" referent="18">
5329
5487
  <Properties>
5330
5488
  <string name="Name">ScriptHandlers</string>
5331
5489
  <string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
@@ -6021,7 +6179,7 @@ return {
6021
6179
  ]]></string>
6022
6180
  </Properties>
6023
6181
  </Item>
6024
- <Item class="ModuleScript" referent="18">
6182
+ <Item class="ModuleScript" referent="19">
6025
6183
  <Properties>
6026
6184
  <string name="Name">SerializationHandlers</string>
6027
6185
  <string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
@@ -6207,7 +6365,7 @@ return {
6207
6365
  ]]></string>
6208
6366
  </Properties>
6209
6367
  </Item>
6210
- <Item class="ModuleScript" referent="19">
6368
+ <Item class="ModuleScript" referent="20">
6211
6369
  <Properties>
6212
6370
  <string name="Name">TestHandlers</string>
6213
6371
  <string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
@@ -6840,7 +6998,7 @@ return {
6840
6998
  </Properties>
6841
6999
  </Item>
6842
7000
  </Item>
6843
- <Item class="ModuleScript" referent="20">
7001
+ <Item class="ModuleScript" referent="21">
6844
7002
  <Properties>
6845
7003
  <string name="Name">LuauExec</string>
6846
7004
  <string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
@@ -6849,10 +7007,12 @@ return {
6849
7007
  -- and the play-client peer (ClientBroker.handleExecuteLuau). Three things this
6850
7008
  -- module owns:
6851
7009
  --
6852
- -- 1. The IIFE wrapper that captures print/warn, runs user code in xpcall,
6853
- -- and always returns { ok, value, output } so the ModuleScript itself
6854
- -- always returns exactly one value (otherwise `print("hi")` with no
6855
- -- return would fail with "Module code did not return exactly one value").
7010
+ -- 1. The IIFE wrapper that captures print/warn, wraps require() so nested
7011
+ -- ModuleScript load failures can recover the real LogService diagnostic,
7012
+ -- runs user code in xpcall, and always returns { ok, value, output } so
7013
+ -- the ModuleScript itself always returns exactly one value (otherwise
7014
+ -- `print("hi")` with no return would fail with "Module code did not
7015
+ -- return exactly one value").
6856
7016
  --
6857
7017
  -- 2. The loadstring-then-ModuleScript-require fallback, with the parse-error
6858
7018
  -- recovery hack that pulls the real diagnostic from LogService.
@@ -6870,14 +7030,14 @@ return {
6870
7030
  local HttpService = game:GetService("HttpService")
6871
7031
  local LogService = game:GetService("LogService")
6872
7032
  local PAYLOAD_INSTANCE_NAME = "__MCPExecLuauPayload"
6873
- local PAYLOAD_PATH_PREFIX = `Workspace.{PAYLOAD_INSTANCE_NAME}:`
7033
+ local REQUIRE_GENERIC_ERROR = "Requested module experienced an error while loading"
6874
7034
  -- Number of lines the wrapper emits BEFORE the first line of user code.
6875
7035
  -- Used both inside the wrapper (Luau __mcp_LINE_OFFSET) and on the TS side
6876
7036
  -- (remapPayloadLines, for compile errors recovered from LogService) so user
6877
7037
  -- code errors report user-relative line numbers instead of the inflated
6878
- -- "line 23" the wrapper would otherwise expose. If you reorder buildWrapper's
6879
- -- prefix lines, update this constant — there's a self-check below.
6880
- local WRAPPER_LINE_OFFSET = 23
7038
+ -- "line 49" the wrapper would otherwise expose. If you reorder buildWrapper's
7039
+ -- prefix lines, update this constant.
7040
+ local WRAPPER_LINE_OFFSET = 84
6881
7041
  -- Count source lines so the wrapper can filter traceback frames that fall
6882
7042
  -- outside the user code range (the wrapper's own preamble/postamble lines).
6883
7043
  local function countLines(s)
@@ -6902,20 +7062,31 @@ local function countLines(s)
6902
7062
  end
6903
7063
  return n
6904
7064
  end
6905
- local function buildWrapper(code)
7065
+ local function luaPatternEscape(s)
7066
+ local escaped = string.gsub(s, "([^%w])", "%%%1")
7067
+ return escaped
7068
+ end
7069
+ local function buildWrapper(code, payloadInstanceName)
7070
+ if payloadInstanceName == nil then
7071
+ payloadInstanceName = PAYLOAD_INSTANCE_NAME
7072
+ end
6906
7073
  -- If you reorder the prefix lines below, update WRAPPER_LINE_OFFSET to
6907
7074
  -- match the number of lines emitted BEFORE the ${code} substitution.
6908
7075
  -- The constant is mirrored inside the wrapper (__mcp_LINE_OFFSET) and
6909
7076
  -- used by remapPayloadLines on the TS side.
6910
7077
  local userLines = countLines(code)
7078
+ local payloadPattern = luaPatternEscape(payloadInstanceName)
6911
7079
  return `return ((function()\
6912
7080
  \tlocal __mcp_traceback\
6913
7081
  \tlocal __mcp_remap\
6914
7082
  \tlocal __mcp_LINE_OFFSET = {WRAPPER_LINE_OFFSET}\
6915
7083
  \tlocal __mcp_USER_LINES = {userLines}\
7084
+ \tlocal __mcp_LogService = game:GetService("LogService")\
7085
+ \tlocal __mcp_REQUIRE_GENERIC = "{REQUIRE_GENERIC_ERROR}"\
6916
7086
  \tlocal __mcp_output = \{\}\
6917
7087
  \tlocal __mcp_real_print = print\
6918
7088
  \tlocal __mcp_real_warn = warn\
7089
+ \tlocal __mcp_real_require = require\
6919
7090
  \tlocal print = function(...)\
6920
7091
  \t\t__mcp_real_print(...)\
6921
7092
  \t\tlocal args = \{...\}\
@@ -6930,6 +7101,64 @@ local function buildWrapper(code)
6930
7101
  \t\tfor i, a in ipairs(args) do parts[i] = tostring(a) end\
6931
7102
  \t\ttable.insert(__mcp_output, "[warn] " .. table.concat(parts, "\\t"))\
6932
7103
  \tend\
7104
+ \tlocal function __mcp_is_stack_noise(msg)\
7105
+ \t\treturn msg == "Stack Begin" or msg == "Stack End" or string.sub(msg, 1, 8) == "Script '"\
7106
+ \tend\
7107
+ \tlocal function __mcp_is_actionable_require_log(entry)\
7108
+ \t\tif not entry or entry.messageType ~= Enum.MessageType.MessageError then return false end\
7109
+ \t\tlocal msg = tostring(entry.message)\
7110
+ \t\treturn msg ~= __mcp_REQUIRE_GENERIC and not __mcp_is_stack_noise(msg)\
7111
+ \tend\
7112
+ \tlocal function __mcp_entry_mentions_module(entry, module_path)\
7113
+ \t\tif not entry or not module_path or module_path == "" then return false end\
7114
+ \t\treturn string.find(tostring(entry.message), module_path, 1, true) ~= nil\
7115
+ \tend\
7116
+ \tlocal function __mcp_prior_module_error(hist, module_path)\
7117
+ \t\tif not module_path or module_path == "" then return nil end\
7118
+ \t\tfor i = #hist, 1, -1 do\
7119
+ \t\t\tlocal entry = hist[i]\
7120
+ \t\t\tif __mcp_entry_mentions_module(entry, module_path) then\
7121
+ \t\t\t\tif __mcp_is_actionable_require_log(entry) then\
7122
+ \t\t\t\t\treturn tostring(entry.message)\
7123
+ \t\t\t\tend\
7124
+ \t\t\t\tfor j = i - 1, math.max(1, i - 6), -1 do\
7125
+ \t\t\t\t\tlocal previous = hist[j]\
7126
+ \t\t\t\t\tif __mcp_is_actionable_require_log(previous) then\
7127
+ \t\t\t\t\t\treturn tostring(previous.message)\
7128
+ \t\t\t\t\tend\
7129
+ \t\t\t\tend\
7130
+ \t\t\tend\
7131
+ \t\tend\
7132
+ \t\treturn nil\
7133
+ \tend\
7134
+ \tlocal function __mcp_recover_require_error(err, history_start, module)\
7135
+ \t\tlocal err_msg = tostring(err)\
7136
+ \t\tif err_msg ~= __mcp_REQUIRE_GENERIC then return err_msg end\
7137
+ \t\tlocal module_path\
7138
+ \t\tif typeof(module) == "Instance" then\
7139
+ \t\t\tlocal ok_path, path = pcall(function()\
7140
+ \t\t\t\treturn module:GetFullName()\
7141
+ \t\t\tend)\
7142
+ \t\t\tif ok_path then module_path = path end\
7143
+ \t\tend\
7144
+ \t\ttask.wait(0.05)\
7145
+ \t\tlocal hist = __mcp_LogService:GetLogHistory()\
7146
+ \t\tfor i = #hist, history_start + 1, -1 do\
7147
+ \t\t\tlocal entry = hist[i]\
7148
+ \t\t\tif __mcp_is_actionable_require_log(entry) then\
7149
+ \t\t\t\treturn tostring(entry.message)\
7150
+ \t\t\tend\
7151
+ \t\tend\
7152
+ \t\tlocal prior = __mcp_prior_module_error(hist, module_path)\
7153
+ \t\tif prior then return prior end\
7154
+ \t\treturn err_msg\
7155
+ \tend\
7156
+ \tlocal function require(module)\
7157
+ \t\tlocal history_start = #__mcp_LogService:GetLogHistory()\
7158
+ \t\tlocal ok, value = pcall(__mcp_real_require, module)\
7159
+ \t\tif ok then return value end\
7160
+ \t\terror(__mcp_recover_require_error(value, history_start, module), 0)\
7161
+ \tend\
6933
7162
  \tlocal function __mcp_run()\
6934
7163
  {code}\
6935
7164
  \tend\
@@ -6940,15 +7169,20 @@ local function buildWrapper(code)
6940
7169
  \t\t-- Subtract LINE_OFFSET to get the user-relative number, then clamp.\
6941
7170
  \t\t-- Clamping matters for unclosed constructs ("local x = (") where the\
6942
7171
  \t\t-- parser keeps reading into wrapper postamble and reports a payload\
6943
- \t\t-- line past user EOF. Without clamping the message says "user_code:49"\
6944
- \t\t-- for one-line input, framing the wrapper as user code.\
7172
+ \t\t-- line past user EOF. Without clamping, that frames wrapper postamble\
7173
+ \t\t-- as user code.\
6945
7174
  \t\tlocal function __mcp_user_line(payload_n)\
6946
7175
  \t\t\tlocal user_n = payload_n - __mcp_LINE_OFFSET\
6947
7176
  \t\t\tif user_n < 1 then return "1" end\
6948
7177
  \t\t\tif user_n > __mcp_USER_LINES then return tostring(__mcp_USER_LINES) .. " (at end of input)" end\
6949
7178
  \t\t\treturn tostring(user_n)\
6950
7179
  \t\tend\
6951
- \t\ts = string.gsub(s, "__MCPExecLuauPayload:(%d+)", function(num)\
7180
+ \t\ts = string.gsub(s, "Workspace%.{payloadPattern}:(%d+)", function(num)\
7181
+ \t\t\tlocal n = tonumber(num)\
7182
+ \t\t\tif n then return "user_code:" .. __mcp_user_line(n) end\
7183
+ \t\t\treturn "user_code:" .. num\
7184
+ \t\tend)\
7185
+ \t\ts = string.gsub(s, "{payloadPattern}:(%d+)", function(num)\
6952
7186
  \t\t\tlocal n = tonumber(num)\
6953
7187
  \t\t\tif n then return "user_code:" .. __mcp_user_line(n) end\
6954
7188
  \t\t\treturn "user_code:" .. num\
@@ -6998,7 +7232,10 @@ end
6998
7232
  -- pulling the real compile-error diagnostic out of LogService — that error
6999
7233
  -- references the payload module's line number directly, and never passes
7000
7234
  -- through the IIFE's runtime wrapper.
7001
- local function remapPayloadLines(s, userLines)
7235
+ local function remapPayloadLines(s, userLines, payloadInstanceName)
7236
+ if payloadInstanceName == nil then
7237
+ payloadInstanceName = PAYLOAD_INSTANCE_NAME
7238
+ end
7002
7239
  -- Mirror of the Lua __mcp_remap inside the wrapper, for paths that
7003
7240
  -- don't pass through the IIFE (compile errors recovered from
7004
7241
  -- LogService, the immediate loadstring compileError surface). Same
@@ -7016,8 +7253,9 @@ local function remapPayloadLines(s, userLines)
7016
7253
  end
7017
7254
  return tostring(u)
7018
7255
  end
7256
+ local payloadPattern = luaPatternEscape(payloadInstanceName)
7019
7257
  local out = s
7020
- local a = string.gsub(out, "__MCPExecLuauPayload:(%d+)", function(num)
7258
+ local a = string.gsub(out, `Workspace%.{payloadPattern}:(%d+)`, function(num)
7021
7259
  local n = tonumber(num)
7022
7260
  if n ~= nil then
7023
7261
  return `user_code:{userLine(n)}`
@@ -7025,7 +7263,7 @@ local function remapPayloadLines(s, userLines)
7025
7263
  return `user_code:{num}`
7026
7264
  end)
7027
7265
  out = a
7028
- local b = string.gsub(out, '%[string "[^"]+"%]:(%d+)', function(num)
7266
+ local b = string.gsub(out, `{payloadPattern}:(%d+)`, function(num)
7029
7267
  local n = tonumber(num)
7030
7268
  if n ~= nil then
7031
7269
  return `user_code:{userLine(n)}`
@@ -7033,8 +7271,16 @@ local function remapPayloadLines(s, userLines)
7033
7271
  return `user_code:{num}`
7034
7272
  end)
7035
7273
  out = b
7036
- return out
7274
+ local c = string.gsub(out, '%[string "[^"]+"%]:(%d+)', function(num)
7275
+ local n = tonumber(num)
7276
+ if n ~= nil then
7277
+ return `user_code:{userLine(n)}`
7278
+ end
7279
+ return `user_code:{num}`
7280
+ end)
7281
+ return c
7037
7282
  end
7283
+ local recoverPayloadRequireError
7038
7284
  local function runViaModuleScript(wrapped, userLines)
7039
7285
  local m = Instance.new("ModuleScript")
7040
7286
  m.Name = PAYLOAD_INSTANCE_NAME
@@ -7054,26 +7300,11 @@ local function runViaModuleScript(wrapped, userLines)
7054
7300
  end)
7055
7301
  m:Destroy()
7056
7302
  if not okReq then
7057
- local errMsg = tostring(reqResult)
7058
- -- pcall(require, m) collapses parse/compile failures into the canned
7059
- -- engine string. The real diagnostic was emitted to LogService on the
7060
- -- next engine frame — give it ~50ms to land then scan backward.
7061
- if errMsg == "Requested module experienced an error while loading" then
7062
- task.wait(0.05)
7063
- local hist = LogService:GetLogHistory()
7064
- for i = #hist - 1, 0, -1 do
7065
- local e = hist[i + 1]
7066
- if e.messageType == Enum.MessageType.MessageError and string.sub(e.message, 1, #PAYLOAD_PATH_PREFIX) == PAYLOAD_PATH_PREFIX then
7067
- errMsg = e.message
7068
- break
7069
- end
7070
- end
7071
- end
7072
7303
  -- Compile errors reference the payload module's line number directly
7073
7304
  -- — remap + clamp to user-relative line numbers so `local x = 1 +`
7074
7305
  -- reports :1: instead of :23:, and reports the clamp annotation
7075
7306
  -- when the parser ran off the end of user code into wrapper code.
7076
- error(remapPayloadLines(errMsg, userLines), 0)
7307
+ error(recoverPayloadRequireError(reqResult, userLines, PAYLOAD_INSTANCE_NAME), 0)
7077
7308
  end
7078
7309
  return reqResult
7079
7310
  end
@@ -7100,6 +7331,44 @@ local function formatReturnValue(value)
7100
7331
  end
7101
7332
  return tostring(value)
7102
7333
  end
7334
+ function recoverPayloadRequireError(err, userLines, payloadInstanceName, historyStart)
7335
+ if payloadInstanceName == nil then
7336
+ payloadInstanceName = PAYLOAD_INSTANCE_NAME
7337
+ end
7338
+ if historyStart == nil then
7339
+ historyStart = 0
7340
+ end
7341
+ local errMsg = tostring(err)
7342
+ -- pcall(require, m) collapses parse/compile failures into the canned
7343
+ -- engine string. The real diagnostic is emitted to LogService on the
7344
+ -- next engine frame — give it ~50ms to land then scan backward.
7345
+ if errMsg == REQUIRE_GENERIC_ERROR then
7346
+ task.wait(0.05)
7347
+ local payloadPathPrefix = `Workspace.{payloadInstanceName}:`
7348
+ local hist = LogService:GetLogHistory()
7349
+ local start = math.max(0, historyStart)
7350
+ do
7351
+ local i = #hist - 1
7352
+ local _shouldIncrement = false
7353
+ while true do
7354
+ if _shouldIncrement then
7355
+ i -= 1
7356
+ else
7357
+ _shouldIncrement = true
7358
+ end
7359
+ if not (i >= start) then
7360
+ break
7361
+ end
7362
+ local e = hist[i + 1]
7363
+ if e.messageType == Enum.MessageType.MessageError and string.sub(e.message, 1, #payloadPathPrefix) == payloadPathPrefix then
7364
+ errMsg = e.message
7365
+ break
7366
+ end
7367
+ end
7368
+ end
7369
+ end
7370
+ return remapPayloadLines(errMsg, userLines, payloadInstanceName)
7371
+ end
7103
7372
  local function execute(code)
7104
7373
  if not (code ~= "" and code) or code == "" then
7105
7374
  return {
@@ -7153,12 +7422,17 @@ local function execute(code)
7153
7422
  }
7154
7423
  end
7155
7424
  return {
7425
+ buildWrapper = buildWrapper,
7426
+ countLines = countLines,
7156
7427
  execute = execute,
7428
+ formatReturnValue = formatReturnValue,
7429
+ recoverPayloadRequireError = recoverPayloadRequireError,
7430
+ remapPayloadLines = remapPayloadLines,
7157
7431
  }
7158
7432
  ]]></string>
7159
7433
  </Properties>
7160
7434
  </Item>
7161
- <Item class="ModuleScript" referent="21">
7435
+ <Item class="ModuleScript" referent="22">
7162
7436
  <Properties>
7163
7437
  <string name="Name">Recording</string>
7164
7438
  <string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
@@ -7188,7 +7462,7 @@ return {
7188
7462
  ]]></string>
7189
7463
  </Properties>
7190
7464
  </Item>
7191
- <Item class="ModuleScript" referent="22">
7465
+ <Item class="ModuleScript" referent="23">
7192
7466
  <Properties>
7193
7467
  <string name="Name">RenderMonitor</string>
7194
7468
  <string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
@@ -7256,12 +7530,12 @@ return {
7256
7530
  ]]></string>
7257
7531
  </Properties>
7258
7532
  </Item>
7259
- <Item class="ModuleScript" referent="23">
7533
+ <Item class="ModuleScript" referent="24">
7260
7534
  <Properties>
7261
7535
  <string name="Name">RuntimeLogBuffer</string>
7262
7536
  <string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
7263
7537
  local TS = require(script.Parent.Parent.include.RuntimeLib)
7264
- -- Per-peer in-memory ring buffer for LogService.MessageOut events.
7538
+ -- Per-capture in-memory ring buffer for LogService.MessageOut events.
7265
7539
  -- Powers the get_runtime_logs MCP tool. Replaces the out-of-tree LogBuffer
7266
7540
  -- primitives + StringValue approach from chrrxs/roblox-mcp-primitives.
7267
7541
  --
@@ -7271,12 +7545,12 @@ local TS = require(script.Parent.Parent.include.RuntimeLib)
7271
7545
  -- DataModel. The buffer is bounded by a message-byte budget; oldest entries
7272
7546
  -- drop when over budget.
7273
7547
  --
7274
- -- Peer-tag caveat: returned entries reflect which peer's plugin CAPTURED the
7548
+ -- Capture caveat: returned entries reflect which plugin buffer CAPTURED the
7275
7549
  -- entry, NOT which peer's script originated the print. LogService reflects
7276
- -- prints across peers in Studio Play (a server print ends up in both the
7277
- -- server and client LogService:GetLogHistory()) and origin is empirically
7278
- -- undetectable from inside MessageOut. The MCP-side aggregator handles
7279
- -- cross-peer dedup via a 2s timestamp window.
7550
+ -- prints across peers in ordinary Studio Play (a server print can appear in
7551
+ -- server and client LogService:GetLogHistory()). The MCP-side aggregator
7552
+ -- exposes that as capturedBy, and only promotes it to origin peer in
7553
+ -- StudioTestService multiplayer sessions where peer attribution is reliable.
7280
7554
  local _services = TS.import(script, script.Parent.Parent, "node_modules", "@rbxts", "services")
7281
7555
  local LogService = _services.LogService
7282
7556
  local RunService = _services.RunService
@@ -7340,7 +7614,7 @@ local function detectPeer()
7340
7614
  end
7341
7615
  return "client"
7342
7616
  end
7343
- local function query(opts, peer)
7617
+ local function query(opts, capturedBy)
7344
7618
  local _result
7345
7619
  if opts.since ~= nil then
7346
7620
  -- ▼ ReadonlyArray.filter ▼
@@ -7411,7 +7685,7 @@ local function query(opts, peer)
7411
7685
  end
7412
7686
  local last = if #entries > 0 then entries[#entries] else nil
7413
7687
  local _object = {
7414
- peer = peer,
7688
+ capturedBy = capturedBy,
7415
7689
  entries = result,
7416
7690
  totalDropped = totalDropped,
7417
7691
  }
@@ -7437,11 +7711,12 @@ return {
7437
7711
  ]]></string>
7438
7712
  </Properties>
7439
7713
  </Item>
7440
- <Item class="ModuleScript" referent="24">
7714
+ <Item class="ModuleScript" referent="25">
7441
7715
  <Properties>
7442
7716
  <string name="Name">State</string>
7443
7717
  <string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
7444
- local CURRENT_VERSION = "2.15.0"
7718
+ local CURRENT_VERSION = "2.15.2"
7719
+ local PLUGIN_VARIANT = "inspector"
7445
7720
  local MAX_CONNECTIONS = 5
7446
7721
  local BASE_PORT = 58741
7447
7722
  local activeTabIndex = 0
@@ -7519,6 +7794,7 @@ local function getConnections()
7519
7794
  end
7520
7795
  return {
7521
7796
  CURRENT_VERSION = CURRENT_VERSION,
7797
+ PLUGIN_VARIANT = PLUGIN_VARIANT,
7522
7798
  MAX_CONNECTIONS = MAX_CONNECTIONS,
7523
7799
  BASE_PORT = BASE_PORT,
7524
7800
  connections = connections,
@@ -7533,7 +7809,7 @@ return {
7533
7809
  ]]></string>
7534
7810
  </Properties>
7535
7811
  </Item>
7536
- <Item class="ModuleScript" referent="25">
7812
+ <Item class="ModuleScript" referent="26">
7537
7813
  <Properties>
7538
7814
  <string name="Name">StopPlayMonitor</string>
7539
7815
  <string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
@@ -7678,7 +7954,7 @@ return {
7678
7954
  ]]></string>
7679
7955
  </Properties>
7680
7956
  </Item>
7681
- <Item class="ModuleScript" referent="26">
7957
+ <Item class="ModuleScript" referent="27">
7682
7958
  <Properties>
7683
7959
  <string name="Name">UI</string>
7684
7960
  <string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
@@ -7691,6 +7967,7 @@ local buttonHover = false
7691
7967
  local toolbarButton
7692
7968
  local toolbarIcons
7693
7969
  local lastToolbarIcon
7970
+ local activeBannerKind
7694
7971
  local updateToolbarIcon
7695
7972
  local function setToolbarButton(btn, icons)
7696
7973
  toolbarButton = btn
@@ -7721,6 +7998,23 @@ local TWEEN_QUICK = TweenInfo.new(0.15, Enum.EasingStyle.Quad, Enum.EasingDirect
7721
7998
  local function tweenProp(instance, props)
7722
7999
  TweenService:Create(instance, TWEEN_QUICK, props):Play()
7723
8000
  end
8001
+ local function showBanner(kind, text)
8002
+ activeBannerKind = kind
8003
+ elements.updateBannerText.Text = text
8004
+ elements.updateBanner.Visible = true
8005
+ elements.contentFrame.Position = UDim2.new(0, 8, 0, 92)
8006
+ elements.contentFrame.Size = UDim2.new(1, -16, 1, -100)
8007
+ end
8008
+ local function hideBanner(kind)
8009
+ if kind ~= nil and activeBannerKind ~= kind then
8010
+ return nil
8011
+ end
8012
+ activeBannerKind = nil
8013
+ elements.updateBanner.Visible = false
8014
+ elements.updateBannerText.Text = ""
8015
+ elements.contentFrame.Position = UDim2.new(0, 8, 0, 66)
8016
+ elements.contentFrame.Size = UDim2.new(1, -16, 1, -74)
8017
+ end
7724
8018
  local C = {
7725
8019
  bg = Color3.fromRGB(14, 14, 14),
7726
8020
  card = Color3.fromRGB(22, 22, 22),
@@ -8422,6 +8716,8 @@ return {
8422
8716
  startPulseAnimation = startPulseAnimation,
8423
8717
  setToolbarButton = setToolbarButton,
8424
8718
  updateToolbarIcon = updateToolbarIcon,
8719
+ showBanner = showBanner,
8720
+ hideBanner = hideBanner,
8425
8721
  getElements = function()
8426
8722
  return elements
8427
8723
  end,
@@ -8429,7 +8725,7 @@ return {
8429
8725
  ]]></string>
8430
8726
  </Properties>
8431
8727
  </Item>
8432
- <Item class="ModuleScript" referent="27">
8728
+ <Item class="ModuleScript" referent="28">
8433
8729
  <Properties>
8434
8730
  <string name="Name">Utils</string>
8435
8731
  <string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
@@ -8959,11 +9255,11 @@ return {
8959
9255
  </Properties>
8960
9256
  </Item>
8961
9257
  </Item>
8962
- <Item class="Folder" referent="31">
9258
+ <Item class="Folder" referent="32">
8963
9259
  <Properties>
8964
9260
  <string name="Name">include</string>
8965
9261
  </Properties>
8966
- <Item class="ModuleScript" referent="28">
9262
+ <Item class="ModuleScript" referent="29">
8967
9263
  <Properties>
8968
9264
  <string name="Name">Promise</string>
8969
9265
  <string name="Source"><![CDATA[--[[
@@ -11037,7 +11333,7 @@ return Promise
11037
11333
  ]]></string>
11038
11334
  </Properties>
11039
11335
  </Item>
11040
- <Item class="ModuleScript" referent="29">
11336
+ <Item class="ModuleScript" referent="30">
11041
11337
  <Properties>
11042
11338
  <string name="Name">RuntimeLib</string>
11043
11339
  <string name="Source"><![CDATA[local Promise = require(script.Parent.Promise)
@@ -11304,15 +11600,15 @@ return TS
11304
11600
  </Properties>
11305
11601
  </Item>
11306
11602
  </Item>
11307
- <Item class="Folder" referent="32">
11603
+ <Item class="Folder" referent="33">
11308
11604
  <Properties>
11309
11605
  <string name="Name">node_modules</string>
11310
11606
  </Properties>
11311
- <Item class="Folder" referent="33">
11607
+ <Item class="Folder" referent="34">
11312
11608
  <Properties>
11313
11609
  <string name="Name">@rbxts</string>
11314
11610
  </Properties>
11315
- <Item class="ModuleScript" referent="30">
11611
+ <Item class="ModuleScript" referent="31">
11316
11612
  <Properties>
11317
11613
  <string name="Name">services</string>
11318
11614
  <string name="Source"><![CDATA[return setmetatable({}, {