clay-server 5.2.0-beta.9 → 5.2.1-beta.1

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 (109) 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/project-session-notes.js +33 -8
  9. package/lib/project-sessions.js +63 -24
  10. package/lib/project-start-protocol.js +29 -0
  11. package/lib/project-vendor-login.js +2 -0
  12. package/lib/project.js +5 -1
  13. package/lib/public/app.js +30 -18
  14. package/lib/public/css/command-palette.css +12 -2
  15. package/lib/public/css/contextual-clay.css +1 -1
  16. package/lib/public/css/filebrowser.css +4 -5
  17. package/lib/public/css/mate-loading-scene.css +21 -0
  18. package/lib/public/css/messages.css +30 -11
  19. package/lib/public/css/overlays.css +67 -256
  20. package/lib/public/css/project-mate-creation.css +5 -6
  21. package/lib/public/css/project-start.css +108 -0
  22. package/lib/public/css/projects-hub.css +53 -14
  23. package/lib/public/css/right-workbench.css +28 -0
  24. package/lib/public/css/settings-shell.css +103 -0
  25. package/lib/public/css/sidebar.css +7 -0
  26. package/lib/public/css/title-bar.css +99 -0
  27. package/lib/public/index.html +54 -66
  28. package/lib/public/modules/account-transport-settings.js +8 -2
  29. package/lib/public/modules/admin-settings-email.js +7 -2
  30. package/lib/public/modules/admin-settings-invitations.js +2 -2
  31. package/lib/public/modules/admin-settings-projects.js +2 -2
  32. package/lib/public/modules/admin-settings-ui.js +20 -4
  33. package/lib/public/modules/admin-settings-users.js +4 -4
  34. package/lib/public/modules/app-home-hub.js +2 -1
  35. package/lib/public/modules/app-messages.js +10 -2
  36. package/lib/public/modules/app-projects.js +1 -407
  37. package/lib/public/modules/app-rate-limit.js +12 -1
  38. package/lib/public/modules/clay-tool-presentation.js +1 -0
  39. package/lib/public/modules/contextual-clay.js +3 -1
  40. package/lib/public/modules/github-star.js +66 -0
  41. package/lib/public/modules/home-surface-boot.js +0 -1
  42. package/lib/public/modules/mate-loading-scene.js +17 -0
  43. package/lib/public/modules/pair-result-status.js +89 -39
  44. package/lib/public/modules/pane-appearance-bridge.js +59 -0
  45. package/lib/public/modules/profile.js +11 -3
  46. package/lib/public/modules/project-mate-creation-workspace.js +13 -4
  47. package/lib/public/modules/project-session-visibility-settings.js +19 -16
  48. package/lib/public/modules/project-settings-environment.js +12 -1
  49. package/lib/public/modules/project-settings.js +119 -129
  50. package/lib/public/modules/project-start-dialog.js +130 -0
  51. package/lib/public/modules/project-start-folder-state.js +78 -0
  52. package/lib/public/modules/project-start-folder-view.js +190 -0
  53. package/lib/public/modules/project-start-forms.js +66 -0
  54. package/lib/public/modules/project-start-path.js +60 -0
  55. package/lib/public/modules/project-start-state.js +54 -0
  56. package/lib/public/modules/project-start-submit.js +67 -0
  57. package/lib/public/modules/projects-hub-view.js +32 -44
  58. package/lib/public/modules/projects-hub.js +39 -28
  59. package/lib/public/modules/server-settings.js +36 -91
  60. package/lib/public/modules/settings-project-target.js +56 -0
  61. package/lib/public/modules/settings-select.js +1 -1
  62. package/lib/public/modules/settings-shell-registry.js +88 -0
  63. package/lib/public/modules/settings-shell-view.js +104 -0
  64. package/lib/public/modules/settings-shell.js +342 -0
  65. package/lib/public/modules/sidebar-mobile.js +8 -30
  66. package/lib/public/modules/sidebar-projects.js +2 -1
  67. package/lib/public/modules/theme.js +31 -3
  68. package/lib/public/modules/topbar-workbench-controls.js +102 -0
  69. package/lib/public/modules/user-settings-integrations.js +2 -1
  70. package/lib/public/modules/user-settings-layout.js +12 -34
  71. package/lib/public/modules/user-settings-profile.js +1 -1
  72. package/lib/public/modules/user-settings.js +18 -85
  73. package/lib/public/modules/vendor-usage-format.js +93 -0
  74. package/lib/public/modules/vendor-usage-list.js +45 -0
  75. package/lib/public/modules/vendor-usage-model.js +215 -0
  76. package/lib/public/modules/vendor-usage-overview.js +323 -0
  77. package/lib/public/style.css +13 -10
  78. package/lib/public/welcome-art/conversation-desk.png +0 -0
  79. package/lib/public/welcome-art/mate-clay.png +0 -0
  80. package/lib/public/welcome-art/project-clay.png +0 -0
  81. package/lib/sdk-bridge.js +6 -1
  82. package/lib/sdk-message-processor.js +9 -1
  83. package/lib/sdk-quota-snapshot.js +20 -2
  84. package/lib/server-github-stars.js +39 -0
  85. package/lib/server-global-ws.js +21 -17
  86. package/lib/server.js +11 -0
  87. package/lib/session-notes-mcp-server.js +26 -4
  88. package/lib/session-notes-writes.js +88 -0
  89. package/lib/session-visibility.js +20 -8
  90. package/lib/sessions.js +12 -2
  91. package/lib/vendor-usage-acp-readers.js +97 -0
  92. package/lib/vendor-usage-capabilities.js +88 -0
  93. package/lib/vendor-usage-http-readers.js +214 -0
  94. package/lib/vendor-usage-metrics.js +47 -0
  95. package/lib/vendor-usage-native-readers.js +106 -0
  96. package/lib/vendor-usage-overview.js +406 -0
  97. package/lib/vendor-usage-service.js +281 -0
  98. package/lib/ws-schema.js +4 -2
  99. package/lib/yoke/adapters/claude.js +12 -0
  100. package/lib/yoke/adapters/codex-query-contract.js +1 -0
  101. package/lib/yoke/adapters/codex.js +45 -12
  102. package/lib/yoke/claude-hook-installer.js +1 -0
  103. package/lib/yoke/codex-dynamic-tool-names.js +80 -0
  104. package/lib/yoke/codex-model-catalog.js +37 -3
  105. package/lib/yoke/muse-services.js +16 -3
  106. package/lib/yoke/providers/claude-model-descriptions.js +22 -2
  107. package/lib/yoke/vendor-registry.js +6 -2
  108. package/package.json +2 -1
  109. 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);
@@ -1656,8 +1671,9 @@ function attachSessions(ctx) {
1656
1671
  parentDir = path.dirname(absTarget);
1657
1672
  prefix = path.basename(absTarget).toLowerCase();
1658
1673
  }
1659
- if (opts.canUsePath && !opts.canUsePath(ws._clayUser && ws._clayUser.id, parentDir)) {
1660
- 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" }));
1661
1677
  return true;
1662
1678
  }
1663
1679
  try {
@@ -1673,9 +1689,9 @@ function attachSessions(ctx) {
1673
1689
  dirEntries.push({ name: d.name, path: path.join(parentDir, d.name) });
1674
1690
  }
1675
1691
  dirEntries.sort(function (a, b) { return a.name.localeCompare(b.name); });
1676
- 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 }));
1677
1693
  } catch (e) {
1678
- 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 }));
1679
1695
  }
1680
1696
  return true;
1681
1697
  }
@@ -1685,24 +1701,24 @@ function attachSessions(ctx) {
1685
1701
  var addPath = (msg.path || "").replace(/^~/, require("./config").REAL_HOME);
1686
1702
  var addAbs = path.resolve(addPath);
1687
1703
  if (opts.canUsePath && !opts.canUsePath(ws._clayUser && ws._clayUser.id, addAbs)) {
1688
- 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" }));
1689
1705
  return true;
1690
1706
  }
1691
1707
  try {
1692
1708
  var addStat = fs.statSync(addAbs);
1693
1709
  if (!addStat.isDirectory()) {
1694
- 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" }));
1695
1711
  return true;
1696
1712
  }
1697
1713
  } catch (e) {
1698
- 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" }));
1699
1715
  return true;
1700
1716
  }
1701
1717
  if (typeof opts.onAddProject === "function") {
1702
1718
  var result = opts.onAddProject(addAbs, ws._clayUser);
1703
- sendTo(ws, Object.assign({}, result, { type: "add_project_result" }));
1719
+ sendTo(ws, startReply(msg, Object.assign({}, result, { type: "add_project_result" })));
1704
1720
  } else {
1705
- 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" }));
1706
1722
  }
1707
1723
  return true;
1708
1724
  }
@@ -1712,7 +1728,7 @@ function attachSessions(ctx) {
1712
1728
  if (ws._clayUser) {
1713
1729
  var cpPerms = usersModule.getEffectivePermissions(ws._clayUser, osUsers);
1714
1730
  if (!cpPerms.createProject) {
1715
- 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" }));
1716
1732
  return true;
1717
1733
  }
1718
1734
  }
@@ -1720,14 +1736,14 @@ function attachSessions(ctx) {
1720
1736
  if (msg.type === "create_project") {
1721
1737
  var createName = (msg.name || "").trim();
1722
1738
  if (!createName || !/^[a-zA-Z0-9_-]+$/.test(createName)) {
1723
- 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." }));
1724
1740
  return true;
1725
1741
  }
1726
1742
  if (typeof opts.onCreateProject === "function") {
1727
1743
  var createResult = opts.onCreateProject(createName, ws._clayUser);
1728
- sendTo(ws, Object.assign({}, createResult, { type: "add_project_result" }));
1744
+ sendTo(ws, startReply(msg, Object.assign({}, createResult, { type: "add_project_result" })));
1729
1745
  } else {
1730
- 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" }));
1731
1747
  }
1732
1748
  return true;
1733
1749
  }
@@ -1736,16 +1752,16 @@ function attachSessions(ctx) {
1736
1752
  if (msg.type === "clone_project") {
1737
1753
  var cloneUrl = (msg.url || "").trim();
1738
1754
  if (!cloneUrl || (!/^https?:\/\//.test(cloneUrl) && !/^git@/.test(cloneUrl))) {
1739
- 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." }));
1740
1756
  return true;
1741
1757
  }
1742
- sendTo(ws, { type: "clone_project_progress", status: "cloning" });
1758
+ sendTo(ws, startReply(msg, { type: "clone_project_progress", status: "cloning" }));
1743
1759
  if (typeof opts.onCloneProject === "function") {
1744
1760
  opts.onCloneProject(cloneUrl, ws._clayUser, function (cloneResult) {
1745
- 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 }));
1746
1762
  });
1747
1763
  } else {
1748
- 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" }));
1749
1765
  }
1750
1766
  return true;
1751
1767
  }
@@ -1858,30 +1874,41 @@ function attachSessions(ctx) {
1858
1874
 
1859
1875
  // --- Set project title (rename) ---
1860
1876
  if (msg.type === "set_project_title") {
1877
+ var titleRequestId = boundedRequestId(msg);
1861
1878
  if (!msg.slug) {
1862
- 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 });
1863
1885
  return true;
1864
1886
  }
1865
1887
  if (typeof opts.onSetProjectTitle === "function") {
1866
1888
  var titleResult = opts.onSetProjectTitle(msg.slug, msg.title || null);
1867
- 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 }));
1868
1890
  } else {
1869
- 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 });
1870
1892
  }
1871
1893
  return true;
1872
1894
  }
1873
1895
 
1874
1896
  // --- Set project icon (emoji) ---
1875
1897
  if (msg.type === "set_project_icon") {
1898
+ var iconRequestId = boundedRequestId(msg);
1876
1899
  if (!msg.slug) {
1877
- 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 });
1878
1905
  return true;
1879
1906
  }
1880
1907
  if (typeof opts.onSetProjectIcon === "function") {
1881
1908
  var iconResult = opts.onSetProjectIcon(msg.slug, msg.icon || null);
1882
- 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 });
1883
1910
  } else {
1884
- 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 });
1885
1912
  }
1886
1913
  return true;
1887
1914
  }
@@ -2007,6 +2034,18 @@ function attachSessions(ctx) {
2007
2034
  }
2008
2035
 
2009
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
+ },
2010
2049
  handleSessionsMessage: handleSessionsMessage,
2011
2050
  resolveSessionForView: resolveSessionForView,
2012
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
@@ -658,7 +658,8 @@ function createProjectContext(opts) {
658
658
  var access = opts.getProjectAccess ? opts.getProjectAccess() : { visibility: "public", ownerId: projectOwnerId || null };
659
659
  if (!viewer || (opts.canAccessProjectSlug && !opts.canAccessProjectSlug(viewer.id, slug)) || !usersModule.canAccessSession(viewer.id, session, access)) return;
660
660
  }
661
- 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));
662
663
  });
663
664
  },
664
665
  authorize: function (session, actor) {
@@ -2186,6 +2187,7 @@ function createProjectContext(opts) {
2186
2187
  resolveDefaultAi: opts.resolveDefaultAi,
2187
2188
  resolveMateSessionDefaults: opts.resolveMateSessionDefaults,
2188
2189
  getVendorAvailability: function (ws) { return _models.getVendorAvailability(ws); },
2190
+ getLinuxUserForWs: getLinuxUserForWs,
2189
2191
  canCreateScheduledTaskInterview: function (ws, session) { return !!(_scheduledTasks && _scheduledTasks.canCreateInterview(ws, session)); },
2190
2192
  canStartScheduledTaskInterview: function (ws, session) { return !!(_scheduledTasks && _scheduledTasks.canStartInterview(ws, session)); },
2191
2193
  onHumanPairStop: function (session) {
@@ -2398,6 +2400,7 @@ function createProjectContext(opts) {
2398
2400
 
2399
2401
  // --- Vendor login flow (delegated to project-vendor-login.js) ---
2400
2402
  var _vendorLogin = attachVendorLogin({
2403
+ onVendorAuthChanged: function () { _sessions.invalidateVendorUsage(); },
2401
2404
  requestAccess: _requestAccess,
2402
2405
  clients: clients,
2403
2406
  slug: slug,
@@ -3039,6 +3042,7 @@ function createProjectContext(opts) {
3039
3042
  requestProductControlReview: _productControl.requestProductControlReview,
3040
3043
  applyProductSessionAction: _productControl.applyProductSessionAction,
3041
3044
  inspectProductSessionQueue: _productControl.inspectProductSessionQueue,
3045
+ readVendorUsage: function (request) { return _sessions.readVendorUsage(request); },
3042
3046
  inspectProductSessionRuntime: _sessions.sessionRuntimeActions.inspect,
3043
3047
  inspectMateLifecycleRuntime: _sessions.sessionRuntimeActions.inspectLifecycle,
3044
3048
  inspectDelegatedFollowUp: _delegatedFollowUp.inspectDelegatedFollowUp,
package/lib/public/app.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { initStudioBridge } from './modules/clay-studio-bridge.js';
2
2
  import { initSharedBrowser } from './modules/shared-browser.js';
3
+ import { initTopbarWorkbenchControls } from './modules/topbar-workbench-controls.js';
3
4
  import { initGenerationStop } from './modules/generation-stop.js';
4
5
  import { initIssueLinks } from './modules/clay-issue-links.js';
5
6
  import { initIssues, closeIssues } from './modules/issues.js';
@@ -31,7 +32,7 @@ import { initMateMemory, renderMemoryList, hideMemory } from './modules/mate-mem
31
32
  import { initRewind, setRewindMode, showRewindModal, clearPendingRewindUuid, addRewindButton, onRewindComplete, onRewindError } from './modules/rewind.js';
32
33
  import { initNotifications, showDoneNotification, playDoneSound, isNotifAlertEnabled, isNotifSoundEnabled } from './modules/notifications.js';
33
34
  import { initInput, clearPendingImages, handleInputSync, autoResize, builtinCommands, sendMessage, hasSendableContent, setScheduleBtnDisabled, setScheduleDelayMs, clearScheduleDelay } from './modules/input.js';
34
- import { initQrCode, triggerShare } from './modules/qrcode.js';
35
+ import { initQrCode } from './modules/qrcode.js';
35
36
  import { initFileBrowser, loadRootDirectory, reopenFileViewer, refreshTree, handleFsList, handleFsRead, handleDirChanged, refreshIfOpen, handleFileChanged, handleFileHistory, handleGitDiff, handleFileAt, getPendingNavigate, closeFileViewer, resetFileBrowser } from './modules/filebrowser.js';
36
37
  import { initGitPanel } from './modules/git-panel.js';
37
38
  import { initWorkerPaneLock } from './modules/worker-pane-lock.js';
@@ -46,6 +47,7 @@ import { initTheme, getThemeColor, getComputedVar, onThemeChange, getCurrentThem
46
47
  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';
47
48
  import { initServerSettings, updateSettingsStats, updateDaemonConfig, handleSetPinResult, handleKeepAwakeChanged, handleAutoContinueChanged, handleRestartResult, handleShutdownResult, handleSharedEnv, handleSharedEnvSaved } from './modules/server-settings.js';
48
49
  import { initProjectSettings } from './modules/project-settings.js';
50
+ import { initSettingsShell, openSettings } from './modules/settings-shell.js';
49
51
  import { initSkills, handleSkillInstalled, handleSkillUninstalled } from './modules/skills.js';
50
52
  import { initMcp } from './modules/mcp-ui.js';
51
53
  import { initScheduler, resetScheduler, handleLoopRegistryUpdated, handleScheduleRunStarted, handleScheduleRunFinished, handleLoopScheduled, openSchedulerToTab, isSchedulerOpen, closeScheduler, enterCraftingMode, exitCraftingMode, handleLoopRegistryFiles, getUpcomingSchedules } from './modules/scheduler.js';
@@ -63,6 +65,7 @@ import { initProjectLogs, closeProjectLogs } from './modules/project-logs.js';
63
65
  import { initProjectSwitcher } from './modules/project-switcher.js';
64
66
  import { initSplitView } from './modules/split-view.js';
65
67
  import { initPaneBridge } from './modules/pane-bridge.js';
68
+ import { initPaneAppearanceBridge } from './modules/pane-appearance-bridge.js';
66
69
  import { initAdmin, checkAdminAccess } from './modules/admin.js';
67
70
  import { initSessionSearch, toggleSearch, closeSearch, isSearchOpen, handleFindInSessionResults, onHistoryPrepended as onSessionSearchHistoryPrepended } from './modules/session-search.js';
68
71
  import { initTooltips, registerTooltip } from './modules/tooltip.js';
@@ -72,10 +75,15 @@ import { initShellCommand } from './modules/shell-command.js';
72
75
  import { initConnection, connect as _connConnect, setStatus as _connSetStatus, scheduleReconnect as _connScheduleReconnect, cancelReconnect as _connCancelReconnect } from './modules/app-connection.js';
73
76
  import { processMessage as _msgProcessMessage } from './modules/app-messages.js';
74
77
  import { getWs as _getWsRef, setWs as _setWsRef } from './modules/ws-ref.js';
78
+ import { initialProjectStart } from './modules/project-start-state.js';
79
+ import { emptyFolderState } from './modules/project-start-folder-state.js';
80
+ import { openAddProjectModal, initProjectStart } from './modules/project-start-dialog.js';
75
81
  import { initProjectsHub, showProjectsHub, hideProjectsHub } from './modules/projects-hub.js';
76
82
  import { initHomeSurfaceBoot } from './modules/home-surface-boot.js';
77
83
  import { rememberHomePrimarySurface } from './modules/home-surface.js';
78
84
  import { initRateLimit, handleRateLimitEvent as _rlHandleRateLimitEvent, updateRateLimitUsage as _rlUpdateRateLimitUsage, handleFastModeState as _rlHandleFastModeState, resetRateLimitState } from './modules/app-rate-limit.js';
85
+ import { initVendorUsageOverview } from './modules/vendor-usage-overview.js';
86
+ import { initGithubStar } from './modules/github-star.js';
79
87
  import { initCursors, handleRemoteCursorMove as _curHandleRemoteCursorMove, handleRemoteCursorLeave as _curHandleRemoteCursorLeave, handleRemoteSelection as _curHandleRemoteSelection, clearRemoteCursors as _curClearRemoteCursors, initCursorToggle } from './modules/app-cursors.js';
80
88
  import { initDefaultAi } from './modules/default-ai.js';
81
89
  import { requestDefaultVendor } from './modules/default-vendor.js';
@@ -97,7 +105,7 @@ import { initProjectMateCreationWorkspace } from './modules/project-mate-creatio
97
105
  import { initProjectDebatePlanningWorkspace } from './modules/project-debate-planning-workspace.js';
98
106
  import { initProjectSessionNavigation } from './modules/project-session-navigation.js';
99
107
  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';
100
- 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';
108
+ 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';
101
109
  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';
102
110
  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';
103
111
  import { initMention, handleMentionStart, handleMentionStream, handleMentionDone, handleMentionError, handleMentionActivity, renderMentionUser, renderMentionResponse } from './modules/mention.js';
@@ -192,7 +200,7 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
192
200
  function updateProjectList(msg) { _projUpdateProjectList(msg); }
193
201
  function renderTopbarPresence(serverUsers) { _projRenderTopbarPresence(serverUsers); }
194
202
  function renderProjectList() { _projRenderProjectList(); }
195
- // projectListAddBtn listener -> modules/app-projects.js (initProjects)
203
+ // projectListAddBtn listener -> modules/project-start-dialog.js (initProjectStart)
196
204
 
197
205
  // Prevent Cmd+Z / Cmd+Shift+Z from triggering browser back/forward (Arc, etc.)
198
206
  // Always block browser default for Cmd+Z and manually invoke undo/redo via execCommand
@@ -280,6 +288,7 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
280
288
  createStore({
281
289
  networkSettings: { loaded: false, busy: false, confirming: false, draft: "", saved: "", status: "", errorKind: "", requestId: 0 },
282
290
  vendorLoginBrowser: null,
291
+ githubStarCount: null,
283
292
  sharedBrowser: null,
284
293
  sharedBrowsers: [],
285
294
  mcpSkillsWorkbench: {},
@@ -324,6 +333,7 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
324
333
  splitDelegations: {},
325
334
  pairResultStatus: [],
326
335
  notesTemporarilyHidden: false,
336
+ pairResultTrayOpen: false,
327
337
  dmMode: false,
328
338
  historyFrom: 0,
329
339
  loadingMore: false,
@@ -420,7 +430,8 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
420
430
  projectListLoaded: false,
421
431
  projectsAccessLoaded: false,
422
432
  projectsHubRestoreRequested: false,
423
- projectsShowGuide: false,
433
+ projectStart: initialProjectStart(),
434
+ projectStartFolder: emptyFolderState(),
424
435
  onboardingStep: 'welcome',
425
436
  onboardingIntent: null,
426
437
  currentProjectOwnerId: null,
@@ -645,6 +656,8 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
645
656
  vendorInfo: {},
646
657
  // Rate-limit data is projected only for the displayed session/vendor.
647
658
  rateLimitState: {},
659
+ vendorUsageOverview: null,
660
+ vendorUsageOpen: null,
648
661
  currentMode: "default",
649
662
  effectivePermissionMode: null,
650
663
  permissionCapabilities: { auto: false, mcpOverride: false },
@@ -698,6 +711,7 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
698
711
 
699
712
  // --- Theme (module) ---
700
713
  initTheme();
714
+ initPaneAppearanceBridge();
701
715
 
702
716
  // --- Tooltips ---
703
717
  initTooltips();
@@ -789,8 +803,7 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
789
803
  switch (action) {
790
804
  case "createMate": startClayMateCreation(); break;
791
805
  case "openSettings":
792
- var sb = document.getElementById("server-settings-btn");
793
- if (sb) sb.click();
806
+ openSettings();
794
807
  break;
795
808
  case "showHome": showHomeHub(); break;
796
809
  }
@@ -1162,7 +1175,10 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
1162
1175
  basePath: basePath,
1163
1176
  });
1164
1177
 
1165
- // --- User settings (full-screen overlay) ---
1178
+ // --- Unified full-screen settings shell (Personal, Project, Server panes) ---
1179
+ initSettingsShell();
1180
+
1181
+ // --- User settings (personal pane) ---
1166
1182
  initUserSettings({
1167
1183
  basePath: basePath,
1168
1184
  });
@@ -1230,11 +1246,11 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
1230
1246
  }
1231
1247
  }
1232
1248
  }).catch(function () {});
1233
- // Hide server settings and update controls for non-admin users in multi-user mode
1249
+ // Hide update controls for non-admin users in multi-user mode. The settings
1250
+ // gear stays for everyone: Personal and Project settings are not admin-only,
1251
+ // and the shell only offers Server settings to administrators.
1234
1252
  checkAdminAccess().then(function (isAdmin) {
1235
1253
  if (store.get('isMultiUserMode') && !isAdmin) {
1236
- var settingsBtn = document.getElementById("server-settings-btn");
1237
- if (settingsBtn) settingsBtn.style.display = "none";
1238
1254
  var updatePill = document.getElementById("update-pill-wrap");
1239
1255
  if (updatePill) updatePill.style.display = "none";
1240
1256
  }
@@ -1262,8 +1278,6 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
1262
1278
 
1263
1279
  // --- QR code ---
1264
1280
  initQrCode();
1265
- var sharePill = document.getElementById("share-pill");
1266
- if (sharePill) sharePill.addEventListener("click", triggerShare);
1267
1281
 
1268
1282
  // --- File browser ---
1269
1283
  initFileBrowser({
@@ -1276,6 +1290,7 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
1276
1290
  fileViewerEl: $("file-viewer"),
1277
1291
  });
1278
1292
  initGitPanel();
1293
+ initTopbarWorkbenchControls();
1279
1294
  initSharedBrowser();
1280
1295
  initStudioBridge();
1281
1296
  initWorkerPaneLock();
@@ -1447,11 +1462,6 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
1447
1462
  function confirmRemoveProject(slug, name) { _projConfirmRemoveProject(slug, name); }
1448
1463
  function handleRemoveProjectCheckResult(msg) { _projHandleRemoveProjectCheckResult(msg); }
1449
1464
  function handleRemoveProjectResult(msg) { _projHandleRemoveProjectResult(msg); }
1450
- function openAddProjectModal() { _projOpenAddProjectModal(); }
1451
- function closeAddProjectModal() { _projCloseAddProjectModal(); }
1452
- function handleBrowseDirResult(msg) { _projHandleBrowseDirResult(msg); }
1453
- function handleAddProjectResult(msg) { _projHandleAddProjectResult(msg); }
1454
- function handleCloneProgress(msg) { _projHandleCloneProgress(msg); }
1455
1465
 
1456
1466
  // --- PWA install prompt -> modules/app-misc.js (set up in initMisc)
1457
1467
 
@@ -1467,9 +1477,11 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
1467
1477
 
1468
1478
  // --- Rate Limit module ---
1469
1479
  initRateLimit();
1480
+ initVendorUsageOverview();
1481
+ initGithubStar();
1470
1482
 
1471
1483
  // --- Projects module ---
1472
- initProjects();
1484
+ initProjectStart();
1473
1485
 
1474
1486
  // --- Home Hub module ---
1475
1487
  initProjectMateNavigation();
@@ -5,7 +5,6 @@
5
5
  /* --- Top bar search bar trigger (Slack-style) --- */
6
6
  .cmd-palette-searchbar {
7
7
  position: absolute;
8
- left: 50%;
9
8
  transform: translateX(-50%);
10
9
  display: flex;
11
10
  align-items: center;
@@ -20,8 +19,19 @@
20
19
  font-size: 12px;
21
20
  color: var(--text-dimmer);
22
21
  transition: background 0.15s, border-color 0.15s;
23
- width: min(520px, 40%);
22
+ /* topbar-workbench-controls.js publishes the width the free gap allows. */
23
+ width: var(--top-bar-search-width, min(520px, 40%));
24
+ left: var(--top-bar-search-left, 50%);
25
+ }
26
+
27
+ /* Not enough gap beside the actions: same trigger, icon only. */
28
+ #top-bar.search-icon .cmd-palette-searchbar {
29
+ width: 24px;
30
+ padding: 0;
31
+ justify-content: center;
24
32
  }
33
+ #top-bar.search-icon .cmd-palette-searchbar-text,
34
+ #top-bar.search-icon .cmd-palette-searchbar-kbd { display: none; }
25
35
 
26
36
  .cmd-palette-searchbar:hover {
27
37
  background: rgba(var(--overlay-rgb), 0.08);
@@ -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,
@@ -390,7 +390,8 @@
390
390
 
391
391
  /* --- Panel fullscreen --- */
392
392
  @media (min-width: 1024px) {
393
- #main-column:has(.panel-fullscreen:not(.hidden)) > .title-bar-content { display: none; }
393
+ /* Browser, Terminal and Files expand below the session title; see right-workbench.css. */
394
+ #main-column:has(.panel-fullscreen:not(.hidden):not(#shared-browser-panel, #terminal-container, #file-viewer)) > .title-bar-content { display: none; }
394
395
  #main-panels:has(.panel-fullscreen:not(.hidden)) > #app { display: none; }
395
396
  }
396
397
  @media (max-width: 1023px) {
@@ -1060,15 +1061,13 @@
1060
1061
  width: 70%;
1061
1062
  max-width: 1200px;
1062
1063
  }
1064
+ /* Same in-flow region as Browser and Terminal (right-workbench.css). */
1063
1065
  #file-viewer.panel-fullscreen {
1064
- position: fixed;
1065
- inset: 0;
1066
1066
  width: 100%;
1067
- height: 100dvh;
1067
+ height: 100%;
1068
1068
  max-width: none;
1069
1069
  min-width: 0;
1070
1070
  margin: 0;
1071
- z-index: 10000;
1072
1071
  border: 0;
1073
1072
  border-radius: 0;
1074
1073
  background: var(--bg);
@@ -0,0 +1,21 @@
1
+ .mate-loading {
2
+ display: flex;
3
+ flex-direction: column;
4
+ align-items: center;
5
+ justify-content: center;
6
+ gap: 12px;
7
+ color: var(--text-muted);
8
+ text-align: center;
9
+ }
10
+ .mate-loading[hidden] { display: none; }
11
+ .mate-loading-scene { position: relative; width: 210px; height: 148px; flex-shrink: 0; }
12
+ .mate-loading-avatar { position: absolute; width: 94px; height: 94px; object-fit: contain; left: 58px; top: 22px; }
13
+ .mate-loading-desk { position: absolute; width: 210px; height: auto; left: 0; bottom: 0; }
14
+ .mate-loading-label { margin: 0; max-width: 100%; font-size: 13px; line-height: 1.5; overflow-wrap: anywhere; }
15
+ .mate-loading-dots { display: flex; gap: 6px; padding: 3px 0; }
16
+ .mate-loading-dots span { width: 6px; height: 6px; border-radius: 45%; background: var(--text-muted); opacity: .45; animation: mate-loading-bob 1.5s ease-in-out infinite; }
17
+ .mate-loading-dots span:nth-child(2) { animation-delay: .18s; }
18
+ .mate-loading-dots span:nth-child(3) { animation-delay: .36s; }
19
+ @keyframes mate-loading-bob { 0%, 60%, 100% { transform: translateY(0); opacity: .4; } 30% { transform: translateY(-4px); opacity: .8; } }
20
+ @media (prefers-reduced-motion: reduce) { .mate-loading-dots span { animation: none; } }
21
+ @media (max-height: 450px) { .mate-loading-scene { zoom: .7; } .mate-loading { gap: 8px; } }
@@ -1278,23 +1278,42 @@ pre.mermaid-error {
1278
1278
  .plan-banner.plan-exit { color: var(--success); }
1279
1279
  .plan-banner.plan-exit .plan-banner-icon { color: var(--success); }
1280
1280
 
1281
- /* Recovery notices use the same quiet surface as session work references. */
1282
- .pair-result-status { flex: 0 0 auto; margin: 8px 16px 0; padding: 8px 10px; border: 1px solid var(--border); border-radius: 6px; background: var(--bg-alt); color: var(--text-muted); font-size: 12px; line-height: 1.5; max-height: 35vh; overflow-y: auto; }
1283
- .pair-result-status-header { display: flex; align-items: center; justify-content: space-between; gap: 12px; color: var(--text-secondary); }
1284
- .pair-result-status button { font: inherit; color: var(--text-secondary); background: transparent; border: 1px solid var(--border); border-radius: 5px; cursor: pointer; }
1285
- .pair-result-status .pair-result-status-dismiss { display: grid; place-items: center; flex: 0 0 28px; width: 28px; height: 28px; padding: 0; border: 0; font-size: 20px; line-height: 1; }
1286
- .pair-result-status button:hover { background: var(--input-bg); color: var(--text); }
1287
- .pair-result-status button:focus-visible, .pair-result-status summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
1288
- .pair-result-status-details > summary { cursor: pointer; width: fit-content; }
1289
- .pair-result-status-card { padding: 10px 0; border-top: 1px solid var(--border); margin-top: 8px; }
1281
+ /* Worker recovery notice: one compact, content-width disclosure aligned to the conversation inset. */
1282
+ .pair-result-status { position: relative; flex: 0 0 auto; align-self: flex-start; box-sizing: border-box; width: fit-content; min-width: 0; max-width: calc(100% - 32px); margin: 8px 16px 0; border: 1px solid var(--border-subtle); border-radius: 8px; background: rgba(var(--overlay-rgb), 0.02); color: var(--text-muted); font-size: 12px; line-height: 1.5; }
1283
+ .pair-result-status.is-open { width: min(640px, calc(100% - 32px)); }
1284
+ .pair-result-status-bar { display: flex; align-items: center; gap: 2px; min-height: 30px; padding: 0 2px 0 0; }
1285
+ .pair-result-status button { font: inherit; color: var(--text-secondary); background: transparent; border: 0; border-radius: 6px; cursor: pointer; }
1286
+ .pair-result-status-toggle { display: inline-flex; align-items: center; gap: 6px; min-height: 28px; padding: 0 8px; margin: 1px 0 1px 1px; }
1287
+ .pair-result-status-icon { display: inline-flex; color: var(--text-dimmer); }
1288
+ .pair-result-status-tone-blocked .pair-result-status-icon, .pair-result-status-tone-uncertain .pair-result-status-icon { color: var(--warning); }
1289
+ .pair-result-status .lucide { width: 14px; height: 14px; }
1290
+ .pair-result-status-label { color: var(--text-secondary); font-weight: 500; white-space: nowrap; }
1291
+ .pair-result-status-review { color: var(--text-muted); white-space: nowrap; }
1292
+ .pair-result-status-chevron { display: inline-flex; color: var(--text-dimmer); transition: transform 0.15s; }
1293
+ .pair-result-status.is-open .pair-result-status-chevron { transform: rotate(180deg); }
1294
+ .pair-result-status-state, .pair-result-status-live { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
1295
+ .pair-result-status .pair-result-status-dismiss { display: grid; place-items: center; flex: 0 0 28px; width: 28px; height: 28px; padding: 0; color: var(--text-dimmer); }
1296
+ .pair-result-status button:hover { background: rgba(var(--overlay-rgb), 0.06); color: var(--text); }
1297
+ .pair-result-status button:focus-visible, .pair-result-status summary:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
1298
+ .pair-result-status-tray { border-radius: 0 0 7px 7px; max-height: min(40vh, 360px); overflow-y: auto; padding: 0 10px 8px; border-top: 1px solid var(--border-subtle); overflow-wrap: anywhere; }
1299
+ .pair-result-status-tray[hidden] { display: none; }
1300
+ .pair-result-status-card { padding: 8px 0; }
1301
+ .pair-result-status-card + .pair-result-status-card { border-top: 1px solid var(--border-subtle); }
1290
1302
  .pair-result-status-card > strong, .pair-result-status-card > span { display: block; }
1291
1303
  .pair-result-status-card > strong { color: var(--text-secondary); font-weight: 500; margin-bottom: 3px; }
1292
1304
  .pair-result-status-card details { margin-top: 7px; }
1293
1305
  .pair-result-status-card summary { cursor: pointer; }
1294
1306
  .pair-result-status-card pre { max-height: 180px; overflow: auto; white-space: pre-wrap; overflow-wrap: anywhere; font: inherit; }
1295
- .pair-result-status-retry { margin-top: 8px; padding: 4px 8px; }
1307
+ .pair-result-status .pair-result-status-retry { margin-top: 8px; padding: 4px 8px; border: 1px solid var(--border); border-radius: 5px; }
1296
1308
  .pair-result-status-retry:disabled { cursor: not-allowed; opacity: 0.55; }
1297
- @media (max-width: 600px) { .pair-result-status { margin: 6px 8px 0; } .pair-result-status .pair-result-status-dismiss { min-width: 36px; height: 36px; } }
1309
+ @media (prefers-reduced-motion: reduce) { .pair-result-status-chevron { transition: none; } }
1310
+ @media (max-width: 600px) {
1311
+ .pair-result-status { margin: 6px 8px 0; max-width: calc(100% - 16px); }
1312
+ .pair-result-status.is-open { width: calc(100% - 16px); }
1313
+ .pair-result-status-bar { min-height: 36px; }
1314
+ .pair-result-status-toggle { min-height: 34px; }
1315
+ .pair-result-status .pair-result-status-dismiss { flex-basis: 36px; width: 36px; height: 36px; }
1316
+ }
1298
1317
 
1299
1318
 
1300
1319
  /* --- Plan card --- */