clay-server 5.1.0-beta.1 → 5.1.0-beta.3

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 (231) hide show
  1. package/LICENSES/README.md +4 -0
  2. package/LICENSES/dicebear-avatars.md +16 -0
  3. package/bin/cli.js +9 -91
  4. package/lib/builtin-mates.js +7 -7
  5. package/lib/daemon-project-access.js +7 -1
  6. package/lib/daemon-settings.js +11 -0
  7. package/lib/daemon.js +10 -37
  8. package/lib/dicebear-avatar.js +62 -0
  9. package/lib/knowledge-db-service.js +175 -0
  10. package/lib/knowledge-db-validation.js +0 -0
  11. package/lib/knowledge-document-index.js +49 -0
  12. package/lib/knowledge-draft-store.js +56 -0
  13. package/lib/knowledge-file-store.js +199 -0
  14. package/lib/knowledge-markdown.js +128 -0
  15. package/lib/linear-store.js +35 -8
  16. package/lib/mate-identity-file.js +39 -0
  17. package/lib/mate-knowledge-mcp-server.js +29 -6
  18. package/lib/mate-knowledge-service.js +23 -2
  19. package/lib/mate-knowledge-sync.js +37 -22
  20. package/lib/mate-ready-creation.js +2 -1
  21. package/lib/mate-workspace-color.js +4 -0
  22. package/lib/mates-identity.js +4 -4
  23. package/lib/mates-prompts.js +4 -3
  24. package/lib/mates.js +30 -11
  25. package/lib/os-users.js +5 -16
  26. package/lib/project-connection.js +36 -9
  27. package/lib/project-debate-proposal.js +6 -0
  28. package/lib/project-debate-workspace.js +43 -0
  29. package/lib/project-debate.js +4 -2
  30. package/lib/project-filesystem.js +21 -39
  31. package/lib/project-knowledge.js +315 -136
  32. package/lib/project-mate-creation-proposal.js +1 -0
  33. package/lib/project-mate-interaction.js +7 -6
  34. package/lib/project-server-settings.js +31 -0
  35. package/lib/project-sessions.js +92 -182
  36. package/lib/project-user-knowledge.js +60 -0
  37. package/lib/project.js +62 -21
  38. package/lib/public/app.js +79 -71
  39. package/lib/public/clay-studio-wordmark-bold.svg +20 -0
  40. package/lib/public/css/clay-workspace.css +105 -0
  41. package/lib/public/css/debates-workbench.css +47 -0
  42. package/lib/public/css/home-debate-live.css +26 -0
  43. package/lib/public/css/home-hub.css +3 -1
  44. package/lib/public/css/home-mate-model-picker.css +15 -131
  45. package/lib/public/css/home-mate-settings.css +168 -16
  46. package/lib/public/css/icon-strip.css +249 -32
  47. package/lib/public/css/input.css +3 -1
  48. package/lib/public/css/mate-conversation.css +176 -0
  49. package/lib/public/css/mate-knowledge-workbench.css +118 -0
  50. package/lib/public/css/mate-workspace-color.css +16 -0
  51. package/lib/public/css/mates.css +28 -28
  52. package/lib/public/css/menus.css +3 -0
  53. package/lib/public/css/messages.css +1 -114
  54. package/lib/public/css/mobile-nav.css +16 -16
  55. package/lib/public/css/overlays.css +15 -0
  56. package/lib/public/css/project-logs.css +7 -0
  57. package/lib/public/css/project-mate-creation.css +105 -0
  58. package/lib/public/css/project-settings-modal.css +56 -0
  59. package/lib/public/css/right-workbench.css +3 -3
  60. package/lib/public/css/server-settings-system.css +125 -0
  61. package/lib/public/css/session-folders.css +80 -21
  62. package/lib/public/css/session-github.css +25 -16
  63. package/lib/public/css/settings-ui.css +247 -22
  64. package/lib/public/css/shared-browser.css +7 -3
  65. package/lib/public/css/sidebar.css +183 -107
  66. package/lib/public/css/title-bar.css +156 -1
  67. package/lib/public/css/worker-delegation.css +47 -0
  68. package/lib/public/css/worker-proposal.css +72 -150
  69. package/lib/public/css/you.css +74 -0
  70. package/lib/public/index.html +206 -548
  71. package/lib/public/modules/account-island.js +232 -0
  72. package/lib/public/modules/admin-settings-email.js +197 -0
  73. package/lib/public/modules/admin-settings-invitations.js +183 -0
  74. package/lib/public/modules/admin-settings-projects.js +198 -0
  75. package/lib/public/modules/admin-settings-ui.js +100 -0
  76. package/lib/public/modules/admin-settings-users.js +331 -0
  77. package/lib/public/modules/admin.js +19 -949
  78. package/lib/public/modules/app-cursors.js +19 -43
  79. package/lib/public/modules/app-debate-ui.js +4 -3
  80. package/lib/public/modules/app-dm.js +0 -2
  81. package/lib/public/modules/app-header.js +106 -0
  82. package/lib/public/modules/app-message-router.js +12 -3
  83. package/lib/public/modules/app-messages.js +47 -61
  84. package/lib/public/modules/app-notifications.js +4 -7
  85. package/lib/public/modules/app-projects.js +5 -38
  86. package/lib/public/modules/app-rate-limit.js +70 -12
  87. package/lib/public/modules/app-rendering.js +12 -13
  88. package/lib/public/modules/avatar.js +30 -9
  89. package/lib/public/modules/chat-render-runtime.js +16 -4
  90. package/lib/public/modules/command-palette.js +2 -15
  91. package/lib/public/modules/context-sources.js +27 -18
  92. package/lib/public/modules/cursor-sharing-preference.js +3 -3
  93. package/lib/public/modules/debate-controls.js +299 -0
  94. package/lib/public/modules/debate.js +2 -0
  95. package/lib/public/modules/debates-workbench.js +339 -0
  96. package/lib/public/modules/default-ai.js +88 -65
  97. package/lib/public/modules/header-tui-font.js +10 -20
  98. package/lib/public/modules/home-debate-controls.js +18 -289
  99. package/lib/public/modules/home-mate-avatar-editor.js +228 -0
  100. package/lib/public/modules/home-mate-color-picker.js +66 -0
  101. package/lib/public/modules/home-mate-creation.js +1 -1
  102. package/lib/public/modules/home-mate-instructions.js +90 -0
  103. package/lib/public/modules/home-mate-model-picker.js +80 -271
  104. package/lib/public/modules/home-mate-profile-editor.js +72 -0
  105. package/lib/public/modules/home-mate-settings.js +118 -125
  106. package/lib/public/modules/home-shell.js +3 -0
  107. package/lib/public/modules/home-surface-boot.js +1 -9
  108. package/lib/public/modules/icon-strip-tooltip.js +95 -0
  109. package/lib/public/modules/knowledge-db-edit-state.js +35 -0
  110. package/lib/public/modules/knowledge-db-workbench.js +234 -0
  111. package/lib/public/modules/knowledge-history-state.js +55 -0
  112. package/lib/public/modules/knowledge-markdown-editor.js +57 -0
  113. package/lib/public/modules/knowledge-preview-links.js +70 -0
  114. package/lib/public/modules/knowledge-workbench-shell.js +21 -0
  115. package/lib/public/modules/linear-settings.js +3 -2
  116. package/lib/public/modules/markdown.js +5 -6
  117. package/lib/public/modules/mate-knowledge-workbench-state.js +174 -0
  118. package/lib/public/modules/mate-knowledge-workbench.js +500 -0
  119. package/lib/public/modules/mate-knowledge.js +11 -3
  120. package/lib/public/modules/mate-management.js +54 -10
  121. package/lib/public/modules/mate-sidebar.js +11 -1
  122. package/lib/public/modules/mate-workspace-color.js +33 -0
  123. package/lib/public/modules/mention.js +5 -13
  124. package/lib/public/modules/notification-settings.js +81 -0
  125. package/lib/public/modules/notifications.js +44 -28
  126. package/lib/public/modules/personal-settings-dialog.js +51 -0
  127. package/lib/public/modules/profile.js +66 -24
  128. package/lib/public/modules/project-chat-identity.js +79 -0
  129. package/lib/public/modules/project-debate-planning-workspace.js +250 -0
  130. package/lib/public/modules/project-debate-workspace.js +185 -0
  131. package/lib/public/modules/project-header-identity.js +127 -0
  132. package/lib/public/modules/project-logs-render.js +9 -4
  133. package/lib/public/modules/project-mate-creation-workspace.js +414 -0
  134. package/lib/public/modules/project-mate-navigation.js +238 -0
  135. package/lib/public/modules/project-session-navigation.js +29 -0
  136. package/lib/public/modules/project-settings-environment.js +106 -0
  137. package/lib/public/modules/project-settings.js +72 -628
  138. package/lib/public/modules/project-switcher.js +6 -8
  139. package/lib/public/modules/projects-hub-view.js +5 -6
  140. package/lib/public/modules/projects-hub.js +2 -6
  141. package/lib/public/modules/qrcode.js +27 -8
  142. package/lib/public/modules/search-clay-chat.js +8 -11
  143. package/lib/public/modules/server-settings-controls.js +316 -0
  144. package/lib/public/modules/server-settings-environment.js +194 -0
  145. package/lib/public/modules/server-settings-navigation.js +39 -0
  146. package/lib/public/modules/server-settings-state.js +54 -0
  147. package/lib/public/modules/server-settings.js +116 -788
  148. package/lib/public/modules/server-storage-settings.js +84 -0
  149. package/lib/public/modules/session-activity-tooltip.js +12 -0
  150. package/lib/public/modules/session-create-form.js +86 -14
  151. package/lib/public/modules/session-create.js +50 -7
  152. package/lib/public/modules/session-github.js +2 -1
  153. package/lib/public/modules/session-list-visibility.js +41 -0
  154. package/lib/public/modules/session-presence-ui.js +3 -0
  155. package/lib/public/modules/session-title-marquee.js +51 -0
  156. package/lib/public/modules/settings-env-values.js +34 -0
  157. package/lib/public/modules/settings-select.js +121 -0
  158. package/lib/public/modules/shared-browser.js +22 -12
  159. package/lib/public/modules/sidebar-mates.js +3 -50
  160. package/lib/public/modules/sidebar-mobile.js +63 -60
  161. package/lib/public/modules/sidebar-projects.js +84 -97
  162. package/lib/public/modules/sidebar-session-folders-ui.js +2 -2
  163. package/lib/public/modules/sidebar-session-hierarchy.js +149 -70
  164. package/lib/public/modules/sidebar-sessions.js +49 -50
  165. package/lib/public/modules/sidebar.js +11 -13
  166. package/lib/public/modules/stt-picker.js +14 -5
  167. package/lib/public/modules/terminal-prefs.js +22 -0
  168. package/lib/public/modules/theme.js +88 -52
  169. package/lib/public/modules/thinking-lifecycle.js +7 -6
  170. package/lib/public/modules/tool-palette-order.js +4 -3
  171. package/lib/public/modules/tool-palette.js +13 -3
  172. package/lib/public/modules/tools.js +22 -22
  173. package/lib/public/modules/user-settings-account.js +318 -0
  174. package/lib/public/modules/user-settings-appearance.js +55 -0
  175. package/lib/public/modules/user-settings-editors.js +24 -0
  176. package/lib/public/modules/user-settings-integrations.js +104 -0
  177. package/lib/public/modules/user-settings-layout.js +80 -0
  178. package/lib/public/modules/user-settings-preferences.js +48 -0
  179. package/lib/public/modules/user-settings-profile.js +53 -0
  180. package/lib/public/modules/user-settings-themes.js +38 -0
  181. package/lib/public/modules/user-settings-voice.js +75 -0
  182. package/lib/public/modules/user-settings.js +53 -480
  183. package/lib/public/modules/worker-proposal.js +42 -8
  184. package/lib/public/modules/worker-runtime-picker.js +115 -0
  185. package/lib/public/modules/you-activity.js +121 -0
  186. package/lib/public/modules/you.js +251 -0
  187. package/lib/public/style.css +20 -8
  188. package/lib/public/vendor/codemirror/LICENSE +21 -0
  189. package/lib/public/vendor/codemirror/codemirror.css +344 -0
  190. package/lib/public/vendor/codemirror/codemirror.js +9884 -0
  191. package/lib/public/vendor/codemirror/continuelist.js +101 -0
  192. package/lib/public/vendor/codemirror/markdown.js +886 -0
  193. package/lib/public/vendor/codemirror/show-hint.css +37 -0
  194. package/lib/public/vendor/codemirror/show-hint.js +523 -0
  195. package/lib/sdk-bridge.js +15 -3
  196. package/lib/sdk-message-processor.js +16 -4
  197. package/lib/sdk-quota-snapshot.js +37 -0
  198. package/lib/server-admin.js +11 -0
  199. package/lib/server-appearance-settings.js +71 -0
  200. package/lib/server-home-chat-events.js +1 -1
  201. package/lib/server-home-chat.js +25 -3
  202. package/lib/server-home-debate-planning.js +4 -1
  203. package/lib/server-home-debates.js +2 -0
  204. package/lib/server-home-mate-creation.js +4 -1
  205. package/lib/server-linear.js +14 -8
  206. package/lib/server-mate-instructions.js +44 -0
  207. package/lib/server-mates.js +14 -4
  208. package/lib/server-notification-settings.js +67 -0
  209. package/lib/server-palette.js +1 -1
  210. package/lib/server-settings.js +69 -1
  211. package/lib/server-storage.js +57 -0
  212. package/lib/server-user-knowledge.js +119 -0
  213. package/lib/server.js +31 -3
  214. package/lib/session-visibility.js +7 -2
  215. package/lib/sessions.js +35 -10
  216. package/lib/startup-projects.js +40 -0
  217. package/lib/storage-native-walk.js +85 -0
  218. package/lib/storage-usage.js +144 -0
  219. package/lib/user-knowledge-service.js +167 -0
  220. package/lib/users-permissions.js +7 -0
  221. package/lib/users-preferences.js +0 -74
  222. package/lib/users-project-mate-filter-preferences.js +39 -0
  223. package/lib/users.js +6 -4
  224. package/lib/workspace-query-access.js +4 -1
  225. package/lib/ws-schema.js +52 -4
  226. package/lib/yoke/acp-process-manager.js +2 -2
  227. package/lib/yoke/adapters/codex.js +12 -52
  228. package/lib/yoke/codex-app-server.js +2 -2
  229. package/lib/yoke/codex-model-catalog.js +52 -0
  230. package/lib/yoke/codex-rate-limits.js +26 -0
  231. package/package.json +12 -3
@@ -17,7 +17,6 @@ import { updateCrossProjectBlink, stopUrgentBlink, setActivity } from './app-fav
17
17
  import { spawnDustParticles } from './sidebar.js';
18
18
  import { isSearchOpen, closeSearch } from './session-search.js';
19
19
  import { exitDmMode } from './app-dm.js';
20
- import { isHomeHubVisible, hideHomeHub, showHomeHub } from './app-home-hub.js';
21
20
  import { resetFileBrowser } from './filebrowser.js';
22
21
  import { closeNotesBrowser } from './sticky-notes-browser.js';
23
22
  import { hideMemory } from './mate-memory.js';
@@ -40,6 +39,7 @@ import { chooseProjectAfterRemoval } from './project-removal-target.js';
40
39
  import { isProjectActivated, isProjectActivationPending, isProjectContextConnected, projectWsPath } from './project-activation.js';
41
40
  import { rememberHomePrimarySurface } from './home-surface.js';
42
41
  import { clearProjectSplitState } from './split-session-boundary.js';
42
+ import { isMateHeaderWorkspace, renderOrdinaryProjectHeader } from './project-header-identity.js';
43
43
 
44
44
  // --- Module-owned state ---
45
45
  var cachedProjects = [];
@@ -273,17 +273,6 @@ export function updateProjectList(msg) {
273
273
  myMessageAvatars[avatarIndex].src = document.body.dataset.myAvatarUrl;
274
274
  }
275
275
  }
276
- // Render my avatar (always present, hidden behind user-island)
277
- var meEl = document.getElementById("icon-strip-me");
278
- if (meEl && !meEl.hasChildNodes()) {
279
- var myUser = st2.cachedAllUsers.find(function (u) { return u.id === st2.myUserId; });
280
- if (myUser) {
281
- var meAvatar = document.createElement("img");
282
- meAvatar.className = "icon-strip-me-avatar";
283
- meAvatar.src = userAvatarUrl(myUser, 34);
284
- meEl.appendChild(meAvatar);
285
- }
286
- }
287
276
  }
288
277
  }
289
278
 
@@ -338,29 +327,8 @@ export function renderProjectList() {
338
327
  var iconStripActiveSlug = (st.mateProjectSlug && st.savedMainSlug) ? st.savedMainSlug : st.currentSlug;
339
328
  renderIconStrip(iconStripProjects, iconStripActiveSlug);
340
329
  // Update title bar project name and icon if it changed
341
- if (!st.mateProjectSlug) {
342
- for (var pi = 0; pi < cachedProjects.length; pi++) {
343
- if (cachedProjects[pi].slug === st.currentSlug) {
344
- var updatedName = cachedProjects[pi].title || cachedProjects[pi].project;
345
- var tbName = document.getElementById("title-bar-project-name");
346
- if (tbName && updatedName) tbName.textContent = updatedName;
347
- var tbIcon = document.getElementById("title-bar-project-icon");
348
- if (tbIcon) {
349
- var pIcon = cachedProjects[pi].icon || null;
350
- if (pIcon) {
351
- tbIcon.textContent = pIcon;
352
- parseEmojis(tbIcon);
353
- tbIcon.classList.add("has-icon");
354
- try { localStorage.setItem("clay-project-icon-" + (st.currentSlug || "default"), pIcon); } catch (e) {}
355
- } else {
356
- tbIcon.textContent = "";
357
- tbIcon.classList.remove("has-icon");
358
- try { localStorage.removeItem("clay-project-icon-" + (st.currentSlug || "default")); } catch (e) {}
359
- }
360
- }
361
- break;
362
- }
363
- }
330
+ if (!st.mateProjectSlug && !isMateHeaderWorkspace(st)) {
331
+ renderOrdinaryProjectHeader(Object.assign({}, st, { projectsHubList: cachedProjects }));
364
332
  }
365
333
  // Re-apply current socket status to the active icon's dot
366
334
  var dot = getStatusDot();
@@ -425,14 +393,13 @@ export function switchProject(slug) {
425
393
  var st = store.snap();
426
394
  var ws = getWs();
427
395
  var targetWsPath = projectWsPath(slug);
428
- var homeVisible = isHomeHubVisible() || store.get('projectsHubVisible');
396
+ var homeVisible = store.get('projectsHubVisible');
429
397
  var alreadyInProject = isProjectActivated(st, slug, ws);
430
398
  var wasDm = st.dmMode;
431
399
  var wasMate = st.dmMode && st.dmTargetUser && st.dmTargetUser.isMate;
432
400
  if (st.dmMode) exitDmMode(wasMate);
433
401
  if (homeVisible && alreadyInProject) {
434
402
  rememberHomePrimarySurface("project", slug);
435
- hideHomeHub();
436
403
  hideProjectsHub();
437
404
  completeProjectsOnboarding();
438
405
  if (document.documentElement.classList.contains("pwa-standalone")) {
@@ -496,7 +463,7 @@ export function confirmCurrentProjectSessionActivation() {
496
463
  export function finishProjectSessionActivation() {
497
464
  var confirmed = confirmCurrentProjectSessionActivation();
498
465
  if (!confirmed) return false;
499
- if (!store.get('pendingHomeProjectSlug')) return !isHomeHubVisible() && !store.get('projectsHubVisible');
466
+ if (!store.get('pendingHomeProjectSlug')) return !store.get('projectsHubVisible');
500
467
  return completePendingProjectActivation();
501
468
  }
502
469
 
@@ -83,6 +83,22 @@ function setActiveRateLimitState(next) {
83
83
  rateLimitResetState = next;
84
84
  }
85
85
 
86
+ function extraUsageDescription(msg) {
87
+ if (msg.vendor && msg.vendor !== "claude") return "";
88
+ var reason = msg.overageDisabledReason;
89
+ if (reason === "out_of_credits") return "Extra usage has no credits remaining";
90
+ if (reason === "fetch_error") return "Extra usage availability could not be checked";
91
+ if (reason && reason !== "unknown") return "Extra usage is unavailable for this account";
92
+ if (msg.overageStatus === "rejected") {
93
+ var reset = formatResetTime(msg.overageResetsAt);
94
+ return "Extra usage limit reached" + (reset ? "; resets in " + reset : "");
95
+ }
96
+ if (msg.isUsingOverage) return "Extra usage is currently being used";
97
+ if (msg.overageEnabled === false) return "Extra usage is turned off";
98
+ if (msg.overageEnabled === true) return "Extra usage is enabled";
99
+ return "";
100
+ }
101
+
86
102
  function rateLimitTypeLabel(type) {
87
103
  if (!type) return "Usage";
88
104
  var labels = {
@@ -90,7 +106,8 @@ function rateLimitTypeLabel(type) {
90
106
  "seven_day": "7-day",
91
107
  "seven_day_opus": "7-day Opus",
92
108
  "seven_day_sonnet": "7-day Sonnet",
93
- "overage": "Overage",
109
+ "overage": "Extra usage",
110
+ "seven_day_overage_included": "7-day included extra usage",
94
111
  };
95
112
  return labels[type] || type;
96
113
  }
@@ -163,6 +180,12 @@ function showRateLimitPopover(text, isRejected) {
163
180
  pop.className = "rate-limit-popover" + (isRejected ? " rejected" : "");
164
181
  pop.textContent = text;
165
182
  rateLimitIndicatorEl.appendChild(pop);
183
+ if (typeof pop.getBoundingClientRect === "function") {
184
+ var bounds = pop.getBoundingClientRect();
185
+ var viewportWidth = document.documentElement.clientWidth;
186
+ var shift = bounds.left < 16 ? 16 - bounds.left : Math.min(0, viewportWidth - 16 - bounds.right);
187
+ pop.style.marginLeft = shift + "px";
188
+ }
166
189
 
167
190
  // Auto-dismiss after 5s
168
191
  var fadeTimer = setTimeout(function () {
@@ -182,9 +205,7 @@ function clearRateLimitIndicator() {
182
205
 
183
206
  function formatResetTime(resetsAt) {
184
207
  if (!resetsAt) return "";
185
- var d = new Date(resetsAt);
186
- var now = new Date();
187
- var diff = resetsAt - now.getTime();
208
+ var diff = resetsAt - Date.now();
188
209
  if (diff <= 0) return "";
189
210
  var hrs = Math.floor(diff / 3600000);
190
211
  var mins = Math.floor((diff % 3600000) / 60000);
@@ -200,10 +221,31 @@ function rateLimitTypeShortLabel(type) {
200
221
  return type || "";
201
222
  }
202
223
 
224
+ function quotaWindowLabel(entry) {
225
+ var mins = entry.windowDurationMins;
226
+ if (!Number.isFinite(mins) || mins <= 0) return "Usage";
227
+ if (mins % 1440 === 0) return (mins / 1440) + "d";
228
+ if (mins % 60 === 0) return (mins / 60) + "h";
229
+ return mins + "m";
230
+ }
231
+
232
+ function applyQuotaSnapshot(msg) {
233
+ var next = Object.assign({}, getActiveRateLimitState());
234
+ Object.keys(next).forEach(function (key) {
235
+ // Retire the old inferred Codex windows as soon as real metadata arrives.
236
+ if (!next[key].source || next[key].limitId === msg.limitId) delete next[key];
237
+ });
238
+ msg.quotaWindows.forEach(function (window) {
239
+ if (window.resetsAt && window.resetsAt <= Date.now()) return;
240
+ var key = JSON.stringify([msg.limitId, window.key]);
241
+ next[key] = Object.assign({}, window, { source: "account_snapshot", limitId: msg.limitId });
242
+ });
243
+ setActiveRateLimitState(next);
244
+ }
245
+
203
246
  function tickRateLimitUsage() {
204
247
  if (!rateLimitUsageEl) return;
205
- var parts = [];
206
- var types = ["five_hour", "seven_day", "seven_day_opus", "seven_day_sonnet"];
248
+ var types = Object.keys(rateLimitResetState);
207
249
  var nextState = Object.assign({}, rateLimitResetState);
208
250
  var changed = false;
209
251
  for (var i = 0; i < types.length; i++) {
@@ -211,13 +253,8 @@ function tickRateLimitUsage() {
211
253
  if (!entry || !entry.resetsAt) continue;
212
254
  var timeStr = formatResetTime(entry.resetsAt);
213
255
  if (!timeStr) { delete nextState[types[i]]; changed = true; continue; }
214
- parts.push(rateLimitTypeShortLabel(types[i]) + " resets " + timeStr);
215
256
  }
216
257
  if (changed) setActiveRateLimitState(nextState);
217
- if (parts.length === 0) {
218
- renderRateLimitUsage();
219
- return;
220
- }
221
258
  renderRateLimitUsage();
222
259
  }
223
260
 
@@ -247,9 +284,19 @@ function renderRateLimitUsage() {
247
284
  rateLimitUsageEl.style.display = "";
248
285
 
249
286
  var parts = [];
250
- var types = ["five_hour", "seven_day", "seven_day_opus", "seven_day_sonnet"];
287
+ var types = Object.keys(rateLimitResetState);
288
+ var hasWarning = false;
251
289
  for (var i = 0; i < types.length; i++) {
252
290
  var entry = rateLimitResetState[types[i]];
291
+ if (entry && entry.source === "account_snapshot") {
292
+ if (entry.resetsAt && entry.resetsAt <= Date.now()) continue;
293
+ var quotaText = quotaWindowLabel(entry);
294
+ if (Number.isFinite(entry.usedPercent)) quotaText += " " + Math.round(entry.usedPercent) + "% used";
295
+ if (entry.resetsAt) quotaText += " · resets " + formatResetTime(entry.resetsAt);
296
+ if (entry.usedPercent >= 80) hasWarning = true;
297
+ parts.push(quotaText);
298
+ continue;
299
+ }
253
300
  if (!entry || !entry.resetsAt) continue;
254
301
  var timeStr = formatResetTime(entry.resetsAt);
255
302
  if (!timeStr) continue;
@@ -257,6 +304,7 @@ function renderRateLimitUsage() {
257
304
  }
258
305
 
259
306
  var label = parts.length > 0 ? parts.join(" · ") : "Check usage";
307
+ rateLimitUsageEl.className = "top-bar-pill usage-check-link " + (hasWarning ? "pill-warning" : "pill-dim");
260
308
  var meta = getVendorUsageMeta(activeVendor);
261
309
  if (meta) {
262
310
  rateLimitUsageEl.href = meta.href;
@@ -301,6 +349,8 @@ export function initRateLimit() {
301
349
 
302
350
  export function handleRateLimitEvent(msg) {
303
351
  if (!rateLimitEventAppliesToPane(msg, store.snap())) return;
352
+ // Old Codex history contains quota snapshots misrecorded as rejections.
353
+ if (msg.vendor === "codex" && !msg.source) return;
304
354
  if (msg.resetsAt && msg.resetsAt <= Date.now()) {
305
355
  var expiredState = Object.assign({}, getActiveRateLimitState());
306
356
  if (msg.rateLimitType) delete expiredState[msg.rateLimitType];
@@ -351,11 +401,19 @@ export function handleRateLimitEvent(msg) {
351
401
  updateRateLimitIndicator(msg);
352
402
  }
353
403
 
404
+ var extraUsage = extraUsageDescription(msg);
405
+ if (extraUsage) popoverText += ". " + extraUsage;
354
406
  showRateLimitPopover(popoverText, isRejected);
355
407
  }
356
408
 
357
409
  export function updateRateLimitUsage(msg) {
358
410
  if (!rateLimitEventAppliesToPane(msg, store.snap())) return;
411
+ if (msg.vendor === "codex" && msg.source === "account_snapshot" && Array.isArray(msg.quotaWindows)) {
412
+ applyQuotaSnapshot(msg);
413
+ renderRateLimitUsage();
414
+ return;
415
+ }
416
+ if (msg.vendor === "codex" && !msg.source) return;
359
417
  var activeVendor = store.get('currentVendor') || "claude";
360
418
  if (!vendorTracksRateLimits(activeVendor)) {
361
419
  renderRateLimitUsage();
@@ -8,7 +8,8 @@ import { refreshIcons } from './icons.js';
8
8
  import { closeToolGroup } from './tools.js';
9
9
  import { sendMessage, hasSendableContent } from './input.js';
10
10
  import { getChatLayout } from './theme.js';
11
- import { addToMessages, scrollToBottom, forceScrollToBottom, getMsgTime, shouldGroupMessage, getTurnCounter, getActivityEl, VENDOR_AVATARS, VENDOR_NAMES } from './chat-render-runtime.js';
11
+ import { addToMessages, scrollToBottom, forceScrollToBottom, getMsgTime, shouldGroupMessage, getTurnCounter, getActivityEl } from './chat-render-runtime.js';
12
+ import { resolveProjectAssistantIdentity, applyProjectAssistantIdentity } from './project-chat-identity.js';
12
13
  export { VENDOR_ORDER, EXPERIMENTAL_VENDORS, isExperimentalVendor } from './vendor-priority.js';
13
14
  export { initRendering, addToMessages, scrollToBottom, forceScrollToBottom, getMsgTime, shouldGroupMessage, getTurnCounter, setTurnCounter, getPrependAnchor, setPrependAnchor, getActivityEl, setActivityEl, getIsUserScrolledUp, setIsUserScrolledUp, getStickyBottom, armStickyBottom, disarmStickyBottom, VENDOR_AVATARS, VENDOR_NAMES, VENDOR_HOMEPAGES } from './chat-render-runtime.js';
14
15
  export { addUserMessage, addSystemMessage, addConflictMessage, addContextOverflowMessage } from './app-message-cards.js';
@@ -22,15 +23,15 @@ var streamDrainTimer = null;
22
23
  export function ensureAssistantBlock() {
23
24
  var _el = store.get('currentMsgEl');
24
25
  if (!_el) {
25
- var _isDm2 = document.body.classList.contains("mate-dm-active") && document.body.dataset.mateAvatarUrl;
26
26
  var vendor = store.get('currentVendor') || "claude";
27
- var dmTarget = store.get('dmTargetUser');
27
+ var identity = resolveProjectAssistantIdentity(store.snap(), vendor);
28
28
  _el = createAssistantBubble({
29
29
  turn: getTurnCounter(),
30
- avatarUrl: _isDm2 ? document.body.dataset.mateAvatarUrl : (VENDOR_AVATARS[vendor] || VENDOR_AVATARS.claude),
31
- name: _isDm2 ? ((dmTarget && dmTarget.displayName) || "Mate") : (VENDOR_NAMES[vendor] || VENDOR_NAMES.claude),
30
+ avatarUrl: identity.avatarUrl,
31
+ name: identity.name,
32
32
  time: getMsgTime(),
33
33
  });
34
+ applyProjectAssistantIdentity(_el, identity);
34
35
  if (shouldGroupMessage("msg-assistant")) _el.classList.add("grouped");
35
36
  addToMessages(_el);
36
37
  store.set({ currentMsgEl: _el, currentFullText: "" });
@@ -119,20 +120,17 @@ export function showClaudePreThinking() {
119
120
  if (getChatLayout() !== "channel") return;
120
121
  removeMatePreThinking();
121
122
  var vendor = store.get('currentVendor') || "claude";
122
- var vendorAvatar = VENDOR_AVATARS[vendor] || VENDOR_AVATARS.claude;
123
- var vendorName = VENDOR_NAMES[vendor] || VENDOR_NAMES.claude;
124
- doShowMatePreThinking(vendorName, vendorAvatar);
123
+ var identity = resolveProjectAssistantIdentity(store.snap(), vendor);
124
+ doShowMatePreThinking(identity.name, identity.avatarUrl, identity);
125
125
  }
126
126
 
127
127
  export function showMatePreThinking() {
128
128
  removeMatePreThinking();
129
- var dmTarget = store.get('dmTargetUser');
130
- var mateName = dmTarget ? (dmTarget.displayName || "Mate") : "Mate";
131
- var mateAvatar = document.body.dataset.mateAvatarUrl || "";
132
- doShowMatePreThinking(mateName, mateAvatar);
129
+ var identity = resolveProjectAssistantIdentity(store.snap(), store.get('currentVendor'));
130
+ doShowMatePreThinking(identity.name, identity.avatarUrl, identity);
133
131
  }
134
132
 
135
- function doShowMatePreThinking(mateName, mateAvatar) {
133
+ function doShowMatePreThinking(mateName, mateAvatar, identity) {
136
134
  var _el = document.createElement("div");
137
135
  _el.className = "thinking-item mate-thinking mate-pre-thinking";
138
136
  _el.innerHTML =
@@ -141,6 +139,7 @@ function doShowMatePreThinking(mateName, mateAvatar) {
141
139
  '<div class="dm-bubble-header"><span class="dm-bubble-name">' + escapeHtml(mateName) + '</span></div>' +
142
140
  '<div class="mate-thinking-dots"><span></span><span></span><span></span></div>' +
143
141
  '</div>';
142
+ applyProjectAssistantIdentity(_el, identity);
144
143
  store.set({ matePreThinkingEl: _el });
145
144
  var activity = getActivityEl();
146
145
  if (activity && activity.parentNode) {
@@ -1,18 +1,35 @@
1
1
  // Centralized local avatar URL builder for people and Mates.
2
2
  import { imprintDataUrl, mateMarkDataUrl } from './avatar-imprint.js';
3
3
 
4
- var imprintCache = new Map();
5
4
  var mateMarkCache = new Map();
6
5
 
7
- // Legacy style remains part of the identity hash so existing selections map
8
- // deterministically into the local pixel identity system.
6
+ // Historical DiceBear 9.x choices, now generated by Clay's local server.
7
+ export var MATE_AVATAR_STYLES = [
8
+ { id: 'thumbs', name: 'Thumbs' },
9
+ { id: 'bottts', name: 'Bots' },
10
+ { id: 'pixel-art', name: 'Pixel' },
11
+ { id: 'adventurer', name: 'Adventurer' },
12
+ { id: 'micah', name: 'Micah' },
13
+ { id: 'fun-emoji', name: 'Emoji' },
14
+ { id: 'icons', name: 'Icons' },
15
+ ];
16
+
9
17
  export function avatarUrl(style, seed, size) {
10
- var cacheKey = [style || 'imprint', seed || 'anonymous', size || 64].join('|');
11
- if (imprintCache.has(cacheKey)) return imprintCache.get(cacheKey);
12
- var url = imprintDataUrl({ style: style || 'imprint', seed: seed || 'anonymous', size: size || 64 });
13
- if (imprintCache.size >= 512) imprintCache.delete(imprintCache.keys().next().value);
14
- imprintCache.set(cacheKey, url);
15
- return url;
18
+ // Legacy user styles remain part of the seed identity, never the artwork.
19
+ return imprintDataUrl({ style: style || 'imprint', seed: seed || 'anonymous', size: size || 64 });
20
+ }
21
+
22
+ export function mateStyleAvatarUrl(style, seed, size) {
23
+ var normalizedStyle = style || 'imprint';
24
+ var normalizedSeed = seed || 'anonymous';
25
+ var normalizedSize = size || 64;
26
+ var isHistorical = MATE_AVATAR_STYLES.some(function (entry) { return entry.id === normalizedStyle; });
27
+ if (isHistorical) {
28
+ return '/api/generated-avatar?style=' + encodeURIComponent(normalizedStyle)
29
+ + '&seed=' + encodeURIComponent(normalizedSeed)
30
+ + '&size=' + encodeURIComponent(normalizedSize);
31
+ }
32
+ return imprintDataUrl({ style: normalizedStyle, seed: normalizedSeed, size: normalizedSize });
16
33
  }
17
34
 
18
35
  // Use the same bounded account identity for fresh fallbacks across every surface.
@@ -46,5 +63,9 @@ export function mateAvatarUrl(mate, size) {
46
63
  var p = mate.profile || mate;
47
64
  if (p.avatarCustom || mate.avatarCustom) return p.avatarCustom || mate.avatarCustom;
48
65
  var identity = p.displayName || mate.name || mate.displayName || 'Mate';
66
+ var style = p.avatarStyle || mate.avatarStyle || 'imprint';
67
+ if (style !== 'imprint' && style !== 'initial') {
68
+ return mateStyleAvatarUrl(style, p.avatarSeed || mate.avatarSeed || mate.id || identity, size);
69
+ }
49
70
  return mateMarkUrl(identity, size);
50
71
  }
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { store } from './store.js';
4
4
  import { getMessagesEl } from './dom-refs.js';
5
+ import { getProjectAssistantPlaceholder } from './project-chat-identity.js';
5
6
 
6
7
  export var VENDOR_AVATARS = {
7
8
  claude: "/claude-code-avatar.png",
@@ -124,11 +125,22 @@ export function disarmStickyBottom() {
124
125
  }
125
126
 
126
127
  export function initRendering() {
128
+ var lastAssistantPlaceholder = '';
129
+ function updateInputPlaceholder(state) {
130
+ var inputEl = document.getElementById("input");
131
+ if (!inputEl) return;
132
+ var nextPlaceholder = getProjectAssistantPlaceholder(state, state.currentVendor);
133
+ var ownsPlaceholder = !lastAssistantPlaceholder || inputEl.placeholder === lastAssistantPlaceholder;
134
+ lastAssistantPlaceholder = nextPlaceholder;
135
+ if (inputEl.disabled || document.getElementById("input-mention-chip") || !ownsPlaceholder) return;
136
+ inputEl.placeholder = nextPlaceholder;
137
+ }
138
+ updateInputPlaceholder(store.snap());
127
139
  store.subscribe(function (state, prev) {
128
- if (state.currentVendor !== prev.currentVendor) {
129
- var inputEl = document.getElementById("input");
130
- if (inputEl) inputEl.placeholder = "Message " + (VENDOR_NAMES[state.currentVendor] || VENDOR_NAMES.claude) + "...";
131
- }
140
+ if (state.currentVendor !== prev.currentVendor || state.activeSessionMateId !== prev.activeSessionMateId ||
141
+ state.activeSessionMateName !== prev.activeSessionMateName || state.activeProjectMateId !== prev.activeProjectMateId ||
142
+ state.activeProjectMateName !== prev.activeProjectMateName || state.cachedMatesList !== prev.cachedMatesList ||
143
+ state.dmMode !== prev.dmMode || state.dmTargetUser !== prev.dmTargetUser) updateInputPlaceholder(state);
132
144
  });
133
145
  }
134
146
 
@@ -2,8 +2,6 @@ import { mateAvatarUrl } from './avatar.js';
2
2
  import { escapeHtml } from './utils.js';
3
3
  import { refreshIcons } from './icons.js';
4
4
  import { openSearch as openSessionSearch } from './session-search.js';
5
- import { showHomeHub, minimizeHomeHub } from './app-home-hub.js';
6
- import { openHomeConversation } from './home-mate-chat.js';
7
5
  import { startSearchClayChat, attachSearchClayChat, detachSearchClayChat, getSearchClayChatSummary } from './search-clay-chat.js';
8
6
  import { getWs } from './ws-ref.js';
9
7
  import { openProjectLog } from './project-logs.js';
@@ -110,13 +108,7 @@ export function handleClaySessionTarget(msg) {
110
108
  navigateToLog(msg.target);
111
109
  return true;
112
110
  }
113
- if (msg.target.isMate && msg.target.mateId && msg.target.homeSessionId) {
114
- showHomeHub();
115
- openHomeConversation(msg.target.mateId, msg.target.homeSessionId);
116
- } else {
117
- minimizeHomeHub();
118
- navigateToSession({ projectSlug: msg.target.projectSlug, sessionId: msg.target.sessionId }, null);
119
- }
111
+ navigateToSession({ projectSlug: msg.target.projectSlug, sessionId: msg.target.sessionId }, null);
120
112
  return true;
121
113
  }
122
114
 
@@ -166,7 +158,6 @@ export function handlePaletteSessionSwitch() {
166
158
 
167
159
  function navigateToLog(target) {
168
160
  closeCommandPalette();
169
- minimizeHomeHub();
170
161
  if (ctx.currentSlug && ctx.currentSlug() === target.projectSlug) {
171
162
  openProjectLog(target.ref);
172
163
  } else {
@@ -363,11 +354,7 @@ function resumeClayChat() {
363
354
  function activateItem(entry) {
364
355
  if (entry.type === "resume-clay") { resumeClayChat(); return; }
365
356
  if (entry.type === "ask-clay") { beginClayChat(entry.query); return; }
366
- if (entry.type === "mate-session") {
367
- closeCommandPalette();
368
- showHomeHub();
369
- openHomeConversation(entry.data.mateId, entry.data.sessionId);
370
- } else if (entry.type === "session") navigateToSession(entry.data, entry.query);
357
+ if (entry.type === "mate-session" || entry.type === "session") navigateToSession(entry.data, entry.query);
371
358
  }
372
359
 
373
360
  function navigateToSession(item, query) {
@@ -1,3 +1,5 @@
1
+ import { showToast } from './utils.js';
2
+ import { preparePersonalConnectionDialog } from './personal-settings-dialog.js';
1
3
  // Context Sources — attach terminal output, browser tabs, and email accounts as context for Claude
2
4
 
3
5
  var ctx = null;
@@ -433,25 +435,32 @@ export function getEmailAccountListCache() {
433
435
  return emailAccountList;
434
436
  }
435
437
 
436
- export function showEmailSetupModal() {
438
+ export function showEmailSetupModal(provider, host) {
439
+ var providerNames = { gmail: "Gmail", outlook: "Outlook Mail", yahoo: "Yahoo Mail", custom: "Custom email" };
440
+ if (!Object.hasOwn(providerNames, provider)) provider = null;
437
441
  // Remove any existing modal
438
442
  var existing = document.getElementById("email-setup-modal");
439
- if (existing) existing.remove();
443
+ if (existing) {existing.querySelector("input:not([type=hidden]),button").focus();return;}
440
444
 
441
445
  var overlay = document.createElement("div");
442
446
  overlay.id = "email-setup-modal";
443
- overlay.className = "email-setup-overlay";
444
-
445
- var providerOptions = '<option value="gmail">Gmail</option><option value="outlook">Outlook</option><option value="yahoo">Yahoo</option><option value="custom">Custom</option>';
446
-
447
+ overlay.className = host ? "email-setup-inline" : "email-setup-overlay";
448
+
449
+ if (!provider) {
450
+ var picker = document.createElement('div'); picker.className = 'email-setup-dialog';
451
+ picker.innerHTML = '<h3>Choose an email provider</h3><div class="us-integration-tiles"></div><button id="email-setup-cancel" type="button" class="email-setup-btn">Cancel</button>';
452
+ Object.keys(providerNames).forEach(function (id) {
453
+ var button = document.createElement('button'); button.type = 'button'; button.className = 'email-setup-btn'; button.textContent = providerNames[id];
454
+ button.onclick = function () { closePicker(); showEmailSetupModal(id, host); };
455
+ picker.querySelector('.us-integration-tiles').appendChild(button);
456
+ });
457
+ overlay.appendChild(picker); (host || document.body).appendChild(overlay); var closePicker = preparePersonalConnectionDialog(overlay, !!host); return;
458
+ }
447
459
  var modal = document.createElement("div");
448
460
  modal.className = "email-setup-dialog";
449
461
  modal.innerHTML =
450
- '<h3 class="email-setup-title">Add Email Account</h3>' +
451
- '<div class="email-setup-field">' +
452
- '<label class="email-setup-label">Provider</label>' +
453
- '<select id="email-setup-provider" class="email-setup-input">' + providerOptions + '</select>' +
454
- '</div>' +
462
+ '<h3 class="email-setup-title">Add ' + providerNames[provider] + ' account</h3>' +
463
+ '<input type="hidden" id="email-setup-provider" value="' + provider + '">' +
455
464
  '<div class="email-setup-field">' +
456
465
  '<label class="email-setup-label">Email Address</label>' +
457
466
  '<input id="email-setup-address" type="email" placeholder="you@example.com" class="email-setup-input" />' +
@@ -488,11 +497,12 @@ export function showEmailSetupModal() {
488
497
  '</div>';
489
498
 
490
499
  overlay.appendChild(modal);
491
- document.body.appendChild(overlay);
500
+ (host || document.body).appendChild(overlay);
501
+ preparePersonalConnectionDialog(overlay, !!host);
492
502
 
493
503
  // Event: close on overlay click
494
504
  overlay.addEventListener("click", function (e) {
495
- if (e.target === overlay) overlay.remove();
505
+ if (!host && e.target === overlay) overlay.remove();
496
506
  });
497
507
 
498
508
  // Event: cancel button
@@ -547,9 +557,7 @@ export function showEmailSetupModal() {
547
557
  }
548
558
  }
549
559
 
550
- providerSelect.addEventListener("change", function () {
551
- applyProviderMeta(providerSelect.value);
552
- });
560
+ applyProviderMeta(provider);
553
561
 
554
562
  // Reset test state
555
563
  _emailTestPassed = false;
@@ -564,7 +572,7 @@ export function showEmailSetupModal() {
564
572
  }
565
573
  document.getElementById("email-setup-address").addEventListener("input", onInputChange);
566
574
  document.getElementById("email-setup-password").addEventListener("input", onInputChange);
567
- providerSelect.addEventListener("change", onInputChange);
575
+
568
576
 
569
577
  function runTest() {
570
578
  var statusEl = document.getElementById("email-setup-status");
@@ -701,7 +709,8 @@ export function handleEmailAddResult(msg) {
701
709
 
702
710
  // Handle email_account_remove_result from server
703
711
  export function handleEmailRemoveResult(msg) {
704
- // Account removed; list will be refreshed via email_accounts_list message
712
+ showToast(msg.ok ? 'Personal mailbox disconnected.' : msg.error || 'Could not disconnect mailbox. Retry from Email settings.');
713
+ // The acknowledged list arrives separately through email_accounts_list.
705
714
  }
706
715
 
707
716
  function getSourceLabel(id) {
@@ -15,18 +15,18 @@ export function handleCursorSharingMessage(msg) {
15
15
  }
16
16
  if (msg.ready !== true || msg.accountAvailable === false || typeof msg.cursorSharing !== "boolean") {
17
17
  if (store.get("cursorSharingHydrated") && store.get("cursorSharingEnabled")) clearPresence();
18
- store.set({ cursorSharingEnabled: false, cursorSharingHydrated: false, cursorSharingAccountId: null });
18
+ store.set({ cursorSharingEnabled: false, cursorSharingHydrated: false, cursorSharingAccountId: null, cursorSharingPending: null, cursorSharingError: msg.error || "unavailable" });
19
19
  showToast(msg.error || "Cursor sharing is unavailable.", "warn");
20
20
  return true;
21
21
  }
22
22
  var knownUserId = store.get("myUserId");
23
23
  if (knownUserId && msg.accountId && knownUserId !== msg.accountId) {
24
24
  if (store.get("cursorSharingHydrated") && store.get("cursorSharingEnabled")) clearPresence();
25
- store.set({ cursorSharingEnabled: false, cursorSharingHydrated: false, cursorSharingAccountId: null });
25
+ store.set({ cursorSharingEnabled: false, cursorSharingHydrated: false, cursorSharingAccountId: null, cursorSharingPending: null, cursorSharingError: "account-changed" });
26
26
  return true;
27
27
  }
28
28
  var wasEnabled = store.get("cursorSharingHydrated") && store.get("cursorSharingEnabled");
29
- store.set({ cursorSharingEnabled: msg.cursorSharing, cursorSharingHydrated: true, cursorSharingAccountId: msg.accountId || null });
29
+ store.set({ cursorSharingEnabled: msg.cursorSharing, cursorSharingHydrated: true, cursorSharingAccountId: msg.accountId || null, cursorSharingPending: null, cursorSharingError: null });
30
30
  if (wasEnabled && !msg.cursorSharing) {
31
31
  var ws = getWs();
32
32
  if (ws && ws.readyState === 1) {