clay-server 5.2.0-beta.8 → 5.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/README.md +0 -4
  2. package/lib/clay-product-control-tools.js +1 -0
  3. package/lib/clay-product-control.js +14 -11
  4. package/lib/clay-vendor-usage.js +44 -0
  5. package/lib/github-star-count.js +147 -0
  6. package/lib/notes-checklist.js +43 -1
  7. package/lib/notes.js +2 -1
  8. package/lib/pane-pending-interactions.js +20 -0
  9. package/lib/pane-session-activation.js +114 -0
  10. package/lib/pane-session-transport.js +26 -0
  11. package/lib/project-session-notes.js +33 -8
  12. package/lib/project-sessions.js +78 -29
  13. package/lib/project-start-protocol.js +29 -0
  14. package/lib/project-vendor-login.js +2 -0
  15. package/lib/project.js +16 -4
  16. package/lib/public/app.js +41 -18
  17. package/lib/public/css/contextual-clay.css +2 -2
  18. package/lib/public/css/filebrowser.css +1 -1
  19. package/lib/public/css/input.css +6 -0
  20. package/lib/public/css/mate-loading-scene.css +21 -0
  21. package/lib/public/css/overlays.css +64 -256
  22. package/lib/public/css/project-mate-creation.css +5 -6
  23. package/lib/public/css/project-start.css +108 -0
  24. package/lib/public/css/projects-hub.css +53 -14
  25. package/lib/public/css/settings-shell.css +103 -0
  26. package/lib/public/css/sidebar.css +7 -0
  27. package/lib/public/css/sticky-notes.css +37 -11
  28. package/lib/public/css/title-bar.css +83 -0
  29. package/lib/public/index.html +50 -63
  30. package/lib/public/modules/account-transport-settings.js +8 -2
  31. package/lib/public/modules/admin-settings-email.js +7 -2
  32. package/lib/public/modules/admin-settings-invitations.js +2 -2
  33. package/lib/public/modules/admin-settings-projects.js +2 -2
  34. package/lib/public/modules/admin-settings-ui.js +20 -4
  35. package/lib/public/modules/admin-settings-users.js +4 -4
  36. package/lib/public/modules/app-connection.js +3 -0
  37. package/lib/public/modules/app-home-hub.js +2 -1
  38. package/lib/public/modules/app-message-router.js +13 -0
  39. package/lib/public/modules/app-messages.js +35 -7
  40. package/lib/public/modules/app-misc.js +3 -1
  41. package/lib/public/modules/app-projects.js +6 -407
  42. package/lib/public/modules/app-rate-limit.js +12 -1
  43. package/lib/public/modules/clay-owner-navigation.js +0 -3
  44. package/lib/public/modules/clay-studio-render.js +3 -1
  45. package/lib/public/modules/clay-tool-presentation.js +1 -0
  46. package/lib/public/modules/command-palette.js +52 -14
  47. package/lib/public/modules/contextual-clay-workspace.js +14 -2
  48. package/lib/public/modules/contextual-clay.js +19 -4
  49. package/lib/public/modules/contextual-mate-context.js +6 -4
  50. package/lib/public/modules/elicitation-form.js +5 -0
  51. package/lib/public/modules/github-star.js +66 -0
  52. package/lib/public/modules/history-replay-batch.js +8 -0
  53. package/lib/public/modules/home-surface-boot.js +0 -1
  54. package/lib/public/modules/input.js +55 -11
  55. package/lib/public/modules/mate-loading-scene.js +17 -0
  56. package/lib/public/modules/message-delivery.js +10 -2
  57. package/lib/public/modules/model-picker.js +2 -0
  58. package/lib/public/modules/pane-activation.js +214 -0
  59. package/lib/public/modules/pane-appearance-bridge.js +59 -0
  60. package/lib/public/modules/pane-binding.js +82 -0
  61. package/lib/public/modules/pane-bridge.js +5 -2
  62. package/lib/public/modules/pane-browser-bridge.js +5 -3
  63. package/lib/public/modules/pane-file-bridge.js +4 -0
  64. package/lib/public/modules/pane-issue-bridge.js +4 -1
  65. package/lib/public/modules/profile.js +11 -3
  66. package/lib/public/modules/project-mate-creation-workspace.js +13 -4
  67. package/lib/public/modules/project-session-visibility-settings.js +19 -16
  68. package/lib/public/modules/project-settings-environment.js +12 -1
  69. package/lib/public/modules/project-settings.js +119 -129
  70. package/lib/public/modules/project-start-dialog.js +130 -0
  71. package/lib/public/modules/project-start-folder-state.js +78 -0
  72. package/lib/public/modules/project-start-folder-view.js +190 -0
  73. package/lib/public/modules/project-start-forms.js +66 -0
  74. package/lib/public/modules/project-start-path.js +60 -0
  75. package/lib/public/modules/project-start-state.js +54 -0
  76. package/lib/public/modules/project-start-submit.js +67 -0
  77. package/lib/public/modules/projects-hub-view.js +32 -44
  78. package/lib/public/modules/projects-hub.js +39 -28
  79. package/lib/public/modules/rewind.js +5 -0
  80. package/lib/public/modules/server-settings.js +36 -91
  81. package/lib/public/modules/session-actions.js +5 -2
  82. package/lib/public/modules/session-context-menu.js +5 -0
  83. package/lib/public/modules/session-rich-drafts.js +21 -0
  84. package/lib/public/modules/session-switch-defer.js +67 -0
  85. package/lib/public/modules/settings-project-target.js +56 -0
  86. package/lib/public/modules/settings-select.js +1 -1
  87. package/lib/public/modules/settings-shell-registry.js +88 -0
  88. package/lib/public/modules/settings-shell-view.js +104 -0
  89. package/lib/public/modules/settings-shell.js +342 -0
  90. package/lib/public/modules/sidebar-mobile.js +8 -30
  91. package/lib/public/modules/sidebar-projects.js +2 -1
  92. package/lib/public/modules/split-group-restore.js +68 -0
  93. package/lib/public/modules/split-pair-ui.js +1 -0
  94. package/lib/public/modules/split-pane-reconciler.js +15 -0
  95. package/lib/public/modules/split-pane-renderer.js +87 -6
  96. package/lib/public/modules/split-session-boundary.js +1 -1
  97. package/lib/public/modules/split-view.js +51 -52
  98. package/lib/public/modules/sticky-notes-checklist.js +51 -6
  99. package/lib/public/modules/stt-controller.js +4 -1
  100. package/lib/public/modules/stt-recording.js +1 -1
  101. package/lib/public/modules/theme.js +31 -3
  102. package/lib/public/modules/tools.js +18 -2
  103. package/lib/public/modules/user-settings-integrations.js +2 -1
  104. package/lib/public/modules/user-settings-layout.js +12 -34
  105. package/lib/public/modules/user-settings-profile.js +1 -1
  106. package/lib/public/modules/user-settings.js +18 -85
  107. package/lib/public/modules/vendor-usage-format.js +93 -0
  108. package/lib/public/modules/vendor-usage-list.js +45 -0
  109. package/lib/public/modules/vendor-usage-model.js +215 -0
  110. package/lib/public/modules/vendor-usage-overview.js +323 -0
  111. package/lib/public/style.css +10 -7
  112. package/lib/public/welcome-art/conversation-desk.png +0 -0
  113. package/lib/public/welcome-art/mate-clay.png +0 -0
  114. package/lib/public/welcome-art/project-clay.png +0 -0
  115. package/lib/sdk-bridge.js +6 -1
  116. package/lib/sdk-message-processor.js +9 -1
  117. package/lib/sdk-quota-snapshot.js +20 -2
  118. package/lib/server-github-stars.js +39 -0
  119. package/lib/server-global-ws.js +21 -17
  120. package/lib/server.js +11 -0
  121. package/lib/session-notes-mcp-server.js +26 -4
  122. package/lib/session-notes-writes.js +88 -0
  123. package/lib/session-visibility.js +20 -8
  124. package/lib/sessions.js +27 -10
  125. package/lib/vendor-usage-acp-readers.js +97 -0
  126. package/lib/vendor-usage-capabilities.js +88 -0
  127. package/lib/vendor-usage-http-readers.js +214 -0
  128. package/lib/vendor-usage-metrics.js +47 -0
  129. package/lib/vendor-usage-native-readers.js +106 -0
  130. package/lib/vendor-usage-overview.js +406 -0
  131. package/lib/vendor-usage-service.js +281 -0
  132. package/lib/ws-schema.js +4 -2
  133. package/lib/yoke/adapters/claude.js +12 -0
  134. package/lib/yoke/adapters/codex-query-contract.js +1 -0
  135. package/lib/yoke/adapters/codex.js +45 -12
  136. package/lib/yoke/claude-hook-installer.js +1 -0
  137. package/lib/yoke/codex-dynamic-tool-names.js +80 -0
  138. package/lib/yoke/codex-model-catalog.js +37 -3
  139. package/lib/yoke/muse-services.js +16 -3
  140. package/lib/yoke/providers/claude-model-descriptions.js +22 -2
  141. package/lib/yoke/vendor-registry.js +6 -2
  142. package/package.json +5 -3
  143. package/lib/public/modules/server-settings-navigation.js +0 -39
@@ -5,12 +5,16 @@ var { execFileSync } = require("child_process");
5
5
  var providerSettings = require("./yoke/settings");
6
6
  var updateSnooze = require("./update-snooze");
7
7
  var yoke = require("./yoke");
8
+ var startProtocol = require("./project-start-protocol");
9
+ var startReply = startProtocol.withRequestId;
10
+ var browseMeta = startProtocol.browseMeta;
8
11
  var sessionVisibility = require("./session-visibility");
9
12
  var contextUsageModule = require("./context-usage");
10
13
  var sessionDeleteModule = require("./project-session-delete");
11
14
  var sessionFoldersModule = require("./project-session-folders");
12
15
  var elicitationResponse = require("./project-elicitation-response");
13
16
  var sessionCreationModule = require("./project-session-creation");
17
+ var vendorUsageOverviewModule = require("./vendor-usage-overview");
14
18
 
15
19
  function vendorSupportsTui(vendor) {
16
20
  var info = yoke.getVendorInfo(vendor);
@@ -223,6 +227,10 @@ function attachSessions(ctx) {
223
227
  resolveSessionHome: resolveSessionHome, cancelPendingAskUser: cancelPendingAskUser,
224
228
  }));
225
229
 
230
+ function boundedRequestId(msg) {
231
+ return typeof msg.requestId === "string" ? msg.requestId.substring(0, 200) : undefined;
232
+ }
233
+
226
234
  function canAccessProjectSlug(ws, targetSlug) {
227
235
  if (!targetSlug) return false;
228
236
  if (!usersModule || typeof usersModule.isMultiUser !== "function" || !usersModule.isMultiUser()) return true;
@@ -361,6 +369,11 @@ function attachSessions(ctx) {
361
369
  getProjectAccess: getProjectAccess,
362
370
  getVendorAvailability: function (ws) { return ctx.getVendorAvailability(ws); },
363
371
  });
372
+ var vendorUsage = vendorUsageOverviewModule.attachVendorUsageOverview({
373
+ slug: slug, sm: sm, usersModule: usersModule, clients: clients, sendTo: sendTo, getProjectAccess: getProjectAccess,
374
+ getVendorAvailability: function (ws) { return ctx.getVendorAvailability(ws); },
375
+ adapters: ctx.adapters, osUsers: osUsers, getLinuxUserForWs: ctx.getLinuxUserForWs,
376
+ });
364
377
  // Kick off watchers for any TUI sessions already loaded from disk so
365
378
  // their titles refresh as soon as Claude Code rewrites the jsonl, even
366
379
  // before the user clicks them.
@@ -755,13 +768,15 @@ function attachSessions(ctx) {
755
768
  if (msg.type === "set_project_session_visibility_default") {
756
769
  sessionVisibility.handleDefaultChange({
757
770
  slug: slug, isMate: isMate, osUsers: osUsers, usersModule: usersModule, getProjectAccess: getProjectAccess,
758
- opts: opts, sendTo: sendTo,
771
+ getTargetAccess: typeof opts.onGetProjectAccess === "function" ? opts.onGetProjectAccess : null,
772
+ canAccessProjectSlug: canAccessProjectSlug, opts: opts, sendTo: sendTo,
759
773
  }, ws, msg);
760
774
  return true;
761
775
  }
762
776
 
763
777
  if (sessionFolders.handleMessage(ws, msg)) return true;
764
778
  if (sessionCreation.handleMessage(ws, msg)) return true;
779
+ if (vendorUsage.handleMessage(ws, msg)) return true;
765
780
 
766
781
  if (msg.type === "bulk_delete_sessions") {
767
782
  sessionDelete.bulkDeleteSessions(ws, msg);
@@ -804,6 +819,8 @@ function attachSessions(ctx) {
804
819
  // adoptOrphanedCliSessions). Click a session in the sidebar instead.
805
820
 
806
821
  if (msg.type === "switch_session") {
822
+ var paneActivation = require('./pane-session-activation').begin(ctx, ws, msg);
823
+ if (paneActivation === false) return true;
807
824
  if (msg.id && sm.sessions.has(msg.id)) {
808
825
  // resolveSessionForView sets runtimeMode / runtimeTerminalId /
809
826
  // tuiSuspended (and hydrates the transcript) before sm.switchSession
@@ -817,6 +834,7 @@ function attachSessions(ctx) {
817
834
  resolveSessionForView(xmTarget, ws);
818
835
  }
819
836
  var switchTargetSess = sm.sessions.get(msg.id);
837
+ if (paneActivation && !paneActivation.validate()) return true;
820
838
  var switchTargetVendor = switchTargetSess && (switchTargetSess.vendor || sm.defaultVendor || "claude");
821
839
  if (switchTargetSess && !switchTargetSess.effort) {
822
840
  switchTargetSess.effort = yoke.clampEffort(
@@ -829,20 +847,20 @@ function attachSessions(ctx) {
829
847
  if (usersModule.isMultiUser() && ws._clayUser) {
830
848
  var switchTarget = sm.sessions.get(msg.id);
831
849
  if (!usersModule.canAccessSession(ws._clayUser.id, switchTarget, { visibility: "public" })) return true;
832
- ws._clayActiveSession = msg.id;
833
- sm.switchSession(msg.id, ws, hydrateImageRefs);
850
+ sm.switchSession(msg.id, ws, hydrateImageRefs, paneActivation && paneActivation.send, paneActivation && paneActivation.validate);
834
851
  if (!ws._clayPane) broadcastPresence();
835
852
  } else {
836
- ws._clayActiveSession = msg.id;
837
- sm.switchSession(msg.id, ws, hydrateImageRefs);
853
+ sm.switchSession(msg.id, ws, hydrateImageRefs, paneActivation && paneActivation.send, paneActivation && paneActivation.validate);
838
854
  }
839
855
  // Send per-session context sources
840
856
  if (typeof loadContextSources === "function") {
841
857
  var switchedSources = loadContextSources(slug, msg.id);
842
- sendTo(ws, { type: "context_sources_state", active: switchedSources });
858
+ if (paneActivation) paneActivation.send({ type: "context_sources_state", active: switchedSources });
859
+ else sendTo(ws, { type: "context_sources_state", active: switchedSources });
843
860
  }
844
861
  var swPresKey = ws._clayUser ? ws._clayUser.id : "_default";
845
862
  if (!ws._clayPane) userPresence.setPresence(slug, swPresKey, userPresence.sessionIdForPersistence(switchTargetSess), null);
863
+ if (paneActivation) paneActivation.finish();
846
864
  }
847
865
  return true;
848
866
  }
@@ -1084,6 +1102,12 @@ function attachSessions(ctx) {
1084
1102
 
1085
1103
  if (msg.type === "stop") {
1086
1104
  var session = getSessionForWs(ws);
1105
+ if (msg.sessionId != null || msg.sessionOriginId != null) {
1106
+ session = sm.sessions.get(msg.sessionId);
1107
+ if (!session || session.sessionOriginId !== msg.sessionOriginId ||
1108
+ !usersModule.isMultiUser() && session.ownerId ||
1109
+ usersModule.isMultiUser() && (!ws._clayUser || !usersModule.canAccessSession(ws._clayUser.id, session, getProjectAccess()))) return true;
1110
+ }
1087
1111
  stopSession(session, ws && ws._clayUser);
1088
1112
  return true;
1089
1113
  }
@@ -1465,6 +1489,7 @@ function attachSessions(ctx) {
1465
1489
  var sessionId = sm.permissionRequestIndex[requestId];
1466
1490
  var session = sessionId ? sm.sessions.get(sessionId) : getSessionForWs(ws);
1467
1491
  if (!session) return true;
1492
+ if (ws._clayPaneActivation && session.localId !== ws._clayPaneActivation.sessionId) return true;
1468
1493
  var pending = session.pendingPermissions[requestId];
1469
1494
  if (!pending) return true;
1470
1495
  delete sm.permissionRequestIndex[requestId];
@@ -1646,8 +1671,9 @@ function attachSessions(ctx) {
1646
1671
  parentDir = path.dirname(absTarget);
1647
1672
  prefix = path.basename(absTarget).toLowerCase();
1648
1673
  }
1649
- if (opts.canUsePath && !opts.canUsePath(ws._clayUser && ws._clayUser.id, parentDir)) {
1650
- sendTo(ws, { type: "browse_dir_result", entries: [], error: "Directory access is not permitted" });
1674
+ var canList = function (candidate) { return !opts.canUsePath || opts.canUsePath(ws._clayUser && ws._clayUser.id, candidate); };
1675
+ if (!canList(parentDir)) {
1676
+ sendTo(ws, startReply(msg, { type: "browse_dir_result", path: msg.path, entries: [], error: "Directory access is not permitted" }));
1651
1677
  return true;
1652
1678
  }
1653
1679
  try {
@@ -1663,9 +1689,9 @@ function attachSessions(ctx) {
1663
1689
  dirEntries.push({ name: d.name, path: path.join(parentDir, d.name) });
1664
1690
  }
1665
1691
  dirEntries.sort(function (a, b) { return a.name.localeCompare(b.name); });
1666
- sendTo(ws, { type: "browse_dir_result", path: msg.path, entries: dirEntries });
1692
+ sendTo(ws, Object.assign(browseMeta(msg, parentDir, prefix, canList), { type: "browse_dir_result", path: msg.path, entries: dirEntries }));
1667
1693
  } catch (e) {
1668
- sendTo(ws, { type: "browse_dir_result", path: msg.path, entries: [], error: e.message });
1694
+ sendTo(ws, startReply(msg, { type: "browse_dir_result", path: msg.path, entries: [], error: e.message }));
1669
1695
  }
1670
1696
  return true;
1671
1697
  }
@@ -1675,24 +1701,24 @@ function attachSessions(ctx) {
1675
1701
  var addPath = (msg.path || "").replace(/^~/, require("./config").REAL_HOME);
1676
1702
  var addAbs = path.resolve(addPath);
1677
1703
  if (opts.canUsePath && !opts.canUsePath(ws._clayUser && ws._clayUser.id, addAbs)) {
1678
- sendTo(ws, { type: "add_project_result", ok: false, error: "Directory access is not permitted" });
1704
+ sendTo(ws, startReply(msg, { type: "add_project_result", ok: false, error: "Directory access is not permitted" }));
1679
1705
  return true;
1680
1706
  }
1681
1707
  try {
1682
1708
  var addStat = fs.statSync(addAbs);
1683
1709
  if (!addStat.isDirectory()) {
1684
- sendTo(ws, { type: "add_project_result", ok: false, error: "Not a directory" });
1710
+ sendTo(ws, startReply(msg, { type: "add_project_result", ok: false, error: "Not a directory" }));
1685
1711
  return true;
1686
1712
  }
1687
1713
  } catch (e) {
1688
- sendTo(ws, { type: "add_project_result", ok: false, error: "Directory not found" });
1714
+ sendTo(ws, startReply(msg, { type: "add_project_result", ok: false, error: "Directory not found" }));
1689
1715
  return true;
1690
1716
  }
1691
1717
  if (typeof opts.onAddProject === "function") {
1692
1718
  var result = opts.onAddProject(addAbs, ws._clayUser);
1693
- sendTo(ws, Object.assign({}, result, { type: "add_project_result" }));
1719
+ sendTo(ws, startReply(msg, Object.assign({}, result, { type: "add_project_result" })));
1694
1720
  } else {
1695
- sendTo(ws, { type: "add_project_result", ok: false, error: "Not supported" });
1721
+ sendTo(ws, startReply(msg, { type: "add_project_result", ok: false, error: "Not supported" }));
1696
1722
  }
1697
1723
  return true;
1698
1724
  }
@@ -1702,7 +1728,7 @@ function attachSessions(ctx) {
1702
1728
  if (ws._clayUser) {
1703
1729
  var cpPerms = usersModule.getEffectivePermissions(ws._clayUser, osUsers);
1704
1730
  if (!cpPerms.createProject) {
1705
- sendTo(ws, { type: "add_project_result", ok: false, error: "You do not have permission to create projects" });
1731
+ sendTo(ws, startReply(msg, { type: "add_project_result", ok: false, error: "You do not have permission to create projects" }));
1706
1732
  return true;
1707
1733
  }
1708
1734
  }
@@ -1710,14 +1736,14 @@ function attachSessions(ctx) {
1710
1736
  if (msg.type === "create_project") {
1711
1737
  var createName = (msg.name || "").trim();
1712
1738
  if (!createName || !/^[a-zA-Z0-9_-]+$/.test(createName)) {
1713
- sendTo(ws, { type: "add_project_result", ok: false, error: "Invalid name. Use only letters, numbers, dashes, and underscores." });
1739
+ sendTo(ws, startReply(msg, { type: "add_project_result", ok: false, error: "Invalid name. Use only letters, numbers, dashes, and underscores." }));
1714
1740
  return true;
1715
1741
  }
1716
1742
  if (typeof opts.onCreateProject === "function") {
1717
1743
  var createResult = opts.onCreateProject(createName, ws._clayUser);
1718
- sendTo(ws, Object.assign({}, createResult, { type: "add_project_result" }));
1744
+ sendTo(ws, startReply(msg, Object.assign({}, createResult, { type: "add_project_result" })));
1719
1745
  } else {
1720
- sendTo(ws, { type: "add_project_result", ok: false, error: "Not supported" });
1746
+ sendTo(ws, startReply(msg, { type: "add_project_result", ok: false, error: "Not supported" }));
1721
1747
  }
1722
1748
  return true;
1723
1749
  }
@@ -1726,16 +1752,16 @@ function attachSessions(ctx) {
1726
1752
  if (msg.type === "clone_project") {
1727
1753
  var cloneUrl = (msg.url || "").trim();
1728
1754
  if (!cloneUrl || (!/^https?:\/\//.test(cloneUrl) && !/^git@/.test(cloneUrl))) {
1729
- sendTo(ws, { type: "add_project_result", ok: false, error: "Invalid URL. Use https:// or git@ format." });
1755
+ sendTo(ws, startReply(msg, { type: "add_project_result", ok: false, error: "Invalid URL. Use https:// or git@ format." }));
1730
1756
  return true;
1731
1757
  }
1732
- sendTo(ws, { type: "clone_project_progress", status: "cloning" });
1758
+ sendTo(ws, startReply(msg, { type: "clone_project_progress", status: "cloning" }));
1733
1759
  if (typeof opts.onCloneProject === "function") {
1734
1760
  opts.onCloneProject(cloneUrl, ws._clayUser, function (cloneResult) {
1735
- sendTo(ws, { type: "add_project_result", ok: cloneResult.ok, slug: cloneResult.slug, error: cloneResult.error });
1761
+ sendTo(ws, startReply(msg, { type: "add_project_result", ok: cloneResult.ok, slug: cloneResult.slug, error: cloneResult.error }));
1736
1762
  });
1737
1763
  } else {
1738
- sendTo(ws, { type: "add_project_result", ok: false, error: "Not supported" });
1764
+ sendTo(ws, startReply(msg, { type: "add_project_result", ok: false, error: "Not supported" }));
1739
1765
  }
1740
1766
  return true;
1741
1767
  }
@@ -1848,30 +1874,41 @@ function attachSessions(ctx) {
1848
1874
 
1849
1875
  // --- Set project title (rename) ---
1850
1876
  if (msg.type === "set_project_title") {
1877
+ var titleRequestId = boundedRequestId(msg);
1851
1878
  if (!msg.slug) {
1852
- sendTo(ws, { type: "set_project_title_result", ok: false, error: "Missing slug" });
1879
+ sendTo(ws, { type: "set_project_title_result", ok: false, error: "Missing slug", requestId: titleRequestId });
1880
+ return true;
1881
+ }
1882
+ // The target is named by the client; it must be a project this account can access.
1883
+ if (!canAccessProjectSlug(ws, msg.slug)) {
1884
+ sendTo(ws, { type: "set_project_title_result", ok: false, error: "Project access is not permitted", slug: msg.slug, requestId: titleRequestId });
1853
1885
  return true;
1854
1886
  }
1855
1887
  if (typeof opts.onSetProjectTitle === "function") {
1856
1888
  var titleResult = opts.onSetProjectTitle(msg.slug, msg.title || null);
1857
- sendTo(ws, Object.assign({}, titleResult, { type: "set_project_title_result", slug: msg.slug }));
1889
+ sendTo(ws, Object.assign({}, titleResult, { type: "set_project_title_result", slug: msg.slug, requestId: titleRequestId }));
1858
1890
  } else {
1859
- sendTo(ws, { type: "set_project_title_result", ok: false, error: "Not supported" });
1891
+ sendTo(ws, { type: "set_project_title_result", ok: false, error: "Not supported", requestId: titleRequestId });
1860
1892
  }
1861
1893
  return true;
1862
1894
  }
1863
1895
 
1864
1896
  // --- Set project icon (emoji) ---
1865
1897
  if (msg.type === "set_project_icon") {
1898
+ var iconRequestId = boundedRequestId(msg);
1866
1899
  if (!msg.slug) {
1867
- sendTo(ws, { type: "set_project_icon_result", ok: false, error: "Missing slug" });
1900
+ sendTo(ws, { type: "set_project_icon_result", ok: false, error: "Missing slug", requestId: iconRequestId });
1901
+ return true;
1902
+ }
1903
+ if (!canAccessProjectSlug(ws, msg.slug)) {
1904
+ sendTo(ws, { type: "set_project_icon_result", ok: false, error: "Project access is not permitted", slug: msg.slug, requestId: iconRequestId });
1868
1905
  return true;
1869
1906
  }
1870
1907
  if (typeof opts.onSetProjectIcon === "function") {
1871
1908
  var iconResult = opts.onSetProjectIcon(msg.slug, msg.icon || null);
1872
- sendTo(ws, { type: "set_project_icon_result", ok: iconResult.ok, slug: msg.slug, error: iconResult.error });
1909
+ sendTo(ws, { type: "set_project_icon_result", ok: iconResult.ok, slug: msg.slug, error: iconResult.error, requestId: iconRequestId });
1873
1910
  } else {
1874
- sendTo(ws, { type: "set_project_icon_result", ok: false, error: "Not supported" });
1911
+ sendTo(ws, { type: "set_project_icon_result", ok: false, error: "Not supported", requestId: iconRequestId });
1875
1912
  }
1876
1913
  return true;
1877
1914
  }
@@ -1997,6 +2034,18 @@ function attachSessions(ctx) {
1997
2034
  }
1998
2035
 
1999
2036
  return {
2037
+ invalidateVendorUsage: function () { vendorUsage.invalidateAll(); },
2038
+ // Canonical read-only inspection: the caller has already proven the owner and
2039
+ // project access; the OS user is re-derived from the live user record.
2040
+ readVendorUsage: function (request) {
2041
+ var installed = null;
2042
+ if (Array.isArray(sm.installedVendors)) {
2043
+ installed = Object.create(null);
2044
+ sm.installedVendors.forEach(function (vendor) { installed[vendor] = true; });
2045
+ }
2046
+ var linuxUser = osUsers && request.owner !== "default" ? ctx.getLinuxUserForSession({ ownerId: request.owner }) : null;
2047
+ return vendorUsage.readForOwner({ owner: request.owner, linuxUser: linuxUser, installed: installed, isCurrent: request.isCurrent });
2048
+ },
2000
2049
  handleSessionsMessage: handleSessionsMessage,
2001
2050
  resolveSessionForView: resolveSessionForView,
2002
2051
  sendSessionFoldersState: sessionFolders.sendStateTo,
@@ -0,0 +1,29 @@
1
+ // Shared reply helpers for the project-start dialog (browse_dir and add/create/clone results).
2
+ // A string requestId on the request is echoed on its reply so the client can drop stale answers.
3
+
4
+ var path = require("path");
5
+
6
+ function requestIdOf(msg) {
7
+ return msg && typeof msg.requestId === "string" && msg.requestId.length <= 64 ? msg.requestId : undefined;
8
+ }
9
+
10
+ // Canonical listing metadata. `dir` is the directory whose children are listed, `prefix` is
11
+ // non-empty when the requested path did not exist or was not a directory, and `parent` is set
12
+ // only when the caller may list the parent as well.
13
+ function browseMeta(msg, dir, prefix, canList) {
14
+ var parent = path.dirname(dir);
15
+ return {
16
+ requestId: requestIdOf(msg),
17
+ dir: dir,
18
+ prefix: prefix || "",
19
+ parent: parent !== dir && (!canList || canList(parent)) ? parent : null,
20
+ };
21
+ }
22
+
23
+ function withRequestId(msg, payload) {
24
+ var id = requestIdOf(msg);
25
+ if (id !== undefined) payload.requestId = id;
26
+ return payload;
27
+ }
28
+
29
+ module.exports = { requestIdOf: requestIdOf, browseMeta: browseMeta, withRequestId: withRequestId };
@@ -171,6 +171,8 @@ function attachVendorLogin(ctx) {
171
171
  // reused by every project, so tearing one down here would kill unrelated
172
172
  // sessions; those runtimes pick up new credentials on their own.
173
173
  function refreshVendorAuth(vendor, linuxUser) {
174
+ // Cached account usage described the previous credentials.
175
+ if (typeof ctx.onVendorAuthChanged === "function") { try { ctx.onVendorAuthChanged(vendor, linuxUser || null); } catch (e) {} }
174
176
  var adapter = adapters[vendor];
175
177
  if (!adapter || typeof adapter.shutdown !== "function") return Promise.resolve(false);
176
178
  if (adapter.shared) {
package/lib/project.js CHANGED
@@ -459,7 +459,8 @@ function createProjectContext(opts) {
459
459
  if (obj.type === "loop_registry_updated") payload.records = getHubSchedules(userId);
460
460
  else payload.schedules = getHubSchedules(userId);
461
461
  }
462
- ws.send(JSON.stringify(payload));
462
+ payload = require('./pane-session-transport').outbound(ws, payload);
463
+ if (payload) ws.send(JSON.stringify(payload));
463
464
  }
464
465
 
465
466
  function sendToAdmins(obj) {
@@ -555,7 +556,8 @@ function createProjectContext(opts) {
555
556
  var data = JSON.stringify(obj), delivered = 0;
556
557
  for (var ws of clients) {
557
558
  if (ws.readyState === 1 && canReceiveMate(ws) && canReceiveYou(ws, sessionId) && ws._clayActiveSession === sessionId) {
558
- ws.send(data); delivered++; if (onSent) onSent(ws);
559
+ var panePayload = require('./pane-session-transport').outbound(ws, obj, sessionId);
560
+ if (panePayload) { ws.send(JSON.stringify(panePayload)); delivered++; if (onSent) onSent(ws); }
559
561
  }
560
562
  }
561
563
  homeDebateClients.forEach(function (ws) {
@@ -569,7 +571,8 @@ function createProjectContext(opts) {
569
571
  var data = JSON.stringify(obj);
570
572
  for (var ws of clients) {
571
573
  if (ws !== sender && ws.readyState === 1 && canReceiveMate(ws) && canReceiveYou(ws, sessionId) && ws._clayActiveSession === sessionId) {
572
- ws.send(data);
574
+ var panePayload = require('./pane-session-transport').outbound(ws, obj, sessionId);
575
+ if (panePayload) ws.send(JSON.stringify(panePayload));
573
576
  }
574
577
  }
575
578
  }
@@ -655,7 +658,8 @@ function createProjectContext(opts) {
655
658
  var access = opts.getProjectAccess ? opts.getProjectAccess() : { visibility: "public", ownerId: projectOwnerId || null };
656
659
  if (!viewer || (opts.canAccessProjectSlug && !opts.canAccessProjectSlug(viewer.id, slug)) || !usersModule.canAccessSession(viewer.id, session, access)) return;
657
660
  }
658
- ws.send(data);
661
+ var panePayload = ws._clayPaneActivation ? require('./pane-session-transport').outbound(ws, message, session.localId) : message;
662
+ if (panePayload) ws.send(panePayload === message ? data : JSON.stringify(panePayload));
659
663
  });
660
664
  },
661
665
  authorize: function (session, actor) {
@@ -1827,6 +1831,10 @@ function createProjectContext(opts) {
1827
1831
  }
1828
1832
 
1829
1833
  function handleMessage(ws, msg) {
1834
+ return require('./pane-session-transport').run(ws, msg, dispatchMessage);
1835
+ }
1836
+
1837
+ function dispatchMessage(ws, msg) {
1830
1838
  if (!_requestAccess.permitMessage(ws, msg)) return;
1831
1839
  // --- Keep-alive (delegated to project-connection.js) ---
1832
1840
  // Answered before any routing so a ping can never be forwarded elsewhere.
@@ -2147,6 +2155,7 @@ function createProjectContext(opts) {
2147
2155
  });
2148
2156
  sm.setSessionHydrator(_sessionSkillCatalog.hydrate);
2149
2157
  var _sessions = attachSessions({
2158
+ splitStore: _splitGroups.store,
2150
2159
  adapters: adapters,
2151
2160
  isPendingQueuePaused: function (session) { return _pendingMessageQueue.isPaused(session); },
2152
2161
  cwd: cwd,
@@ -2178,6 +2187,7 @@ function createProjectContext(opts) {
2178
2187
  resolveDefaultAi: opts.resolveDefaultAi,
2179
2188
  resolveMateSessionDefaults: opts.resolveMateSessionDefaults,
2180
2189
  getVendorAvailability: function (ws) { return _models.getVendorAvailability(ws); },
2190
+ getLinuxUserForWs: getLinuxUserForWs,
2181
2191
  canCreateScheduledTaskInterview: function (ws, session) { return !!(_scheduledTasks && _scheduledTasks.canCreateInterview(ws, session)); },
2182
2192
  canStartScheduledTaskInterview: function (ws, session) { return !!(_scheduledTasks && _scheduledTasks.canStartInterview(ws, session)); },
2183
2193
  onHumanPairStop: function (session) {
@@ -2390,6 +2400,7 @@ function createProjectContext(opts) {
2390
2400
 
2391
2401
  // --- Vendor login flow (delegated to project-vendor-login.js) ---
2392
2402
  var _vendorLogin = attachVendorLogin({
2403
+ onVendorAuthChanged: function () { _sessions.invalidateVendorUsage(); },
2393
2404
  requestAccess: _requestAccess,
2394
2405
  clients: clients,
2395
2406
  slug: slug,
@@ -3031,6 +3042,7 @@ function createProjectContext(opts) {
3031
3042
  requestProductControlReview: _productControl.requestProductControlReview,
3032
3043
  applyProductSessionAction: _productControl.applyProductSessionAction,
3033
3044
  inspectProductSessionQueue: _productControl.inspectProductSessionQueue,
3045
+ readVendorUsage: function (request) { return _sessions.readVendorUsage(request); },
3034
3046
  inspectProductSessionRuntime: _sessions.sessionRuntimeActions.inspect,
3035
3047
  inspectMateLifecycleRuntime: _sessions.sessionRuntimeActions.inspectLifecycle,
3036
3048
  inspectDelegatedFollowUp: _delegatedFollowUp.inspectDelegatedFollowUp,
package/lib/public/app.js CHANGED
@@ -31,10 +31,11 @@ import { initMateMemory, renderMemoryList, hideMemory } from './modules/mate-mem
31
31
  import { initRewind, setRewindMode, showRewindModal, clearPendingRewindUuid, addRewindButton, onRewindComplete, onRewindError } from './modules/rewind.js';
32
32
  import { initNotifications, showDoneNotification, playDoneSound, isNotifAlertEnabled, isNotifSoundEnabled } from './modules/notifications.js';
33
33
  import { initInput, clearPendingImages, handleInputSync, autoResize, builtinCommands, sendMessage, hasSendableContent, setScheduleBtnDisabled, setScheduleDelayMs, clearScheduleDelay } from './modules/input.js';
34
- import { initQrCode, triggerShare } from './modules/qrcode.js';
34
+ import { initQrCode } from './modules/qrcode.js';
35
35
  import { initFileBrowser, loadRootDirectory, reopenFileViewer, refreshTree, handleFsList, handleFsRead, handleDirChanged, refreshIfOpen, handleFileChanged, handleFileHistory, handleGitDiff, handleFileAt, getPendingNavigate, closeFileViewer, resetFileBrowser } from './modules/filebrowser.js';
36
36
  import { initGitPanel } from './modules/git-panel.js';
37
37
  import { initWorkerPaneLock } from './modules/worker-pane-lock.js';
38
+ import { initPaneActivation } from './modules/pane-activation.js';
38
39
  import { initAutonomousRun } from './modules/autonomous-run.js';
39
40
  import { initLoopInterview } from './modules/loop-interview.js';
40
41
  import { initTerminal, openTerminal, closeTerminal, resetTerminals, handleTermList, handleTermCreated, handleTermOutput, handleTermResized, handleTermExited, handleTermClosed, sendTerminalCommand } from './modules/terminal.js';
@@ -45,6 +46,7 @@ import { initTheme, getThemeColor, getComputedVar, onThemeChange, getCurrentThem
45
46
  import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUserQuestion, markAskUserAnswered, renderPermissionRequest, markPermissionResolved, markPermissionCancelled, renderElicitationRequest, markElicitationResolved, renderPlanBanner, renderPlanCard, handleTodoWrite, handleTaskCreate, handleTaskUpdate, startThinking, appendThinking, stopThinking, resetThinkingGroup, createToolItem, updateToolExecuting, updateToolResult, markAllToolsDone, addTurnMeta, resetTurnMetaCost, enableMainInput, getTools, getPlanContent, setPlanContent, isPlanFilePath, getTodoTools, updateSubagentActivity, addSubagentToolEntry, markSubagentDone, updateSubagentProgress, initSubagentStop, closeToolGroup, removeToolFromGroup } from './modules/tools.js';
46
47
  import { initServerSettings, updateSettingsStats, updateDaemonConfig, handleSetPinResult, handleKeepAwakeChanged, handleAutoContinueChanged, handleRestartResult, handleShutdownResult, handleSharedEnv, handleSharedEnvSaved } from './modules/server-settings.js';
47
48
  import { initProjectSettings } from './modules/project-settings.js';
49
+ import { initSettingsShell, openSettings } from './modules/settings-shell.js';
48
50
  import { initSkills, handleSkillInstalled, handleSkillUninstalled } from './modules/skills.js';
49
51
  import { initMcp } from './modules/mcp-ui.js';
50
52
  import { initScheduler, resetScheduler, handleLoopRegistryUpdated, handleScheduleRunStarted, handleScheduleRunFinished, handleLoopScheduled, openSchedulerToTab, isSchedulerOpen, closeScheduler, enterCraftingMode, exitCraftingMode, handleLoopRegistryFiles, getUpcomingSchedules } from './modules/scheduler.js';
@@ -62,6 +64,7 @@ import { initProjectLogs, closeProjectLogs } from './modules/project-logs.js';
62
64
  import { initProjectSwitcher } from './modules/project-switcher.js';
63
65
  import { initSplitView } from './modules/split-view.js';
64
66
  import { initPaneBridge } from './modules/pane-bridge.js';
67
+ import { initPaneAppearanceBridge } from './modules/pane-appearance-bridge.js';
65
68
  import { initAdmin, checkAdminAccess } from './modules/admin.js';
66
69
  import { initSessionSearch, toggleSearch, closeSearch, isSearchOpen, handleFindInSessionResults, onHistoryPrepended as onSessionSearchHistoryPrepended } from './modules/session-search.js';
67
70
  import { initTooltips, registerTooltip } from './modules/tooltip.js';
@@ -71,10 +74,15 @@ import { initShellCommand } from './modules/shell-command.js';
71
74
  import { initConnection, connect as _connConnect, setStatus as _connSetStatus, scheduleReconnect as _connScheduleReconnect, cancelReconnect as _connCancelReconnect } from './modules/app-connection.js';
72
75
  import { processMessage as _msgProcessMessage } from './modules/app-messages.js';
73
76
  import { getWs as _getWsRef, setWs as _setWsRef } from './modules/ws-ref.js';
77
+ import { initialProjectStart } from './modules/project-start-state.js';
78
+ import { emptyFolderState } from './modules/project-start-folder-state.js';
79
+ import { openAddProjectModal, initProjectStart } from './modules/project-start-dialog.js';
74
80
  import { initProjectsHub, showProjectsHub, hideProjectsHub } from './modules/projects-hub.js';
75
81
  import { initHomeSurfaceBoot } from './modules/home-surface-boot.js';
76
82
  import { rememberHomePrimarySurface } from './modules/home-surface.js';
77
83
  import { initRateLimit, handleRateLimitEvent as _rlHandleRateLimitEvent, updateRateLimitUsage as _rlUpdateRateLimitUsage, handleFastModeState as _rlHandleFastModeState, resetRateLimitState } from './modules/app-rate-limit.js';
84
+ import { initVendorUsageOverview } from './modules/vendor-usage-overview.js';
85
+ import { initGithubStar } from './modules/github-star.js';
78
86
  import { initCursors, handleRemoteCursorMove as _curHandleRemoteCursorMove, handleRemoteCursorLeave as _curHandleRemoteCursorLeave, handleRemoteSelection as _curHandleRemoteSelection, clearRemoteCursors as _curClearRemoteCursors, initCursorToggle } from './modules/app-cursors.js';
79
87
  import { initDefaultAi } from './modules/default-ai.js';
80
88
  import { requestDefaultVendor } from './modules/default-vendor.js';
@@ -96,7 +104,7 @@ import { initProjectMateCreationWorkspace } from './modules/project-mate-creatio
96
104
  import { initProjectDebatePlanningWorkspace } from './modules/project-debate-planning-workspace.js';
97
105
  import { initProjectSessionNavigation } from './modules/project-session-navigation.js';
98
106
  import { initPanels, updateConfigChip as _panUpdateConfigChip, getModelEffortLevels as _panGetModelEffortLevels, accumulateUsage as _panAccumulateUsage, updateUsagePanel as _panUpdateUsagePanel, resetUsage as _panResetUsage, toggleUsagePanel as _panToggleUsagePanel, formatTokens as _panFormatTokens, updateStatusPanel as _panUpdateStatusPanel, requestProcessStats as _panRequestProcessStats, toggleStatusPanel as _panToggleStatusPanel, accumulateContext as _panAccumulateContext, updateContextPanel as _panUpdateContextPanel, resetContext as _panResetContext, resetContextData as _panResetContextData, minimizeContext as _panMinimizeContext, expandContext as _panExpandContext, toggleContextPanel as _panToggleContextPanel, getContextView as _panGetContextView, renderCtxPopover as _panRenderCtxPopover, hideCtxPopover as _panHideCtxPopover, formatBytes as _panFormatBytes, formatUptime as _panFormatUptime, getModelSupportsEffort as _panGetModelSupportsEffort, getSessionUsage, setSessionUsage, getContextData, setContextData, setContextView as _panSetContextView, applyContextView as _panApplyContextView } from './modules/app-panels.js';
99
- import { initProjects, updateProjectList as _projUpdateProjectList, renderProjectList as _projRenderProjectList, renderTopbarPresence as _projRenderTopbarPresence, switchProject as _projSwitchProject, resetClientState as _projResetClientState, confirmRemoveProject as _projConfirmRemoveProject, handleRemoveProjectCheckResult as _projHandleRemoveProjectCheckResult, handleRemoveProjectResult as _projHandleRemoveProjectResult, openAddProjectModal as _projOpenAddProjectModal, closeAddProjectModal as _projCloseAddProjectModal, handleBrowseDirResult as _projHandleBrowseDirResult, handleAddProjectResult as _projHandleAddProjectResult, handleCloneProgress as _projHandleCloneProgress, showUpdateAvailable as _projShowUpdateAvailable, getCachedProjects, setCachedProjects, getCachedProjectCount, getCachedRemovedProjects, setCachedRemovedProjects } from './modules/app-projects.js';
107
+ import { updateProjectList as _projUpdateProjectList, renderProjectList as _projRenderProjectList, renderTopbarPresence as _projRenderTopbarPresence, switchProject as _projSwitchProject, resetClientState as _projResetClientState, confirmRemoveProject as _projConfirmRemoveProject, handleRemoveProjectCheckResult as _projHandleRemoveProjectCheckResult, handleRemoveProjectResult as _projHandleRemoveProjectResult, showUpdateAvailable as _projShowUpdateAvailable, getCachedProjects, setCachedProjects, getCachedProjectCount, getCachedRemovedProjects, setCachedRemovedProjects } from './modules/app-projects.js';
100
108
  import { initRendering, addToMessages as _renAddToMessages, scrollToBottom as _renScrollToBottom, forceScrollToBottom as _renForceScrollToBottom, addUserMessage as _renAddUserMessage, getMsgTime as _renGetMsgTime, shouldGroupMessage as _renShouldGroupMessage, ensureAssistantBlock as _renEnsureAssistantBlock, addCopyHandler as _renAddCopyHandler, appendDelta as _renAppendDelta, flushStreamBuffer as _renFlushStreamBuffer, finalizeAssistantBlock as _renFinalizeAssistantBlock, addSystemMessage as _renAddSystemMessage, addConflictMessage as _renAddConflictMessage, addContextOverflowMessage as _renAddContextOverflowMessage, showClaudePreThinking as _renShowClaudePreThinking, showMatePreThinking as _renShowMatePreThinking, removeMatePreThinking as _renRemoveMatePreThinking, showSuggestionChips as _renShowSuggestionChips, hideSuggestionChips as _renHideSuggestionChips, getGhostSuggestion as _renGetGhostSuggestion, getTurnCounter, setTurnCounter, getPrependAnchor, setPrependAnchor, getActivityEl, setActivityEl, getIsUserScrolledUp, setIsUserScrolledUp, getStickyBottom, armStickyBottom, disarmStickyBottom } from './modules/app-rendering.js';
101
109
  import { initDm, openDm as _dmOpenDm, enterDmMode as _dmEnterDmMode, exitDmMode as _dmExitDmMode, handleMateCreatedInApp as _dmHandleMateCreatedInApp, buildMateInterviewPrompt as _dmBuildMateInterviewPrompt, updateMateIconStatus as _dmUpdateMateIconStatus, connectMateProject as _dmConnectMateProject, disconnectMateProject as _dmDisconnectMateProject, appendDmMessage as _dmAppendDmMessage, showDmTypingIndicator as _dmShowDmTypingIndicator, handleDmSend as _dmHandleDmSend } from './modules/app-dm.js';
102
110
  import { initMention, handleMentionStart, handleMentionStream, handleMentionDone, handleMentionError, handleMentionActivity, renderMentionUser, renderMentionResponse } from './modules/mention.js';
@@ -191,7 +199,7 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
191
199
  function updateProjectList(msg) { _projUpdateProjectList(msg); }
192
200
  function renderTopbarPresence(serverUsers) { _projRenderTopbarPresence(serverUsers); }
193
201
  function renderProjectList() { _projRenderProjectList(); }
194
- // projectListAddBtn listener -> modules/app-projects.js (initProjects)
202
+ // projectListAddBtn listener -> modules/project-start-dialog.js (initProjectStart)
195
203
 
196
204
  // Prevent Cmd+Z / Cmd+Shift+Z from triggering browser back/forward (Arc, etc.)
197
205
  // Always block browser default for Cmd+Z and manually invoke undo/redo via execCommand
@@ -279,6 +287,7 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
279
287
  createStore({
280
288
  networkSettings: { loaded: false, busy: false, confirming: false, draft: "", saved: "", status: "", errorKind: "", requestId: 0 },
281
289
  vendorLoginBrowser: null,
290
+ githubStarCount: null,
282
291
  sharedBrowser: null,
283
292
  sharedBrowsers: [],
284
293
  mcpSkillsWorkbench: {},
@@ -295,6 +304,16 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
295
304
  paneMode: paneMode,
296
305
  paneSessionId: paneSessionId,
297
306
  panePinPending: paneMode && !!paneSessionId,
307
+ paneCommittedBinding: null, panePendingBinding: null, paneReady: false, paneSuspended: false,
308
+ paneActivationGeneration: 0, paneActivationTimer: null, panePointerBinding: null, paneRichDrafts: {},
309
+ sessionRichDrafts: {},
310
+ sessionActionEpoch: 0, composerUploadingCount: 0, deferredSessionSwitch: null,
311
+ paneCallbackBinding: null, paneActionEvents: [], paneHeldKeys: {}, rewindAction: null, handoffAction: null,
312
+ authIdentityLoaded: false,
313
+ splitIdentityBlockedSocket: null,
314
+ splitIdentityHydrated: false,
315
+ paneActivationUnsupported: false,
316
+ paneBindingSessions: [],
298
317
  splitPanes: null,
299
318
  splitGroups: [],
300
319
  sessionFolders: null,
@@ -320,6 +339,7 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
320
339
  replayingHistory: false,
321
340
  historyReplayBatch: null,
322
341
  pendingSessionEntryNavigation: null,
342
+ sessionEntryHighlight: null,
323
343
  sessionNavigationRequestId: null,
324
344
  projectName: projectName,
325
345
  cwd: "",
@@ -408,7 +428,8 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
408
428
  projectListLoaded: false,
409
429
  projectsAccessLoaded: false,
410
430
  projectsHubRestoreRequested: false,
411
- projectsShowGuide: false,
431
+ projectStart: initialProjectStart(),
432
+ projectStartFolder: emptyFolderState(),
412
433
  onboardingStep: 'welcome',
413
434
  onboardingIntent: null,
414
435
  currentProjectOwnerId: null,
@@ -633,6 +654,8 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
633
654
  vendorInfo: {},
634
655
  // Rate-limit data is projected only for the displayed session/vendor.
635
656
  rateLimitState: {},
657
+ vendorUsageOverview: null,
658
+ vendorUsageOpen: null,
636
659
  currentMode: "default",
637
660
  effectivePermissionMode: null,
638
661
  permissionCapabilities: { auto: false, mcpOverride: false },
@@ -686,6 +709,7 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
686
709
 
687
710
  // --- Theme (module) ---
688
711
  initTheme();
712
+ initPaneAppearanceBridge();
689
713
 
690
714
  // --- Tooltips ---
691
715
  initTooltips();
@@ -777,8 +801,7 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
777
801
  switch (action) {
778
802
  case "createMate": startClayMateCreation(); break;
779
803
  case "openSettings":
780
- var sb = document.getElementById("server-settings-btn");
781
- if (sb) sb.click();
804
+ openSettings();
782
805
  break;
783
806
  case "showHome": showHomeHub(); break;
784
807
  }
@@ -1150,7 +1173,10 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
1150
1173
  basePath: basePath,
1151
1174
  });
1152
1175
 
1153
- // --- User settings (full-screen overlay) ---
1176
+ // --- Unified full-screen settings shell (Personal, Project, Server panes) ---
1177
+ initSettingsShell();
1178
+
1179
+ // --- User settings (personal pane) ---
1154
1180
  initUserSettings({
1155
1181
  basePath: basePath,
1156
1182
  });
@@ -1169,6 +1195,7 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
1169
1195
  if (document.body) document.body.classList.add('is-multi-user');
1170
1196
  }
1171
1197
  if (d.user && d.user.id) { store.set({ myUserId: d.user.id }); requestDefaultVendor(); }
1198
+ store.set({ authIdentityLoaded: true });
1172
1199
  store.set({ permissions: d.permissions || null, projectsAccessLoaded: true });
1173
1200
  if (d.mustChangePin) showForceChangePinOverlay();
1174
1201
  // Single-user mode: clear user strip skeletons immediately (no presence message will arrive)
@@ -1217,11 +1244,11 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
1217
1244
  }
1218
1245
  }
1219
1246
  }).catch(function () {});
1220
- // Hide server settings and update controls for non-admin users in multi-user mode
1247
+ // Hide update controls for non-admin users in multi-user mode. The settings
1248
+ // gear stays for everyone: Personal and Project settings are not admin-only,
1249
+ // and the shell only offers Server settings to administrators.
1221
1250
  checkAdminAccess().then(function (isAdmin) {
1222
1251
  if (store.get('isMultiUserMode') && !isAdmin) {
1223
- var settingsBtn = document.getElementById("server-settings-btn");
1224
- if (settingsBtn) settingsBtn.style.display = "none";
1225
1252
  var updatePill = document.getElementById("update-pill-wrap");
1226
1253
  if (updatePill) updatePill.style.display = "none";
1227
1254
  }
@@ -1249,8 +1276,6 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
1249
1276
 
1250
1277
  // --- QR code ---
1251
1278
  initQrCode();
1252
- var sharePill = document.getElementById("share-pill");
1253
- if (sharePill) sharePill.addEventListener("click", triggerShare);
1254
1279
 
1255
1280
  // --- File browser ---
1256
1281
  initFileBrowser({
@@ -1266,6 +1291,7 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
1266
1291
  initSharedBrowser();
1267
1292
  initStudioBridge();
1268
1293
  initWorkerPaneLock();
1294
+ initPaneActivation();
1269
1295
 
1270
1296
  // --- Terminal ---
1271
1297
  initTerminal({
@@ -1433,11 +1459,6 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
1433
1459
  function confirmRemoveProject(slug, name) { _projConfirmRemoveProject(slug, name); }
1434
1460
  function handleRemoveProjectCheckResult(msg) { _projHandleRemoveProjectCheckResult(msg); }
1435
1461
  function handleRemoveProjectResult(msg) { _projHandleRemoveProjectResult(msg); }
1436
- function openAddProjectModal() { _projOpenAddProjectModal(); }
1437
- function closeAddProjectModal() { _projCloseAddProjectModal(); }
1438
- function handleBrowseDirResult(msg) { _projHandleBrowseDirResult(msg); }
1439
- function handleAddProjectResult(msg) { _projHandleAddProjectResult(msg); }
1440
- function handleCloneProgress(msg) { _projHandleCloneProgress(msg); }
1441
1462
 
1442
1463
  // --- PWA install prompt -> modules/app-misc.js (set up in initMisc)
1443
1464
 
@@ -1453,9 +1474,11 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
1453
1474
 
1454
1475
  // --- Rate Limit module ---
1455
1476
  initRateLimit();
1477
+ initVendorUsageOverview();
1478
+ initGithubStar();
1456
1479
 
1457
1480
  // --- Projects module ---
1458
- initProjects();
1481
+ initProjectStart();
1459
1482
 
1460
1483
  // --- Home Hub module ---
1461
1484
  initProjectMateNavigation();
@@ -50,7 +50,7 @@ html.contextual-pane-boot #connect-overlay { display: none !important; }
50
50
  #contextual-clay [data-conversation-notice]:not([hidden]) { display: flex; align-items: center; gap: 8px; flex-shrink: 0; padding: 6px 12px; border-bottom: 1px solid var(--border-subtle); font-size: 12px; color: var(--text-secondary); }
51
51
  #contextual-clay [data-conversation-notice] span { flex: 1; }
52
52
  #contextual-clay [data-conversation-notice] button { color: var(--accent); white-space: nowrap; }
53
- #contextual-clay [data-clay-expand] span, #contextual-clay [data-mate-new] span { display: none; }
53
+ #contextual-clay [data-clay-expand] span { display: none; }
54
54
  @media (pointer: coarse) { #contextual-clay [data-mate-history] summary, #contextual-clay [data-mate-session-list] button { min-width: 44px; min-height: 44px; } }
55
55
  @starting-style {
56
56
  #contextual-clay[data-mode="popover"]:not([hidden]) { opacity: 0; transform: translateX(-4px); }
@@ -81,7 +81,7 @@ html.contextual-pane-boot #connect-overlay { display: none !important; }
81
81
  #contextual-clay button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; color: var(--text); }
82
82
  #contextual-clay button .lucide { width: 15px; height: 15px; }
83
83
  #contextual-clay iframe { border: 0; flex: 1; width: 100%; min-height: 0; background: var(--bg); }
84
- #contextual-clay [data-clay-loading]:not([hidden]) { position: absolute; inset: 0; margin: 0; padding: 18px 12px; display: block; color: var(--text-muted); font-size: 13px; background: var(--bg); pointer-events: none; }
84
+ #contextual-clay [data-clay-loading]:not([hidden]) { position: absolute; inset: 0; margin: 0; padding: 18px 12px; display: flex; z-index: 1; color: var(--text-muted); font-size: 13px; background: var(--bg); pointer-events: none; }
85
85
  body:is(.contextual-chat-pane, .contextual-clay-native) { --content-width: 760px; }
86
86
  body:is(.contextual-chat-pane, .contextual-clay-native) #messages { padding-top: 14px; }
87
87
  body:is(.contextual-chat-pane, .contextual-clay-native) #messages .msg-assistant .md-content,