clay-server 3.8.1 → 4.0.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 (189) hide show
  1. package/lib/ask-user-mcp-server.js +5 -119
  2. package/lib/builtin-mates.js +6 -6
  3. package/lib/claude-hook-installer.js +15 -0
  4. package/lib/clay-history-mcp-server.js +62 -297
  5. package/lib/daemon.js +1 -1
  6. package/lib/debate-model-selection.js +119 -0
  7. package/lib/home-debate-tool-policy.js +164 -0
  8. package/lib/home-response-notifications.js +26 -0
  9. package/lib/mate-creation-mcp-server.js +55 -0
  10. package/lib/mate-ready-creation.js +95 -0
  11. package/lib/mates.js +2 -0
  12. package/lib/notes.js +2 -1
  13. package/lib/project-ask-user.js +64 -0
  14. package/lib/project-capsule-catalog.js +101 -0
  15. package/lib/project-connection.js +4 -0
  16. package/lib/project-debate-proposal.js +133 -17
  17. package/lib/project-debate.js +214 -110
  18. package/lib/project-delegated-follow-up.js +84 -0
  19. package/lib/project-delegated-session.js +53 -0
  20. package/lib/project-filesystem.js +7 -9
  21. package/lib/project-knowledge.js +1 -0
  22. package/lib/project-mate-creation-proposal.js +120 -0
  23. package/lib/project-memory.js +14 -7
  24. package/lib/project-models.js +157 -32
  25. package/lib/project-session-notes.js +2 -2
  26. package/lib/project-session-pair.js +8 -8
  27. package/lib/project-sessions.js +66 -51
  28. package/lib/project-vendor-login.js +397 -0
  29. package/lib/project-worker-proposal.js +25 -25
  30. package/lib/project-workspace-query.js +144 -0
  31. package/lib/project.js +349 -102
  32. package/lib/public/app.js +85 -42
  33. package/lib/public/css/avatar-imprints.css +0 -1
  34. package/lib/public/css/base.css +4 -0
  35. package/lib/public/css/capsule-ui.css +389 -0
  36. package/lib/public/css/command-palette.css +306 -6
  37. package/lib/public/css/home-capsule-library.css +243 -0
  38. package/lib/public/css/home-capsule-source.css +192 -0
  39. package/lib/public/css/home-debate-live.css +146 -0
  40. package/lib/public/css/home-debate-planning.css +125 -0
  41. package/lib/public/css/home-debates-archive.css +281 -0
  42. package/lib/public/css/home-hub.css +896 -405
  43. package/lib/public/css/home-mate-creation.css +34 -0
  44. package/lib/public/css/home-mate-model-picker.css +131 -0
  45. package/lib/public/css/home-mate-settings.css +344 -0
  46. package/lib/public/css/home-session-actions.css +265 -0
  47. package/lib/public/css/home-sidebar.css +667 -0
  48. package/lib/public/css/icon-strip.css +1 -2
  49. package/lib/public/css/mates.css +7 -34
  50. package/lib/public/css/pane.css +1 -1
  51. package/lib/public/css/playbook.css +0 -80
  52. package/lib/public/css/sticky-notes.css +53 -117
  53. package/lib/public/css/tui-attention.css +0 -44
  54. package/lib/public/css/workspace-assignment.css +45 -0
  55. package/lib/public/fonts/source-serif-4/LICENSE.md +93 -0
  56. package/lib/public/fonts/source-serif-4/SourceSerif4Caption-Semibold.ttf.woff2 +0 -0
  57. package/lib/public/index.html +93 -74
  58. package/lib/public/modules/app-connection.js +10 -1
  59. package/lib/public/modules/app-dm.js +43 -160
  60. package/lib/public/modules/app-home-hub.js +328 -585
  61. package/lib/public/modules/app-message-cards.js +242 -0
  62. package/lib/public/modules/app-message-router.js +84 -0
  63. package/lib/public/modules/app-messages.js +102 -37
  64. package/lib/public/modules/app-notifications.js +8 -119
  65. package/lib/public/modules/app-projects.js +9 -1
  66. package/lib/public/modules/app-rendering.js +21 -602
  67. package/lib/public/modules/avatar-imprint.js +11 -8
  68. package/lib/public/modules/chat-bubble-renderer.js +152 -0
  69. package/lib/public/modules/chat-render-runtime.js +198 -0
  70. package/lib/public/modules/command-palette.js +225 -436
  71. package/lib/public/modules/dm-render.js +82 -0
  72. package/lib/public/modules/home-capsule-creation-intent.js +31 -0
  73. package/lib/public/modules/home-capsule-library.js +146 -0
  74. package/lib/public/modules/home-capsule-source.js +383 -0
  75. package/lib/public/modules/home-chat-identity.js +35 -0
  76. package/lib/public/modules/home-chat-scroll.js +77 -0
  77. package/lib/public/modules/home-chat-stream-state.js +34 -0
  78. package/lib/public/modules/home-composer-focus.js +25 -0
  79. package/lib/public/modules/home-conversations-sheet.js +179 -0
  80. package/lib/public/modules/home-debate-activity.js +48 -0
  81. package/lib/public/modules/home-debate-controls.js +295 -0
  82. package/lib/public/modules/home-debate-launch.js +41 -0
  83. package/lib/public/modules/home-debate-live.js +284 -0
  84. package/lib/public/modules/home-debate-models.js +73 -0
  85. package/lib/public/modules/home-debate-planning.js +335 -0
  86. package/lib/public/modules/home-debates-archive.js +279 -0
  87. package/lib/public/modules/home-dock-resize.js +74 -0
  88. package/lib/public/modules/home-dock.js +446 -0
  89. package/lib/public/modules/home-mate-chat.js +496 -0
  90. package/lib/public/modules/home-mate-creation.js +109 -0
  91. package/lib/public/modules/home-mate-model-picker.js +281 -0
  92. package/lib/public/modules/home-mate-selection.js +38 -0
  93. package/lib/public/modules/home-mate-settings-menu.js +131 -0
  94. package/lib/public/modules/home-mate-settings.js +383 -0
  95. package/lib/public/modules/home-session-actions.js +373 -0
  96. package/lib/public/modules/home-session-model-confirmation.js +13 -0
  97. package/lib/public/modules/home-shell.js +9 -0
  98. package/lib/public/modules/home-sidebar-chat-list.js +63 -0
  99. package/lib/public/modules/home-sidebar.js +158 -0
  100. package/lib/public/modules/home-sub-surface.js +19 -0
  101. package/lib/public/modules/home-surface-boot.js +68 -0
  102. package/lib/public/modules/home-surface.js +141 -0
  103. package/lib/public/modules/home-tools.js +271 -0
  104. package/lib/public/modules/markdown.js +50 -2
  105. package/lib/public/modules/mate-management.js +86 -0
  106. package/lib/public/modules/mate-wizard.js +0 -7
  107. package/lib/public/modules/mention.js +17 -19
  108. package/lib/public/modules/pane-bridge.js +9 -0
  109. package/lib/public/modules/profile.js +4 -5
  110. package/lib/public/modules/project-settings.js +34 -28
  111. package/lib/public/modules/project-switcher.js +11 -5
  112. package/lib/public/modules/project-workspace-assignment.js +23 -0
  113. package/lib/public/modules/search-clay-chat.js +402 -0
  114. package/lib/public/modules/server-settings.js +14 -12
  115. package/lib/public/modules/session-actions.js +1 -1
  116. package/lib/public/modules/sidebar-mates.js +56 -552
  117. package/lib/public/modules/sidebar-mobile.js +7 -64
  118. package/lib/public/modules/sidebar-presence.js +37 -0
  119. package/lib/public/modules/split-pair-ui.js +13 -13
  120. package/lib/public/modules/split-view.js +7 -0
  121. package/lib/public/modules/sticky-notes.js +7 -6
  122. package/lib/public/modules/tool-input-composition.js +48 -0
  123. package/lib/public/modules/tool-llm-status.js +167 -0
  124. package/lib/public/modules/tool-renderer-advanced.js +285 -0
  125. package/lib/public/modules/tool-renderer-chart.js +158 -0
  126. package/lib/public/modules/tool-renderer-semantics.js +35 -0
  127. package/lib/public/modules/tool-renderer.js +464 -0
  128. package/lib/public/modules/tool-runtime.js +309 -0
  129. package/lib/public/modules/tool-ui-evaluator.js +97 -0
  130. package/lib/public/modules/tool-ui-tree.js +21 -0
  131. package/lib/public/modules/tui-attention.js +10 -0
  132. package/lib/public/modules/user-settings.js +16 -23
  133. package/lib/public/modules/vendor-login.js +287 -0
  134. package/lib/public/modules/worker-proposal.js +2 -2
  135. package/lib/public/modules/workspace-assignment-card.js +149 -0
  136. package/lib/public/modules/workspace-assignment-routing.js +4 -0
  137. package/lib/public/style.css +13 -2
  138. package/lib/runtime-env.js +65 -0
  139. package/lib/sdk-bridge.js +120 -32
  140. package/lib/sdk-message-processor.js +10 -6
  141. package/lib/server-home-capsule-creation.js +36 -0
  142. package/lib/server-home-chat-events.js +287 -0
  143. package/lib/server-home-chat.js +518 -0
  144. package/lib/server-home-clay-entry.js +65 -0
  145. package/lib/server-home-clay-session-links.js +26 -0
  146. package/lib/server-home-debate-planning.js +214 -0
  147. package/lib/server-home-debates.js +137 -0
  148. package/lib/server-home-mate-creation.js +261 -0
  149. package/lib/server-home-models.js +241 -0
  150. package/lib/server-home-preferences.js +61 -0
  151. package/lib/server-mates.js +37 -0
  152. package/lib/server-tools.js +377 -0
  153. package/lib/server.js +111 -59
  154. package/lib/session-pair-mcp-server.js +6 -6
  155. package/lib/sessions.js +18 -3
  156. package/lib/tool-capsule-source.js +142 -0
  157. package/lib/tool-control-mcp-server.js +126 -0
  158. package/lib/tool-llm.js +48 -0
  159. package/lib/tool-storage.js +77 -0
  160. package/lib/tool-ui-spec-advanced.js +82 -0
  161. package/lib/tool-ui-spec.js +261 -0
  162. package/lib/tools-registry.js +286 -0
  163. package/lib/users-home-dock-preferences.js +77 -0
  164. package/lib/users-home-surface-preferences.js +99 -0
  165. package/lib/users-preferences.js +4 -7
  166. package/lib/users.js +12 -0
  167. package/lib/whats-new-content.js +3 -3
  168. package/lib/workspace-assignment-service.js +420 -0
  169. package/lib/workspace-query-mcp-server.js +154 -0
  170. package/lib/workspace-query-service.js +492 -0
  171. package/lib/ws-schema.js +93 -12
  172. package/lib/yoke/adapters/acp.js +2 -1
  173. package/lib/yoke/adapters/antigravity.js +2 -1
  174. package/lib/yoke/adapters/claude-worker.js +11 -4
  175. package/lib/yoke/adapters/claude.js +73 -43
  176. package/lib/yoke/adapters/codex.js +40 -10
  177. package/lib/yoke/adapters/kiro.js +2 -1
  178. package/lib/yoke/claude-user-input.js +38 -0
  179. package/lib/yoke/codex-app-server.js +20 -2
  180. package/lib/yoke/codex-user-input.js +64 -0
  181. package/lib/yoke/complete-once.js +164 -0
  182. package/lib/yoke/index.js +5 -1
  183. package/lib/yoke/interface.js +19 -0
  184. package/lib/yoke/mcp-bridge-server.js +2 -6
  185. package/lib/yoke/user-input.js +254 -0
  186. package/package.json +3 -2
  187. package/lib/public/css/home-chat.css +0 -380
  188. package/lib/public/modules/home-chat.js +0 -438
  189. package/lib/server-clay-home.js +0 -245
@@ -1,438 +0,0 @@
1
- // Clay FAB + popover chat — phablet-style, persistent across the app.
2
- // Self-contained: own DOM, own renderer, own WS protocol (home_clay_*).
3
- // Does not interfere with the active project session.
4
-
5
- import { escapeHtml } from './utils.js';
6
- import { getWs } from './ws-ref.js';
7
- import { renderMarkdown } from './markdown.js';
8
- import { switchProject } from './app-projects.js';
9
-
10
- var initialized = false;
11
- var openState = false;
12
- var fabBtn = null;
13
- var popoverEl = null;
14
- var messagesEl = null;
15
- var inputEl = null;
16
- var sendBtn = null;
17
- var typingEl = null;
18
- var newBtnEl = null;
19
- var closeBtnEl = null;
20
-
21
- // Drag state for the FAB.
22
- var FAB_POS_KEY = "clay-fab-pos";
23
- var DRAG_THRESHOLD_PX = 5;
24
- var dragState = null;
25
-
26
- // Per-turn assembly state. Server may emit many delta events for a single
27
- // assistant turn; we accumulate text and render incrementally into the
28
- // last bubble.
29
- var currentAssistantBubble = null;
30
- var currentAssistantText = "";
31
- var openedOnce = false; // gate the initial home_clay_open request
32
-
33
- export function initHomeChat() {
34
- if (initialized) return;
35
- initialized = true;
36
-
37
- fabBtn = document.getElementById("clay-fab");
38
- popoverEl = document.getElementById("clay-popover");
39
- messagesEl = document.getElementById("home-chat-messages");
40
- inputEl = document.getElementById("home-chat-input");
41
- sendBtn = document.getElementById("home-chat-send-btn");
42
- typingEl = document.getElementById("home-chat-typing");
43
- newBtnEl = document.getElementById("home-chat-new-btn");
44
- closeBtnEl = document.getElementById("home-chat-close-btn");
45
-
46
- if (!fabBtn || !popoverEl || !messagesEl || !inputEl || !sendBtn) return;
47
-
48
- // --- Restore persisted FAB position ---
49
- restoreFabPosition();
50
- // Re-clamp on viewport resize so a saved position from a wider window
51
- // doesn't strand the FAB off-screen.
52
- window.addEventListener("resize", function () {
53
- if (fabBtn.classList.contains("user-positioned")) clampFabIntoView();
54
- });
55
-
56
- // --- FAB drag + click ---
57
- // mousedown/touchstart begins a potential drag. We only treat it as a
58
- // click (toggle popover) if the pointer didn't move past DRAG_THRESHOLD_PX.
59
- fabBtn.addEventListener("mousedown", onPointerDown);
60
- fabBtn.addEventListener("touchstart", onPointerDown, { passive: false });
61
-
62
- if (closeBtnEl) closeBtnEl.addEventListener("click", closePopover);
63
-
64
- // ESC closes the popover.
65
- document.addEventListener("keydown", function (e) {
66
- if (e.key === "Escape" && openState) {
67
- closePopover();
68
- }
69
- });
70
-
71
- // --- Input handling ---
72
- inputEl.addEventListener("input", function () {
73
- autoResize();
74
- sendBtn.disabled = inputEl.value.trim().length === 0;
75
- });
76
- inputEl.addEventListener("keydown", function (e) {
77
- if (e.key === "Enter" && !e.shiftKey && !e.isComposing) {
78
- e.preventDefault();
79
- doSend();
80
- }
81
- });
82
- sendBtn.addEventListener("click", doSend);
83
- if (newBtnEl) {
84
- newBtnEl.addEventListener("click", function () {
85
- var ws = getWs();
86
- if (!ws || ws.readyState !== 1) return;
87
- ws.send(JSON.stringify({ type: "home_clay_new_session" }));
88
- messagesEl.innerHTML = "";
89
- currentAssistantBubble = null;
90
- currentAssistantText = "";
91
- hideTyping();
92
- addSystemBubble("New conversation started.");
93
- });
94
- }
95
- }
96
-
97
- // --- FAB drag mechanics ---
98
-
99
- function getPointer(e) {
100
- if (e.touches && e.touches.length > 0) {
101
- return { x: e.touches[0].clientX, y: e.touches[0].clientY };
102
- }
103
- return { x: e.clientX, y: e.clientY };
104
- }
105
-
106
- function onPointerDown(e) {
107
- // Ignore right-clicks and modifier-clicks.
108
- if (e.button && e.button !== 0) return;
109
- var p = getPointer(e);
110
- var rect = fabBtn.getBoundingClientRect();
111
- dragState = {
112
- startX: p.x,
113
- startY: p.y,
114
- offsetX: p.x - rect.left,
115
- offsetY: p.y - rect.top,
116
- moved: false,
117
- };
118
- document.addEventListener("mousemove", onPointerMove);
119
- document.addEventListener("mouseup", onPointerUp);
120
- document.addEventListener("touchmove", onPointerMove, { passive: false });
121
- document.addEventListener("touchend", onPointerUp);
122
- document.addEventListener("touchcancel", onPointerUp);
123
- // Don't preventDefault yet — we let the browser distinguish between a
124
- // tap (which should fire click → toggle) and a drag.
125
- }
126
-
127
- function onPointerMove(e) {
128
- if (!dragState) return;
129
- var p = getPointer(e);
130
- var dx = p.x - dragState.startX;
131
- var dy = p.y - dragState.startY;
132
- if (!dragState.moved && Math.abs(dx) + Math.abs(dy) < DRAG_THRESHOLD_PX) {
133
- return;
134
- }
135
- dragState.moved = true;
136
- fabBtn.classList.add("dragging");
137
- // Touch needs explicit prevent so the page doesn't scroll.
138
- if (e.cancelable) e.preventDefault();
139
- var x = p.x - dragState.offsetX;
140
- var y = p.y - dragState.offsetY;
141
- setFabPosition(x, y);
142
- }
143
-
144
- function onPointerUp() {
145
- document.removeEventListener("mousemove", onPointerMove);
146
- document.removeEventListener("mouseup", onPointerUp);
147
- document.removeEventListener("touchmove", onPointerMove);
148
- document.removeEventListener("touchend", onPointerUp);
149
- document.removeEventListener("touchcancel", onPointerUp);
150
- if (!dragState) return;
151
- if (dragState.moved) {
152
- fabBtn.classList.remove("dragging");
153
- persistFabPosition();
154
- clampFabIntoView();
155
- } else {
156
- // Pure tap → toggle popover.
157
- toggleOpen();
158
- }
159
- dragState = null;
160
- }
161
-
162
- function setFabPosition(x, y) {
163
- // Ensure the FAB stays within the viewport with a small margin.
164
- var margin = 4;
165
- var w = fabBtn.offsetWidth;
166
- var h = fabBtn.offsetHeight;
167
- var maxX = window.innerWidth - w - margin;
168
- var maxY = window.innerHeight - h - margin;
169
- if (x < margin) x = margin;
170
- if (y < margin) y = margin;
171
- if (x > maxX) x = maxX;
172
- if (y > maxY) y = maxY;
173
- fabBtn.classList.add("user-positioned");
174
- fabBtn.style.left = x + "px";
175
- fabBtn.style.top = y + "px";
176
- fabBtn.style.right = "auto";
177
- fabBtn.style.bottom = "auto";
178
- // If the popover is open, keep it anchored to the FAB.
179
- if (openState) anchorPopoverToFab();
180
- }
181
-
182
- function clampFabIntoView() {
183
- if (!fabBtn) return;
184
- var rect = fabBtn.getBoundingClientRect();
185
- setFabPosition(rect.left, rect.top);
186
- }
187
-
188
- function persistFabPosition() {
189
- if (!fabBtn) return;
190
- try {
191
- var rect = fabBtn.getBoundingClientRect();
192
- localStorage.setItem(FAB_POS_KEY, JSON.stringify({ x: rect.left, y: rect.top }));
193
- } catch (e) {}
194
- }
195
-
196
- function restoreFabPosition() {
197
- try {
198
- var raw = localStorage.getItem(FAB_POS_KEY);
199
- if (!raw) return;
200
- var pos = JSON.parse(raw);
201
- if (pos && typeof pos.x === "number" && typeof pos.y === "number") {
202
- setFabPosition(pos.x, pos.y);
203
- }
204
- } catch (e) {}
205
- }
206
-
207
- // Anchor the popover so its corner sits next to the FAB. We pick the
208
- // corner that gives the most room — popover opens "into" the screen,
209
- // not off-screen.
210
- function anchorPopoverToFab() {
211
- if (!fabBtn || !popoverEl) return;
212
- var fr = fabBtn.getBoundingClientRect();
213
- var pw = popoverEl.offsetWidth || 320;
214
- var ph = popoverEl.offsetHeight || 480;
215
- var margin = 12;
216
- // Decide vertical: open above FAB if there's more room above.
217
- var roomAbove = fr.top;
218
- var roomBelow = window.innerHeight - fr.bottom;
219
- var openUp = roomAbove >= roomBelow;
220
- // Decide horizontal: align right edge of popover with right edge of FAB
221
- // when the FAB is on the right half of the screen, else left edge.
222
- var fabRightSide = (fr.left + fr.width / 2) > window.innerWidth / 2;
223
-
224
- var top, left;
225
- if (openUp) {
226
- top = Math.max(margin, fr.top - ph - 8);
227
- popoverEl.style.transformOrigin = fabRightSide ? "bottom right" : "bottom left";
228
- } else {
229
- top = Math.min(window.innerHeight - ph - margin, fr.bottom + 8);
230
- popoverEl.style.transformOrigin = fabRightSide ? "top right" : "top left";
231
- }
232
- if (fabRightSide) {
233
- left = Math.max(margin, fr.right - pw);
234
- } else {
235
- left = Math.min(window.innerWidth - pw - margin, fr.left);
236
- }
237
- popoverEl.style.top = top + "px";
238
- popoverEl.style.left = left + "px";
239
- popoverEl.style.right = "auto";
240
- popoverEl.style.bottom = "auto";
241
- }
242
-
243
- function openPopover() {
244
- if (!popoverEl || openState) return;
245
- openState = true;
246
- // Anchor BEFORE unhiding so the slide-up animation uses the correct
247
- // transform-origin (top vs bottom, left vs right) for the FAB's
248
- // current corner.
249
- anchorPopoverToFab();
250
- popoverEl.classList.remove("hidden");
251
- if (fabBtn) fabBtn.classList.add("open");
252
- // Pull session history on first open. If WS isn't ready yet, leave
253
- // openedOnce false so the next open retries.
254
- if (!openedOnce) {
255
- var ws = getWs();
256
- if (ws && ws.readyState === 1) {
257
- openedOnce = true;
258
- requestSession();
259
- } else {
260
- addSystemBubble("Connecting…");
261
- }
262
- }
263
- // Focus the input so the user can start typing immediately.
264
- setTimeout(function () { if (inputEl) inputEl.focus(); }, 60);
265
- }
266
-
267
- function closePopover() {
268
- if (!openState) return;
269
- openState = false;
270
- if (popoverEl) popoverEl.classList.add("hidden");
271
- if (fabBtn) {
272
- fabBtn.classList.remove("open");
273
- fabBtn.focus();
274
- }
275
- }
276
-
277
- function toggleOpen() {
278
- if (openState) closePopover(); else openPopover();
279
- }
280
-
281
- function autoResize() {
282
- inputEl.style.height = "auto";
283
- inputEl.style.height = Math.min(120, inputEl.scrollHeight) + "px";
284
- }
285
-
286
- function requestSession() {
287
- var ws = getWs();
288
- if (!ws || ws.readyState !== 1) return;
289
- ws.send(JSON.stringify({ type: "home_clay_open" }));
290
- }
291
-
292
- function doSend() {
293
- var text = inputEl.value.trim();
294
- if (!text) return;
295
- var ws = getWs();
296
- if (!ws || ws.readyState !== 1) return;
297
-
298
- // Optimistic render of the user's message.
299
- addUserBubble(text);
300
- inputEl.value = "";
301
- autoResize();
302
- sendBtn.disabled = true;
303
-
304
- ws.send(JSON.stringify({ type: "home_clay_send", text: text }));
305
- showTyping();
306
- }
307
-
308
- // --- Rendering ---
309
-
310
- function addUserBubble(text) {
311
- finalizeAssistant();
312
- var bubble = document.createElement("div");
313
- bubble.className = "home-chat-bubble home-chat-bubble-user";
314
- bubble.textContent = text;
315
- messagesEl.appendChild(bubble);
316
- scrollToBottom();
317
- }
318
-
319
- function ensureAssistantBubble() {
320
- if (currentAssistantBubble) return currentAssistantBubble;
321
- var bubble = document.createElement("div");
322
- bubble.className = "home-chat-bubble home-chat-bubble-clay";
323
- messagesEl.appendChild(bubble);
324
- currentAssistantBubble = bubble;
325
- currentAssistantText = "";
326
- return bubble;
327
- }
328
-
329
- function appendAssistantText(text) {
330
- var bubble = ensureAssistantBubble();
331
- currentAssistantText += text;
332
- bubble.innerHTML = linkifyRefs(renderMarkdown(currentAssistantText));
333
- scrollToBottom();
334
- }
335
-
336
- function finalizeAssistant() {
337
- if (currentAssistantBubble && !currentAssistantText) {
338
- currentAssistantBubble.remove();
339
- }
340
- currentAssistantBubble = null;
341
- currentAssistantText = "";
342
- }
343
-
344
- function addSystemBubble(text) {
345
- var bubble = document.createElement("div");
346
- bubble.className = "home-chat-bubble home-chat-bubble-system";
347
- bubble.textContent = text;
348
- messagesEl.appendChild(bubble);
349
- scrollToBottom();
350
- }
351
-
352
- function linkifyRefs(html) {
353
- // Match [slug/sess_id - date]. Conservative: slug is alphanumeric/-/_,
354
- // sess id starts with sess_.
355
- var re = /\[([a-zA-Z0-9_\-]+)\/(sess_[a-zA-Z0-9_\-]+)(?:\s+[—-]\s+([0-9]{4}-[0-9]{2}-[0-9]{2}))?\]/g;
356
- return html.replace(re, function (_full, slug, sessId, date) {
357
- var label = slug + "/" + sessId.substring(0, 14) + (date ? " · " + date : "");
358
- return '<span class="home-chat-ref" data-slug="' + escapeHtml(slug) + '" data-session="' + escapeHtml(sessId) + '">' + escapeHtml(label) + '</span>';
359
- });
360
- }
361
-
362
- function scrollToBottom() {
363
- if (!messagesEl) return;
364
- requestAnimationFrame(function () {
365
- messagesEl.scrollTop = messagesEl.scrollHeight;
366
- });
367
- }
368
-
369
- function showTyping() { if (typingEl) typingEl.classList.remove("hidden"); }
370
- function hideTyping() { if (typingEl) typingEl.classList.add("hidden"); }
371
-
372
- // --- Server message handlers (called from app-messages.js dispatcher) ---
373
-
374
- export function handleHomeClayHistory(msg) {
375
- if (!messagesEl) return;
376
- messagesEl.innerHTML = "";
377
- currentAssistantBubble = null;
378
- currentAssistantText = "";
379
- hideTyping();
380
- var entries = msg.messages || [];
381
- if (entries.length === 0) {
382
- addSystemBubble("Hi — I'm Clay. I can search every session, project, and decision in your workspace. What are you trying to find?");
383
- return;
384
- }
385
- for (var i = 0; i < entries.length; i++) {
386
- var e = entries[i];
387
- if (e.role === "user") {
388
- addUserBubble(e.text || "");
389
- } else if (e.role === "assistant") {
390
- appendAssistantText(e.text || "");
391
- finalizeAssistant();
392
- }
393
- }
394
- }
395
-
396
- export function handleHomeClayDelta(msg) {
397
- hideTyping();
398
- if (typeof msg.text === "string") appendAssistantText(msg.text);
399
- }
400
-
401
- export function handleHomeClayDone() {
402
- hideTyping();
403
- finalizeAssistant();
404
- }
405
-
406
- export function handleHomeClayError(msg) {
407
- hideTyping();
408
- finalizeAssistant();
409
- addSystemBubble("Error: " + (msg.text || "unknown"));
410
- }
411
-
412
- // --- Click delegation for session ref chips ---
413
-
414
- document.addEventListener("click", function (e) {
415
- var chip = e.target && e.target.closest && e.target.closest(".home-chat-ref");
416
- if (!chip) return;
417
- var slug = chip.dataset.slug;
418
- if (!slug) return;
419
- closePopover();
420
- if (typeof switchProject === "function") {
421
- switchProject(slug);
422
- }
423
- });
424
-
425
- // --- Initialize on DOM ready ---
426
- // FAB chat is currently disabled. The DOM markup (#clay-fab, #clay-popover)
427
- // has been removed from index.html, so initHomeChat() would return early
428
- // anyway, but we skip the auto-init outright to make the disable explicit.
429
- // To re-enable: restore the markup in index.html, the CSS import in
430
- // style.css, and uncomment the block below.
431
- //
432
- // if (typeof document !== "undefined") {
433
- // if (document.readyState === "loading") {
434
- // document.addEventListener("DOMContentLoaded", initHomeChat);
435
- // } else {
436
- // initHomeChat();
437
- // }
438
- // }
@@ -1,245 +0,0 @@
1
- // Home Chat (Clay) — server-side handler.
2
- // Routes home_clay_* WS messages to the user's Clay (host agent) mate
3
- // project session, independent of which project the WS is currently
4
- // bound to. Mirrors session events back to the WS as home_clay_* so the
5
- // client renders into its own home-chat panel without disturbing the
6
- // active project view.
7
-
8
- function attachClayHome(deps) {
9
- var users = deps.users;
10
- var mates = deps.mates;
11
- var projects = deps.projects;
12
- var addProject = deps.addProject;
13
-
14
- // Per-WS subscription state.
15
- // ws._homeClayTap = { unsubscribe, sessionId, claySlug }
16
- // Stored on the ws itself rather than a side map so it's GC'd with the
17
- // socket and so handleDisconnection sees it without a registry lookup.
18
-
19
- function findClayProject(userId, ensureRegistered) {
20
- var mateCtx = mates.buildMateCtx(userId);
21
- var allMates = mates.getAllMates(mateCtx);
22
- var clay = null;
23
- for (var i = 0; i < allMates.length; i++) {
24
- if (allMates[i] && allMates[i].builtinKey === "clay") { clay = allMates[i]; break; }
25
- }
26
- if (!clay) return null;
27
- var slug = "mate-" + clay.id;
28
- if (!projects.has(slug)) {
29
- if (!ensureRegistered) return null;
30
- var dir = mates.getMateDir(mateCtx, clay.id);
31
- var fs = require("fs");
32
- try { fs.mkdirSync(dir, { recursive: true }); } catch (e) {}
33
- var name = (clay.profile && clay.profile.displayName) || clay.name || "Clay";
34
- addProject(dir, slug, name, null, clay.createdBy || userId, null, { isMate: true, mateDisplayName: name, isHostAgent: true });
35
- }
36
- var ctx = projects.get(slug);
37
- return ctx ? { ctx: ctx, slug: slug, mate: clay } : null;
38
- }
39
-
40
- // Pick the most recent visible session in Clay's project owned by this
41
- // user. Create one if none exists. The home chat is "single thread per
42
- // user" by default; home_clay_new_session forks a fresh one on demand.
43
- function getOrCreateHomeSession(found, userId) {
44
- var sm = found.ctx.getSessionManager();
45
- if (!sm) return null;
46
- var best = null;
47
- sm.sessions.forEach(function (s) {
48
- if (s.hidden) return;
49
- if (s.ownerId && s.ownerId !== userId) return;
50
- if (!best || (s.lastActivity || 0) > (best.lastActivity || 0)) best = s;
51
- });
52
- if (best) return best;
53
- var sess = sm.createSession({ ownerId: userId, vendor: "claude" }, null);
54
- return sess;
55
- }
56
-
57
- // Convert a session.history entry stream into the simplified home-chat
58
- // shape (alternating user / assistant turns, assistant text coalesced
59
- // across deltas). Tool calls and intermediate events are dropped — the
60
- // home chat surface intentionally hides them.
61
- function historyToHomeChat(history) {
62
- var msgs = [];
63
- var pending = "";
64
- function flushAssistant() {
65
- if (pending) {
66
- msgs.push({ role: "assistant", text: pending });
67
- pending = "";
68
- }
69
- }
70
- for (var i = 0; i < history.length; i++) {
71
- var e = history[i];
72
- if (!e) continue;
73
- if (e.type === "user_message" && e.text) {
74
- flushAssistant();
75
- msgs.push({ role: "user", text: e.text });
76
- } else if (e.type === "delta" && typeof e.text === "string") {
77
- pending += e.text;
78
- } else if (e.type === "result" || e.type === "done") {
79
- flushAssistant();
80
- } else if (e.type === "error" && e.text) {
81
- flushAssistant();
82
- msgs.push({ role: "assistant", text: "[error] " + e.text });
83
- }
84
- }
85
- flushAssistant();
86
- return msgs;
87
- }
88
-
89
- function transformEvent(obj) {
90
- if (!obj || typeof obj.type !== "string") return null;
91
- if (obj.type === "delta" && typeof obj.text === "string") {
92
- return { type: "home_clay_delta", text: obj.text };
93
- }
94
- if (obj.type === "result" || obj.type === "done") {
95
- return { type: "home_clay_done" };
96
- }
97
- if (obj.type === "error") {
98
- return { type: "home_clay_error", text: obj.text || "Unknown error" };
99
- }
100
- // intentionally skip: tool_*, thinking_*, status, plan_*, debate, etc.
101
- return null;
102
- }
103
-
104
- function teardownTap(ws) {
105
- if (ws && ws._homeClayTap && typeof ws._homeClayTap.unsubscribe === "function") {
106
- try { ws._homeClayTap.unsubscribe(); } catch (e) {}
107
- }
108
- if (ws) ws._homeClayTap = null;
109
- }
110
-
111
- function setupTap(ws, ctx, sessionId) {
112
- teardownTap(ws);
113
- var sm = ctx.getSessionManager();
114
- if (!sm || typeof sm.subscribeSession !== "function") return;
115
- var unsubscribe = sm.subscribeSession(sessionId, function (obj) {
116
- if (ws.readyState !== 1) return;
117
- var transformed = transformEvent(obj);
118
- if (!transformed) return;
119
- try { ws.send(JSON.stringify(transformed)); } catch (e) {}
120
- });
121
- if (!unsubscribe) return;
122
- ws._homeClayTap = { unsubscribe: unsubscribe, sessionId: sessionId, claySlug: ctx.slug || "" };
123
- }
124
-
125
- function sendError(ws, text) {
126
- if (ws.readyState !== 1) return;
127
- try {
128
- ws.send(JSON.stringify({ type: "home_clay_error", text: text }));
129
- } catch (e) {}
130
- }
131
-
132
- function handleMessage(ws, msg) {
133
- if (!msg || typeof msg.type !== "string") return false;
134
- if (msg.type !== "home_clay_open" && msg.type !== "home_clay_send" && msg.type !== "home_clay_new_session" && msg.type !== "home_clay_close") {
135
- return false;
136
- }
137
-
138
- if (msg.type === "home_clay_close") {
139
- teardownTap(ws);
140
- return true;
141
- }
142
-
143
- var userId = ws._clayUser ? ws._clayUser.id : null;
144
- if (users.isMultiUser() && !userId) {
145
- sendError(ws, "Not authenticated.");
146
- return true;
147
- }
148
-
149
- var found = findClayProject(userId, true);
150
- if (!found) {
151
- sendError(ws, "Clay mate not available yet — open the Mates panel once to seed.");
152
- return true;
153
- }
154
-
155
- if (msg.type === "home_clay_open") {
156
- var session = getOrCreateHomeSession(found, userId);
157
- if (!session) {
158
- sendError(ws, "Could not open Clay session.");
159
- return true;
160
- }
161
- setupTap(ws, found.ctx, session.localId);
162
- try {
163
- ws.send(JSON.stringify({
164
- type: "home_clay_history",
165
- sessionId: session.localId,
166
- messages: historyToHomeChat(session.history || []),
167
- }));
168
- } catch (e) {}
169
- return true;
170
- }
171
-
172
- if (msg.type === "home_clay_new_session") {
173
- var sm = found.ctx.getSessionManager();
174
- if (!sm) { sendError(ws, "Session manager unavailable."); return true; }
175
- var fresh = sm.createSession({ ownerId: userId, vendor: "claude" }, null);
176
- setupTap(ws, found.ctx, fresh.localId);
177
- try {
178
- ws.send(JSON.stringify({
179
- type: "home_clay_history",
180
- sessionId: fresh.localId,
181
- messages: [],
182
- }));
183
- } catch (e) {}
184
- return true;
185
- }
186
-
187
- if (msg.type === "home_clay_send") {
188
- var text = (msg.text || "").trim();
189
- if (!text) return true;
190
-
191
- var sm2 = found.ctx.getSessionManager();
192
- if (!sm2) { sendError(ws, "Session manager unavailable."); return true; }
193
-
194
- // Resume the tap if the WS reconnected since open.
195
- var tap = ws._homeClayTap;
196
- var sessionId = tap ? tap.sessionId : null;
197
- if (!sessionId) {
198
- var s2 = getOrCreateHomeSession(found, userId);
199
- if (!s2) { sendError(ws, "Could not open Clay session."); return true; }
200
- sessionId = s2.localId;
201
- setupTap(ws, found.ctx, sessionId);
202
- }
203
- var session2 = sm2.sessions.get(sessionId);
204
- if (!session2) {
205
- sendError(ws, "Session not found: " + sessionId);
206
- return true;
207
- }
208
-
209
- // Drive the SDK exactly the way the regular project user-message
210
- // path does. The session's own subscriber forwards events back as
211
- // home_clay_* via the tap installed above.
212
- var sdk = found.ctx.sdk;
213
- if (!sdk) {
214
- sendError(ws, "Clay SDK bridge unavailable.");
215
- return true;
216
- }
217
- try {
218
- if (!session2.isProcessing) {
219
- session2.isProcessing = true;
220
- session2.sentToolResults = {};
221
- if (!session2.queryInstance && (!session2.worker || session2.messageQueue !== "worker")) {
222
- sdk.startQuery(session2, text, null, null);
223
- } else {
224
- sdk.pushMessage(session2, text, null);
225
- }
226
- } else {
227
- sdk.pushMessage(session2, text, null);
228
- }
229
- } catch (e) {
230
- sendError(ws, "Failed to dispatch: " + (e.message || String(e)));
231
- }
232
- return true;
233
- }
234
-
235
- return false;
236
- }
237
-
238
- function handleDisconnection(ws) {
239
- teardownTap(ws);
240
- }
241
-
242
- return { handleMessage: handleMessage, handleDisconnection: handleDisconnection };
243
- }
244
-
245
- module.exports = { attachClayHome: attachClayHome };