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
@@ -1,3 +1,4 @@
1
+ import { setChatLayout } from './theme.js';
1
2
  // User profile module — profile popovers for people and Mates
2
3
  // Stores profile server-side in ~/.clay/profile.json
3
4
  // Generated identities use deterministic local pixel identicons.
@@ -7,7 +8,6 @@ import { avatarUrl, mateAvatarUrl, mateMarkUrl, userAvatarSeed } from './avatar.
7
8
  import { store } from './store.js';
8
9
  import { applyTerminalFont } from './terminal-prefs.js';
9
10
 
10
- var ctx;
11
11
  var profile = { name: '', lang: 'en-US', avatarStyle: 'imprint', avatarSeed: '', avatarColor: '#5857fc', avatarCustom: '' };
12
12
  var profileUsername = '';
13
13
  var popoverEl = null;
@@ -84,6 +84,54 @@ 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 async function saveProfileUpdates(updates) {
91
+ var next = Object.assign({}, profile, updates);
92
+ var response = await fetch('/api/profile', {method:'PUT',headers:{'Content-Type':'application/json'},body:JSON.stringify(next)});
93
+ var result = await response.json();
94
+ if (!response.ok || result.error) throw new Error(result.error || 'Could not save your profile. Your draft is retained.');
95
+ profile = next;
96
+ applyToIsland();
97
+ return result;
98
+ }
99
+
100
+ export function updateProfileName(name) {
101
+ profile.name = String(name || '').trim();
102
+ applyToIsland();
103
+ debouncedSave();
104
+ }
105
+
106
+ export function usePixelAvatar() {
107
+ profile.avatarCustom = '';
108
+ applyToIsland();
109
+ debouncedSave();
110
+ }
111
+
112
+ export function regenerateProfileAvatar() {
113
+ profile.avatarCustom = '';
114
+ profile.avatarSeed = 'clay-' + Date.now();
115
+ applyToIsland();
116
+ debouncedSave();
117
+ }
118
+
119
+ export function uploadAvatarBlob(blob) {
120
+ if (!blob) return Promise.reject(new Error('Missing avatar image.'));
121
+ return blob.arrayBuffer().then(function(ab) {
122
+ var buf = new Uint8Array(ab);
123
+ return fetch('/api/avatar', {
124
+ method: 'POST',
125
+ headers: { 'Content-Type': 'application/octet-stream' },
126
+ body: buf,
127
+ });
128
+ }).then(function(r) { return r.json(); }).then(function(data) {
129
+ if (!data.ok || !data.avatar) throw new Error(data.error || 'Avatar upload failed.');
130
+ profile.avatarCustom = data.avatar;
131
+ applyToIsland();
132
+ debouncedSave();
133
+ return data;
134
+ });
87
135
  }
88
136
 
89
137
  // --- Avatar position picker ---
@@ -128,6 +176,8 @@ export function showAvatarPositioner(img, objectUrl, onDone) {
128
176
 
129
177
  var closeBtn = document.createElement('button');
130
178
  closeBtn.className = 'avatar-positioner-close';
179
+ closeBtn.type = 'button';
180
+ closeBtn.setAttribute('aria-label', 'Close avatar crop editor');
131
181
  closeBtn.innerHTML = '×';
132
182
  container.appendChild(closeBtn);
133
183
 
@@ -239,12 +289,21 @@ export function showAvatarPositioner(img, objectUrl, onDone) {
239
289
  }, { passive: false });
240
290
 
241
291
  function cleanup(revoke) {
292
+ document.removeEventListener('keydown', onOverlayKeydown);
242
293
  overlay.remove();
243
294
  if (revoke) URL.revokeObjectURL(objectUrl);
244
295
  }
245
296
 
297
+ function onOverlayKeydown(event) {
298
+ if (event.key !== 'Escape') return;
299
+ event.preventDefault();
300
+ event.stopPropagation();
301
+ cleanup(true);
302
+ }
303
+
246
304
  closeBtn.addEventListener('click', function() { cleanup(true); });
247
305
  cancelBtn.addEventListener('click', function() { cleanup(true); });
306
+ document.addEventListener('keydown', onOverlayKeydown);
248
307
 
249
308
  doneBtn.addEventListener('click', function() {
250
309
  // Render cropped area to canvas
@@ -269,7 +328,7 @@ export function showAvatarPositioner(img, objectUrl, onDone) {
269
328
  }
270
329
 
271
330
  // --- Popover ---
272
- function showPopover() {
331
+ export function openProfileEditor() {
273
332
  if (popoverEl) {
274
333
  hidePopover();
275
334
  return;
@@ -286,7 +345,7 @@ function showPopover() {
286
345
 
287
346
  // Banner + close
288
347
  html += '<div class="profile-banner">';
289
- html += '<button class="profile-close-btn">&times;</button>';
348
+ html += '<button class="profile-close-btn" type="button" aria-label="Close profile editor">&times;</button>';
290
349
  html += '</div>';
291
350
 
292
351
  // Avatar row (overlapping banner)
@@ -479,28 +538,11 @@ function escapeAttr(str) {
479
538
  }
480
539
 
481
540
  // --- Init ---
482
- export function initProfile(_ctx) {
483
- ctx = _ctx;
541
+ export function initProfile() {
484
542
 
485
543
  var island = document.getElementById('user-island');
486
544
  if (!island) return;
487
545
 
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
546
  fetchProfile().then(function(data) {
505
547
  if (data.name !== undefined) profile.name = data.name;
506
548
  if (data.lang) profile.lang = data.lang;
@@ -523,13 +565,13 @@ export function initProfile(_ctx) {
523
565
  // when the user opened the Settings modal (user-settings.js's
524
566
  // populateAccount), so a hard refresh would drop persisted picks back
525
567
  // to defaults until the modal was visited.
568
+ if(data.chatLayout)setChatLayout(data.chatLayout);
526
569
  if (data.terminalFont && typeof data.terminalFont === "object") {
527
570
  applyTerminalFont(data.terminalFont.family, data.terminalFont.size);
528
571
  }
529
572
 
530
- // Apply the project-only Mate DM shortcut preference at boot. Home and
531
- // the rest of the Mate capability surface remain available.
532
- var matesOn = data.matesEnabled === true;
573
+ // Keep project Mate DM shortcuts available for legacy profiles too.
574
+ var matesOn = true;
533
575
  store.set({ projectMateDmsEnabled: matesOn });
534
576
  if (document.body) document.body.classList.toggle('project-mate-dms-disabled', !matesOn);
535
577
  }).catch(function(err) {
@@ -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
+ }
@@ -0,0 +1,185 @@
1
+ // Adapt project events to the shared Home debate presentation.
2
+ import { store } from './store.js';
3
+ import { getWs } from './ws-ref.js';
4
+ import { getMessagesEl } from './dom-refs.js';
5
+ import { addToMessages, scrollToBottom } from './chat-render-runtime.js';
6
+ import { isMateWorkspace } from './project-mate-navigation.js';
7
+ import { applyHomeDebateEvent, createHomeDebateLiveCard } from './home-debate-live.js';
8
+ import { createHomeDebateProposalCard, applyHomeDebateProposal, resolveHomeDebateProposal } from './home-debate-planning.js';
9
+ import { createDebateControls } from './debate-controls.js';
10
+ import { renderAssistantBubbleText, finalizeAssistantBubble, disposeChatBubbleTree } from './chat-bubble-renderer.js';
11
+
12
+ var transcriptEl = null;
13
+ var prependEl = null;
14
+ var controls = createDebateControls({
15
+ slotId: 'project-debate-controls',
16
+ composerId: 'input-area',
17
+ send: function (action, data, sessionId) {
18
+ var ws = getWs();
19
+ if (!ws || ws.readyState !== 1 || String(store.get('activeSessionId')) !== sessionId) return false;
20
+ ws.send(JSON.stringify({ type: 'project_debate_control', sessionId: store.get('activeSessionId'), action: action, text: data.text || '', response: data.response || null }));
21
+ return true;
22
+ }
23
+ });
24
+
25
+ function stateKey() {
26
+ return store.get('projectDebatePrepending') ? 'projectDebatePrependMessages' : 'projectDebateMessages';
27
+ }
28
+
29
+ function ensureTranscript() {
30
+ var prepending = store.get('projectDebatePrepending');
31
+ var root = prepending ? prependEl : transcriptEl;
32
+ if (!root || !root.isConnected) {
33
+ root = document.createElement('section');
34
+ root.className = 'project-debate-transcript home-mate-chat-transcript home-chat-bubble-layout';
35
+ root.setAttribute('aria-label', 'Debate transcript');
36
+ addToMessages(root);
37
+ if (prepending) prependEl = root;
38
+ else transcriptEl = root;
39
+ }
40
+ return root;
41
+ }
42
+
43
+ function renderControls() {
44
+ var slot = document.getElementById('project-debate-controls');
45
+ var composer = document.getElementById('input-area');
46
+ if (!slot && composer) {
47
+ slot = document.createElement('div');
48
+ slot.id = 'project-debate-controls';
49
+ slot.className = 'home-debate-controls-slot project-debate-controls';
50
+ slot.hidden = true;
51
+ composer.parentNode.insertBefore(slot, composer);
52
+ }
53
+ controls.render(store.get('projectDebateMessages') || [], String(store.get('activeSessionId') || ''));
54
+ }
55
+
56
+ function respondToProposal(message, action, opener, answers, modelOverrides) {
57
+ var ws = getWs();
58
+ if (!ws || ws.readyState !== 1 || message.status === 'submitting') return;
59
+ ws.send(JSON.stringify({ type: 'debate_proposal_response', proposalId: message.proposal.proposalId, sessionId: store.get('activeSessionId'), action: action, modelOverrides: modelOverrides || [] }));
60
+ var messages = (store.get('projectDebateMessages') || []).map(function (item) {
61
+ return item === message ? Object.assign({}, item, { status: 'submitting' }) : item;
62
+ });
63
+ store.set({ projectDebateMessages: messages });
64
+ renderMessages(messages);
65
+ }
66
+
67
+ function renderMessages(messages) {
68
+ var root = ensureTranscript();
69
+ messages.forEach(function (message, index) {
70
+ var existing = root.children[index];
71
+ if (existing && existing._debateMessage === message) return;
72
+ if (existing && message.role === 'debate_turn' && existing.classList.contains('home-debate-live-turn')) {
73
+ var activity = existing.querySelector('.home-debate-live-activity');
74
+ if (message.status === 'done') {
75
+ if (activity) activity.remove();
76
+ finalizeAssistantBubble(existing, message.text || '', !store.get('replayingHistory'));
77
+ } else {
78
+ if (activity) {
79
+ var label = message.activity || (message.text ? 'Speaking' : 'Preparing');
80
+ activity.firstChild.textContent = label;
81
+ activity.setAttribute('aria-label', (message.mateName || 'Mate') + ' is ' + label.toLowerCase());
82
+ }
83
+ renderAssistantBubbleText(existing, message.text || '', false);
84
+ }
85
+ existing._debateMessage = message;
86
+ return;
87
+ }
88
+ var card = message.role === 'proposal'
89
+ ? createHomeDebateProposalCard(message, respondToProposal)
90
+ : createHomeDebateLiveCard(message, message.status === 'done');
91
+ card._debateMessage = message;
92
+ if (existing) { disposeChatBubbleTree(existing); existing.replaceWith(card); }
93
+ else root.appendChild(card);
94
+ });
95
+ }
96
+
97
+ export function resetProjectDebateWorkspace() {
98
+ store.set({ projectDebateMessages: [], projectDebatePrependMessages: [], projectDebatePrepending: false });
99
+ transcriptEl = null;
100
+ prependEl = null;
101
+ controls.render([], null);
102
+ }
103
+
104
+ export function beginProjectDebatePrepend() {
105
+ prependEl = null;
106
+ store.set({ projectDebatePrepending: true, projectDebatePrependMessages: [] });
107
+ }
108
+
109
+ export function endProjectDebatePrepend() {
110
+ prependEl = null;
111
+ store.set({ projectDebatePrepending: false, projectDebatePrependMessages: [] });
112
+ }
113
+
114
+ export function handleProjectDebateMessage(msg) {
115
+ if (!isMateWorkspace(store.snap())) return false;
116
+ if (msg.type === 'project_debate_control_result') {
117
+ if (String(msg.sessionId) !== String(store.get('activeSessionId'))) return true;
118
+ if (!msg.ok) {
119
+ controls.render([], null);
120
+ renderControls();
121
+ var slot = document.getElementById('project-debate-controls');
122
+ var error = document.createElement('p');
123
+ error.className = 'project-debate-error';
124
+ error.setAttribute('role', 'alert');
125
+ error.textContent = msg.error || 'The debate action could not be completed.';
126
+ if (slot) slot.appendChild(error);
127
+ }
128
+ return true;
129
+ }
130
+ if (msg.type === 'history_done') {
131
+ renderControls();
132
+ var ws = getWs();
133
+ if (ws && ws.readyState === 1 && (store.get('projectDebateMessages') || []).length) {
134
+ ws.send(JSON.stringify({ type: 'project_debate_state', sessionId: store.get('activeSessionId') }));
135
+ }
136
+ return false;
137
+ }
138
+ if (msg.type === 'project_debate_state') {
139
+ if (String(msg.sessionId) !== String(store.get('activeSessionId')) || !msg.header) return true;
140
+ var restored = (store.get('projectDebateMessages') || []).slice();
141
+ var headerIndex = -1;
142
+ restored.forEach(function (item, index) { if (item.role === 'debate_header') headerIndex = index; });
143
+ if (headerIndex === -1) restored.unshift(msg.header);
144
+ else restored[headerIndex] = msg.header;
145
+ if (msg.header.phase !== 'live') restored = restored.map(function (item) {
146
+ return item.role === 'debate_turn' && item.status === 'active' ? Object.assign({}, item, { status: 'done', activity: '' }) : item;
147
+ });
148
+ store.set({ projectDebateMessages: restored });
149
+ renderMessages(restored);
150
+ renderControls();
151
+ return true;
152
+ }
153
+ if (msg.type === 'debate_preparing') {
154
+ var root = ensureTranscript();
155
+ var preparation = document.createElement('p');
156
+ preparation.className = 'debate-preparing-indicator home-debate-live-status';
157
+ preparation.setAttribute('role', 'status');
158
+ preparation.textContent = (msg.moderatorName || 'The moderator') + ' is preparing the debate…';
159
+ root.appendChild(preparation);
160
+ return true;
161
+ }
162
+ if (msg.type === 'tool_executing' && msg.name && msg.name.indexOf('propose_debate') !== -1) return true;
163
+ var types = ['debate_started', 'debate_turn', 'debate_activity', 'debate_stream', 'debate_turn_done', 'debate_tool_decision', 'debate_stop_requested', 'debate_stop_cancelled', 'debate_hand_raised', 'debate_conclude_confirm', 'debate_user_floor', 'debate_user_floor_done', 'debate_comment_injected', 'debate_user_resume', 'debate_resumed', 'debate_ended', 'debate_proposal', 'debate_proposal_resolved'];
164
+ if (types.indexOf(msg.type) === -1) return false;
165
+ var key = stateKey();
166
+ var messages = store.get(key) || [];
167
+ if (msg.type === 'debate_tool_decision') messages = messages.map(function (item) {
168
+ return item.role === 'debate_tool_decision' ? Object.assign({}, item, { entries: (item.entries || []).slice() }) : item;
169
+ });
170
+ if (msg.type === 'debate_proposal') messages = applyHomeDebateProposal(messages, msg);
171
+ else if (msg.type === 'debate_proposal_resolved') messages = resolveHomeDebateProposal(messages, msg);
172
+ else messages = applyHomeDebateEvent(messages, Object.assign({}, msg, { eventType: msg.type, speakerMateId: msg.mateId }));
173
+ if (msg.type === 'debate_ended') messages = messages.map(function (item) {
174
+ return item.role === 'debate_turn' && item.status === 'active' ? Object.assign({}, item, { status: 'done', activity: '' }) : item;
175
+ });
176
+ var patch = {};
177
+ patch[key] = messages;
178
+ store.set(patch);
179
+ var preparation = getMessagesEl().querySelector('.debate-preparing-indicator');
180
+ if (preparation) preparation.remove();
181
+ renderMessages(messages);
182
+ if (!store.get('projectDebatePrepending') && !store.get('replayingHistory')) renderControls();
183
+ scrollToBottom();
184
+ return true;
185
+ }