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,414 @@
1
+ // Project-chat adapter for the existing Clay-owned Mate creation workflow.
2
+
3
+ import { store } from './store.js';
4
+ import { getWs } from './ws-ref.js';
5
+ import { iconHtml, refreshIcons } from './icons.js';
6
+ import { addToMessages, scrollToBottom } from './chat-render-runtime.js';
7
+ import { openMateWorkspace } from './project-mate-navigation.js';
8
+ import { createHomeDebateQuestionCard, normalizeHomeTranscript, failHomeDebateQuestion } from './home-debate-planning.js';
9
+ import { createHomeMateProposalCard, applyHomeMateProposal, resolveHomeMateProposal } from './home-mate-creation.js';
10
+
11
+ var choiceDialog = null;
12
+ var creationRoot = null;
13
+ var requestSequence = 0;
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 nextRequestId() {
29
+ requestSequence++;
30
+ return 'project-mate-creation-' + Date.now() + '-' + requestSequence;
31
+ }
32
+
33
+ function closeChoice(restoreFocus) {
34
+ var opener = choiceDialog ? choiceDialog._opener : null;
35
+ if (choiceDialog) choiceDialog.remove();
36
+ choiceDialog = null;
37
+ document.removeEventListener('keydown', handleChoiceKeydown, true);
38
+ if (restoreFocus && opener && opener.isConnected) opener.focus({ preventScroll: true });
39
+ }
40
+
41
+ function handleChoiceKeydown(event) {
42
+ if (!choiceDialog) return;
43
+ if (event.key === 'Escape') {
44
+ event.preventDefault();
45
+ closeChoice(true);
46
+ return;
47
+ }
48
+ if (event.key !== 'Tab') return;
49
+ var controls = choiceDialog.querySelectorAll('button:not([disabled])');
50
+ if (!controls.length) return;
51
+ if (event.shiftKey && document.activeElement === controls[0]) {
52
+ event.preventDefault();
53
+ controls[controls.length - 1].focus();
54
+ } else if (!event.shiftKey && document.activeElement === controls[controls.length - 1]) {
55
+ event.preventDefault();
56
+ controls[0].focus();
57
+ }
58
+ }
59
+
60
+ function startMateCreation() {
61
+ var clay = clayMate();
62
+ if (!clay) {
63
+ var status = choiceDialog && choiceDialog.querySelector('[role="alert"]');
64
+ if (status) status.textContent = 'Clay is unavailable. Refresh and try again.';
65
+ return;
66
+ }
67
+ closeChoice(false);
68
+ store.set({
69
+ projectMateCreationActive: true,
70
+ projectMateCreationMateId: clay.id,
71
+ projectMateCreationSessionId: null,
72
+ projectMateCreationRequestId: null,
73
+ projectMateCreationPendingStart: true,
74
+ projectMateCreationAwaitingHistory: false,
75
+ projectMateCreationAwaitingMateList: false,
76
+ projectMateCreationHistoryReady: false,
77
+ projectMateCreationLocalId: null,
78
+ projectMateCreationMessages: [],
79
+ });
80
+ openMateWorkspace(clay.id);
81
+ maybeStartPendingCreation();
82
+ }
83
+
84
+ function maybeStartPendingCreation() {
85
+ var state = store.snap();
86
+ if (!state.projectMateCreationPendingStart || !state.connected || state.activeProjectSlug !== 'mate-' + state.projectMateCreationMateId) return;
87
+ var requestId = nextRequestId();
88
+ if (!send({ type: 'home_mate_creation_plan', mateId: state.projectMateCreationMateId, requestId: requestId })) return;
89
+ store.set({ projectMateCreationPendingStart: false, projectMateCreationAwaitingHistory: true, projectMateCreationRequestId: requestId });
90
+ }
91
+
92
+ function choiceButton(icon, title, description) {
93
+ var button = document.createElement('button');
94
+ button.type = 'button';
95
+ button.className = 'project-create-choice-option';
96
+ button.innerHTML = iconHtml(icon);
97
+ var copy = document.createElement('span');
98
+ var heading = document.createElement('strong');
99
+ heading.textContent = title;
100
+ var detail = document.createElement('small');
101
+ detail.textContent = description;
102
+ copy.appendChild(heading);
103
+ copy.appendChild(detail);
104
+ button.appendChild(copy);
105
+ return button;
106
+ }
107
+
108
+ export function openProjectCreateChoice(opener, openProject) {
109
+ closeChoice(false);
110
+ var overlay = document.createElement('div');
111
+ overlay.className = 'project-create-choice-overlay';
112
+ overlay.setAttribute('role', 'presentation');
113
+ var dialog = document.createElement('section');
114
+ dialog.className = 'project-create-choice';
115
+ dialog.setAttribute('role', 'dialog');
116
+ dialog.setAttribute('aria-modal', 'true');
117
+ dialog.setAttribute('aria-labelledby', 'project-create-choice-title');
118
+ var eyebrow = document.createElement('span');
119
+ eyebrow.className = 'project-create-choice-eyebrow';
120
+ eyebrow.textContent = 'Create new';
121
+ var title = document.createElement('h2');
122
+ title.id = 'project-create-choice-title';
123
+ title.textContent = 'What would you like to add?';
124
+ var mate = choiceButton('users', 'Mate', 'Shape a new collaborator with Clay');
125
+ mate.addEventListener('click', startMateCreation);
126
+ var project = choiceButton('folder-plus', 'Project', 'Add a local workspace');
127
+ project.addEventListener('click', function () {
128
+ closeChoice(false);
129
+ openProject();
130
+ });
131
+ var cancel = document.createElement('button');
132
+ cancel.type = 'button';
133
+ cancel.className = 'project-create-choice-cancel';
134
+ cancel.textContent = 'Cancel';
135
+ cancel.addEventListener('click', function () { closeChoice(true); });
136
+ var status = document.createElement('p');
137
+ status.className = 'project-create-choice-error';
138
+ status.setAttribute('role', 'alert');
139
+ dialog.appendChild(eyebrow);
140
+ dialog.appendChild(title);
141
+ dialog.appendChild(mate);
142
+ dialog.appendChild(project);
143
+ dialog.appendChild(status);
144
+ dialog.appendChild(cancel);
145
+ overlay.appendChild(dialog);
146
+ overlay.addEventListener('click', function (event) { if (event.target === overlay) closeChoice(true); });
147
+ overlay._opener = opener || document.activeElement;
148
+ document.body.appendChild(overlay);
149
+ choiceDialog = overlay;
150
+ document.addEventListener('keydown', handleChoiceKeydown, true);
151
+ refreshIcons();
152
+ requestAnimationFrame(function () { mate.focus({ preventScroll: true }); });
153
+ }
154
+
155
+ function context() {
156
+ return {
157
+ mateId: store.get('projectMateCreationMateId'),
158
+ sessionId: store.get('projectMateCreationSessionId'),
159
+ requestId: store.get('projectMateCreationRequestId'),
160
+ };
161
+ }
162
+
163
+ function sameValue(left, right) {
164
+ return left != null && right != null && String(left) === String(right);
165
+ }
166
+
167
+ export function isExactProjectMateCreationMessage(state, msg, allowUnboundSession) {
168
+ if (!state.projectMateCreationActive || !sameValue(msg.mateId, state.projectMateCreationMateId) || !sameValue(msg.requestId, state.projectMateCreationRequestId)) return false;
169
+ if (!state.projectMateCreationSessionId) return allowUnboundSession === true;
170
+ return sameValue(msg.sessionId, state.projectMateCreationSessionId);
171
+ }
172
+
173
+ function creationMessages() {
174
+ return store.get('projectMateCreationMessages') || [];
175
+ }
176
+
177
+ function setCreationMessages(messages) {
178
+ store.set({ projectMateCreationMessages: messages });
179
+ }
180
+
181
+ function respondToQuestion(message, action, opener, answers) {
182
+ if (!message || message.status === 'submitting') return;
183
+ var payload = Object.assign({ type: 'home_mate_creation_question_response', toolId: message.toolId, answers: answers || {} }, context());
184
+ if (!send(payload)) {
185
+ setCreationMessages(creationMessages().map(function (item) { return item === message ? Object.assign({}, item, { error: 'Clay is offline. Reconnect and try again.' }) : item; }));
186
+ renderCreationMessages();
187
+ return;
188
+ }
189
+ setCreationMessages(creationMessages().map(function (item) { return item === message ? Object.assign({}, item, { status: 'submitting' }) : item; }));
190
+ renderCreationMessages();
191
+ }
192
+
193
+ function respondToProposal(message, action) {
194
+ if (!message || message.status === 'submitting') return;
195
+ var payload = Object.assign({ type: 'home_mate_creation_proposal_response', proposalId: message.proposal.proposalId, action: action === 'create' ? 'create' : 'cancel' }, context());
196
+ if (!send(payload)) {
197
+ setCreationMessages(creationMessages().map(function (item) { return item === message ? Object.assign({}, item, { error: 'Clay is offline. Reconnect and try again.' }) : item; }));
198
+ renderCreationMessages();
199
+ return;
200
+ }
201
+ setCreationMessages(creationMessages().map(function (item) { return item === message ? Object.assign({}, item, { status: 'submitting' }) : item; }));
202
+ renderCreationMessages();
203
+ }
204
+
205
+ function ensureCreationRoot() {
206
+ if (creationRoot && creationRoot.isConnected) return creationRoot;
207
+ creationRoot = document.createElement('section');
208
+ creationRoot.className = 'project-mate-creation-transcript home-mate-chat-transcript home-chat-bubble-layout';
209
+ creationRoot.setAttribute('aria-label', 'Mate creation interview');
210
+ addToMessages(creationRoot);
211
+ return creationRoot;
212
+ }
213
+
214
+ function renderCreationMessages() {
215
+ var root = ensureCreationRoot();
216
+ root.innerHTML = '';
217
+ var intro = document.createElement('header');
218
+ intro.className = 'project-mate-creation-intro';
219
+ intro.innerHTML = iconHtml('sparkles');
220
+ var copy = document.createElement('span');
221
+ copy.textContent = 'Create a Mate with Clay';
222
+ intro.appendChild(copy);
223
+ root.appendChild(intro);
224
+ var messages = creationMessages();
225
+ for (var i = 0; i < messages.length; i++) {
226
+ var message = messages[i];
227
+ if (message.role === 'question') root.appendChild(createHomeDebateQuestionCard(message, respondToQuestion));
228
+ if (message.role === 'mate_proposal') root.appendChild(createHomeMateProposalCard(message, respondToProposal, function (created) { openMateWorkspace(created.mateId); }));
229
+ }
230
+ scrollToBottom();
231
+ refreshIcons();
232
+ }
233
+
234
+ function renderCreationError(text) {
235
+ var root = ensureCreationRoot();
236
+ var prior = root.querySelector('.project-mate-creation-error');
237
+ if (prior) prior.remove();
238
+ var error = document.createElement('div');
239
+ error.className = 'project-mate-creation-error';
240
+ error.setAttribute('role', 'alert');
241
+ var copy = document.createElement('span');
242
+ copy.textContent = text || 'The Mate interview could not continue.';
243
+ var retry = document.createElement('button');
244
+ retry.type = 'button';
245
+ retry.textContent = 'Try again';
246
+ retry.addEventListener('click', function () {
247
+ error.remove();
248
+ send(Object.assign({ type: 'home_mate_creation_plan' }, context()));
249
+ });
250
+ error.appendChild(copy);
251
+ error.appendChild(retry);
252
+ root.appendChild(error);
253
+ retry.focus({ preventScroll: true });
254
+ }
255
+
256
+ function openCreationSession(msg) {
257
+ var state = store.snap();
258
+ if (!isExactProjectMateCreationMessage(state, msg, state.projectMateCreationAwaitingHistory && !state.projectMateCreationSessionId)) return false;
259
+ store.set({
260
+ projectMateCreationSessionId: msg.sessionId,
261
+ projectMateCreationLocalId: typeof msg.localId === 'number' ? msg.localId : state.projectMateCreationLocalId,
262
+ projectMateCreationAwaitingHistory: false,
263
+ });
264
+ if (typeof msg.localId === 'number' && String(store.get('activeSessionId')) !== String(msg.localId)) {
265
+ send({ type: 'switch_session', id: msg.localId });
266
+ return true;
267
+ }
268
+ setCreationMessages(normalizeHomeTranscript(msg.messages || []).filter(function (message) {
269
+ return message.role === 'question' || message.role === 'mate_proposal';
270
+ }));
271
+ renderCreationMessages();
272
+ return true;
273
+ }
274
+
275
+ function navigateCreatedMate(mateId) {
276
+ var mates = store.get('cachedMatesList') || [];
277
+ for (var i = 0; i < mates.length; i++) if (mates[i] && mates[i].id === mateId) {
278
+ openMateWorkspace(mateId);
279
+ return;
280
+ }
281
+ store.set({ projectMateCreationPendingMateId: mateId });
282
+ }
283
+
284
+ function resumeActiveCreation() {
285
+ var state = store.snap();
286
+ if (!state.projectMateCreationActive || !state.projectMateCreationMateId || !state.projectMateCreationSessionId) return;
287
+ var requestId = nextRequestId();
288
+ if (!send({ type: 'home_mate_session_open', mateId: state.projectMateCreationMateId, sessionId: state.projectMateCreationSessionId, requestId: requestId })) return;
289
+ store.set({ projectMateCreationRequestId: requestId, projectMateCreationAwaitingHistory: true });
290
+ }
291
+
292
+ function clearCreationBinding() {
293
+ creationRoot = null;
294
+ store.set({
295
+ projectMateCreationActive: false,
296
+ projectMateCreationMateId: null,
297
+ projectMateCreationSessionId: null,
298
+ projectMateCreationRequestId: null,
299
+ projectMateCreationPendingStart: false,
300
+ projectMateCreationAwaitingHistory: false,
301
+ projectMateCreationAwaitingMateList: false,
302
+ projectMateCreationHistoryReady: false,
303
+ projectMateCreationLocalId: null,
304
+ projectMateCreationMessages: [],
305
+ });
306
+ }
307
+
308
+ function activateRestoredCreation(msg, clay) {
309
+ creationRoot = null;
310
+ store.set({
311
+ projectMateCreationActive: true,
312
+ projectMateCreationMateId: clay.id,
313
+ projectMateCreationSessionId: msg.cliSessionId || 'local:' + msg.id,
314
+ projectMateCreationRequestId: nextRequestId(),
315
+ projectMateCreationAwaitingHistory: false,
316
+ projectMateCreationAwaitingMateList: false,
317
+ projectMateCreationHistoryReady: false,
318
+ projectMateCreationLocalId: msg.id,
319
+ projectMateCreationMessages: [],
320
+ });
321
+ }
322
+
323
+ function restoreAfterMateList() {
324
+ var state = store.snap();
325
+ if (!state.projectMateCreationAwaitingMateList) return;
326
+ var clay = clayMate();
327
+ if (!clay || state.currentSlug !== 'mate-' + clay.id || state.projectMateCreationLocalId == null) return;
328
+ var historyReady = state.projectMateCreationHistoryReady;
329
+ activateRestoredCreation({ id: state.projectMateCreationLocalId, cliSessionId: state.projectMateCreationSessionId && state.projectMateCreationSessionId.indexOf('local:') !== 0 ? state.projectMateCreationSessionId : null }, clay);
330
+ if (historyReady) resumeActiveCreation();
331
+ }
332
+
333
+ export function handleProjectMateCreationMessage(msg) {
334
+ if (msg.type === 'session_switched') {
335
+ var clay = clayMate();
336
+ var state = store.snap();
337
+ var startingHere = msg.mateCreation === true && state.projectMateCreationActive && state.projectMateCreationAwaitingHistory && !state.projectMateCreationSessionId && state.currentSlug === 'mate-' + state.projectMateCreationMateId;
338
+ if (startingHere) {
339
+ creationRoot = null;
340
+ store.set({ projectMateCreationSessionId: msg.cliSessionId || 'local:' + msg.id, projectMateCreationLocalId: msg.id, projectMateCreationMessages: [] });
341
+ } else if (msg.mateCreation === true && clay && state.currentSlug === 'mate-' + clay.id) activateRestoredCreation(msg, clay);
342
+ else if (msg.mateCreation === true && !clay && /^mate-/.test(state.currentSlug || '')) {
343
+ creationRoot = null;
344
+ store.set({ projectMateCreationActive: false, projectMateCreationAwaitingMateList: true, projectMateCreationHistoryReady: false, projectMateCreationLocalId: msg.id, projectMateCreationSessionId: msg.cliSessionId || 'local:' + msg.id, projectMateCreationMessages: [] });
345
+ } else if (!(state.projectMateCreationActive && (state.projectMateCreationPendingStart || state.projectMateCreationAwaitingHistory) && state.currentSlug === 'mate-' + state.projectMateCreationMateId && !state.projectMateCreationSessionId)) clearCreationBinding();
346
+ return false;
347
+ }
348
+ if (msg.type === 'history_done') {
349
+ if (store.get('projectMateCreationAwaitingMateList')) store.set({ projectMateCreationHistoryReady: true });
350
+ else if (!store.get('projectMateCreationAwaitingHistory')) resumeActiveCreation();
351
+ return false;
352
+ }
353
+ var rawCreationEvent = (msg.type === 'tool_executing' && msg.name === 'AskUserQuestion') || msg.type === 'mate_creation_proposal' || msg.type === 'mate_creation_proposal_resolved' || msg.type === 'ask_user_answered' || msg.type === 'delta';
354
+ if (rawCreationEvent && store.get('projectMateCreationAwaitingMateList')) return sameValue(store.get('activeSessionId'), store.get('projectMateCreationLocalId'));
355
+ if (!store.get('projectMateCreationActive')) return false;
356
+ if (msg.type === 'home_mate_history' && msg.mateCreation === true) {
357
+ return openCreationSession(msg);
358
+ }
359
+ if (msg.type === 'home_mate_session_identity') {
360
+ var identityState = store.snap();
361
+ if (!isExactProjectMateCreationMessage(identityState, Object.assign({}, msg, { sessionId: msg.previousSessionId }), false)) return false;
362
+ store.set({ projectMateCreationSessionId: msg.sessionId });
363
+ return true;
364
+ }
365
+ if (rawCreationEvent) return sameValue(store.get('activeSessionId'), store.get('projectMateCreationLocalId'));
366
+ if (!isExactProjectMateCreationMessage(store.snap(), msg, msg.type === 'home_mate_error' && store.get('projectMateCreationAwaitingHistory'))) return false;
367
+ if (msg.type === 'home_mate_creation_question') {
368
+ var questions = creationMessages().slice();
369
+ questions.push({ role: 'question', flow: 'mate_creation', toolId: msg.toolId, questions: msg.questions || [], status: 'pending', error: '' });
370
+ setCreationMessages(questions);
371
+ renderCreationMessages();
372
+ return true;
373
+ }
374
+ if (msg.type === 'home_mate_creation_question_resolved') {
375
+ setCreationMessages(creationMessages().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; }));
376
+ renderCreationMessages();
377
+ return true;
378
+ }
379
+ if (msg.type === 'home_mate_creation_proposal') {
380
+ setCreationMessages(applyHomeMateProposal(creationMessages(), msg));
381
+ renderCreationMessages();
382
+ return true;
383
+ }
384
+ if (msg.type === 'home_mate_creation_proposal_resolved') {
385
+ setCreationMessages(resolveHomeMateProposal(creationMessages(), msg));
386
+ renderCreationMessages();
387
+ if (msg.action === 'create' && msg.createdMateId) navigateCreatedMate(msg.createdMateId);
388
+ return true;
389
+ }
390
+ if (msg.type === 'home_mate_error') {
391
+ if (/^question_/.test(msg.code || '')) {
392
+ setCreationMessages(failHomeDebateQuestion(creationMessages(), msg));
393
+ renderCreationMessages();
394
+ } else renderCreationError(msg.text);
395
+ return true;
396
+ }
397
+ return false;
398
+ }
399
+
400
+ export function initProjectMateCreationWorkspace() {
401
+ store.subscribe(function (state, previous) {
402
+ if (state.projectMateCreationPendingStart !== previous.projectMateCreationPendingStart || state.connected !== previous.connected || state.activeProjectSlug !== previous.activeProjectSlug) maybeStartPendingCreation();
403
+ if (state.cachedMatesList !== previous.cachedMatesList) restoreAfterMateList();
404
+ if (state.projectMateCreationPendingMateId && state.cachedMatesList !== previous.cachedMatesList) {
405
+ var pending = state.projectMateCreationPendingMateId;
406
+ var mates = state.cachedMatesList || [];
407
+ for (var i = 0; i < mates.length; i++) if (mates[i] && mates[i].id === pending) {
408
+ store.set({ projectMateCreationPendingMateId: null });
409
+ openMateWorkspace(pending);
410
+ break;
411
+ }
412
+ }
413
+ });
414
+ }
@@ -0,0 +1,238 @@
1
+ // Existing Mates share the project rail while retaining their conversations.
2
+ import { store } from './store.js';
3
+ import { applyWorkspaceColor } from './mate-workspace-color.js';
4
+ import { mateAvatarUrl } from './avatar.js';
5
+ import { renderMateProjectHeader, renderOrdinaryProjectHeader } from './project-header-identity.js';
6
+ import { getHomeMateName } from './home-mate-selection.js';
7
+ import { switchProject } from './app-projects.js';
8
+ import { closeTerminal } from './terminal.js';
9
+ import { closeFileViewer } from './filebrowser.js';
10
+ import { getActiveMentionMateIds, onMentionActiveChange, bindIconTooltip, hideIconTooltip, showMateCtxMenu } from './sidebar-mates.js';
11
+
12
+ var FILTER_MODES = ['all', 'projects', 'mates'];
13
+ var FILTER_LABELS = { all: 'All', projects: 'Projects', mates: 'Mates' };
14
+ var filterSaveQueue = Promise.resolve();
15
+
16
+ export function normalizeProjectMateFilterMode(mode) {
17
+ return FILTER_MODES.indexOf(mode) === -1 ? 'all' : mode;
18
+ }
19
+
20
+ export function advanceProjectMateFilter(mode, turns) {
21
+ var index = FILTER_MODES.indexOf(normalizeProjectMateFilterMode(mode));
22
+ return { mode: FILTER_MODES[(index + 1) % FILTER_MODES.length], turns: (Number(turns) || 0) + 1 };
23
+ }
24
+
25
+ function applyProjectMateFilter() {
26
+ var rail = document.getElementById('icon-strip-projects');
27
+ var knob = document.getElementById('icon-strip-filter-knob');
28
+ if (!rail || !knob) return;
29
+ var mode = normalizeProjectMateFilterMode(store.get('projectMateFilterMode'));
30
+ var turns = Number(store.get('projectMateFilterTurns')) || 0;
31
+ var next = FILTER_MODES[(FILTER_MODES.indexOf(mode) + 1) % FILTER_MODES.length];
32
+ rail.dataset.filterMode = mode;
33
+ knob.dataset.mode = mode;
34
+ knob.setAttribute('aria-label', 'Showing ' + FILTER_LABELS[mode].toLowerCase() + '. Activate to show ' + FILTER_LABELS[next].toLowerCase());
35
+ knob.querySelector('.icon-strip-filter-pointer').style.transform = 'rotate(' + (turns * 120) + 'deg)';
36
+ document.getElementById('icon-strip-filter-label').textContent = FILTER_LABELS[mode];
37
+ }
38
+
39
+ function saveProjectMateFilter(mode) {
40
+ filterSaveQueue = filterSaveQueue.catch(function () {}).then(function () {
41
+ return fetch('/api/user/project-mate-filter', {
42
+ method: 'PUT',
43
+ headers: { 'Content-Type': 'application/json' },
44
+ credentials: 'same-origin',
45
+ keepalive: true,
46
+ body: JSON.stringify({ mode: mode })
47
+ });
48
+ }).catch(function () {});
49
+ }
50
+
51
+ function activateProjectMateFilter() {
52
+ var next = advanceProjectMateFilter(store.get('projectMateFilterMode'), store.get('projectMateFilterTurns'));
53
+ store.set({ projectMateFilterMode: next.mode, projectMateFilterTurns: next.turns, projectMateFilterActivated: true });
54
+ saveProjectMateFilter(next.mode);
55
+ }
56
+
57
+ function restoreProjectMateFilter() {
58
+ fetch('/api/profile', { credentials: 'same-origin' }).then(function (response) {
59
+ if (!response.ok) throw new Error('Unable to restore project and Mate filter');
60
+ return response.json();
61
+ }).then(function (profile) {
62
+ if (store.get('projectMateFilterActivated')) return;
63
+ var mode = normalizeProjectMateFilterMode(profile.projectMateFilterMode);
64
+ store.set({ projectMateFilterMode: mode, projectMateFilterTurns: FILTER_MODES.indexOf(mode) });
65
+ }).catch(function () {});
66
+ }
67
+
68
+ export function navigationMates(mates) {
69
+ return (Array.isArray(mates) ? mates : []).filter(function (mate) {
70
+ return mate && mate.id && !mate.archived;
71
+ }).sort(function (a, b) {
72
+ var aClay = a.builtinKey === 'clay' ? 0 : 1;
73
+ var bClay = b.builtinKey === 'clay' ? 0 : 1;
74
+ return aClay - bClay || (a.createdAt || 0) - (b.createdAt || 0);
75
+ });
76
+ }
77
+
78
+ export function isMateWorkspace(state) {
79
+ return (!!state.activeProjectMateId && state.activeProjectSlug === state.currentSlug) || (state.projectsHubList || []).some(function (project) {
80
+ return project.slug === state.currentSlug && project.isMate === true;
81
+ }) || navigationMates(state.cachedMatesList).some(function (mate) {
82
+ return state.currentSlug === 'mate-' + mate.id;
83
+ });
84
+ }
85
+
86
+ export function mateForWorkspace(state) {
87
+ var mates = navigationMates(state.cachedMatesList);
88
+ for (var i = 0; i < mates.length; i++) if (state.currentSlug === 'mate-' + mates[i].id) return mates[i];
89
+ return null;
90
+ }
91
+
92
+ function renderMateHeaderDefault(state) {
93
+ var mate = mateForWorkspace(state);
94
+ if (!mate || state.homeShellVisible) {
95
+ renderOrdinaryProjectHeader(state);
96
+ return;
97
+ }
98
+ renderMateProjectHeader(state, mate);
99
+ }
100
+
101
+ export function openMateWorkspace(mateId) {
102
+ var mate = navigationMates(store.get('cachedMatesList')).find(function (item) { return item.id === mateId; });
103
+ if (!mate) return;
104
+ if (store.get('currentSlug') !== 'mate-' + mateId) {
105
+ closeTerminal();
106
+ closeFileViewer();
107
+ }
108
+ switchProject('mate-' + mateId);
109
+ }
110
+
111
+ function createMateIcon(mateId) {
112
+ var button = document.createElement('button');
113
+ button.type = 'button';
114
+ button.className = 'icon-strip-item icon-strip-project-mate';
115
+ button.dataset.mateId = mateId;
116
+ button.innerHTML = '<img class="icon-strip-mate-avatar" alt="" draggable="false"><span class="icon-strip-pill"></span><span class="icon-strip-status"></span><span class="icon-strip-project-badge"></span>';
117
+ button.addEventListener('click', function () {
118
+ hideIconTooltip();
119
+ openMateWorkspace(mateId);
120
+ });
121
+ bindIconTooltip(button, function () {
122
+ var mate = navigationMates(store.get('cachedMatesList')).find(function (item) { return item.id === mateId; });
123
+ return {
124
+ text: mate ? getHomeMateName(mate) : 'Mate',
125
+ kind: 'mate',
126
+ avatarUrl: mate ? mateAvatarUrl(mate, 24) : '',
127
+ };
128
+ });
129
+ button.addEventListener('contextmenu', function (event) {
130
+ event.preventDefault();
131
+ var mate = navigationMates(store.get('cachedMatesList')).find(function (item) { return item.id === mateId; });
132
+ if (mate) showMateCtxMenu(button, mate);
133
+ });
134
+ return button;
135
+ }
136
+
137
+ export function renderProjectMates() {
138
+ var rail = document.getElementById('icon-strip-projects');
139
+ if (!rail) return;
140
+ hideIconTooltip();
141
+ var state = store.snap();
142
+ var clay = navigationMates(state.cachedMatesList).find(function (mate) { return mate.builtinKey === 'clay'; });
143
+ var clayActive = !!clay && !state.homeShellVisible && !state.dmMode && state.currentSlug === 'mate-' + clay.id;
144
+ document.body.classList.toggle('clay-workspace-active', clayActive);
145
+ var clayEntry = document.querySelector('.icon-strip-brand');
146
+ if (clayEntry) {
147
+ clayEntry.disabled = !clay;
148
+ clayEntry.classList.toggle('active', clayActive);
149
+ clayEntry.setAttribute('aria-label', clay ? 'Open Clay' : 'Clay is unavailable');
150
+ if (clayActive) clayEntry.setAttribute('aria-current', 'page');
151
+ else clayEntry.removeAttribute('aria-current');
152
+ }
153
+ document.body.classList.toggle('mate-workspace-active', !state.homeShellVisible && isMateWorkspace(state));
154
+ applyWorkspaceColor(mateForWorkspace(state));
155
+ renderMateHeaderDefault(state);
156
+ var mates = navigationMates(state.cachedMatesList).filter(function (mate) {
157
+ return mate.builtinKey !== 'clay';
158
+ });
159
+ var mentionActive = getActiveMentionMateIds();
160
+ var projects = state.projectsHubList || [];
161
+ var unread = state.dmUnread || {};
162
+ var icons = {};
163
+ rail.querySelectorAll('[data-mate-id]').forEach(function (button) { icons[button.dataset.mateId] = button; });
164
+ mates.forEach(function (mate) {
165
+ var button = icons[mate.id] || createMateIcon(mate.id);
166
+ delete icons[mate.id];
167
+ var active = !state.homeShellVisible && !state.dmMode && state.currentSlug === 'mate-' + mate.id;
168
+ var project = projects.find(function (item) { return item.slug === 'mate-' + mate.id; });
169
+ var busy = !!(project && project.isProcessing) || !!mentionActive[mate.id];
170
+ var count = active ? 0 : (unread[mate.id] || 0);
171
+ button.classList.toggle('active', !!active);
172
+ button.setAttribute('aria-label', getHomeMateName(mate) + ', Mate' + (busy ? ', working' : '') + (count ? ', ' + count + ' unread' : ''));
173
+ if (active) button.setAttribute('aria-current', 'page');
174
+ else button.removeAttribute('aria-current');
175
+ var avatar = button.querySelector('img');
176
+ var url = mateAvatarUrl(mate, 38);
177
+ if (avatar.getAttribute('src') !== url) avatar.src = url;
178
+ var status = button.querySelector('.icon-strip-status');
179
+ status.classList.toggle('processing', busy);
180
+ status.classList.toggle('connected', !!state.connected);
181
+ var badge = button.querySelector('.icon-strip-project-badge');
182
+ badge.classList.toggle('has-unread', count > 0);
183
+ badge.textContent = count > 99 ? '99+' : count ? String(count) : '';
184
+ if (!button.parentNode) rail.appendChild(button);
185
+ });
186
+ Object.keys(icons).forEach(function (id) { icons[id].remove(); });
187
+ var ordered = Array.from(rail.querySelectorAll('[data-mate-id]'));
188
+ mates.forEach(function (mate, index) {
189
+ var button = ordered.find(function (item) { return item.dataset.mateId === mate.id; });
190
+ if (ordered[index] === button) return;
191
+ rail.insertBefore(button, ordered[index] || null);
192
+ ordered.splice(ordered.indexOf(button), 1);
193
+ ordered.splice(index, 0, button);
194
+ });
195
+ var separator = rail.querySelector('.icon-strip-peer-separator');
196
+ var hasProjects = !!rail.querySelector(':scope > .icon-strip-item[data-slug], :scope > .icon-strip-group');
197
+ if (hasProjects && mates.length) {
198
+ if (!separator) {
199
+ separator = document.createElement('div');
200
+ separator.className = 'icon-strip-peer-separator';
201
+ }
202
+ var firstMate = rail.querySelector(':scope > [data-mate-id]');
203
+ if (firstMate) rail.insertBefore(separator, firstMate);
204
+ } else if (separator) {
205
+ separator.remove();
206
+ }
207
+ rail.querySelectorAll('[data-slug]').forEach(function (item) {
208
+ item.classList.toggle('active', !state.homeShellVisible && !state.dmMode && item.dataset.slug === state.currentSlug);
209
+ });
210
+ var home = document.querySelector('.icon-strip-home');
211
+ if (home && !home.classList.contains('icon-strip-brand')) home.classList.toggle('active', !!state.homeShellVisible);
212
+ applyProjectMateFilter();
213
+ }
214
+
215
+ export function initProjectMateNavigation() {
216
+ var clayEntry = document.querySelector('.icon-strip-brand');
217
+ if (clayEntry) {
218
+ clayEntry.addEventListener('click', function () {
219
+ var clay = navigationMates(store.get('cachedMatesList')).find(function (mate) { return mate.builtinKey === 'clay'; });
220
+ hideIconTooltip();
221
+ if (clay) openMateWorkspace(clay.id);
222
+ });
223
+ bindIconTooltip(clayEntry, { text: 'Clay', kind: 'mate', avatarUrl: 'clay-studio-symbol.png' });
224
+ }
225
+ var knob = document.getElementById('icon-strip-filter-knob');
226
+ if (knob) knob.addEventListener('click', activateProjectMateFilter);
227
+ store.subscribe(function (state, previous) {
228
+ if (state.cachedMatesList !== previous.cachedMatesList || state.homeChatMateId !== previous.homeChatMateId ||
229
+ state.homeShellVisible !== previous.homeShellVisible || state.projectsHubList !== previous.projectsHubList ||
230
+ state.dmUnread !== previous.dmUnread || state.currentSlug !== previous.currentSlug ||
231
+ state.dmMode !== previous.dmMode || state.connected !== previous.connected) renderProjectMates();
232
+ if (state.projectMateFilterMode !== previous.projectMateFilterMode ||
233
+ state.projectMateFilterTurns !== previous.projectMateFilterTurns) applyProjectMateFilter();
234
+ });
235
+ onMentionActiveChange(renderProjectMates);
236
+ renderProjectMates();
237
+ restoreProjectMateFilter();
238
+ }
@@ -0,0 +1,29 @@
1
+ // Exact project/session navigation shared by search, notifications and links.
2
+ import { store } from './store.js';
3
+ import { getWs } from './ws-ref.js';
4
+ import { switchProject } from './app-projects.js';
5
+
6
+ function flush() {
7
+ var pending = store.get('pendingProjectSessionNavigation');
8
+ var ws = getWs();
9
+ if (!pending || store.get('activeProjectSlug') !== pending.projectSlug || !ws || ws.readyState !== 1) return false;
10
+ store.set({ pendingProjectSessionNavigation: null });
11
+ ws.send(JSON.stringify({ type: 'switch_session', id: pending.sessionId }));
12
+ return true;
13
+ }
14
+
15
+ export function openProjectSession(projectSlug, sessionId) {
16
+ var id = Number(sessionId);
17
+ if (!projectSlug || !Number.isSafeInteger(id) || id < 1) return false;
18
+ store.set({ pendingProjectSessionNavigation: { projectSlug: projectSlug, sessionId: id } });
19
+ switchProject(projectSlug);
20
+ flush();
21
+ return true;
22
+ }
23
+
24
+ export function initProjectSessionNavigation() {
25
+ store.subscribe(function (state, previous) {
26
+ if (state.activeProjectSlug !== previous.activeProjectSlug || state.connected !== previous.connected || state.pendingProjectSessionNavigation !== previous.pendingProjectSessionNavigation) flush();
27
+ });
28
+ flush();
29
+ }