dominus-cli 0.5.8 → 0.6.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.
package/dist/index.js CHANGED
@@ -252,7 +252,12 @@ var init_ws_server = __esm({
252
252
  init_protocol();
253
253
  DominusServer = class extends EventEmitter {
254
254
  wss = null;
255
- studioClient = null;
255
+ /** All connected Studio instances, keyed by placeId (0 for unknown) */
256
+ studioClients = /* @__PURE__ */ new Map();
257
+ /** Reverse lookup: ws → placeId */
258
+ wsToPlaceId = /* @__PURE__ */ new Map();
259
+ /** Which placeId to target. 0 = auto-select if only one is connected. */
260
+ activePlaceId = 0;
256
261
  controllers = /* @__PURE__ */ new Set();
257
262
  pendingRequests = /* @__PURE__ */ new Map();
258
263
  relayRequests = /* @__PURE__ */ new Map();
@@ -301,7 +306,9 @@ var init_ws_server = __esm({
301
306
  this.addController(ws);
302
307
  ws.send(serializeMessage(createMessage("controller:welcome", {
303
308
  studioConnected: this.isConnected(),
304
- port: this.port
309
+ port: this.port,
310
+ connections: this.listConnections(),
311
+ activePlaceId: this.activePlaceId
305
312
  })));
306
313
  return;
307
314
  }
@@ -309,7 +316,7 @@ var init_ws_server = __esm({
309
316
  if (this.controllers.has(ws)) {
310
317
  this.handleControllerMessage(ws, msg);
311
318
  } else {
312
- this.handleStudioMessage(msg);
319
+ this.handleStudioMessage(ws, msg);
313
320
  }
314
321
  } catch (err) {
315
322
  this.emit("server:error", err);
@@ -321,14 +328,22 @@ var init_ws_server = __esm({
321
328
  for (const [id, controllerWs] of this.relayRequests) {
322
329
  if (controllerWs === ws) this.relayRequests.delete(id);
323
330
  }
324
- } else if (this.studioClient?.ws === ws) {
325
- this.studioClient = null;
326
- this.emit("studio:disconnected");
327
- this.broadcastToControllers("studio:disconnected", {});
328
- for (const [id, pending] of this.pendingRequests) {
329
- clearTimeout(pending.timer);
330
- pending.reject(new Error("Studio disconnected"));
331
- this.pendingRequests.delete(id);
331
+ } else {
332
+ const placeId = this.wsToPlaceId.get(ws);
333
+ if (placeId !== void 0) {
334
+ const conn = this.studioClients.get(placeId);
335
+ this.studioClients.delete(placeId);
336
+ this.wsToPlaceId.delete(ws);
337
+ this.emit("studio:disconnected", { placeId, placeName: conn?.placeName });
338
+ this.broadcastToControllers("studio:disconnected", { placeId, placeName: conn?.placeName });
339
+ for (const [id, pending] of this.pendingRequests) {
340
+ clearTimeout(pending.timer);
341
+ pending.reject(new Error(`Studio disconnected (place ${placeId})`));
342
+ this.pendingRequests.delete(id);
343
+ }
344
+ if (this.activePlaceId === placeId) {
345
+ this.activePlaceId = 0;
346
+ }
332
347
  }
333
348
  }
334
349
  });
@@ -337,19 +352,24 @@ var init_ws_server = __esm({
337
352
  });
338
353
  }
339
354
  setStudioClient(ws, msg) {
340
- if (this.studioClient) {
341
- this.studioClient.ws.close();
342
- }
343
355
  const payload = msg.payload;
344
- this.studioClient = {
356
+ const placeId = payload.placeId ?? 0;
357
+ const existing = this.studioClients.get(placeId);
358
+ if (existing && existing.ws !== ws) {
359
+ existing.ws.close();
360
+ this.wsToPlaceId.delete(existing.ws);
361
+ }
362
+ const conn = {
345
363
  ws,
346
364
  connectedAt: Date.now(),
347
365
  studioVersion: payload.studioVersion,
348
- placeId: payload.placeId,
366
+ placeId,
349
367
  placeName: payload.placeName
350
368
  };
351
- this.emit("studio:connected", payload);
352
- this.broadcastToControllers("studio:connected", payload);
369
+ this.studioClients.set(placeId, conn);
370
+ this.wsToPlaceId.set(ws, placeId);
371
+ this.emit("studio:connected", { ...payload, placeId });
372
+ this.broadcastToControllers("studio:connected", { ...payload, placeId });
353
373
  }
354
374
  addController(ws) {
355
375
  this.controllers.add(ws);
@@ -364,25 +384,30 @@ var init_ws_server = __esm({
364
384
  }
365
385
  }
366
386
  handleControllerMessage(controllerWs, msg) {
367
- if (!this.studioClient || this.studioClient.ws.readyState !== WebSocket.OPEN) {
387
+ const target = this.resolveTarget();
388
+ if (!target || target.ws.readyState !== WebSocket.OPEN) {
368
389
  controllerWs.send(serializeMessage({
369
390
  id: msg.id,
370
391
  type: StudioMsg.RESPONSE,
371
- payload: { error: "Studio not connected" },
392
+ payload: { error: this.getTargetError() },
372
393
  ts: Date.now()
373
394
  }));
374
395
  return;
375
396
  }
376
397
  this.relayRequests.set(msg.id, controllerWs);
377
- this.studioClient.ws.send(serializeMessage(msg));
398
+ target.ws.send(serializeMessage(msg));
378
399
  }
379
- handleStudioMessage(msg) {
400
+ handleStudioMessage(ws, msg) {
380
401
  if (msg.type === StudioMsg.CONNECTED) {
381
402
  const payload = msg.payload;
382
- if (this.studioClient) {
383
- this.studioClient.studioVersion = payload.studioVersion;
384
- this.studioClient.placeId = payload.placeId;
385
- this.studioClient.placeName = payload.placeName;
403
+ const placeId = this.wsToPlaceId.get(ws);
404
+ if (placeId !== void 0) {
405
+ const conn = this.studioClients.get(placeId);
406
+ if (conn) {
407
+ conn.studioVersion = payload.studioVersion;
408
+ conn.placeId = payload.placeId;
409
+ conn.placeName = payload.placeName;
410
+ }
386
411
  }
387
412
  this.emit("studio:connected", payload);
388
413
  this.broadcastToControllers("studio:connected", payload);
@@ -407,10 +432,35 @@ var init_ws_server = __esm({
407
432
  this.emit("message", msg);
408
433
  this.broadcastToControllers(msg.type, msg.payload);
409
434
  }
435
+ /** Resolve which Studio connection to target */
436
+ resolveTarget() {
437
+ if (this.studioClients.size === 0) return null;
438
+ if (this.activePlaceId !== 0) {
439
+ const conn = this.studioClients.get(this.activePlaceId);
440
+ if (conn && conn.ws.readyState === WebSocket.OPEN) return conn;
441
+ return null;
442
+ }
443
+ if (this.studioClients.size === 1) {
444
+ const conn = this.studioClients.values().next().value;
445
+ if (conn.ws.readyState === WebSocket.OPEN) return conn;
446
+ return null;
447
+ }
448
+ return null;
449
+ }
450
+ /** Generate an appropriate error message when no target found */
451
+ getTargetError() {
452
+ if (this.studioClients.size === 0) return "No Studio connection";
453
+ if (this.activePlaceId !== 0) {
454
+ return `Active place ${this.activePlaceId} is not connected. Use set_active_place or list_connections.`;
455
+ }
456
+ const places = [...this.studioClients.values()].map((c) => `${c.placeName ?? "Unknown"} (${c.placeId})`);
457
+ return `Multiple Studio instances connected: ${places.join(", ")}. Use set_active_place to choose one.`;
458
+ }
410
459
  sendRequest(type, payload, timeoutMs = 1e4) {
411
460
  return new Promise((resolve, reject) => {
412
- if (!this.studioClient || this.studioClient.ws.readyState !== WebSocket.OPEN) {
413
- reject(new Error("No Studio connection"));
461
+ const target = this.resolveTarget();
462
+ if (!target || target.ws.readyState !== WebSocket.OPEN) {
463
+ reject(new Error(this.getTargetError()));
414
464
  return;
415
465
  }
416
466
  const msg = createMessage(type, payload);
@@ -423,31 +473,45 @@ var init_ws_server = __esm({
423
473
  reject,
424
474
  timer
425
475
  });
426
- this.studioClient.ws.send(serializeMessage(msg));
476
+ target.ws.send(serializeMessage(msg));
427
477
  });
428
478
  }
429
479
  sendNotification(type, payload) {
430
- if (!this.studioClient || this.studioClient.ws.readyState !== WebSocket.OPEN) {
431
- return;
432
- }
480
+ const target = this.resolveTarget();
481
+ if (!target || target.ws.readyState !== WebSocket.OPEN) return;
433
482
  const msg = createMessage(type, payload);
434
- this.studioClient.ws.send(serializeMessage(msg));
483
+ target.ws.send(serializeMessage(msg));
435
484
  }
436
485
  isConnected() {
437
- return this.studioClient !== null && this.studioClient.ws.readyState === WebSocket.OPEN;
486
+ return this.resolveTarget() !== null;
438
487
  }
439
488
  getConnectionInfo() {
440
- return this.studioClient;
489
+ return this.resolveTarget();
490
+ }
491
+ listConnections() {
492
+ return [...this.studioClients.values()].filter((c) => c.ws.readyState === WebSocket.OPEN).map((c) => ({
493
+ placeId: c.placeId ?? 0,
494
+ placeName: c.placeName,
495
+ studioVersion: c.studioVersion,
496
+ connectedAt: c.connectedAt
497
+ }));
498
+ }
499
+ getActivePlaceId() {
500
+ return this.activePlaceId;
501
+ }
502
+ setActivePlaceId(placeId) {
503
+ this.activePlaceId = placeId;
441
504
  }
442
505
  getPort() {
443
506
  return this.port;
444
507
  }
445
508
  stop() {
446
509
  return new Promise((resolve) => {
447
- if (this.studioClient) {
448
- this.studioClient.ws.close();
449
- this.studioClient = null;
510
+ for (const conn of this.studioClients.values()) {
511
+ conn.ws.close();
450
512
  }
513
+ this.studioClients.clear();
514
+ this.wsToPlaceId.clear();
451
515
  for (const ws of this.controllers) {
452
516
  ws.close();
453
517
  }
@@ -494,7 +558,8 @@ var init_ws_client = __esm({
494
558
  ws = null;
495
559
  port;
496
560
  studioConnected = false;
497
- connectionInfo = null;
561
+ connections = /* @__PURE__ */ new Map();
562
+ activePlaceId = 0;
498
563
  pendingRequests = /* @__PURE__ */ new Map();
499
564
  constructor(port = 18088) {
500
565
  super();
@@ -538,6 +603,20 @@ var init_ws_client = __esm({
538
603
  if (msg.type === "controller:welcome") {
539
604
  const p = msg.payload;
540
605
  this.studioConnected = p.studioConnected;
606
+ if (p.connections) {
607
+ this.connections.clear();
608
+ for (const c of p.connections) {
609
+ this.connections.set(c.placeId, {
610
+ connectedAt: c.connectedAt,
611
+ studioVersion: c.studioVersion,
612
+ placeId: c.placeId,
613
+ placeName: c.placeName
614
+ });
615
+ }
616
+ }
617
+ if (p.activePlaceId !== void 0) {
618
+ this.activePlaceId = p.activePlaceId;
619
+ }
541
620
  if (this.studioConnected) {
542
621
  this.emit("studio:connected", {});
543
622
  }
@@ -546,19 +625,27 @@ var init_ws_client = __esm({
546
625
  if (msg.type === "studio:connected") {
547
626
  this.studioConnected = true;
548
627
  const payload = msg.payload;
549
- this.connectionInfo = {
628
+ const placeId = payload.placeId ?? 0;
629
+ this.connections.set(placeId, {
550
630
  connectedAt: Date.now(),
551
631
  studioVersion: payload.studioVersion,
552
- placeId: payload.placeId,
632
+ placeId,
553
633
  placeName: payload.placeName
554
- };
634
+ });
555
635
  this.emit("studio:connected", payload);
556
636
  return;
557
637
  }
558
638
  if (msg.type === "studio:disconnected") {
559
- this.studioConnected = false;
560
- this.connectionInfo = null;
561
- this.emit("studio:disconnected");
639
+ const payload = msg.payload;
640
+ const placeId = payload.placeId ?? 0;
641
+ this.connections.delete(placeId);
642
+ if (this.connections.size === 0) {
643
+ this.studioConnected = false;
644
+ }
645
+ if (this.activePlaceId === placeId) {
646
+ this.activePlaceId = 0;
647
+ }
648
+ this.emit("studio:disconnected", payload);
562
649
  return;
563
650
  }
564
651
  if (msg.type === StudioMsg.RESPONSE) {
@@ -602,11 +689,27 @@ var init_ws_client = __esm({
602
689
  this.ws.send(serializeMessage(msg));
603
690
  }
604
691
  isConnected() {
605
- return this.studioConnected;
692
+ return this.studioConnected && this.connections.size > 0;
606
693
  }
607
694
  getConnectionInfo() {
608
- if (!this.connectionInfo) return null;
609
- return { ...this.connectionInfo, ws: this.ws };
695
+ if (this.connections.size === 0) return null;
696
+ const target = this.activePlaceId !== 0 ? this.connections.get(this.activePlaceId) : this.connections.values().next().value;
697
+ if (!target) return null;
698
+ return { ...target, ws: this.ws };
699
+ }
700
+ listConnections() {
701
+ return [...this.connections.values()].map((c) => ({
702
+ placeId: c.placeId ?? 0,
703
+ placeName: c.placeName,
704
+ studioVersion: c.studioVersion,
705
+ connectedAt: c.connectedAt
706
+ }));
707
+ }
708
+ getActivePlaceId() {
709
+ return this.activePlaceId;
710
+ }
711
+ setActivePlaceId(placeId) {
712
+ this.activePlaceId = placeId;
610
713
  }
611
714
  getPort() {
612
715
  return this.port;
@@ -683,6 +786,7 @@ These are built-in lessons that ship with Dominus. Follow them exactly.
683
786
  - **Inspecting user's selection \u2192 \`get_selection\`, then \`get_properties\` or \`serialize_ui\`**
684
787
  - **Reading properties \u2192 \`get_properties\` (NEVER \`run_code\` with dump scripts)**
685
788
  - **Browsing the tree \u2192 \`get_explorer\`**
789
+ - **Multiple Studios open \u2192 \`list_connections\` to see all, \`set_active_place\` to target one**
686
790
  - Anything exotic or procedural \u2192 \`run_code\` as last resort
687
791
 
688
792
  ### NEVER use run_code for:
@@ -2605,7 +2709,7 @@ async function startMcpServer() {
2605
2709
  }
2606
2710
  const mcp = new McpServer({
2607
2711
  name: "dominus",
2608
- version: "0.5.8"
2712
+ version: "0.6.0"
2609
2713
  }, {
2610
2714
  instructions: INTERNAL_MEMORY
2611
2715
  });
@@ -2618,6 +2722,11 @@ async function startMcpServer() {
2618
2722
  for (const p of parts) r += `[${luaStr(p)}]`;
2619
2723
  return r;
2620
2724
  }
2725
+ function cleanNum(n) {
2726
+ if (Number.isInteger(n)) return String(n);
2727
+ const rounded = Math.round(n * 1e3) / 1e3;
2728
+ return String(rounded);
2729
+ }
2621
2730
  function generateComponentCode(tree, componentName, framework) {
2622
2731
  const isReact = framework === "react";
2623
2732
  const lib = isReact ? "React" : "Roact";
@@ -2675,7 +2784,7 @@ async function startMcpServer() {
2675
2784
  function luaValue(val, _depth) {
2676
2785
  if (val === null || val === void 0) return "nil";
2677
2786
  if (typeof val === "boolean") return String(val);
2678
- if (typeof val === "number") return String(val);
2787
+ if (typeof val === "number") return cleanNum(val);
2679
2788
  if (typeof val === "string") {
2680
2789
  if (val.startsWith("#") && val.length === 7) {
2681
2790
  return `Color3.fromHex("${val}")`;
@@ -2687,15 +2796,15 @@ async function startMcpServer() {
2687
2796
  }
2688
2797
  if (Array.isArray(val)) {
2689
2798
  if (val.length === 4 && val.every((v) => typeof v === "number")) {
2690
- return `UDim2.new(${val.join(", ")})`;
2799
+ return `UDim2.new(${val.map(cleanNum).join(", ")})`;
2691
2800
  }
2692
2801
  if (val.length === 2 && val.every((v) => typeof v === "number")) {
2693
- return `Vector2.new(${val.join(", ")})`;
2802
+ return `Vector2.new(${val.map(cleanNum).join(", ")})`;
2694
2803
  }
2695
2804
  if (val.length === 3 && val.every((v) => typeof v === "number")) {
2696
2805
  const allSmall = val.every((v) => v <= 1);
2697
- if (allSmall) return `Color3.new(${val.join(", ")})`;
2698
- return `Color3.fromRGB(${val.join(", ")})`;
2806
+ if (allSmall) return `Color3.new(${val.map(cleanNum).join(", ")})`;
2807
+ return `Color3.fromRGB(${val.map((v) => String(Math.round(v))).join(", ")})`;
2699
2808
  }
2700
2809
  return `{${val.map((v) => luaValue(v, _depth)).join(", ")}}`;
2701
2810
  }
@@ -3188,6 +3297,57 @@ ${" ".repeat(_depth)}}`;
3188
3297
  return { content: [{ type: "text", text: code }] };
3189
3298
  }
3190
3299
  );
3300
+ mcp.tool(
3301
+ "list_connections",
3302
+ "List all Roblox Studio instances currently connected to Dominus. Shows placeId, placeName, and connection time for each. Use this when working with multiple Studio windows.",
3303
+ {},
3304
+ async () => {
3305
+ const connections = bridge.listConnections();
3306
+ const activePlaceId = bridge.getActivePlaceId();
3307
+ return {
3308
+ content: [{
3309
+ type: "text",
3310
+ text: JSON.stringify({
3311
+ activePlaceId: activePlaceId || "auto",
3312
+ connections: connections.map((c) => ({
3313
+ ...c,
3314
+ active: activePlaceId === 0 && connections.length === 1 ? true : c.placeId === activePlaceId
3315
+ }))
3316
+ }, null, 2)
3317
+ }]
3318
+ };
3319
+ }
3320
+ );
3321
+ mcp.tool(
3322
+ "set_active_place",
3323
+ "Set which Roblox Studio instance to target when multiple are connected. Pass placeId=0 to reset to auto mode (works when only one is connected). Call list_connections first to see available placeIds.",
3324
+ {
3325
+ placeId: z.number().describe("The placeId to target. Use 0 to reset to auto-select.")
3326
+ },
3327
+ async ({ placeId }) => {
3328
+ const connections = bridge.listConnections();
3329
+ if (placeId !== 0 && !connections.some((c) => c.placeId === placeId)) {
3330
+ const available = connections.map((c) => `${c.placeName ?? "Unknown"} (${c.placeId})`).join(", ");
3331
+ return {
3332
+ content: [{
3333
+ type: "text",
3334
+ text: `Place ${placeId} is not connected. Available: ${available || "none"}`
3335
+ }]
3336
+ };
3337
+ }
3338
+ bridge.setActivePlaceId(placeId);
3339
+ if (placeId === 0) {
3340
+ return { content: [{ type: "text", text: "Active place reset to auto-select." }] };
3341
+ }
3342
+ const target = connections.find((c) => c.placeId === placeId);
3343
+ return {
3344
+ content: [{
3345
+ type: "text",
3346
+ text: `Active place set to ${target?.placeName ?? "Unknown"} (${placeId})`
3347
+ }]
3348
+ };
3349
+ }
3350
+ );
3191
3351
  mcp.tool(
3192
3352
  "recall_memory",
3193
3353
  "Search persistent memory for facts about the current Roblox project",
@@ -3482,11 +3642,20 @@ var Setup = ({ onComplete }) => {
3482
3642
  var StatusBar = React6.memo(({
3483
3643
  connected,
3484
3644
  placeName,
3645
+ connectionCount,
3485
3646
  model,
3486
3647
  sessionTokens
3487
3648
  }) => {
3488
3649
  const statusColor = connected ? "green" : "red";
3489
- const statusText = connected ? `\u25CF Connected${placeName ? ` \u2014 ${placeName}` : ""}` : "\u25CB Disconnected";
3650
+ const count = connectionCount ?? (connected ? 1 : 0);
3651
+ let statusText;
3652
+ if (!connected) {
3653
+ statusText = "\u25CB Disconnected";
3654
+ } else if (count > 1) {
3655
+ statusText = `\u25CF ${count} Studios${placeName ? ` \u2014 active: ${placeName}` : ""}`;
3656
+ } else {
3657
+ statusText = `\u25CF Connected${placeName ? ` \u2014 ${placeName}` : ""}`;
3658
+ }
3490
3659
  const shortModel = model.split("/").pop() ?? model;
3491
3660
  return /* @__PURE__ */ jsxs(
3492
3661
  Box,
@@ -3904,6 +4073,7 @@ var ToolCallView = React6.memo(({ calls }) => {
3904
4073
  var Layout = React6.memo(({
3905
4074
  connected,
3906
4075
  placeName,
4076
+ connectionCount,
3907
4077
  model,
3908
4078
  chatEntries,
3909
4079
  outputEntries,
@@ -3917,7 +4087,7 @@ var Layout = React6.memo(({
3917
4087
  onClearImage
3918
4088
  }) => {
3919
4089
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", width: "100%", children: [
3920
- /* @__PURE__ */ jsx(StatusBar, { connected, placeName, model }),
4090
+ /* @__PURE__ */ jsx(StatusBar, { connected, placeName, connectionCount, model }),
3921
4091
  /* @__PURE__ */ jsxs(Box, { flexGrow: 1, children: [
3922
4092
  /* @__PURE__ */ jsx(ExplorerPane, { tree: explorerTree }),
3923
4093
  /* @__PURE__ */ jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [
@@ -3943,10 +4113,12 @@ var Layout = React6.memo(({
3943
4113
  init_protocol();
3944
4114
  function useStudio(server) {
3945
4115
  const connInfo = server.getConnectionInfo();
4116
+ const connections = server.listConnections();
3946
4117
  const [state, setState] = useState({
3947
4118
  connected: server.isConnected(),
3948
4119
  placeName: connInfo?.placeName,
3949
4120
  placeId: connInfo?.placeId,
4121
+ connectionCount: connections.length,
3950
4122
  explorerTree: [],
3951
4123
  outputLog: []
3952
4124
  });
@@ -3955,27 +4127,36 @@ function useStudio(server) {
3955
4127
  useEffect(() => {
3956
4128
  if (server.isConnected() && !stateRef.current.connected) {
3957
4129
  const info = server.getConnectionInfo();
4130
+ const conns = server.listConnections();
3958
4131
  setState((prev) => ({
3959
4132
  ...prev,
3960
4133
  connected: true,
3961
4134
  placeName: info?.placeName,
3962
- placeId: info?.placeId
4135
+ placeId: info?.placeId,
4136
+ connectionCount: conns.length
3963
4137
  }));
3964
4138
  }
3965
4139
  const onConnected = (payload) => {
4140
+ const conns = server.listConnections();
4141
+ const info = server.getConnectionInfo();
3966
4142
  setState((prev) => ({
3967
4143
  ...prev,
3968
4144
  connected: true,
3969
- placeName: payload.placeName ?? prev.placeName,
3970
- placeId: payload.placeId ?? prev.placeId
4145
+ placeName: info?.placeName ?? payload.placeName ?? prev.placeName,
4146
+ placeId: info?.placeId ?? payload.placeId ?? prev.placeId,
4147
+ connectionCount: conns.length
3971
4148
  }));
3972
4149
  };
3973
4150
  const onDisconnected = () => {
4151
+ const conns = server.listConnections();
4152
+ const info = server.getConnectionInfo();
4153
+ const stillConnected = conns.length > 0;
3974
4154
  setState((prev) => ({
3975
4155
  ...prev,
3976
- connected: false,
3977
- placeName: void 0,
3978
- placeId: void 0
4156
+ connected: stillConnected,
4157
+ placeName: stillConnected ? info?.placeName : void 0,
4158
+ placeId: stillConnected ? info?.placeId : void 0,
4159
+ connectionCount: conns.length
3979
4160
  }));
3980
4161
  };
3981
4162
  const onExplorer = (payload) => {
@@ -4277,6 +4458,7 @@ var App = ({ server, agent: initialAgent, version }) => {
4277
4458
  {
4278
4459
  connected: studio.connected,
4279
4460
  placeName: studio.placeName,
4461
+ connectionCount: studio.connectionCount,
4280
4462
  model: agent?.getModel() ?? resolveProviderSettings(config).model,
4281
4463
  chatEntries,
4282
4464
  outputEntries: studio.outputLog,