clay-server 5.0.2-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 (162) 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-file-path.js +2 -3
  22. package/lib/project-knowledge.js +315 -136
  23. package/lib/project-mate-creation-proposal.js +1 -0
  24. package/lib/project-request-access.js +13 -1
  25. package/lib/project-sessions.js +39 -2
  26. package/lib/project-user-knowledge.js +60 -0
  27. package/lib/project.js +59 -18
  28. package/lib/public/app.js +73 -34
  29. package/lib/public/clay-studio-wordmark-bold.svg +20 -0
  30. package/lib/public/css/clay-workspace.css +105 -0
  31. package/lib/public/css/debates-workbench.css +47 -0
  32. package/lib/public/css/filebrowser.css +10 -0
  33. package/lib/public/css/home-debate-live.css +26 -0
  34. package/lib/public/css/home-hub.css +3 -1
  35. package/lib/public/css/home-mate-model-picker.css +15 -131
  36. package/lib/public/css/home-mate-settings.css +168 -16
  37. package/lib/public/css/icon-strip.css +249 -32
  38. package/lib/public/css/mate-conversation.css +176 -0
  39. package/lib/public/css/mate-knowledge-workbench.css +118 -0
  40. package/lib/public/css/mate-workspace-color.css +16 -0
  41. package/lib/public/css/mates.css +28 -28
  42. package/lib/public/css/messages.css +1 -114
  43. package/lib/public/css/mobile-nav.css +16 -16
  44. package/lib/public/css/overlays.css +8 -0
  45. package/lib/public/css/project-logs.css +7 -0
  46. package/lib/public/css/project-mate-creation.css +105 -0
  47. package/lib/public/css/right-workbench.css +3 -3
  48. package/lib/public/css/session-folders.css +80 -21
  49. package/lib/public/css/sidebar.css +183 -107
  50. package/lib/public/css/title-bar.css +151 -0
  51. package/lib/public/css/worker-delegation.css +47 -0
  52. package/lib/public/css/worker-proposal.css +72 -150
  53. package/lib/public/css/you.css +74 -0
  54. package/lib/public/index.html +33 -101
  55. package/lib/public/modules/account-island.js +230 -0
  56. package/lib/public/modules/app-cursors.js +19 -43
  57. package/lib/public/modules/app-debate-ui.js +4 -3
  58. package/lib/public/modules/app-header.js +106 -0
  59. package/lib/public/modules/app-message-router.js +12 -3
  60. package/lib/public/modules/app-messages.js +34 -9
  61. package/lib/public/modules/app-notifications.js +4 -7
  62. package/lib/public/modules/app-projects.js +5 -38
  63. package/lib/public/modules/app-rendering.js +12 -13
  64. package/lib/public/modules/avatar.js +25 -9
  65. package/lib/public/modules/chat-render-runtime.js +16 -4
  66. package/lib/public/modules/command-palette.js +2 -15
  67. package/lib/public/modules/cursor-sharing-preference.js +3 -3
  68. package/lib/public/modules/debate-controls.js +299 -0
  69. package/lib/public/modules/debate.js +2 -0
  70. package/lib/public/modules/debates-workbench.js +339 -0
  71. package/lib/public/modules/default-ai.js +81 -65
  72. package/lib/public/modules/filebrowser.js +28 -8
  73. package/lib/public/modules/home-debate-controls.js +18 -289
  74. package/lib/public/modules/home-mate-avatar-editor.js +228 -0
  75. package/lib/public/modules/home-mate-color-picker.js +66 -0
  76. package/lib/public/modules/home-mate-creation.js +1 -1
  77. package/lib/public/modules/home-mate-instructions.js +90 -0
  78. package/lib/public/modules/home-mate-model-picker.js +80 -271
  79. package/lib/public/modules/home-mate-profile-editor.js +72 -0
  80. package/lib/public/modules/home-mate-settings.js +118 -125
  81. package/lib/public/modules/home-shell.js +3 -0
  82. package/lib/public/modules/home-surface-boot.js +1 -9
  83. package/lib/public/modules/html-preview.js +19 -0
  84. package/lib/public/modules/icon-strip-tooltip.js +95 -0
  85. package/lib/public/modules/knowledge-db-edit-state.js +35 -0
  86. package/lib/public/modules/knowledge-db-workbench.js +234 -0
  87. package/lib/public/modules/knowledge-history-state.js +55 -0
  88. package/lib/public/modules/knowledge-markdown-editor.js +57 -0
  89. package/lib/public/modules/knowledge-preview-links.js +70 -0
  90. package/lib/public/modules/knowledge-workbench-shell.js +21 -0
  91. package/lib/public/modules/mate-knowledge-workbench-state.js +174 -0
  92. package/lib/public/modules/mate-knowledge-workbench.js +500 -0
  93. package/lib/public/modules/mate-knowledge.js +11 -3
  94. package/lib/public/modules/mate-management.js +54 -10
  95. package/lib/public/modules/mate-sidebar.js +11 -1
  96. package/lib/public/modules/mate-workspace-color.js +33 -0
  97. package/lib/public/modules/mention.js +5 -13
  98. package/lib/public/modules/profile.js +51 -18
  99. package/lib/public/modules/project-chat-identity.js +79 -0
  100. package/lib/public/modules/project-debate-planning-workspace.js +250 -0
  101. package/lib/public/modules/project-debate-workspace.js +185 -0
  102. package/lib/public/modules/project-header-identity.js +127 -0
  103. package/lib/public/modules/project-logs-render.js +9 -4
  104. package/lib/public/modules/project-mate-creation-workspace.js +414 -0
  105. package/lib/public/modules/project-mate-navigation.js +238 -0
  106. package/lib/public/modules/project-session-navigation.js +29 -0
  107. package/lib/public/modules/project-switcher.js +6 -8
  108. package/lib/public/modules/projects-hub-view.js +5 -6
  109. package/lib/public/modules/projects-hub.js +2 -6
  110. package/lib/public/modules/qrcode.js +27 -8
  111. package/lib/public/modules/search-clay-chat.js +8 -11
  112. package/lib/public/modules/session-activity-tooltip.js +12 -0
  113. package/lib/public/modules/session-create-form.js +86 -14
  114. package/lib/public/modules/session-create.js +50 -7
  115. package/lib/public/modules/session-list-visibility.js +41 -0
  116. package/lib/public/modules/session-presence-ui.js +4 -1
  117. package/lib/public/modules/session-title-marquee.js +51 -0
  118. package/lib/public/modules/sidebar-mates.js +3 -50
  119. package/lib/public/modules/sidebar-mobile.js +63 -60
  120. package/lib/public/modules/sidebar-projects.js +84 -97
  121. package/lib/public/modules/sidebar-session-folders-ui.js +2 -2
  122. package/lib/public/modules/sidebar-session-hierarchy.js +149 -70
  123. package/lib/public/modules/sidebar-sessions.js +49 -50
  124. package/lib/public/modules/sidebar.js +11 -13
  125. package/lib/public/modules/thinking-lifecycle.js +7 -6
  126. package/lib/public/modules/tool-palette-order.js +4 -3
  127. package/lib/public/modules/tool-palette.js +13 -3
  128. package/lib/public/modules/tools.js +22 -22
  129. package/lib/public/modules/worker-proposal.js +42 -8
  130. package/lib/public/modules/worker-runtime-picker.js +115 -0
  131. package/lib/public/modules/you-activity.js +121 -0
  132. package/lib/public/modules/you.js +251 -0
  133. package/lib/public/style.css +16 -8
  134. package/lib/public/vendor/codemirror/LICENSE +21 -0
  135. package/lib/public/vendor/codemirror/codemirror.css +344 -0
  136. package/lib/public/vendor/codemirror/codemirror.js +9884 -0
  137. package/lib/public/vendor/codemirror/continuelist.js +101 -0
  138. package/lib/public/vendor/codemirror/markdown.js +886 -0
  139. package/lib/public/vendor/codemirror/show-hint.css +37 -0
  140. package/lib/public/vendor/codemirror/show-hint.js +523 -0
  141. package/lib/sdk-bridge.js +15 -3
  142. package/lib/server-admin.js +11 -0
  143. package/lib/server-home-chat-events.js +1 -1
  144. package/lib/server-home-chat.js +25 -3
  145. package/lib/server-home-debate-planning.js +4 -1
  146. package/lib/server-home-debates.js +2 -0
  147. package/lib/server-home-mate-creation.js +4 -1
  148. package/lib/server-mate-instructions.js +44 -0
  149. package/lib/server-mates.js +12 -3
  150. package/lib/server-palette.js +1 -1
  151. package/lib/server-settings.js +65 -1
  152. package/lib/server-user-knowledge.js +119 -0
  153. package/lib/server.js +21 -1
  154. package/lib/session-visibility.js +7 -2
  155. package/lib/sessions.js +32 -6
  156. package/lib/user-knowledge-service.js +167 -0
  157. package/lib/users-permissions.js +7 -0
  158. package/lib/users-project-mate-filter-preferences.js +39 -0
  159. package/lib/users.js +6 -0
  160. package/lib/workspace-query-access.js +4 -1
  161. package/lib/ws-schema.js +50 -0
  162. package/package.json +10 -1
@@ -0,0 +1,230 @@
1
+ import { store } from './store.js';
2
+ import { mountDefaultAi, unmountDefaultAi } from './default-ai.js';
3
+ import { openUserSettings } from './user-settings.js';
4
+ import { getProfile, updateProfileName, regenerateProfileAvatar, showAvatarPositioner, uploadAvatarBlob } from './profile.js';
5
+ import { requestCursorSharingState, setCursorSharing } from './app-cursors.js';
6
+ import { registerTooltip } from './tooltip.js';
7
+
8
+ var popover = null;
9
+ var trigger = null;
10
+ var lastFocus = null;
11
+
12
+ function accountName() { return document.body.dataset.myDisplayName || 'Clay user'; }
13
+
14
+ function close(returnFocus) {
15
+ if (!popover) return;
16
+ var aiSection = popover.querySelector('#account-ai-section');
17
+ if (aiSection) unmountDefaultAi(aiSection);
18
+ popover.remove();
19
+ popover = null;
20
+ trigger.setAttribute('aria-expanded', 'false');
21
+ trigger.setAttribute('aria-label', 'Open account menu for ' + accountName());
22
+ document.removeEventListener('pointerdown', onOutside);
23
+ document.removeEventListener('focusin', onFocusOutside);
24
+ document.removeEventListener('keydown', onKeydown);
25
+ window.removeEventListener('resize', position);
26
+ store.set({ defaultAiInlineEditing: false });
27
+ if (returnFocus && lastFocus && document.contains(lastFocus)) lastFocus.focus({ preventScroll: true });
28
+ lastFocus = null;
29
+ }
30
+
31
+ function isAvatarPositionerTarget(target) { return target && target.closest && target.closest('.avatar-positioner-overlay'); }
32
+ function onOutside(event) { if (!popover || popover.contains(event.target) || trigger.contains(event.target) || isAvatarPositionerTarget(event.target)) return; close(false); }
33
+ function onFocusOutside(event) { if (!popover || popover.contains(event.target) || trigger.contains(event.target) || isAvatarPositionerTarget(event.target)) return; close(false); }
34
+ function onKeydown(event) {
35
+ if (event.key !== 'Escape') return;
36
+ if (isAvatarPositionerTarget(event.target) || document.querySelector('.avatar-positioner-overlay')) return;
37
+ event.preventDefault();
38
+ event.stopPropagation();
39
+ close(true);
40
+ }
41
+
42
+ function position() {
43
+ if (!popover || !trigger) return;
44
+ var rect = trigger.getBoundingClientRect();
45
+ var width = Math.min(280, window.innerWidth - 24);
46
+ var left = Math.max(12, Math.min(window.innerWidth - width - 12, rect.left));
47
+ popover.style.width = width + 'px';
48
+ popover.style.left = left + 'px';
49
+ popover.style.top = Math.max(12, rect.top - popover.offsetHeight - 8) + 'px';
50
+ }
51
+
52
+ function renderNameResting(restoreFocus) {
53
+ var section = popover && popover.querySelector('.account-identity-copy');
54
+ if (!section) return;
55
+ section.innerHTML = '<button type="button" id="account-name-edit" class="account-name-display"><span></span><i data-lucide="pencil" aria-hidden="true"></i></button><small>Personal account</small>';
56
+ section.querySelector('span').textContent = accountName();
57
+ section.querySelector('#account-name-edit').addEventListener('click', startNameEdit);
58
+ if (window.lucide) window.lucide.createIcons({ nodes: [section] });
59
+ if (restoreFocus) section.querySelector('#account-name-edit').focus();
60
+ }
61
+
62
+ function startNameEdit() {
63
+ var section = popover && popover.querySelector('.account-identity-copy');
64
+ if (!section) return;
65
+ var input = document.createElement('input');
66
+ input.id = 'account-profile-name';
67
+ input.setAttribute('aria-label', 'Display name');
68
+ input.className = 'account-name-input';
69
+ input.type = 'text';
70
+ input.maxLength = 50;
71
+ input.autocomplete = 'name';
72
+ input.value = getProfile().name || '';
73
+ section.innerHTML = '';
74
+ section.appendChild(input);
75
+ var hint = document.createElement('small');
76
+ hint.textContent = 'Enter to save · Escape to cancel';
77
+ section.appendChild(hint);
78
+ input.addEventListener('keydown', function(event) {
79
+ if (event.key === 'Enter') {
80
+ event.preventDefault();
81
+ updateProfileName(input.value);
82
+ renderNameResting(true);
83
+ } else if (event.key === 'Escape') {
84
+ event.preventDefault();
85
+ renderNameResting(true);
86
+ }
87
+ event.stopPropagation();
88
+ });
89
+ input.focus();
90
+ input.select();
91
+ }
92
+
93
+ function toggleAvatarEditor() {
94
+ var editor = popover && popover.querySelector('#account-avatar-editor');
95
+ if (!editor) return;
96
+ editor.hidden = !editor.hidden;
97
+ if (!editor.hidden) editor.querySelector('#account-avatar-upload').focus();
98
+ position();
99
+ }
100
+
101
+ function bindAvatarEditor() {
102
+ var avatar = popover.querySelector('#account-avatar-edit');
103
+ var editor = popover.querySelector('#account-avatar-editor');
104
+ var upload = editor.querySelector('#account-avatar-upload');
105
+ var file = editor.querySelector('#account-avatar-file');
106
+ var generate = editor.querySelector('#account-avatar-generate');
107
+ avatar.addEventListener('click', function(event) { event.stopPropagation(); toggleAvatarEditor(); });
108
+ editor.addEventListener('click', function(event) { event.stopPropagation(); });
109
+ upload.addEventListener('click', function() { file.click(); });
110
+ file.addEventListener('change', function() {
111
+ var selected = file.files && file.files[0];
112
+ if (!selected) return;
113
+ var image = new Image();
114
+ var objectUrl = URL.createObjectURL(selected);
115
+ image.onload = function() {
116
+ showAvatarPositioner(image, objectUrl, function(blob) {
117
+ URL.revokeObjectURL(objectUrl);
118
+ uploadAvatarBlob(blob).then(function() { editor.hidden = true; }).catch(function() { editor.dataset.error = 'true'; });
119
+ });
120
+ };
121
+ image.src = objectUrl;
122
+ });
123
+ generate.addEventListener('click', function() { regenerateProfileAvatar(); editor.hidden = true; });
124
+ }
125
+
126
+ function render() {
127
+ popover = document.createElement('div');
128
+ popover.id = 'account-popover';
129
+ popover.className = 'account-popover';
130
+ popover.setAttribute('role', 'dialog');
131
+ popover.setAttribute('aria-label', 'Account');
132
+ var profile = getProfile();
133
+ var identity = document.createElement('div');
134
+ identity.className = 'account-menu-identity';
135
+ identity.innerHTML = '<button type="button" id="account-avatar-edit" class="account-avatar-edit" aria-label="Edit avatar"><img src="' + (document.body.dataset.myAvatarUrl || '') + '" alt=""></button><div class="account-identity-copy"></div><button type="button" class="account-settings-button" aria-label="User Settings" title="User Settings"><i data-lucide="settings" aria-hidden="true"></i></button>';
136
+ identity.querySelector('#account-avatar-edit img').alt = profile.name || 'Clay user';
137
+ var editor = document.createElement('div');
138
+ editor.id = 'account-avatar-editor';
139
+ editor.className = 'account-avatar-editor';
140
+ editor.hidden = true;
141
+ editor.innerHTML = '<button type="button" id="account-avatar-upload">Upload photo</button><button type="button" id="account-avatar-generate">Generate avatar</button><input id="account-avatar-file" type="file" accept="image/*">';
142
+ popover.appendChild(identity);
143
+ popover.appendChild(editor);
144
+ var aiSection = document.createElement('section');
145
+ aiSection.id = 'account-ai-section';
146
+ aiSection.className = 'account-inline-section account-ai-section';
147
+ aiSection.setAttribute('aria-label', 'Default AI');
148
+ popover.appendChild(aiSection);
149
+ mountDefaultAi(aiSection);
150
+ identity.querySelector('.account-settings-button').addEventListener('click', onSettings);
151
+ document.body.appendChild(popover);
152
+ renderNameResting();
153
+ bindAvatarEditor();
154
+ updateCursorAction();
155
+ if (window.lucide) window.lucide.createIcons({ nodes: [popover] });
156
+ position();
157
+ }
158
+
159
+ function updateCursorAction() {
160
+ var button = document.getElementById('cursor-share-toggle');
161
+ if (!button) return;
162
+ var hydrated = store.get('cursorSharingHydrated') === true;
163
+ var enabled = hydrated && store.get('cursorSharingEnabled') === true;
164
+ var pending = store.get('cursorSharingPending') !== null && store.get('cursorSharingPending') !== undefined;
165
+ var connected = store.get('connected') !== false;
166
+ var error = store.get('cursorSharingError');
167
+ var canRetry = !!error && !hydrated && connected;
168
+ var showOff = hydrated && !enabled && !pending && !error && connected;
169
+ button.hidden = store.get('isMultiUserMode') !== true;
170
+ button.removeAttribute('role');
171
+ button.removeAttribute('aria-checked');
172
+ button.setAttribute('aria-pressed', enabled ? 'true' : 'false');
173
+ button.classList.toggle('on', enabled);
174
+ button.classList.toggle('off', showOff);
175
+ button.classList.toggle('pending', pending);
176
+ button.disabled = pending || !connected || (!hydrated && !canRetry);
177
+ var stateLabel = pending ? 'waiting' : !connected ? 'offline' : error ? (canRetry ? 'error; activate to retry' : 'error') : !hydrated ? 'unavailable' : enabled ? 'on' : 'off';
178
+ button.setAttribute('aria-label', 'Cursor sharing ' + stateLabel);
179
+ var tooltipText = pending ? 'Cursor sharing is waiting for confirmation.' : !connected ? 'Cursor sharing is offline.' : error ? (canRetry ? 'Cursor sharing is unavailable. Click to retry.' : 'Cursor sharing is unavailable.') : !hydrated ? 'Cursor sharing is unavailable.' : enabled ? 'Your cursor is visible to others. Click to stop sharing.' : 'Cursor sharing is off. Click to share your cursor.';
180
+ registerTooltip(button, tooltipText);
181
+ }
182
+
183
+ function onSettings(event) { event.stopPropagation(); close(false); openUserSettings(); }
184
+ function onCursorAction(event) {
185
+ event.stopPropagation();
186
+ if (event.currentTarget.disabled) return;
187
+ if (store.get('cursorSharingError') && store.get('cursorSharingHydrated') !== true) {
188
+ requestCursorSharingState();
189
+ return;
190
+ }
191
+ setCursorSharing(!(store.get('cursorSharingEnabled') === true));
192
+ }
193
+ function focusAi() { var control = popover && popover.querySelector('#account-ai-section select'); if (control) control.focus({ preventScroll: true }); }
194
+ function onOpenDefaultAi() { if (!popover) open(); store.set({ defaultAiInlineEditing: true }); focusAi(); }
195
+
196
+ function open() {
197
+ if (popover) { close(true); return; }
198
+ lastFocus = document.activeElement;
199
+ render();
200
+ trigger.setAttribute('aria-expanded', 'true');
201
+ trigger.setAttribute('aria-label', 'Close account menu for ' + accountName());
202
+ document.addEventListener('pointerdown', onOutside);
203
+ document.addEventListener('focusin', onFocusOutside);
204
+ document.addEventListener('keydown', onKeydown);
205
+ window.addEventListener('resize', position);
206
+ }
207
+
208
+ export function initAccountIsland() {
209
+ trigger = document.getElementById('user-account-trigger');
210
+ if (!trigger) return;
211
+ trigger.addEventListener('click', open);
212
+ document.addEventListener('clay:open-account-default-ai', onOpenDefaultAi);
213
+ var cursorToggle = document.getElementById('cursor-share-toggle');
214
+ if (cursorToggle) cursorToggle.addEventListener('click', onCursorAction);
215
+ updateCursorAction();
216
+ trigger.setAttribute('aria-label', 'Open account menu for ' + accountName());
217
+ store.subscribe(function(state, previous) {
218
+ if (state.myUserId !== previous.myUserId || state.connected === false) close(false);
219
+ updateCursorAction();
220
+ if (popover) position();
221
+ });
222
+ document.addEventListener('clay:profile-updated', function() {
223
+ if (!popover) return;
224
+ var identityName = popover.querySelector('.account-name-display span');
225
+ if (identityName) identityName.textContent = accountName();
226
+ var avatar = popover.querySelector('#account-avatar-edit img');
227
+ if (avatar) avatar.src = document.body.dataset.myAvatarUrl || avatar.src;
228
+ });
229
+ window.addEventListener('resize', position);
230
+ }
@@ -199,50 +199,26 @@ function findClosestTurn(clientY) {
199
199
 
200
200
  // Cursor sharing toggle button in user island (multi-user only)
201
201
  export function initCursorToggle() {
202
- if (!store.get('isMultiUserMode')) return;
203
- var actionsEl = document.querySelector(".user-island-actions");
204
- if (!actionsEl) return;
205
- if (document.getElementById("cursor-share-toggle")) return;
206
-
207
- var btn = document.createElement("button");
208
- btn.id = "cursor-share-toggle";
209
- btn.className = "cursor-share-btn";
210
- btn.innerHTML = '<i data-lucide="mouse-pointer-2"></i>';
211
- var settingsBtn = document.getElementById("user-settings-btn");
212
- if (settingsBtn) {
213
- actionsEl.insertBefore(btn, settingsBtn);
214
- } else {
215
- actionsEl.appendChild(btn);
216
- }
217
-
218
- function updateToggleStyle() {
219
- if (store.get("cursorSharingHydrated") && store.get("cursorSharingEnabled")) {
220
- btn.classList.remove("off");
221
- btn.classList.add("on");
222
- registerTooltip(btn, "Cursor sharing on");
223
- } else {
224
- btn.classList.remove("on");
225
- btn.classList.add("off");
226
- registerTooltip(btn, "Cursor sharing off");
227
- }
228
- }
229
-
230
- store.subscribe(function (state, prev) {
231
- if (state.cursorSharingEnabled !== prev.cursorSharingEnabled || state.cursorSharingHydrated !== prev.cursorSharingHydrated) {
232
- updateToggleStyle();
233
- }
234
- });
202
+ // The labeled rail control is owned by account-island.js.
203
+ return;
204
+ }
235
205
 
236
- updateToggleStyle();
237
- lucide.createIcons({ nodes: [btn] });
206
+ export function setCursorSharing(enabled) {
207
+ if (!store.get('cursorSharingHydrated') || store.get('cursorSharingPending') !== null && store.get('cursorSharingPending') !== undefined) return false;
208
+ if (enabled === true && store.get('isMultiUserMode') !== true) return false;
209
+ var ws = getWs();
210
+ if (!ws || ws.readyState !== 1) return false;
211
+ store.set({ cursorSharingPending: enabled });
212
+ ws.send(JSON.stringify({ type: 'cursor_sharing_set', enabled: enabled === true, requestId: 'cursor-sharing-' + Date.now() }));
213
+ return true;
214
+ }
238
215
 
239
- btn.addEventListener("click", function () {
240
- if (!store.get("cursorSharingHydrated")) return;
241
- var next = !store.get("cursorSharingEnabled");
242
- var ws = getWs();
243
- if (!ws || ws.readyState !== 1) return;
244
- ws.send(JSON.stringify({ type: "cursor_sharing_set", enabled: next, requestId: "cursor-sharing-" + Date.now() }));
245
- });
216
+ export function requestCursorSharingState() {
217
+ var ws = getWs();
218
+ if (!ws || ws.readyState !== 1) return false;
219
+ store.set({ cursorSharingPending: 'refresh' });
220
+ ws.send(JSON.stringify({ type: 'cursor_sharing_get', requestId: 'cursor-sharing-refresh-' + Date.now() }));
221
+ return true;
246
222
  }
247
223
 
248
224
  // --- Exported functions ---
@@ -393,7 +369,7 @@ export function initCursors() {
393
369
  if (cursorThrottleTimer) { clearTimeout(cursorThrottleTimer); cursorThrottleTimer = null; }
394
370
  if (selectionThrottleTimer) { clearTimeout(selectionThrottleTimer); selectionThrottleTimer = null; }
395
371
  lastSelectionKey = "";
396
- store.set({ cursorSharingEnabled: false, cursorSharingHydrated: false, cursorSharingAccountId: null });
372
+ store.set({ cursorSharingEnabled: false, cursorSharingHydrated: false, cursorSharingAccountId: null, cursorSharingPending: null, cursorSharingError: disconnected ? "disconnected" : "account-changed" });
397
373
  if (identityChanged && state.myUserId && state.connected) {
398
374
  var ws = getWs();
399
375
  if (ws && ws.readyState === 1) ws.send(JSON.stringify({ type: "cursor_sharing_get", requestId: "cursor-sharing-refresh-" + Date.now() }));
@@ -6,6 +6,7 @@ import { store } from './store.js';
6
6
  import { getWs } from './ws-ref.js';
7
7
  import { scrollToBottom } from './app-rendering.js';
8
8
  import { exportDebateAsPdf } from './debate.js';
9
+ import { getProjectAssistantPlaceholder } from './project-chat-identity.js';
9
10
 
10
11
  // Module-internal UI var (not in store)
11
12
  var debateHandRaiseOpen = false;
@@ -64,7 +65,7 @@ export function exitDebateConcludeMode() {
64
65
  if (banner) banner.remove();
65
66
  var inputEl = document.getElementById("input");
66
67
  if (inputEl && inputEl._origPlaceholder) {
67
- inputEl.placeholder = inputEl._origPlaceholder;
68
+ inputEl.placeholder = getProjectAssistantPlaceholder(store.snap(), store.get('currentVendor'));
68
69
  delete inputEl._origPlaceholder;
69
70
  }
70
71
  }
@@ -129,7 +130,7 @@ export function exitDebateEndedMode() {
129
130
  if (banner) banner.remove();
130
131
  var inputEl2 = document.getElementById("input");
131
132
  if (inputEl2 && inputEl2._origPlaceholder) {
132
- inputEl2.placeholder = inputEl2._origPlaceholder;
133
+ inputEl2.placeholder = getProjectAssistantPlaceholder(store.snap(), store.get('currentVendor'));
133
134
  delete inputEl2._origPlaceholder;
134
135
  }
135
136
  }
@@ -191,7 +192,7 @@ export function exitDebateFloorMode() {
191
192
  if (banner) banner.remove();
192
193
  var inputEl = document.getElementById("input");
193
194
  if (inputEl && inputEl._origPlaceholder) {
194
- inputEl.placeholder = inputEl._origPlaceholder;
195
+ inputEl.placeholder = getProjectAssistantPlaceholder(store.snap(), store.get('currentVendor'));
195
196
  delete inputEl._origPlaceholder;
196
197
  }
197
198
  }
@@ -1,3 +1,4 @@
1
+ import { beginProjectDebatePrepend, endProjectDebatePrepend } from './project-debate-workspace.js';
1
2
  // app-header.js - Session rename, session info popover, progressive history loading
2
3
  // Extracted from app.js (PR-34)
3
4
 
@@ -12,6 +13,9 @@ import { saveToolState, resetToolState, restoreToolState } from './tools.js';
12
13
  import { getSessionUsage, setSessionUsage, getContextData, setContextData, updateContextPanel, updateUsagePanel } from './app-panels.js';
13
14
  import { onHistoryPrepended as onSessionSearchHistoryPrepended } from './session-search.js';
14
15
  import { getCachedSessions } from './sidebar-sessions.js';
16
+ import { buildSessionHierarchy } from './session-hierarchy.js';
17
+ import { splitGroupRoles } from './split-group-helpers.js';
18
+ import { createWorkerHistoryControl } from './sidebar-session-hierarchy.js';
15
19
  import { bindPermissionControl, renderPermissionControl } from './permission-control.js';
16
20
  import { startHeaderSessionInlineRename } from './session-context-menu.js';
17
21
 
@@ -19,11 +23,104 @@ import { startHeaderSessionInlineRename } from './session-context-menu.js';
19
23
  var sessionInfoPopover = null;
20
24
  var historySentinelObserver = null;
21
25
 
26
+ function splitHistoryOwner(state, sessions) {
27
+ var split = state.splitPanes;
28
+ if (!split || !Array.isArray(split.panes)) return null;
29
+ var groups = state.splitGroups || [];
30
+ var group = null;
31
+ var roles = null;
32
+ for (var gi = 0; gi < groups.length; gi++) {
33
+ var candidateRoles = splitGroupRoles(groups[gi]);
34
+ if (!candidateRoles) continue;
35
+ var matches = false;
36
+ for (var pi = 0; pi < split.panes.length; pi++) {
37
+ if (candidateRoles.orderedIds.indexOf(split.panes[pi].sessionId) !== -1) matches = true;
38
+ }
39
+ if (matches) { group = groups[gi]; roles = candidateRoles; break; }
40
+ }
41
+ if (!group || !roles) return null;
42
+ var hierarchy = buildSessionHierarchy(sessions || []);
43
+ for (var hi = 0; hi < hierarchy.roots.length; hi++) {
44
+ var root = hierarchy.roots[hi];
45
+ if (root.driver.id === roles.driverId && root.workers.length) return { root: root, group: group, roles: roles };
46
+ }
47
+ return null;
48
+ }
49
+
50
+ function splitPaneHistoryUrl(slug, sessionId) {
51
+ return "/p/" + encodeURIComponent(slug) + "/?pane=1&session=" + encodeURIComponent(sessionId);
52
+ }
53
+
54
+ function createHeaderWorkerHistoryControl(root, context) {
55
+ var capturedWs = context.ws;
56
+ var capturedSlug = context.slug;
57
+ var capturedMateId = context.mateId;
58
+ var capturedActiveSessionId = context.activeSessionId;
59
+ var capturedSplit = context.split;
60
+ var historyControl = createWorkerHistoryControl(root.driver, root.workers, {
61
+ className: "header-worker-history-btn",
62
+ label: "Show Split Worker history",
63
+ validate: function (driver) {
64
+ var current = store.snap();
65
+ var ws = getWs();
66
+ if (!ws || ws !== capturedWs || ws.readyState !== 1 || !current.connected || current.currentSlug !== capturedSlug ||
67
+ (current.activeProjectMateId || null) !== capturedMateId || current.dmMode) return false;
68
+ if (!capturedSplit) return current.activeSessionId === driver.id && !current.splitPanes;
69
+ var split = current.splitPanes;
70
+ if (!split || split.groupId !== capturedSplit.groupId || !Array.isArray(split.panes) || split.panes.length !== capturedSplit.panes.length) return false;
71
+ if (current.activeSessionId !== capturedActiveSessionId) return false;
72
+ for (var i = 0; i < split.panes.length; i++) {
73
+ if (split.panes[i].sessionId !== capturedSplit.panes[i].sessionId || split.panes[i].slug !== capturedSplit.panes[i].slug) return false;
74
+ }
75
+ return true;
76
+ },
77
+ navigate: capturedSplit ? function (worker) {
78
+ var opened = window.open(splitPaneHistoryUrl(capturedSlug, worker.id), "_blank", "noopener");
79
+ return !!opened;
80
+ } : null,
81
+ });
82
+ return historyControl;
83
+ }
84
+
22
85
  export function initHeader() {
23
86
  var headerRenameBtn = document.getElementById("header-rename-btn");
24
87
  var headerTitleEl = document.getElementById("header-title");
25
88
  var headerInfoBtn = document.getElementById("header-info-btn");
26
89
  var headerFullAccessBtn = document.getElementById("header-full-access-btn");
90
+ var headerSearchBtn = document.getElementById("find-in-session-btn");
91
+ var headerWorkerHistoryBtn = null;
92
+
93
+ function refreshHeaderWorkerHistory() {
94
+ if (!headerSearchBtn) return;
95
+ if (headerWorkerHistoryBtn) {
96
+ headerWorkerHistoryBtn.remove();
97
+ headerWorkerHistoryBtn = null;
98
+ }
99
+ var state = store.snap();
100
+ var sessions = getCachedSessions() || [];
101
+ var hierarchy = buildSessionHierarchy(sessions);
102
+ var owner = null;
103
+ var context = null;
104
+ if (state.splitPanes && state.splitPanes.panes) {
105
+ var splitOwner = splitHistoryOwner(state, sessions);
106
+ owner = splitOwner ? splitOwner.root : null;
107
+ if (owner) {
108
+ context = {
109
+ ws: getWs(), slug: state.currentSlug, mateId: state.activeProjectMateId || null,
110
+ activeSessionId: state.activeSessionId,
111
+ split: { groupId: state.splitPanes.groupId, panes: state.splitPanes.panes.map(function (pane) { return { sessionId: pane.sessionId, slug: pane.slug }; }) },
112
+ };
113
+ }
114
+ } else {
115
+ for (var hi = 0; hi < hierarchy.roots.length; hi++) {
116
+ if (hierarchy.roots[hi].driver.id === state.activeSessionId && hierarchy.roots[hi].workers.length) { owner = hierarchy.roots[hi]; break; }
117
+ }
118
+ if (owner) context = { ws: getWs(), slug: state.currentSlug, mateId: state.activeProjectMateId || null, activeSessionId: state.activeSessionId, split: null };
119
+ }
120
+ if (!owner || !context) return;
121
+ headerWorkerHistoryBtn = createHeaderWorkerHistoryControl(owner, context);
122
+ if (headerWorkerHistoryBtn) headerSearchBtn.insertAdjacentElement("afterend", headerWorkerHistoryBtn);
123
+ }
27
124
 
28
125
  function permissionControlState() {
29
126
  var s = store.snap();
@@ -121,6 +218,12 @@ export function initHeader() {
121
218
 
122
219
  // --- Session info popover ---
123
220
  if (headerInfoBtn) {
221
+ store.subscribe(function (state, prev) {
222
+ if (state.activeSessionId !== prev.activeSessionId || state.currentSlug !== prev.currentSlug || state.activeProjectMateId !== prev.activeProjectMateId || state.splitPanes !== prev.splitPanes) {
223
+ closeSessionInfoPopover();
224
+ refreshHeaderWorkerHistory();
225
+ }
226
+ });
124
227
  headerInfoBtn.addEventListener("click", function (e) {
125
228
  e.stopPropagation();
126
229
  if (sessionInfoPopover) { closeSessionInfoPopover(); return; }
@@ -193,6 +296,7 @@ export function initHeader() {
193
296
  }
194
297
  });
195
298
  }
299
+ refreshHeaderWorkerHistory();
196
300
  }
197
301
 
198
302
  export function closeSessionInfoPopover() {
@@ -269,11 +373,13 @@ export function prependOlderHistory(items, meta) {
269
373
  var anchorEl = getPrependAnchor();
270
374
  var anchorOffset = anchorEl ? anchorEl.getBoundingClientRect().top : 0;
271
375
 
376
+ beginProjectDebatePrepend();
272
377
  // Process each item through the rendering pipeline
273
378
  for (var i = 0; i < items.length; i++) {
274
379
  processMessage(items[i]);
275
380
  }
276
381
 
382
+ endProjectDebatePrepend();
277
383
  // Finalize any open assistant block from the batch
278
384
  finalizeAssistantBlock();
279
385
 
@@ -2,11 +2,11 @@ import { handleIssueReference } from './clay-issue-links.js';
2
2
  // Narrow pre-router for Home and Capsule protocol responses kept out of the legacy router.
3
3
 
4
4
  import { processMessage as processAppMessage } from './app-messages.js';
5
- import { handleHomeMateModelsState, handleHomeMateModelResult } from './home-mate-settings.js';
5
+ import { handleMateInstructionsResult, handleHomeMateModelsState, handleHomeMateModelResult } from './home-mate-settings.js';
6
6
  import { handleHomeMateSessionIdentity, handleHomeDebateTranscript, handleHomeWorkspaceAssignment } from './home-mate-chat.js';
7
7
  import { handleToolLlmConfigState } from './tool-llm-status.js';
8
8
  import { handleToolSourceState, handleToolMateAccessState } from './home-tools.js';
9
- import { handleHomeDebatesState } from './home-debates-archive.js';
9
+ import { handleDebatesWorkbenchState } from './debates-workbench.js';
10
10
  import { renderProjectWorkspaceAssignment, showProjectWorkspaceAssignmentError } from './project-workspace-assignment.js';
11
11
  import { isHomeWorkspaceAssignmentMessage } from './workspace-assignment-routing.js';
12
12
  import { handleSearchClayMessage } from './search-clay-chat.js';
@@ -15,6 +15,8 @@ import { handleDefaultAiMessage } from './default-ai.js';
15
15
  import { handleCursorSharingMessage } from './cursor-sharing-preference.js';
16
16
  import { handlePendingMessageQueueMessage } from './pending-message-queue.js';
17
17
  import { receiveHistoryReplay } from './history-replay-batch.js';
18
+ import { handleProjectMateCreationMessage } from './project-mate-creation-workspace.js';
19
+ import { handleProjectDebatePlanningMessage } from './project-debate-planning-workspace.js';
18
20
 
19
21
  export function handleHomeProtocolMessage(msg) {
20
22
  if (handleIssueReference(msg)) return true;
@@ -32,9 +34,10 @@ export function handleHomeProtocolMessage(msg) {
32
34
  return true;
33
35
  }
34
36
  if (msg.type === "home_debates_state") {
35
- handleHomeDebatesState(msg);
37
+ handleDebatesWorkbenchState(msg);
36
38
  return true;
37
39
  }
40
+ if (msg.type === "mate_instructions_result") { handleMateInstructionsResult(msg); return true; }
38
41
  if (msg.type === "home_mate_models_state") {
39
42
  handleHomeMateModelsState(msg);
40
43
  return true;
@@ -87,6 +90,12 @@ export function processMessage(msg) {
87
90
  }
88
91
 
89
92
  function dispatchMessage(msg) {
93
+ if (typeof handleProjectDebatePlanningMessage === "function" && handleProjectDebatePlanningMessage(msg)) return;
94
+ if (msg.type === "home_debate_event" && msg.eventType) {
95
+ processAppMessage(Object.assign({}, msg, { type: msg.eventType, mateId: msg.speakerMateId || null }));
96
+ return;
97
+ }
98
+ if (handleProjectMateCreationMessage(msg)) return;
90
99
  if (handlePendingMessageQueueMessage(msg)) return;
91
100
  if (handleDefaultAiMessage(msg)) return;
92
101
  if (handleCursorSharingMessage(msg)) return;