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

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 (157) hide show
  1. package/LICENSES/README.md +4 -0
  2. package/LICENSES/dicebear-avatars.md +16 -0
  3. package/lib/daemon-project-access.js +7 -1
  4. package/lib/daemon.js +2 -0
  5. package/lib/dicebear-avatar.js +62 -0
  6. package/lib/knowledge-db-service.js +175 -0
  7. package/lib/knowledge-db-validation.js +0 -0
  8. package/lib/knowledge-document-index.js +49 -0
  9. package/lib/knowledge-draft-store.js +56 -0
  10. package/lib/knowledge-file-store.js +199 -0
  11. package/lib/knowledge-markdown.js +128 -0
  12. package/lib/mate-knowledge-mcp-server.js +29 -6
  13. package/lib/mate-knowledge-service.js +23 -2
  14. package/lib/mate-knowledge-sync.js +36 -21
  15. package/lib/mate-workspace-color.js +4 -0
  16. package/lib/mates.js +18 -0
  17. package/lib/project-connection.js +36 -2
  18. package/lib/project-debate-proposal.js +6 -0
  19. package/lib/project-debate-workspace.js +43 -0
  20. package/lib/project-debate.js +1 -0
  21. package/lib/project-knowledge.js +315 -136
  22. package/lib/project-mate-creation-proposal.js +1 -0
  23. package/lib/project-sessions.js +39 -2
  24. package/lib/project-user-knowledge.js +60 -0
  25. package/lib/project.js +59 -18
  26. package/lib/public/app.js +73 -34
  27. package/lib/public/clay-studio-wordmark-bold.svg +20 -0
  28. package/lib/public/css/clay-workspace.css +105 -0
  29. package/lib/public/css/debates-workbench.css +47 -0
  30. package/lib/public/css/home-debate-live.css +26 -0
  31. package/lib/public/css/home-hub.css +3 -1
  32. package/lib/public/css/home-mate-model-picker.css +15 -131
  33. package/lib/public/css/home-mate-settings.css +168 -16
  34. package/lib/public/css/icon-strip.css +249 -32
  35. package/lib/public/css/mate-conversation.css +176 -0
  36. package/lib/public/css/mate-knowledge-workbench.css +118 -0
  37. package/lib/public/css/mate-workspace-color.css +16 -0
  38. package/lib/public/css/mates.css +28 -28
  39. package/lib/public/css/messages.css +1 -114
  40. package/lib/public/css/mobile-nav.css +16 -16
  41. package/lib/public/css/overlays.css +8 -0
  42. package/lib/public/css/project-logs.css +7 -0
  43. package/lib/public/css/project-mate-creation.css +105 -0
  44. package/lib/public/css/right-workbench.css +3 -3
  45. package/lib/public/css/session-folders.css +80 -21
  46. package/lib/public/css/sidebar.css +183 -107
  47. package/lib/public/css/title-bar.css +151 -0
  48. package/lib/public/css/worker-delegation.css +47 -0
  49. package/lib/public/css/worker-proposal.css +72 -150
  50. package/lib/public/css/you.css +74 -0
  51. package/lib/public/index.html +33 -101
  52. package/lib/public/modules/account-island.js +230 -0
  53. package/lib/public/modules/app-cursors.js +19 -43
  54. package/lib/public/modules/app-debate-ui.js +4 -3
  55. package/lib/public/modules/app-header.js +106 -0
  56. package/lib/public/modules/app-message-router.js +12 -3
  57. package/lib/public/modules/app-messages.js +34 -9
  58. package/lib/public/modules/app-notifications.js +4 -7
  59. package/lib/public/modules/app-projects.js +5 -38
  60. package/lib/public/modules/app-rendering.js +12 -13
  61. package/lib/public/modules/avatar.js +25 -9
  62. package/lib/public/modules/chat-render-runtime.js +16 -4
  63. package/lib/public/modules/command-palette.js +2 -15
  64. package/lib/public/modules/cursor-sharing-preference.js +3 -3
  65. package/lib/public/modules/debate-controls.js +299 -0
  66. package/lib/public/modules/debate.js +2 -0
  67. package/lib/public/modules/debates-workbench.js +339 -0
  68. package/lib/public/modules/default-ai.js +81 -65
  69. package/lib/public/modules/home-debate-controls.js +18 -289
  70. package/lib/public/modules/home-mate-avatar-editor.js +228 -0
  71. package/lib/public/modules/home-mate-color-picker.js +66 -0
  72. package/lib/public/modules/home-mate-creation.js +1 -1
  73. package/lib/public/modules/home-mate-instructions.js +90 -0
  74. package/lib/public/modules/home-mate-model-picker.js +80 -271
  75. package/lib/public/modules/home-mate-profile-editor.js +72 -0
  76. package/lib/public/modules/home-mate-settings.js +118 -125
  77. package/lib/public/modules/home-shell.js +3 -0
  78. package/lib/public/modules/home-surface-boot.js +1 -9
  79. package/lib/public/modules/icon-strip-tooltip.js +95 -0
  80. package/lib/public/modules/knowledge-db-edit-state.js +35 -0
  81. package/lib/public/modules/knowledge-db-workbench.js +234 -0
  82. package/lib/public/modules/knowledge-history-state.js +55 -0
  83. package/lib/public/modules/knowledge-markdown-editor.js +57 -0
  84. package/lib/public/modules/knowledge-preview-links.js +70 -0
  85. package/lib/public/modules/knowledge-workbench-shell.js +21 -0
  86. package/lib/public/modules/mate-knowledge-workbench-state.js +174 -0
  87. package/lib/public/modules/mate-knowledge-workbench.js +500 -0
  88. package/lib/public/modules/mate-knowledge.js +11 -3
  89. package/lib/public/modules/mate-management.js +54 -10
  90. package/lib/public/modules/mate-sidebar.js +11 -1
  91. package/lib/public/modules/mate-workspace-color.js +33 -0
  92. package/lib/public/modules/mention.js +5 -13
  93. package/lib/public/modules/profile.js +51 -18
  94. package/lib/public/modules/project-chat-identity.js +79 -0
  95. package/lib/public/modules/project-debate-planning-workspace.js +250 -0
  96. package/lib/public/modules/project-debate-workspace.js +185 -0
  97. package/lib/public/modules/project-header-identity.js +127 -0
  98. package/lib/public/modules/project-logs-render.js +9 -4
  99. package/lib/public/modules/project-mate-creation-workspace.js +414 -0
  100. package/lib/public/modules/project-mate-navigation.js +238 -0
  101. package/lib/public/modules/project-session-navigation.js +29 -0
  102. package/lib/public/modules/project-switcher.js +6 -8
  103. package/lib/public/modules/projects-hub-view.js +5 -6
  104. package/lib/public/modules/projects-hub.js +2 -6
  105. package/lib/public/modules/qrcode.js +27 -8
  106. package/lib/public/modules/search-clay-chat.js +8 -11
  107. package/lib/public/modules/session-activity-tooltip.js +12 -0
  108. package/lib/public/modules/session-create-form.js +86 -14
  109. package/lib/public/modules/session-create.js +50 -7
  110. package/lib/public/modules/session-list-visibility.js +41 -0
  111. package/lib/public/modules/session-presence-ui.js +4 -1
  112. package/lib/public/modules/session-title-marquee.js +51 -0
  113. package/lib/public/modules/sidebar-mates.js +3 -50
  114. package/lib/public/modules/sidebar-mobile.js +63 -60
  115. package/lib/public/modules/sidebar-projects.js +84 -97
  116. package/lib/public/modules/sidebar-session-folders-ui.js +2 -2
  117. package/lib/public/modules/sidebar-session-hierarchy.js +149 -70
  118. package/lib/public/modules/sidebar-sessions.js +49 -50
  119. package/lib/public/modules/sidebar.js +11 -13
  120. package/lib/public/modules/thinking-lifecycle.js +7 -6
  121. package/lib/public/modules/tool-palette-order.js +4 -3
  122. package/lib/public/modules/tool-palette.js +13 -3
  123. package/lib/public/modules/tools.js +22 -22
  124. package/lib/public/modules/worker-proposal.js +42 -8
  125. package/lib/public/modules/worker-runtime-picker.js +115 -0
  126. package/lib/public/modules/you-activity.js +121 -0
  127. package/lib/public/modules/you.js +251 -0
  128. package/lib/public/style.css +16 -8
  129. package/lib/public/vendor/codemirror/LICENSE +21 -0
  130. package/lib/public/vendor/codemirror/codemirror.css +344 -0
  131. package/lib/public/vendor/codemirror/codemirror.js +9884 -0
  132. package/lib/public/vendor/codemirror/continuelist.js +101 -0
  133. package/lib/public/vendor/codemirror/markdown.js +886 -0
  134. package/lib/public/vendor/codemirror/show-hint.css +37 -0
  135. package/lib/public/vendor/codemirror/show-hint.js +523 -0
  136. package/lib/sdk-bridge.js +15 -3
  137. package/lib/server-admin.js +11 -0
  138. package/lib/server-home-chat-events.js +1 -1
  139. package/lib/server-home-chat.js +25 -3
  140. package/lib/server-home-debate-planning.js +4 -1
  141. package/lib/server-home-debates.js +2 -0
  142. package/lib/server-home-mate-creation.js +4 -1
  143. package/lib/server-mate-instructions.js +44 -0
  144. package/lib/server-mates.js +12 -3
  145. package/lib/server-palette.js +1 -1
  146. package/lib/server-settings.js +65 -1
  147. package/lib/server-user-knowledge.js +119 -0
  148. package/lib/server.js +21 -1
  149. package/lib/session-visibility.js +7 -2
  150. package/lib/sessions.js +32 -6
  151. package/lib/user-knowledge-service.js +167 -0
  152. package/lib/users-permissions.js +7 -0
  153. package/lib/users-project-mate-filter-preferences.js +39 -0
  154. package/lib/users.js +6 -0
  155. package/lib/workspace-query-access.js +4 -1
  156. package/lib/ws-schema.js +50 -0
  157. package/package.json +10 -1
@@ -2,7 +2,8 @@
2
2
 
3
3
  import { getWs } from './ws-ref.js';
4
4
  import { showMateProfilePopover } from './profile.js';
5
- import { showConfirm } from './app-misc.js';
5
+ import { showToast } from './utils.js';
6
+ import { showConfirm, hideConfirm } from './app-misc.js';
6
7
  import { iconHtml, refreshIcons } from './icons.js';
7
8
 
8
9
  var mateMenu = null;
@@ -21,15 +22,58 @@ export function editMateProfile(anchorEl, mate) {
21
22
 
22
23
  export function confirmMateRemoval(anchorEl, mate, onRemoved) {
23
24
  if (!anchorEl || !mate || mate.primary) return;
24
- showConfirm(
25
- mate.builtinKey ? "Remove this mate? You can restore it from the home switcher." : "Delete this mate permanently?",
26
- function () {
27
- if (onRemoved) onRemoved();
28
- send({ type: "mate_delete", mateId: mate.id });
29
- },
30
- mate.builtinKey ? "Remove" : "Delete",
31
- true
32
- );
25
+ var name = (mate.profile || {}).displayName || mate.name || "this Mate";
26
+ var modal = document.getElementById("confirm-modal");
27
+ var panel = modal.querySelector(".confirm-dialog");
28
+ var ok = document.getElementById("confirm-ok");
29
+ var cancel = document.getElementById("confirm-cancel");
30
+ var input;
31
+ function cleanup() {
32
+ modal.classList.remove("mate-delete-confirm");
33
+ panel.removeAttribute("role"); panel.removeAttribute("aria-modal");
34
+ panel.removeAttribute("aria-labelledby"); panel.removeAttribute("aria-describedby");
35
+ var field = modal.querySelector(".mate-delete-challenge"); if (field) field.remove();
36
+ ok.disabled = false;
37
+ document.removeEventListener("keydown", onKey, true);
38
+ if (anchorEl.isConnected) anchorEl.focus({preventScroll:true});
39
+ }
40
+ function onKey(event) {
41
+ if (event.key === "Escape") { event.preventDefault(); event.stopImmediatePropagation(); hideConfirm(); return; }
42
+ if (event.key !== "Tab") return;
43
+ var controls = ok.disabled ? [input,cancel] : [input,cancel,ok];
44
+ var index = controls.indexOf(document.activeElement);
45
+ event.preventDefault(); event.stopImmediatePropagation();
46
+ controls[(index + (event.shiftKey ? controls.length - 1 : 1)) % controls.length].focus();
47
+ }
48
+ showConfirm("", function () {
49
+ if (input.value !== "understand") { cleanup(); return; }
50
+ var ws = getWs();
51
+ if (!ws || ws.readyState !== 1) { cleanup(); showToast("Clay is offline. The Mate was not deleted.", "error"); return; }
52
+ try { ws.send(JSON.stringify({type:"mate_delete",mateId:mate.id})); }
53
+ catch (error) { cleanup(); showToast("Could not send the deletion request. Try again.", "error"); return; }
54
+ cleanup();
55
+ if (onRemoved) onRemoved(true);
56
+ }, mate.builtinKey ? "Remove permanently" : "Delete permanently", true, cleanup);
57
+ modal.classList.add("mate-delete-confirm");
58
+ panel.setAttribute("role","alertdialog"); panel.setAttribute("aria-modal","true");
59
+ panel.setAttribute("aria-labelledby","mate-delete-title"); panel.setAttribute("aria-describedby","mate-delete-warning");
60
+ var text = document.getElementById("confirm-text");
61
+ var title = document.createElement("h3"); title.id = "mate-delete-title"; title.textContent = "Delete " + name + "?";
62
+ var warning = document.createElement("p"); warning.id = "mate-delete-warning";
63
+ warning.textContent = "This permanently deletes the Mate and the data stored in its workspace, including its prompt and files. This cannot be undone.";
64
+ text.appendChild(title); text.appendChild(warning);
65
+ if (mate.builtinKey) {
66
+ var note = document.createElement("p"); note.textContent = "You can add this built-in Mate again, but its deleted data will not be restored."; text.appendChild(note);
67
+ }
68
+ var field = document.createElement("label"); field.className = "mate-delete-challenge";
69
+ field.textContent = 'Type understand to confirm permanent deletion.';
70
+ input = document.createElement("input"); input.type = "text"; input.autocomplete = "off"; input.spellcheck = false;
71
+ input.setAttribute("autocapitalize","none"); input.setAttribute("aria-label","Type understand to confirm permanent deletion");
72
+ field.appendChild(input); text.after(field);
73
+ ok.disabled = true;
74
+ input.addEventListener("input",function () { ok.disabled = input.value !== "understand"; });
75
+ document.addEventListener("keydown",onKey,true);
76
+ input.focus();
33
77
  }
34
78
 
35
79
  export function closeMateManagementMenu() {
@@ -394,6 +394,16 @@ export function handleMateSearchResults(msg) {
394
394
 
395
395
  var expandedDebateGroups = new Set();
396
396
 
397
+ function appendMateSessionTitleLine(el, textSpan) {
398
+ var processingDot = textSpan.querySelector('.session-processing');
399
+ if (processingDot) processingDot.remove();
400
+ var titleLine = document.createElement('span');
401
+ titleLine.className = 'mate-session-title-line';
402
+ titleLine.appendChild(textSpan);
403
+ if (processingDot) titleLine.appendChild(processingDot);
404
+ el.appendChild(titleLine);
405
+ }
406
+
397
407
  export function renderMateSessionList(sessions) {
398
408
  if (sessions) cachedSessions = sessions;
399
409
  if (!listEl) return;
@@ -529,7 +539,7 @@ function renderDebateGroup(children, groupKey) {
529
539
  textHtml += '<span class="mate-debate-name">' + escapeHtml(debateName) + '</span>';
530
540
  textHtml += '<span class="mate-debate-count">' + children.length + '</span>';
531
541
  textSpan.innerHTML = textHtml;
532
- el.appendChild(textSpan);
542
+ appendMateSessionTitleLine(el, textSpan);
533
543
 
534
544
  // Click row -> switch to latest session
535
545
  el.addEventListener("click", (function (id) {
@@ -0,0 +1,33 @@
1
+ // Theme-specific surfaces keep user color choices separate from text and actions.
2
+ export var MATE_WORKSPACE_COLORS = [
3
+ { id: 'mint', label: 'Mint', pigment: '#07e5a3' },
4
+ { id: 'rose', label: 'Rose', pigment: '#ec82ac' },
5
+ { id: 'plum', label: 'Plum', pigment: '#b780b3' },
6
+ { id: 'coral', label: 'Coral', pigment: '#ff7479' },
7
+ { id: 'peach', label: 'Peach', pigment: '#ed986b' },
8
+ { id: 'gold', label: 'Gold', pigment: '#e0b45b' },
9
+ { id: 'sky', label: 'Sky', pigment: '#69b6d4' },
10
+ { id: 'indigo', label: 'Indigo', pigment: '#8582df' },
11
+ { id: 'stone', label: 'Stone', pigment: '#a5a59e' }
12
+ ];
13
+ function mix(pigment, base, amount) {
14
+ var channels = [1, 3, 5].map(function (offset) {
15
+ var a = parseInt(pigment.slice(offset, offset + 2), 16);
16
+ var b = parseInt(base.slice(offset, offset + 2), 16);
17
+ return Math.round(a * amount + b * (1 - amount)).toString(16).padStart(2, '0');
18
+ });
19
+ return '#' + channels.join('');
20
+ }
21
+ export function workspaceColor(mate) {
22
+ var id = mate && mate.builtinKey !== 'clay' && !mate.primary ? mate.workspaceColor : 'mint';
23
+ var color = MATE_WORKSPACE_COLORS.find(function (item) { return item.id === id; }) || MATE_WORKSPACE_COLORS[0];
24
+ return Object.assign({}, color, {
25
+ light: mix(color.pigment, '#f2f2ef', color.id === 'mint' ? 0.03 : 0.08),
26
+ dark: mix(color.pigment, '#232322', color.id === 'mint' ? 0.02 : 0.10)
27
+ });
28
+ }
29
+ export function applyWorkspaceColor(mate) {
30
+ var color = workspaceColor(mate);
31
+ document.body.style.setProperty('--mate-surround-light', color.light);
32
+ document.body.style.setProperty('--mate-surround-dark', color.dark);
33
+ }
@@ -3,6 +3,7 @@ import { renderMarkdown, highlightCodeBlocks } from './markdown.js';
3
3
  import { escapeHtml, copyToClipboard } from './utils.js';
4
4
  import { iconHtml, refreshIcons } from './icons.js';
5
5
  import { store } from './store.js';
6
+ import { getProjectAssistantPlaceholder } from './project-chat-identity.js';
6
7
 
7
8
  var ctx;
8
9
 
@@ -311,10 +312,6 @@ function color_mix_lighten(r, g, b, amount) {
311
312
  .map(function (v) { return v.toString(16).padStart(2, "0"); }).join("");
312
313
  }
313
314
 
314
- // Saved textarea placeholder so we can restore it when the chip is removed.
315
- // Set on first chip show, cleared on chip removal.
316
- var savedInputPlaceholder = null;
317
-
318
315
  function showInputMentionChip(name, color, avatarSrc) {
319
316
  removeInputMentionChip();
320
317
  var textColor = ensureChipContrast(color);
@@ -333,13 +330,9 @@ function showInputMentionChip(name, color, avatarSrc) {
333
330
  inputRow.insertBefore(chip, textareaWrap);
334
331
  }
335
332
 
336
- // Swap the textarea placeholder so the user sees "Ask @{name}..." instead of
337
- // the session-vendor placeholder ("Message Claude Code...") while a mention
338
- // is queued. Save the original once so re-shows don't clobber it.
333
+ // Swap the textarea placeholder so the user sees "Ask @{name}..." while a
334
+ // mention is queued. Restoration is handled only while this prompt owns it.
339
335
  if (ctx && ctx.inputEl) {
340
- if (savedInputPlaceholder === null) {
341
- savedInputPlaceholder = ctx.inputEl.placeholder || "";
342
- }
343
336
  ctx.inputEl.placeholder = "Ask @" + name + "...";
344
337
  }
345
338
 
@@ -353,9 +346,8 @@ function showInputMentionChip(name, color, avatarSrc) {
353
346
  function removeInputMentionChip() {
354
347
  var existing = document.getElementById("input-mention-chip");
355
348
  if (existing) existing.remove();
356
- if (ctx && ctx.inputEl && savedInputPlaceholder !== null) {
357
- ctx.inputEl.placeholder = savedInputPlaceholder;
358
- savedInputPlaceholder = null;
349
+ if (existing && ctx && ctx.inputEl && !ctx.inputEl.disabled && /^Ask @/.test(ctx.inputEl.placeholder || "")) {
350
+ ctx.inputEl.placeholder = getProjectAssistantPlaceholder(store.snap(), store.get('currentVendor'));
359
351
  }
360
352
  }
361
353
 
@@ -84,6 +84,44 @@ export function applyToIsland() {
84
84
  ctaEl.classList.add('hidden');
85
85
  }
86
86
  }
87
+ document.dispatchEvent(new CustomEvent('clay:profile-updated'));
88
+ }
89
+
90
+ export function updateProfileName(name) {
91
+ profile.name = String(name || '').trim();
92
+ applyToIsland();
93
+ debouncedSave();
94
+ }
95
+
96
+ export function usePixelAvatar() {
97
+ profile.avatarCustom = '';
98
+ applyToIsland();
99
+ debouncedSave();
100
+ }
101
+
102
+ export function regenerateProfileAvatar() {
103
+ profile.avatarCustom = '';
104
+ profile.avatarSeed = 'clay-' + Date.now();
105
+ applyToIsland();
106
+ debouncedSave();
107
+ }
108
+
109
+ export function uploadAvatarBlob(blob) {
110
+ if (!blob) return Promise.reject(new Error('Missing avatar image.'));
111
+ return blob.arrayBuffer().then(function(ab) {
112
+ var buf = new Uint8Array(ab);
113
+ return fetch('/api/avatar', {
114
+ method: 'POST',
115
+ headers: { 'Content-Type': 'application/octet-stream' },
116
+ body: buf,
117
+ });
118
+ }).then(function(r) { return r.json(); }).then(function(data) {
119
+ if (!data.ok || !data.avatar) throw new Error(data.error || 'Avatar upload failed.');
120
+ profile.avatarCustom = data.avatar;
121
+ applyToIsland();
122
+ debouncedSave();
123
+ return data;
124
+ });
87
125
  }
88
126
 
89
127
  // --- Avatar position picker ---
@@ -128,6 +166,8 @@ export function showAvatarPositioner(img, objectUrl, onDone) {
128
166
 
129
167
  var closeBtn = document.createElement('button');
130
168
  closeBtn.className = 'avatar-positioner-close';
169
+ closeBtn.type = 'button';
170
+ closeBtn.setAttribute('aria-label', 'Close avatar crop editor');
131
171
  closeBtn.innerHTML = '&times;';
132
172
  container.appendChild(closeBtn);
133
173
 
@@ -239,12 +279,21 @@ export function showAvatarPositioner(img, objectUrl, onDone) {
239
279
  }, { passive: false });
240
280
 
241
281
  function cleanup(revoke) {
282
+ document.removeEventListener('keydown', onOverlayKeydown);
242
283
  overlay.remove();
243
284
  if (revoke) URL.revokeObjectURL(objectUrl);
244
285
  }
245
286
 
287
+ function onOverlayKeydown(event) {
288
+ if (event.key !== 'Escape') return;
289
+ event.preventDefault();
290
+ event.stopPropagation();
291
+ cleanup(true);
292
+ }
293
+
246
294
  closeBtn.addEventListener('click', function() { cleanup(true); });
247
295
  cancelBtn.addEventListener('click', function() { cleanup(true); });
296
+ document.addEventListener('keydown', onOverlayKeydown);
248
297
 
249
298
  doneBtn.addEventListener('click', function() {
250
299
  // Render cropped area to canvas
@@ -269,7 +318,7 @@ export function showAvatarPositioner(img, objectUrl, onDone) {
269
318
  }
270
319
 
271
320
  // --- Popover ---
272
- function showPopover() {
321
+ export function openProfileEditor() {
273
322
  if (popoverEl) {
274
323
  hidePopover();
275
324
  return;
@@ -286,7 +335,7 @@ function showPopover() {
286
335
 
287
336
  // Banner + close
288
337
  html += '<div class="profile-banner">';
289
- html += '<button class="profile-close-btn">&times;</button>';
338
+ html += '<button class="profile-close-btn" type="button" aria-label="Close profile editor">&times;</button>';
290
339
  html += '</div>';
291
340
 
292
341
  // Avatar row (overlapping banner)
@@ -485,22 +534,6 @@ export function initProfile(_ctx) {
485
534
  var island = document.getElementById('user-island');
486
535
  if (!island) return;
487
536
 
488
- var profileArea = island.querySelector('.user-island-profile');
489
- if (profileArea) {
490
- profileArea.addEventListener('click', function(e) {
491
- e.stopPropagation();
492
- showPopover();
493
- });
494
- }
495
-
496
- var ctaEl = island.querySelector('.user-island-cta');
497
- if (ctaEl) {
498
- ctaEl.addEventListener('click', function(e) {
499
- e.stopPropagation();
500
- showPopover();
501
- });
502
- }
503
-
504
537
  fetchProfile().then(function(data) {
505
538
  if (data.name !== undefined) profile.name = data.name;
506
539
  if (data.lang) profile.lang = data.lang;
@@ -0,0 +1,79 @@
1
+ // Session-bound assistant identity for ordinary project and Mate transcripts.
2
+
3
+ import { store } from './store.js';
4
+ import { mateAvatarUrl } from './avatar.js';
5
+ import { VENDOR_AVATARS, VENDOR_NAMES } from './chat-render-runtime.js';
6
+
7
+ function mateById(state, mateId) {
8
+ var mates = state.cachedMatesList || [];
9
+ for (var i = 0; i < mates.length; i++) {
10
+ if (mates[i] && mates[i].id === mateId) return mates[i];
11
+ }
12
+ return null;
13
+ }
14
+
15
+ function mateName(mate, fallback) {
16
+ var profile = mate && mate.profile ? mate.profile : {};
17
+ return profile.displayName || (mate && (mate.displayName || mate.name)) || fallback || 'Mate';
18
+ }
19
+
20
+ export function resolveProjectAssistantIdentity(state, vendor) {
21
+ state = state || store.snap();
22
+ var dmTarget = state.dmMode && state.dmTargetUser && state.dmTargetUser.isMate ? state.dmTargetUser : null;
23
+ var mateId = dmTarget ? dmTarget.id : state.activeSessionMateId;
24
+ if (mateId) {
25
+ var mate = mateById(state, mateId) || dmTarget;
26
+ var fallbackName = dmTarget && dmTarget.displayName || state.activeSessionMateName || 'Mate';
27
+ var avatarMate = mate || { id: mateId, name: fallbackName, profile: { displayName: fallbackName } };
28
+ return {
29
+ kind: 'mate',
30
+ mateId: mateId,
31
+ name: mateName(mate, fallbackName),
32
+ avatarUrl: mateAvatarUrl(avatarMate, 34),
33
+ };
34
+ }
35
+ var vendorId = vendor || state.currentVendor || 'claude';
36
+ return {
37
+ kind: 'provider',
38
+ mateId: '',
39
+ name: VENDOR_NAMES[vendorId] || VENDOR_NAMES.claude,
40
+ avatarUrl: VENDOR_AVATARS[vendorId] || VENDOR_AVATARS.claude,
41
+ };
42
+ }
43
+
44
+ export function getProjectAssistantPlaceholder(state, vendor) {
45
+ return 'Message ' + resolveProjectAssistantIdentity(state, vendor).name + '...';
46
+ }
47
+
48
+ export function applyProjectAssistantIdentity(element, identity) {
49
+ if (!element || !identity) return element;
50
+ var avatar = element.querySelector('.dm-bubble-avatar');
51
+ var name = element.querySelector('.dm-bubble-name');
52
+ if (avatar) avatar.src = identity.avatarUrl;
53
+ if (name) name.textContent = identity.name;
54
+ if (identity.kind === 'mate') {
55
+ element.dataset.sessionMateId = identity.mateId;
56
+ element.dataset.sessionMateName = identity.name;
57
+ } else {
58
+ delete element.dataset.sessionMateId;
59
+ delete element.dataset.sessionMateName;
60
+ }
61
+ return element;
62
+ }
63
+
64
+ export function refreshProjectAssistantIdentities(root) {
65
+ var target = root || document;
66
+ var elements = target.querySelectorAll('[data-session-mate-id]');
67
+ var state = store.snap();
68
+ for (var i = 0; i < elements.length; i++) {
69
+ var mateId = elements[i].dataset.sessionMateId;
70
+ var mate = mateById(state, mateId);
71
+ if (!mate) continue;
72
+ applyProjectAssistantIdentity(elements[i], {
73
+ kind: 'mate',
74
+ mateId: mateId,
75
+ name: mateName(mate, elements[i].dataset.sessionMateName || 'Mate'),
76
+ avatarUrl: mateAvatarUrl(mate, 34),
77
+ });
78
+ }
79
+ }
@@ -0,0 +1,250 @@
1
+ // Project-chat adapter for Clay's existing server-owned debate setup flow.
2
+
3
+ import { store } from './store.js';
4
+ import { getWs } from './ws-ref.js';
5
+ import { addToMessages, scrollToBottom } from './chat-render-runtime.js';
6
+ import { openMateWorkspace } from './project-mate-navigation.js';
7
+ import { createHomeDebateQuestionCard, createHomeDebateProposalCard, normalizeHomeTranscript, applyHomeDebateProposal, resolveHomeDebateProposal, failHomeDebateQuestion } from './home-debate-planning.js';
8
+ import { refreshIcons, iconHtml } from './icons.js';
9
+
10
+ var root = null;
11
+ var sequence = 0;
12
+ var liveReplaySessionId = null;
13
+ var suppressLiveSetupReplay = false;
14
+
15
+ function send(message) {
16
+ var ws = getWs();
17
+ if (!ws || ws.readyState !== 1) return false;
18
+ ws.send(JSON.stringify(message));
19
+ return true;
20
+ }
21
+
22
+ function clayMate() {
23
+ var mates = store.get('cachedMatesList') || [];
24
+ for (var i = 0; i < mates.length; i++) if (mates[i] && mates[i].builtinKey === 'clay') return mates[i];
25
+ return null;
26
+ }
27
+
28
+ function requestId() { sequence += 1; return 'project-debate-planning-' + Date.now() + '-' + sequence; }
29
+ function same(left, right) { return left != null && right != null && String(left) === String(right); }
30
+
31
+ function context() {
32
+ return { mateId: store.get('projectDebatePlanningMateId'), sessionId: store.get('projectDebatePlanningSessionId'), requestId: store.get('projectDebatePlanningRequestId') };
33
+ }
34
+
35
+ function messages() { return store.get('projectDebatePlanningMessages') || []; }
36
+ function setMessages(value) { store.set({ projectDebatePlanningMessages: value }); }
37
+
38
+ function retryPlanning() {
39
+ var state = store.snap();
40
+ store.set({ projectDebatePlanningError: '', projectDebatePlanningRetryable: false });
41
+ if (!state.projectDebatePlanningSessionId) {
42
+ store.set({ projectDebatePlanningPendingStart: true });
43
+ maybeStart();
44
+ return;
45
+ }
46
+ var id = requestId();
47
+ if (!send({ type: 'home_mate_debate_plan', mateId: state.projectDebatePlanningMateId, sessionId: state.projectDebatePlanningSessionId, topic: state.projectDebatePlanningTopic, requestId: id })) {
48
+ store.set({ projectDebatePlanningError: 'Reconnect to retry debate setup.', projectDebatePlanningRetryable: true });
49
+ render();
50
+ return;
51
+ }
52
+ store.set({ projectDebatePlanningRequestId: id, projectDebatePlanningAwaitingHistory: true });
53
+ }
54
+
55
+ function ensureRoot() {
56
+ if (root && root.isConnected) return root;
57
+ root = document.createElement('section');
58
+ root.className = 'project-debate-planning-transcript home-mate-chat-transcript home-chat-bubble-layout';
59
+ root.setAttribute('aria-label', 'Debate setup');
60
+ addToMessages(root);
61
+ return root;
62
+ }
63
+
64
+ function respondToQuestion(message, action, opener, answers) {
65
+ if (!message || message.status === 'submitting') return;
66
+ if (!send(Object.assign({ type: 'home_debate_question_response', toolId: message.toolId, answers: answers || {} }, context()))) return;
67
+ setMessages(messages().map(function (item) { return item === message ? Object.assign({}, item, { status: 'submitting' }) : item; }));
68
+ render();
69
+ }
70
+
71
+ function respondToProposal(message, action, opener, answers, modelOverrides) {
72
+ if (!message || message.status === 'submitting') return;
73
+ if (!send(Object.assign({ type: 'home_debate_proposal_response', proposalId: message.proposal.proposalId, action: action === 'start' ? 'start' : 'cancel', modelOverrides: modelOverrides || [] }, context()))) return;
74
+ setMessages(messages().map(function (item) { return item === message ? Object.assign({}, item, { status: 'submitting' }) : item; }));
75
+ render();
76
+ }
77
+
78
+ function render() {
79
+ var container = ensureRoot();
80
+ container.innerHTML = '';
81
+ var intro = document.createElement('header');
82
+ intro.className = 'project-mate-creation-intro';
83
+ intro.innerHTML = iconHtml('messages-square') + '<span>Plan a debate with Clay</span>';
84
+ container.appendChild(intro);
85
+ var list = messages();
86
+ for (var i = 0; i < list.length; i++) {
87
+ if (list[i].role === 'question') container.appendChild(createHomeDebateQuestionCard(list[i], respondToQuestion));
88
+ if (list[i].role === 'proposal') container.appendChild(createHomeDebateProposalCard(list[i], respondToProposal));
89
+ }
90
+ var errorText = store.get('projectDebatePlanningError');
91
+ if (errorText) {
92
+ var error = document.createElement('section');
93
+ error.className = 'project-debate-planning-error';
94
+ error.setAttribute('role', 'alert');
95
+ var copy = document.createElement('p');
96
+ copy.textContent = errorText;
97
+ error.appendChild(copy);
98
+ if (store.get('projectDebatePlanningRetryable')) {
99
+ var retry = document.createElement('button');
100
+ retry.type = 'button';
101
+ retry.textContent = 'Retry debate setup';
102
+ retry.addEventListener('click', retryPlanning);
103
+ error.appendChild(retry);
104
+ }
105
+ container.appendChild(error);
106
+ }
107
+ scrollToBottom();
108
+ refreshIcons();
109
+ }
110
+
111
+ function clear() {
112
+ root = null;
113
+ store.set({ projectDebatePlanningActive: false, projectDebatePlanningMateId: null, projectDebatePlanningSessionId: null, projectDebatePlanningLocalId: null, projectDebatePlanningRequestId: null, projectDebatePlanningPendingStart: false, projectDebatePlanningAwaitingHistory: false, projectDebatePlanningAwaitingMateList: false, projectDebatePlanningHistoryReady: false, projectDebatePlanningTopic: '', projectDebatePlanningMessages: [], projectDebatePlanningError: '', projectDebatePlanningRetryable: false });
114
+ }
115
+
116
+ function maybeStart() {
117
+ var state = store.snap();
118
+ if (!state.projectDebatePlanningPendingStart || !state.connected || state.activeProjectSlug !== 'mate-' + state.projectDebatePlanningMateId) return;
119
+ var id = requestId();
120
+ if (!send({ type: 'home_mate_debate_plan', mateId: state.projectDebatePlanningMateId, topic: state.projectDebatePlanningTopic, requestId: id })) return;
121
+ store.set({ projectDebatePlanningPendingStart: false, projectDebatePlanningAwaitingHistory: true, projectDebatePlanningRequestId: id });
122
+ }
123
+
124
+ export function startProjectDebatePlanning(topic) {
125
+ var clay = clayMate();
126
+ if (!clay) {
127
+ store.set({ projectDebatePlanningError: 'Clay is unavailable. Refresh and try again.', projectDebatePlanningRetryable: false });
128
+ return false;
129
+ }
130
+ root = null;
131
+ store.set({ projectDebatePlanningActive: true, projectDebatePlanningMateId: clay.id, projectDebatePlanningSessionId: null, projectDebatePlanningLocalId: null, projectDebatePlanningRequestId: null, projectDebatePlanningPendingStart: true, projectDebatePlanningAwaitingHistory: false, projectDebatePlanningAwaitingMateList: false, projectDebatePlanningHistoryReady: false, projectDebatePlanningTopic: typeof topic === 'string' ? topic.trim().slice(0, 1000) : '', projectDebatePlanningMessages: [], projectDebatePlanningError: '', projectDebatePlanningRetryable: false });
132
+ openMateWorkspace(clay.id);
133
+ maybeStart();
134
+ return true;
135
+ }
136
+
137
+ function exact(msg, allowUnbound) {
138
+ var state = store.snap();
139
+ if (!state.projectDebatePlanningActive || !same(msg.mateId, state.projectDebatePlanningMateId) || !same(msg.requestId, state.projectDebatePlanningRequestId)) return false;
140
+ if (!state.projectDebatePlanningSessionId) return allowUnbound === true;
141
+ return same(msg.sessionId, state.projectDebatePlanningSessionId);
142
+ }
143
+
144
+ function openHistory(msg) {
145
+ if (!exact(msg, store.get('projectDebatePlanningAwaitingHistory') && !store.get('projectDebatePlanningSessionId'))) return false;
146
+ store.set({ projectDebatePlanningSessionId: msg.sessionId, projectDebatePlanningLocalId: typeof msg.localId === 'number' ? msg.localId : store.get('projectDebatePlanningLocalId'), projectDebatePlanningAwaitingHistory: false });
147
+ if (typeof msg.localId === 'number' && String(store.get('activeSessionId')) !== String(msg.localId)) {
148
+ send({ type: 'switch_session', id: msg.localId });
149
+ return true;
150
+ }
151
+ setMessages(normalizeHomeTranscript(msg.messages || []).filter(function (message) { return message.role === 'question' || message.role === 'proposal'; }));
152
+ render();
153
+ return true;
154
+ }
155
+
156
+ function resume() {
157
+ var state = store.snap();
158
+ if (!state.projectDebatePlanningActive || !state.projectDebatePlanningMateId || !state.projectDebatePlanningSessionId) return;
159
+ var id = requestId();
160
+ if (!send({ type: 'home_mate_session_open', mateId: state.projectDebatePlanningMateId, sessionId: state.projectDebatePlanningSessionId, requestId: id })) return;
161
+ store.set({ projectDebatePlanningRequestId: id, projectDebatePlanningAwaitingHistory: true });
162
+ }
163
+
164
+ function activateRestored(msg, clay) {
165
+ root = null;
166
+ store.set({ projectDebatePlanningActive: true, projectDebatePlanningMateId: clay.id, projectDebatePlanningSessionId: msg.cliSessionId || 'local:' + msg.id, projectDebatePlanningLocalId: msg.id, projectDebatePlanningRequestId: requestId(), projectDebatePlanningPendingStart: false, projectDebatePlanningAwaitingHistory: false, projectDebatePlanningAwaitingMateList: false, projectDebatePlanningHistoryReady: false, projectDebatePlanningTopic: '', projectDebatePlanningMessages: [] });
167
+ }
168
+
169
+ function restoreAfterMateList() {
170
+ var state = store.snap();
171
+ if (!state.projectDebatePlanningAwaitingMateList) return;
172
+ var clay = clayMate();
173
+ if (!clay || state.currentSlug !== 'mate-' + clay.id || state.projectDebatePlanningLocalId == null) return;
174
+ var historyReady = state.projectDebatePlanningHistoryReady;
175
+ activateRestored({ id: state.projectDebatePlanningLocalId, cliSessionId: state.projectDebatePlanningSessionId && state.projectDebatePlanningSessionId.indexOf('local:') !== 0 ? state.projectDebatePlanningSessionId : null }, clay);
176
+ if (historyReady) resume();
177
+ }
178
+
179
+ export function handleProjectDebatePlanningMessage(msg) {
180
+ if (msg.type === 'session_switched') {
181
+ if (msg.debatePlanning === true && msg.debatePhase === 'live') {
182
+ clear();
183
+ liveReplaySessionId = msg.id;
184
+ suppressLiveSetupReplay = true;
185
+ return false;
186
+ }
187
+ liveReplaySessionId = null;
188
+ suppressLiveSetupReplay = false;
189
+ var clay = clayMate();
190
+ var state = store.snap();
191
+ var starting = msg.debatePlanning === true && state.projectDebatePlanningActive && state.projectDebatePlanningAwaitingHistory && !state.projectDebatePlanningSessionId && state.currentSlug === 'mate-' + state.projectDebatePlanningMateId;
192
+ if (starting) store.set({ projectDebatePlanningSessionId: msg.cliSessionId || 'local:' + msg.id, projectDebatePlanningLocalId: msg.id, projectDebatePlanningMessages: [] });
193
+ else if (msg.debatePlanning === true && clay && state.currentSlug === 'mate-' + clay.id) activateRestored(msg, clay);
194
+ else if (msg.debatePlanning === true && !clay && /^mate-/.test(state.currentSlug || '')) store.set({ projectDebatePlanningActive: false, projectDebatePlanningAwaitingMateList: true, projectDebatePlanningHistoryReady: false, projectDebatePlanningLocalId: msg.id, projectDebatePlanningSessionId: msg.cliSessionId || 'local:' + msg.id, projectDebatePlanningMessages: [] });
195
+ else if (!(state.projectDebatePlanningActive && (state.projectDebatePlanningPendingStart || state.projectDebatePlanningAwaitingHistory) && state.currentSlug === 'mate-' + state.projectDebatePlanningMateId && !state.projectDebatePlanningSessionId)) clear();
196
+ return false;
197
+ }
198
+ if (msg.type === 'home_mate_session_identity') {
199
+ var identityState = store.snap();
200
+ if (!identityState.projectDebatePlanningActive || !same(msg.mateId, identityState.projectDebatePlanningMateId) || !same(msg.requestId, identityState.projectDebatePlanningRequestId) || !same(msg.previousSessionId, identityState.projectDebatePlanningSessionId) || !msg.sessionId) return false;
201
+ store.set({ projectDebatePlanningSessionId: msg.sessionId });
202
+ return true;
203
+ }
204
+ if (msg.type === 'home_debate_event' && msg.eventType === 'debate_started') {
205
+ suppressLiveSetupReplay = false;
206
+ if (store.get('projectDebatePlanningActive')) clear();
207
+ return false;
208
+ }
209
+ if (msg.type === 'debate_started' && suppressLiveSetupReplay && same(store.get('activeSessionId'), liveReplaySessionId)) {
210
+ suppressLiveSetupReplay = false;
211
+ return false;
212
+ }
213
+ if (suppressLiveSetupReplay && same(store.get('activeSessionId'), liveReplaySessionId)
214
+ && ((msg.type === 'tool_executing' && msg.name === 'AskUserQuestion') || msg.type === 'ask_user_answered'
215
+ || msg.type === 'debate_proposal' || msg.type === 'debate_proposal_resolved' || msg.type === 'delta'
216
+ || msg.type === 'result' || msg.type === 'done' || msg.type === 'error')) return true;
217
+ if (msg.type === 'history_done') {
218
+ if (store.get('projectDebatePlanningAwaitingMateList')) store.set({ projectDebatePlanningHistoryReady: true });
219
+ else if (!store.get('projectDebatePlanningAwaitingHistory')) resume();
220
+ return false;
221
+ }
222
+ var raw = (msg.type === 'tool_executing' && msg.name === 'AskUserQuestion') || msg.type === 'debate_proposal' || msg.type === 'debate_proposal_resolved' || msg.type === 'ask_user_answered' || msg.type === 'delta' || msg.type === 'result' || msg.type === 'done' || msg.type === 'error' || msg.type === 'home_mate_delta' || msg.type === 'home_mate_done';
223
+ if (raw && store.get('projectDebatePlanningAwaitingMateList')) return same(store.get('activeSessionId'), store.get('projectDebatePlanningLocalId'));
224
+ if (!store.get('projectDebatePlanningActive')) return false;
225
+ if (msg.type === 'home_mate_history' && msg.debatePlanning === true) return openHistory(msg);
226
+ if (raw) return same(store.get('activeSessionId'), store.get('projectDebatePlanningLocalId'));
227
+ if (!exact(msg, msg.type === 'home_mate_error' && store.get('projectDebatePlanningAwaitingHistory'))) return false;
228
+ if (msg.type === 'home_debate_question') {
229
+ setMessages(messages().concat([{ role: 'question', flow: 'debate', toolId: msg.toolId, questions: msg.questions || [], status: 'pending', error: '' }])); render(); return true;
230
+ }
231
+ if (msg.type === 'home_debate_question_resolved') {
232
+ setMessages(messages().map(function (item) { return item.role === 'question' && item.toolId === msg.toolId ? Object.assign({}, item, { status: msg.status === 'answered' ? 'answered' : 'expired', answers: msg.answers || null, error: msg.error || '' }) : item; })); render(); return true;
233
+ }
234
+ if (msg.type === 'home_debate_proposal') { setMessages(applyHomeDebateProposal(messages(), msg)); render(); return true; }
235
+ if (msg.type === 'home_debate_proposal_resolved') { setMessages(resolveHomeDebateProposal(messages(), msg)); render(); return true; }
236
+ if (msg.type === 'home_mate_error') {
237
+ if (/^question_/.test(msg.code || '')) setMessages(failHomeDebateQuestion(messages(), msg));
238
+ else store.set({ projectDebatePlanningError: msg.text || 'Debate setup could not continue.', projectDebatePlanningRetryable: true, projectDebatePlanningAwaitingHistory: false });
239
+ render();
240
+ return true;
241
+ }
242
+ return false;
243
+ }
244
+
245
+ export function initProjectDebatePlanningWorkspace() {
246
+ store.subscribe(function (state, previous) {
247
+ if (state.projectDebatePlanningPendingStart !== previous.projectDebatePlanningPendingStart || state.connected !== previous.connected || state.activeProjectSlug !== previous.activeProjectSlug) maybeStart();
248
+ if (state.cachedMatesList !== previous.cachedMatesList) restoreAfterMateList();
249
+ });
250
+ }