@jingyi0605/codingns 0.5.0 → 0.5.5

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 (110) hide show
  1. package/dist/public/assets/AdaptiveButlerPage-CUyNL98E.js +3 -0
  2. package/dist/public/assets/App-BFP7LCSC.js +30 -0
  3. package/dist/public/assets/{BootstrapPage-Vu5oEJ8z.js → BootstrapPage-G74dX2Us.js} +1 -1
  4. package/dist/public/assets/ConversationPage-Bz0_tvvM.js +2 -0
  5. package/dist/public/assets/{DesktopDetachPreviewPage-BgeEqbc5.js → DesktopDetachPreviewPage-IV7oEdOX.js} +1 -1
  6. package/dist/public/assets/DesktopWindowPage-BBmHyRg5.js +2 -0
  7. package/dist/public/assets/FileContextPanel--FVTxDrq.js +1 -0
  8. package/dist/public/assets/GitSidebar-DAiSi9oc.js +6 -0
  9. package/dist/public/assets/{MobileCreateSessionSheet-DLq5qPkx.js → MobileCreateSessionSheet-DqVwz_Hp.js} +1 -1
  10. package/dist/public/assets/MobileSheet-D1lMrcvD.js +1 -0
  11. package/dist/public/assets/{MobileTopHeaderFrame-DArgZI7L.js → MobileTopHeaderFrame-COTc7cRr.js} +1 -1
  12. package/dist/public/assets/{MobileWorkspaceSwitcherHeader-0ywJKfBQ.js → MobileWorkspaceSwitcherHeader-DJPV9ym2.js} +1 -1
  13. package/dist/public/assets/RelayConnectEntryPage-dSwU8VzK.js +1 -0
  14. package/dist/public/assets/{ServerSettingsModal-izoYMx9U.js → ServerSettingsModal-B34ms3ze.js} +1 -1
  15. package/dist/public/assets/{SessionIndexPage-C5aG8FIv.js → SessionIndexPage-D3tG1gmM.js} +1 -1
  16. package/dist/public/assets/SettingsPage-B3-6-5GL.js +1 -0
  17. package/dist/public/assets/TerminalManagerPanel-DhuTEdzV.js +1 -0
  18. package/dist/public/assets/{TerminalPage-CtKXIU0h.js → TerminalPage-DpsvQQVR.js} +19 -19
  19. package/dist/public/assets/{TerminalRuntimeFallbackModal-CRhOQOsT.js → TerminalRuntimeFallbackModal-CNzOt5v5.js} +1 -1
  20. package/dist/public/assets/{ToolFilesPage-DcYPsS-e.js → ToolFilesPage-BX9QDi9Y.js} +1 -1
  21. package/dist/public/assets/{ToolGitPage-CsPl89ty.js → ToolGitPage-4VtFox3p.js} +1 -1
  22. package/dist/public/assets/{ToolProcessesPage-D0dvR8xK.js → ToolProcessesPage-DZJC6Qnt.js} +1 -1
  23. package/dist/public/assets/{ToolsHomePage-4fP-KRiv.js → ToolsHomePage-D7JbrAWv.js} +1 -1
  24. package/dist/public/assets/{WorkbenchLandingPage-kvlfyxRo.js → WorkbenchLandingPage-C0yqnzqh.js} +1 -1
  25. package/dist/public/assets/WorkbenchLayout-Brlj8K3i.js +3 -0
  26. package/dist/public/assets/{WorkbenchModal-Ctob14VR.js → WorkbenchModal-CbDxaCOR.js} +1 -1
  27. package/dist/public/assets/WorkbenchShellRoute-BMcnFadA.css +1 -0
  28. package/dist/public/assets/WorkbenchShellRoute-puGpdDFY.js +1 -0
  29. package/dist/public/assets/{WorkspaceDebugDetailPage-Com5kEXJ.js → WorkspaceDebugDetailPage-fTGweC9N.js} +1 -1
  30. package/dist/public/assets/WorkspaceDetailPage-BtaIzSDB.js +1 -0
  31. package/dist/public/assets/WorkspaceHomePage-CUmmYDrM.js +1 -0
  32. package/dist/public/assets/client-runtime-manager-RHFa_iWo.js +1 -0
  33. package/dist/public/assets/{default-session-permission-mode-CcGwR4Kk.js → default-session-permission-mode-Cu5SreTG.js} +1 -1
  34. package/dist/public/assets/file-tree-icon-BMKuc5pw.js +31 -0
  35. package/dist/public/assets/index-Cq3ue0za.css +1 -0
  36. package/dist/public/assets/index-DEbFT-Aq.js +42 -0
  37. package/dist/public/assets/{preferences-service-KIYeE2gk.js → preferences-service-gv_9vGKz.js} +1 -1
  38. package/dist/public/assets/session-runtime-machine-Bfnxkk9B.js +17 -0
  39. package/dist/public/assets/{terminal-runtime-meta-AWXJpN4r.js → terminal-runtime-meta-B9xJGY__.js} +1 -1
  40. package/dist/public/assets/{useRegisteredDebugTemplates-DBDRdptr.js → useRegisteredDebugTemplates-CDfl54Wt.js} +1 -1
  41. package/dist/public/index.html +2 -2
  42. package/dist/server/config/env.d.ts +1 -0
  43. package/dist/server/config/env.js +36 -0
  44. package/dist/server/config/env.js.map +1 -1
  45. package/dist/server/modules/client/client-controller.js +1 -1
  46. package/dist/server/modules/client/client-controller.js.map +1 -1
  47. package/dist/server/modules/client/client-service.d.ts +16 -2
  48. package/dist/server/modules/client/client-service.js +21 -3
  49. package/dist/server/modules/client/client-service.js.map +1 -1
  50. package/dist/server/modules/relay-tunnel/crypto/relay-tunnel-packets.d.ts +16 -1
  51. package/dist/server/modules/relay-tunnel/crypto/relay-tunnel-packets.js.map +1 -1
  52. package/dist/server/modules/relay-tunnel/crypto/relay-tunnel-protocol.d.ts +2 -1
  53. package/dist/server/modules/relay-tunnel/crypto/relay-tunnel-protocol.js +18 -0
  54. package/dist/server/modules/relay-tunnel/crypto/relay-tunnel-protocol.js.map +1 -1
  55. package/dist/server/modules/relay-tunnel/relay-tunnel-client-context.d.ts +13 -0
  56. package/dist/server/modules/relay-tunnel/relay-tunnel-client-context.js +2 -0
  57. package/dist/server/modules/relay-tunnel/relay-tunnel-client-context.js.map +1 -0
  58. package/dist/server/modules/relay-tunnel/relay-tunnel-gateway-service.d.ts +6 -0
  59. package/dist/server/modules/relay-tunnel/relay-tunnel-gateway-service.js +110 -10
  60. package/dist/server/modules/relay-tunnel/relay-tunnel-gateway-service.js.map +1 -1
  61. package/dist/server/modules/relay-tunnel/relay-tunnel-runtime-adapter.d.ts +16 -4
  62. package/dist/server/modules/relay-tunnel/relay-tunnel-runtime-adapter.js +216 -101
  63. package/dist/server/modules/relay-tunnel/relay-tunnel-runtime-adapter.js.map +1 -1
  64. package/dist/server/modules/relay-tunnel/relay-tunnel-service.d.ts +6 -2
  65. package/dist/server/modules/relay-tunnel/relay-tunnel-service.js +457 -40
  66. package/dist/server/modules/relay-tunnel/relay-tunnel-service.js.map +1 -1
  67. package/dist/server/modules/sessions/session-activity-inspector.js +6 -8
  68. package/dist/server/modules/sessions/session-activity-inspector.js.map +1 -1
  69. package/dist/server/modules/sessions/session-history-service.d.ts +1 -0
  70. package/dist/server/modules/sessions/session-history-service.js +62 -7
  71. package/dist/server/modules/sessions/session-history-service.js.map +1 -1
  72. package/dist/server/modules/workbench/snapshot-revision.d.ts +4 -0
  73. package/dist/server/modules/workbench/snapshot-revision.js +13 -0
  74. package/dist/server/modules/workbench/snapshot-revision.js.map +1 -0
  75. package/dist/server/modules/workbench/workbench-service.d.ts +33 -2
  76. package/dist/server/modules/workbench/workbench-service.js +39 -4
  77. package/dist/server/modules/workbench/workbench-service.js.map +1 -1
  78. package/dist/server/modules/workbench/workspace-panel-snapshot-service.d.ts +6 -1
  79. package/dist/server/modules/workbench/workspace-panel-snapshot-service.js +10 -8
  80. package/dist/server/modules/workbench/workspace-panel-snapshot-service.js.map +1 -1
  81. package/dist/server/server/create-server.js +11 -6
  82. package/dist/server/server/create-server.js.map +1 -1
  83. package/dist/server/types/domain.d.ts +9 -0
  84. package/dist/server/ws/workbench-ws-hub.js +295 -43
  85. package/dist/server/ws/workbench-ws-hub.js.map +1 -1
  86. package/dist/server/ws/ws-server.js +141 -8
  87. package/dist/server/ws/ws-server.js.map +1 -1
  88. package/node_modules/@codingns/session-sync-core/dist/providers/codex.js +33 -6
  89. package/node_modules/@codingns/session-sync-core/dist/providers/codex.js.map +1 -1
  90. package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.js +78 -36
  91. package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.js.map +1 -1
  92. package/package.json +1 -1
  93. package/dist/public/assets/AdaptiveButlerPage-R-XZw7pd.js +0 -3
  94. package/dist/public/assets/App-DkvE5EyM.js +0 -30
  95. package/dist/public/assets/ConversationPage-Cjpg6g0J.js +0 -2
  96. package/dist/public/assets/DesktopWindowPage-1WelvxdH.js +0 -2
  97. package/dist/public/assets/FileContextPanel-D_ghXJuW.js +0 -1
  98. package/dist/public/assets/GitSidebar-D9f9Jxwr.js +0 -6
  99. package/dist/public/assets/MobileSheet-DLg-gX1t.js +0 -1
  100. package/dist/public/assets/SettingsPage-HJIC-P-4.js +0 -1
  101. package/dist/public/assets/TerminalManagerPanel-DpyUTo9k.js +0 -1
  102. package/dist/public/assets/WorkbenchLayout-ByFw4eeu.js +0 -3
  103. package/dist/public/assets/WorkbenchShellRoute-BUITtdAg.css +0 -1
  104. package/dist/public/assets/WorkbenchShellRoute-Kw7JEZI3.js +0 -1
  105. package/dist/public/assets/WorkspaceDetailPage-D0Lrx4Uz.js +0 -1
  106. package/dist/public/assets/WorkspaceHomePage-wR8d3aP9.js +0 -1
  107. package/dist/public/assets/file-tree-icon-UFVoVzhM.js +0 -31
  108. package/dist/public/assets/index-Byp9hJ0c.js +0 -42
  109. package/dist/public/assets/index-_52jxu4a.css +0 -1
  110. package/dist/public/assets/session-runtime-machine-0KNSSPp5.js +0 -17
@@ -1,4 +1,5 @@
1
1
  import { AppError } from "../shared/errors/app-error.js";
2
+ import { logPerformance } from "../shared/utils/perf-log.js";
2
3
  import { logTerminalDebug, terminalDebugNowMs } from "../shared/utils/terminal-debug-log.js";
3
4
  const WORKBENCH_REFRESH_INTERVAL_MS = 60_000;
4
5
  const GIT_SUBSCRIPTION_MIN_REFRESH_INTERVAL_MS = 15_000;
@@ -42,7 +43,7 @@ export class WorkbenchWsHub {
42
43
  try {
43
44
  switch (message.type) {
44
45
  case "workbench.subscribe":
45
- void this.sendWorkbenchSnapshotToClient(client, userId, channel);
46
+ void this.sendWorkbenchSnapshotToClient(client, userId, channel, message.knownRevision);
46
47
  if (this.workbenchService.shouldRefreshSnapshot()) {
47
48
  this.workbenchService.scheduleSnapshotRefresh(userId);
48
49
  }
@@ -51,18 +52,18 @@ export class WorkbenchWsHub {
51
52
  void this.refreshAndBroadcast(userId, true);
52
53
  return true;
53
54
  case "fileTree.subscribe":
54
- this.replaceFileTreeSubscription(client, message.workspaceId, message.paths);
55
+ this.replaceFileTreeSubscription(client, message.workspaceId, message.paths, message.knownRevisions);
55
56
  void this.refreshFileTreeSubscriptions(client);
56
57
  return true;
57
58
  case "fileTree.refresh":
58
59
  for (const path of normalizePanelPaths(message.paths)) {
59
60
  this.workspacePanelSnapshotService.invalidateFileTree(message.workspaceId.trim(), path);
60
61
  }
61
- this.ensureFileTreeSubscription(client, message.workspaceId, message.paths);
62
+ this.ensureFileTreeSubscription(client, message.workspaceId, message.paths, message.knownRevisions);
62
63
  void this.refreshFileTreeSubscriptions(client, true);
63
64
  return true;
64
65
  case "git.subscribe":
65
- this.ensureGitSubscription(client, message.workspaceId);
66
+ this.ensureGitSubscription(client, message.workspaceId, message.knownRevision);
66
67
  void this.refreshGitSubscription(client, false, {
67
68
  deliverIfUnchanged: true,
68
69
  ignoreMinInterval: true
@@ -70,18 +71,18 @@ export class WorkbenchWsHub {
70
71
  return true;
71
72
  case "git.refresh":
72
73
  this.workspacePanelSnapshotService.invalidateGit(message.workspaceId.trim());
73
- this.ensureGitSubscription(client, message.workspaceId);
74
+ this.ensureGitSubscription(client, message.workspaceId, message.knownRevision);
74
75
  this.scheduleGitRefresh(client, {
75
76
  force: true
76
77
  });
77
78
  return true;
78
79
  case "terminalManager.subscribe":
79
- this.ensureTerminalManagerSubscription(client, message.workspaceId);
80
+ this.ensureTerminalManagerSubscription(client, message.workspaceId, message.knownRevision);
80
81
  this.scheduleTerminalManagerRefresh(client);
81
82
  return true;
82
83
  case "terminalManager.refresh":
83
84
  this.workspacePanelSnapshotService.invalidateTerminalManager(message.workspaceId.trim());
84
- this.ensureTerminalManagerSubscription(client, message.workspaceId);
85
+ this.ensureTerminalManagerSubscription(client, message.workspaceId, message.knownRevision);
85
86
  this.scheduleTerminalManagerRefresh(client, {
86
87
  force: true
87
88
  });
@@ -89,7 +90,8 @@ export class WorkbenchWsHub {
89
90
  case "workspaceManagement.subscribe":
90
91
  this.clientWorkspaceManagementSubscriptions.set(client, {
91
92
  workspaceId: message.workspaceId.trim(),
92
- lastPayload: null
93
+ lastPayload: null,
94
+ knownRevision: normalizeKnownRevision(message.knownRevision) ?? null
93
95
  });
94
96
  void this.refreshWorkspaceManagementSubscription(client);
95
97
  return true;
@@ -97,7 +99,8 @@ export class WorkbenchWsHub {
97
99
  this.workspacePanelSnapshotService.invalidateWorkspaceManagement(message.workspaceId.trim());
98
100
  this.clientWorkspaceManagementSubscriptions.set(client, {
99
101
  workspaceId: message.workspaceId.trim(),
100
- lastPayload: null
102
+ lastPayload: null,
103
+ knownRevision: normalizeKnownRevision(message.knownRevision) ?? null
101
104
  });
102
105
  void this.refreshWorkspaceManagementSubscription(client, true);
103
106
  return true;
@@ -185,19 +188,42 @@ export class WorkbenchWsHub {
185
188
  }
186
189
  channel.realtimeBroadcastQueued = false;
187
190
  channel.realtimeBroadcastTask = (async () => {
191
+ const startedAt = Date.now();
188
192
  const startedAtMs = terminalDebugNowMs();
189
193
  try {
190
- const payload = buildWorkbenchPayload(this.workbenchService.getSnapshot(userId));
191
- if (payload === channel.lastWorkbenchPayload) {
194
+ const snapshotStartedAt = Date.now();
195
+ const snapshot = this.workbenchService.getSnapshot(userId);
196
+ const snapshotMs = Date.now() - snapshotStartedAt;
197
+ const serializeStartedAt = Date.now();
198
+ const payload = buildWorkbenchBroadcastPayload(channel.lastWorkbenchSnapshot, snapshot);
199
+ const serializeMs = Date.now() - serializeStartedAt;
200
+ if (snapshot.revision === channel.lastWorkbenchRevision) {
192
201
  return;
193
202
  }
194
203
  channel.lastWorkbenchPayload = payload;
195
- for (const client of channel.clients) {
196
- client.send(payload);
197
- }
204
+ channel.lastWorkbenchRevision = snapshot.revision;
205
+ channel.lastWorkbenchSnapshot = snapshot;
206
+ const sessionCount = snapshot.items.reduce((total, item) => total + countWorkbenchSessions(item), 0);
207
+ const broadcastMetric = broadcastSerializedPayload(channel.clients, payload);
208
+ logPerformance("ws.workbench.realtime_broadcast", Date.now() - startedAt, {
209
+ userId,
210
+ clientCount: channel.clients.size,
211
+ workspaceCount: snapshot.items.length,
212
+ sessionCount,
213
+ snapshotMs,
214
+ serializeMs,
215
+ payloadBytes: broadcastMetric.payloadBytes,
216
+ sendMs: broadcastMetric.sendMs,
217
+ maxBufferedAmount: broadcastMetric.maxBufferedAmount
218
+ }, {
219
+ thresholdMs: 0,
220
+ force: true
221
+ });
198
222
  logTerminalDebug("workbench.realtime_broadcast.completed", {
199
223
  userId,
200
224
  clientCount: channel.clients.size,
225
+ payloadBytes: broadcastMetric.payloadBytes,
226
+ maxBufferedAmount: broadcastMetric.maxBufferedAmount,
201
227
  durationMs: terminalDebugNowMs() - startedAtMs
202
228
  });
203
229
  }
@@ -263,6 +289,8 @@ export class WorkbenchWsHub {
263
289
  channel = {
264
290
  clients: new Set(),
265
291
  lastWorkbenchPayload: null,
292
+ lastWorkbenchRevision: null,
293
+ lastWorkbenchSnapshot: null,
266
294
  workbenchTimer: null,
267
295
  workspaceManagementTimer: null,
268
296
  realtimeBroadcastTimer: null,
@@ -288,11 +316,42 @@ export class WorkbenchWsHub {
288
316
  this.userChannels.set(userId, channel);
289
317
  return channel;
290
318
  }
291
- async sendWorkbenchSnapshotToClient(client, userId, channel) {
319
+ async sendWorkbenchSnapshotToClient(client, userId, channel, knownRevision) {
320
+ const startedAt = Date.now();
292
321
  try {
293
- const payload = buildWorkbenchPayload(this.workbenchService.getSnapshot(userId));
294
- channel.lastWorkbenchPayload = payload;
295
- client.send(payload);
322
+ const snapshotStartedAt = Date.now();
323
+ const snapshot = this.workbenchService.getSnapshot(userId);
324
+ const snapshotMs = Date.now() - snapshotStartedAt;
325
+ const fullPayloadStartedAt = Date.now();
326
+ const fullPayload = buildWorkbenchPayload(snapshot);
327
+ const fullPayloadBuildMs = Date.now() - fullPayloadStartedAt;
328
+ const unchanged = Boolean(knownRevision && knownRevision === snapshot.revision);
329
+ let clientPayload = fullPayload;
330
+ let clientPayloadBuildMs = 0;
331
+ if (unchanged) {
332
+ const clientPayloadStartedAt = Date.now();
333
+ clientPayload = buildWorkbenchPayload(snapshot, knownRevision);
334
+ clientPayloadBuildMs = Date.now() - clientPayloadStartedAt;
335
+ }
336
+ channel.lastWorkbenchPayload = fullPayload;
337
+ channel.lastWorkbenchRevision = snapshot.revision;
338
+ channel.lastWorkbenchSnapshot = snapshot;
339
+ const sendMetric = sendSerializedPayload(client, clientPayload);
340
+ logPerformance("ws.workbench.subscribe", Date.now() - startedAt, {
341
+ userId,
342
+ snapshotMs,
343
+ fullPayloadBuildMs,
344
+ clientPayloadBuildMs,
345
+ payloadBytes: sendMetric.payloadBytes,
346
+ sendMs: sendMetric.sendMs,
347
+ bufferedAmount: sendMetric.bufferedAmount,
348
+ workspaceCount: snapshot.items.length,
349
+ sessionCount: snapshot.items.reduce((total, item) => total + countWorkbenchSessions(item), 0),
350
+ unchanged
351
+ }, {
352
+ thresholdMs: 0,
353
+ force: true
354
+ });
296
355
  }
297
356
  catch (error) {
298
357
  this.reportAsyncError("sendWorkbenchSnapshotToClient", error, { userId });
@@ -307,22 +366,45 @@ export class WorkbenchWsHub {
307
366
  await channel.refreshTask;
308
367
  }
309
368
  channel.refreshTask = (async () => {
369
+ const startedAt = Date.now();
310
370
  const startedAtMs = terminalDebugNowMs();
311
371
  try {
372
+ const snapshotStartedAt = Date.now();
312
373
  const snapshot = await this.workbenchService.refreshSnapshot(userId);
313
- const payload = buildWorkbenchPayload(snapshot);
314
- if (payload === channel.lastWorkbenchPayload) {
374
+ const snapshotMs = Date.now() - snapshotStartedAt;
375
+ const serializeStartedAt = Date.now();
376
+ const payload = buildWorkbenchBroadcastPayload(channel.lastWorkbenchSnapshot, snapshot);
377
+ const serializeMs = Date.now() - serializeStartedAt;
378
+ if (snapshot.revision === channel.lastWorkbenchRevision) {
315
379
  return;
316
380
  }
317
381
  channel.lastWorkbenchPayload = payload;
318
- for (const client of channel.clients) {
319
- client.send(payload);
320
- }
382
+ channel.lastWorkbenchRevision = snapshot.revision;
383
+ channel.lastWorkbenchSnapshot = snapshot;
384
+ const sessionCount = snapshot.items.reduce((total, item) => total + countWorkbenchSessions(item), 0);
385
+ const broadcastMetric = broadcastSerializedPayload(channel.clients, payload);
386
+ logPerformance("ws.workbench.refresh", Date.now() - startedAt, {
387
+ userId,
388
+ force,
389
+ clientCount: channel.clients.size,
390
+ snapshotMs,
391
+ serializeMs,
392
+ payloadBytes: broadcastMetric.payloadBytes,
393
+ sendMs: broadcastMetric.sendMs,
394
+ maxBufferedAmount: broadcastMetric.maxBufferedAmount,
395
+ workspaceCount: snapshot.items.length,
396
+ sessionCount
397
+ }, {
398
+ thresholdMs: 0,
399
+ force: true
400
+ });
321
401
  logTerminalDebug("workbench.refresh.completed", {
322
402
  userId,
323
403
  force,
324
404
  clientCount: channel.clients.size,
325
405
  workspaceCount: snapshot.items.length,
406
+ payloadBytes: broadcastMetric.payloadBytes,
407
+ maxBufferedAmount: broadcastMetric.maxBufferedAmount,
326
408
  durationMs: terminalDebugNowMs() - startedAtMs
327
409
  });
328
410
  }
@@ -334,20 +416,23 @@ export class WorkbenchWsHub {
334
416
  });
335
417
  return channel.refreshTask;
336
418
  }
337
- ensureFileTreeSubscription(client, workspaceId, paths) {
419
+ ensureFileTreeSubscription(client, workspaceId, paths, knownRevisions) {
338
420
  const current = this.clientFileTreeSubscriptions.get(client);
339
421
  const normalizedWorkspaceId = workspaceId.trim();
340
422
  const normalizedPaths = normalizePanelPaths(paths);
341
423
  const nextPaths = normalizedPaths.length > 0 ? normalizedPaths : [""];
424
+ const nextKnownRevisionByPath = buildKnownRevisionByPathMap(nextPaths, knownRevisions);
342
425
  if (current &&
343
426
  current.workspaceId === normalizedWorkspaceId &&
344
427
  areStringArraysEqual(current.paths, nextPaths)) {
428
+ current.knownRevisionByPath = nextKnownRevisionByPath;
345
429
  return current;
346
430
  }
347
431
  const next = {
348
432
  workspaceId: normalizedWorkspaceId,
349
433
  paths: nextPaths,
350
- lastPayloadByPath: new Map()
434
+ lastPayloadByPath: new Map(),
435
+ knownRevisionByPath: nextKnownRevisionByPath
351
436
  };
352
437
  this.fileWatcher.subscribeFileTree(normalizedWorkspaceId, nextPaths);
353
438
  this.clientFileTreeSubscriptions.set(client, next);
@@ -356,8 +441,8 @@ export class WorkbenchWsHub {
356
441
  }
357
442
  return next;
358
443
  }
359
- replaceFileTreeSubscription(client, workspaceId, paths) {
360
- return this.ensureFileTreeSubscription(client, workspaceId, paths);
444
+ replaceFileTreeSubscription(client, workspaceId, paths, knownRevisions) {
445
+ return this.ensureFileTreeSubscription(client, workspaceId, paths, knownRevisions);
361
446
  }
362
447
  async refreshFileTreeSubscriptions(client, force = false) {
363
448
  const subscription = this.clientFileTreeSubscriptions.get(client);
@@ -368,11 +453,12 @@ export class WorkbenchWsHub {
368
453
  const uniquePaths = normalizePanelPaths(subscription.paths);
369
454
  for (const path of uniquePaths) {
370
455
  const snapshot = await this.workspacePanelSnapshotService.getFileTreeSnapshot(subscription.workspaceId, path, { force });
371
- const payload = buildFileTreePayload(snapshot);
456
+ const payload = buildFileTreePayload(snapshot, subscription.knownRevisionByPath.get(path) ?? null);
372
457
  const lastPayload = subscription.lastPayloadByPath.get(path) ?? null;
373
458
  if (payload === lastPayload) {
374
459
  continue;
375
460
  }
461
+ subscription.knownRevisionByPath.set(path, snapshot.revision);
376
462
  subscription.lastPayloadByPath.set(path, payload);
377
463
  client.send(payload);
378
464
  }
@@ -422,10 +508,13 @@ export class WorkbenchWsHub {
422
508
  if (controller.signal.aborted) {
423
509
  return;
424
510
  }
425
- const payload = buildGitPayload(snapshot);
426
- if (payload === subscription.lastPayload && !options?.deliverIfUnchanged) {
511
+ const payload = buildGitPayload(snapshot, subscription.knownRevision, {
512
+ includeSnapshotWhenUnchanged: force || options?.deliverIfUnchanged === true
513
+ });
514
+ if (payload === subscription.lastPayload && !options?.deliverIfUnchanged && !force) {
427
515
  return;
428
516
  }
517
+ subscription.knownRevision = snapshot.revision;
429
518
  subscription.lastPayload = payload;
430
519
  client.send(payload);
431
520
  logTerminalDebug("workbench.git_refresh.completed", {
@@ -455,15 +544,18 @@ export class WorkbenchWsHub {
455
544
  });
456
545
  return subscription.refreshTask;
457
546
  }
458
- ensureGitSubscription(client, workspaceId) {
547
+ ensureGitSubscription(client, workspaceId, knownRevision) {
459
548
  const normalizedWorkspaceId = workspaceId.trim();
460
549
  const current = this.clientGitSubscriptions.get(client);
550
+ const normalizedKnownRevision = normalizeKnownRevision(knownRevision) ?? null;
461
551
  if (current && current.workspaceId === normalizedWorkspaceId) {
552
+ current.knownRevision = normalizedKnownRevision;
462
553
  return current;
463
554
  }
464
555
  const next = {
465
556
  workspaceId: normalizedWorkspaceId,
466
557
  lastPayload: null,
558
+ knownRevision: normalizedKnownRevision,
467
559
  lastRequestedAt: 0,
468
560
  refreshTask: null,
469
561
  refreshController: null,
@@ -523,10 +615,12 @@ export class WorkbenchWsHub {
523
615
  this.flushQueuedGitRefresh(client);
524
616
  }, Math.max(0, delayMs));
525
617
  }
526
- ensureTerminalManagerSubscription(client, workspaceId) {
618
+ ensureTerminalManagerSubscription(client, workspaceId, knownRevision) {
527
619
  const normalizedWorkspaceId = workspaceId.trim();
528
620
  const current = this.clientTerminalManagerSubscriptions.get(client);
621
+ const normalizedKnownRevision = normalizeKnownRevision(knownRevision) ?? null;
529
622
  if (current && current.workspaceId === normalizedWorkspaceId) {
623
+ current.knownRevision = normalizedKnownRevision;
530
624
  return current;
531
625
  }
532
626
  if (current?.refreshTimer) {
@@ -535,6 +629,7 @@ export class WorkbenchWsHub {
535
629
  const next = {
536
630
  workspaceId: normalizedWorkspaceId,
537
631
  lastPayload: null,
632
+ knownRevision: normalizedKnownRevision,
538
633
  refreshTask: null,
539
634
  refreshTimer: null,
540
635
  queuedRefresh: false,
@@ -602,11 +697,12 @@ export class WorkbenchWsHub {
602
697
  const startedAtMs = terminalDebugNowMs();
603
698
  const snapshot = await this.workspacePanelSnapshotService.getTerminalManagerSnapshot(subscription.workspaceId, { force });
604
699
  const payloadStartedAtMs = terminalDebugNowMs();
605
- const payload = buildTerminalManagerPayload(snapshot);
700
+ const payload = buildTerminalManagerPayload(snapshot, subscription.knownRevision);
606
701
  const payloadBuildMs = terminalDebugNowMs() - payloadStartedAtMs;
607
702
  if (payload === subscription.lastPayload) {
608
703
  return;
609
704
  }
705
+ subscription.knownRevision = snapshot.revision;
610
706
  subscription.lastPayload = payload;
611
707
  const sendStartedAtMs = terminalDebugNowMs();
612
708
  client.send(payload);
@@ -641,10 +737,11 @@ export class WorkbenchWsHub {
641
737
  }
642
738
  try {
643
739
  const snapshot = await this.workspacePanelSnapshotService.getWorkspaceManagementSnapshot(subscription.workspaceId, { force });
644
- const payload = buildWorkspaceManagementPayload(snapshot);
740
+ const payload = buildWorkspaceManagementPayload(snapshot, subscription.knownRevision);
645
741
  if (payload === subscription.lastPayload) {
646
742
  return;
647
743
  }
744
+ subscription.knownRevision = snapshot.revision;
648
745
  subscription.lastPayload = payload;
649
746
  client.send(payload);
650
747
  }
@@ -700,7 +797,8 @@ function parseWorkbenchMessage(payload) {
700
797
  case "workbench.subscribe":
701
798
  case "workbench.refresh":
702
799
  return {
703
- type: candidate.type
800
+ type: candidate.type,
801
+ knownRevision: normalizeKnownRevision(candidate.knownRevision)
704
802
  };
705
803
  case "fileTree.subscribe":
706
804
  case "fileTree.refresh":
@@ -710,7 +808,8 @@ function parseWorkbenchMessage(payload) {
710
808
  workspaceId: candidate.workspaceId,
711
809
  paths: Array.isArray(candidate.paths)
712
810
  ? candidate.paths.filter((value) => typeof value === "string")
713
- : undefined
811
+ : undefined,
812
+ knownRevisions: normalizeKnownRevisionRecord(candidate.knownRevisions)
714
813
  }
715
814
  : null;
716
815
  case "git.subscribe":
@@ -718,7 +817,8 @@ function parseWorkbenchMessage(payload) {
718
817
  return typeof candidate.workspaceId === "string"
719
818
  ? {
720
819
  type: candidate.type,
721
- workspaceId: candidate.workspaceId
820
+ workspaceId: candidate.workspaceId,
821
+ knownRevision: normalizeKnownRevision(candidate.knownRevision)
722
822
  }
723
823
  : null;
724
824
  case "terminalManager.subscribe":
@@ -728,7 +828,8 @@ function parseWorkbenchMessage(payload) {
728
828
  return typeof candidate.workspaceId === "string"
729
829
  ? {
730
830
  type: candidate.type,
731
- workspaceId: candidate.workspaceId
831
+ workspaceId: candidate.workspaceId,
832
+ knownRevision: normalizeKnownRevision(candidate.knownRevision)
732
833
  }
733
834
  : null;
734
835
  default:
@@ -764,34 +865,185 @@ function areStringArraysEqual(left, right) {
764
865
  }
765
866
  return left.every((value, index) => value === right[index]);
766
867
  }
767
- function buildWorkbenchPayload(snapshot) {
868
+ function sendSerializedPayload(client, payload) {
869
+ const sendStartedAt = Date.now();
870
+ client.send(payload);
871
+ return {
872
+ payloadBytes: Buffer.byteLength(payload),
873
+ sendMs: Date.now() - sendStartedAt,
874
+ bufferedAmount: client.bufferedAmount
875
+ };
876
+ }
877
+ function broadcastSerializedPayload(clients, payload) {
878
+ const sendStartedAt = Date.now();
879
+ let maxBufferedAmount = 0;
880
+ for (const client of clients) {
881
+ client.send(payload);
882
+ maxBufferedAmount = Math.max(maxBufferedAmount, client.bufferedAmount);
883
+ }
884
+ return {
885
+ payloadBytes: Buffer.byteLength(payload),
886
+ sendMs: Date.now() - sendStartedAt,
887
+ maxBufferedAmount
888
+ };
889
+ }
890
+ function buildWorkbenchPayload(snapshot, knownRevision) {
891
+ if (knownRevision && knownRevision === snapshot.revision) {
892
+ return JSON.stringify({
893
+ type: "workbench.snapshot",
894
+ revision: snapshot.revision,
895
+ unchanged: true,
896
+ snapshot: null
897
+ });
898
+ }
768
899
  return JSON.stringify({
769
900
  type: "workbench.snapshot",
901
+ revision: snapshot.revision,
902
+ unchanged: false,
770
903
  snapshot
771
904
  });
772
905
  }
773
- function buildFileTreePayload(snapshot) {
906
+ function buildWorkbenchBroadcastPayload(previousSnapshot, nextSnapshot) {
907
+ if (!previousSnapshot || previousSnapshot.revision === nextSnapshot.revision) {
908
+ return buildWorkbenchPayload(nextSnapshot);
909
+ }
910
+ const fullPayload = buildWorkbenchPayload(nextSnapshot);
911
+ const deltaPayload = JSON.stringify(buildWorkbenchDeltaPayload(previousSnapshot, nextSnapshot));
912
+ return Buffer.byteLength(deltaPayload, "utf8") < Buffer.byteLength(fullPayload, "utf8")
913
+ ? deltaPayload
914
+ : fullPayload;
915
+ }
916
+ function buildWorkbenchDeltaPayload(previousSnapshot, nextSnapshot) {
917
+ const previousByWorkspaceId = new Map(previousSnapshot.items.map((item) => [item.workspace.id, item]));
918
+ const nextByWorkspaceId = new Map(nextSnapshot.items.map((item) => [item.workspace.id, item]));
919
+ const changedItems = [];
920
+ for (const item of nextSnapshot.items) {
921
+ const previous = previousByWorkspaceId.get(item.workspace.id);
922
+ if (!previous || JSON.stringify(previous) !== JSON.stringify(item)) {
923
+ changedItems.push(item);
924
+ }
925
+ }
926
+ return {
927
+ type: "workbench.delta",
928
+ baseRevision: previousSnapshot.revision,
929
+ revision: nextSnapshot.revision,
930
+ orderedWorkspaceIds: nextSnapshot.items.map((item) => item.workspace.id),
931
+ removedWorkspaceIds: previousSnapshot.items
932
+ .map((item) => item.workspace.id)
933
+ .filter((workspaceId) => !nextByWorkspaceId.has(workspaceId)),
934
+ changedItems
935
+ };
936
+ }
937
+ function buildFileTreePayload(snapshot, knownRevision) {
938
+ if (knownRevision && knownRevision === snapshot.revision) {
939
+ return JSON.stringify({
940
+ type: "fileTree.snapshot",
941
+ revision: snapshot.revision,
942
+ unchanged: true,
943
+ snapshot: null
944
+ });
945
+ }
774
946
  return JSON.stringify({
775
947
  type: "fileTree.snapshot",
948
+ revision: snapshot.revision,
949
+ unchanged: false,
776
950
  snapshot
777
951
  });
778
952
  }
779
- function buildGitPayload(snapshot) {
953
+ function buildGitPayload(snapshot, knownRevision, options) {
954
+ if (knownRevision
955
+ && knownRevision === snapshot.revision
956
+ && options?.includeSnapshotWhenUnchanged !== true) {
957
+ return JSON.stringify({
958
+ type: "git.snapshot",
959
+ revision: snapshot.revision,
960
+ unchanged: true,
961
+ snapshot: null
962
+ });
963
+ }
780
964
  return JSON.stringify({
781
965
  type: "git.snapshot",
966
+ revision: snapshot.revision,
967
+ unchanged: false,
782
968
  snapshot
783
969
  });
784
970
  }
785
- function buildTerminalManagerPayload(snapshot) {
971
+ function buildTerminalManagerPayload(snapshot, knownRevision) {
972
+ if (knownRevision && knownRevision === snapshot.revision) {
973
+ return JSON.stringify({
974
+ type: "terminalManager.snapshot",
975
+ revision: snapshot.revision,
976
+ unchanged: true,
977
+ snapshot: null
978
+ });
979
+ }
786
980
  return JSON.stringify({
787
981
  type: "terminalManager.snapshot",
982
+ revision: snapshot.revision,
983
+ unchanged: false,
788
984
  snapshot
789
985
  });
790
986
  }
791
- function buildWorkspaceManagementPayload(snapshot) {
987
+ function buildWorkspaceManagementPayload(snapshot, knownRevision) {
988
+ if (knownRevision && knownRevision === snapshot.revision) {
989
+ return JSON.stringify({
990
+ type: "workspaceManagement.snapshot",
991
+ revision: snapshot.revision,
992
+ unchanged: true,
993
+ snapshot: null
994
+ });
995
+ }
792
996
  return JSON.stringify({
793
997
  type: "workspaceManagement.snapshot",
998
+ revision: snapshot.revision,
999
+ unchanged: false,
794
1000
  snapshot
795
1001
  });
796
1002
  }
1003
+ function normalizeKnownRevision(value) {
1004
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
1005
+ }
1006
+ function normalizeKnownRevisionRecord(value) {
1007
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
1008
+ return undefined;
1009
+ }
1010
+ const normalizedEntries = Object.entries(value)
1011
+ .map(([key, candidate]) => [key.trim(), normalizeKnownRevision(candidate)])
1012
+ .filter((entry) => Boolean(entry[1]));
1013
+ if (normalizedEntries.length === 0) {
1014
+ return undefined;
1015
+ }
1016
+ return Object.fromEntries(normalizedEntries);
1017
+ }
1018
+ function buildKnownRevisionByPathMap(paths, knownRevisions) {
1019
+ const map = new Map();
1020
+ if (!knownRevisions) {
1021
+ return map;
1022
+ }
1023
+ for (const path of paths) {
1024
+ const knownRevision = knownRevisions[path];
1025
+ if (knownRevision) {
1026
+ map.set(path, knownRevision);
1027
+ }
1028
+ }
1029
+ return map;
1030
+ }
1031
+ function countWorkbenchSessions(item) {
1032
+ const childNodes = Array.isArray(item.childWorktrees)
1033
+ ? item.childWorktrees
1034
+ : Array.isArray(item.children)
1035
+ ? item.children
1036
+ : [];
1037
+ return item.sessions.length + childNodes.reduce((total, child) => {
1038
+ if (typeof child !== "object" || child === null) {
1039
+ return total;
1040
+ }
1041
+ const candidate = child;
1042
+ return total + countWorkbenchSessions({
1043
+ sessions: Array.isArray(candidate.sessions) ? candidate.sessions : [],
1044
+ childWorktrees: candidate.childWorktrees,
1045
+ children: candidate.children
1046
+ });
1047
+ }, 0);
1048
+ }
797
1049
  //# sourceMappingURL=workbench-ws-hub.js.map