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
@@ -0,0 +1,518 @@
1
+ // Embedded mate chat server handler.
2
+ // Routes home_mate_* messages through whichever project socket is open and
3
+ // mirrors conversational session events into the home work hub.
4
+
5
+ var fs = require("fs");
6
+ var attachHomeModels = require("./server-home-models").attachHomeModels;
7
+ var attachHomeDebatePlanning = require("./server-home-debate-planning").attachHomeDebatePlanning;
8
+ var attachHomeMateCreation = require("./server-home-mate-creation").attachHomeMateCreation, attachHomeClaySessionLinks = require("./server-home-clay-session-links").attachHomeClaySessionLinks;
9
+ var attachHomeDebates = require("./server-home-debates").attachHomeDebates, attachHomeClayEntry = require("./server-home-clay-entry").attachHomeClayEntry;
10
+ var homeChatEvents = require("./server-home-chat-events");
11
+ var homeCapsuleCreation = require("./server-home-capsule-creation");
12
+ var historyToHomeChat = homeChatEvents.historyToHomeChat;
13
+ var transformEvent = homeChatEvents.transformEvent;
14
+ function attachHomeChat(deps) {
15
+ var users = deps.users;
16
+ var mates = deps.mates;
17
+ var projects = deps.projects;
18
+ var addProject = deps.addProject;
19
+ function findMateProject(userId, mateId, ensureRegistered) {
20
+ var mateCtx = mates.buildMateCtx(userId);
21
+ var allMates = mates.getAllMates(mateCtx);
22
+ var mate = null;
23
+ for (var i = 0; i < allMates.length; i++) {
24
+ if (!allMates[i]) continue;
25
+ if (mateId && allMates[i].id === mateId) {
26
+ mate = allMates[i];
27
+ break;
28
+ }
29
+ if (!mateId && allMates[i].builtinKey === "clay") mate = allMates[i];
30
+ }
31
+ if (!mate) return null;
32
+ var slug = "mate-" + mate.id;
33
+ if (!projects.has(slug)) {
34
+ if (!ensureRegistered) return null;
35
+ var dir = mates.getMateDir(mateCtx, mate.id);
36
+ try { fs.mkdirSync(dir, { recursive: true }); } catch (e) {}
37
+ var name = (mate.profile && mate.profile.displayName) || mate.name || "Mate";
38
+ addProject(dir, slug, name, null, mate.createdBy || userId, null, {
39
+ isMate: true,
40
+ mateDisplayName: name,
41
+ isHostAgent: mate.builtinKey === "clay",
42
+ });
43
+ }
44
+ var ctx = projects.get(slug);
45
+ return ctx ? { ctx: ctx, slug: slug, mate: mate } : null;
46
+ }
47
+ function ownsSession(session, userId) {
48
+ if (!session || session.hidden) return false;
49
+ if (users.isMultiUser()) return !!userId && session.ownerId === userId;
50
+ return !session.ownerId;
51
+ }
52
+
53
+ function sessionString(value, maxLength) {
54
+ if (typeof value !== "string") return null;
55
+ var clean = value.replace(/[\u0000-\u001f\u007f]/g, " ").trim();
56
+ return clean ? clean.slice(0, maxLength) : null;
57
+ }
58
+
59
+ function sessionTimestamp(value) {
60
+ return typeof value === "number" && isFinite(value) && value >= 0 ? value : 0;
61
+ }
62
+
63
+ function sessionReference(session) {
64
+ return sessionString(session.cliSessionId, 512) || "local:" + session.localId;
65
+ }
66
+
67
+ function resolveHomeSession(found, userId, reference) {
68
+ if (!reference || typeof reference !== "string") return null;
69
+ var sessionManager = found.ctx.getSessionManager();
70
+ if (!sessionManager) return null;
71
+ var localMatch = reference.match(/^local:(\d+)$/);
72
+ var candidate = localMatch ? sessionManager.sessions.get(parseInt(localMatch[1], 10)) : null;
73
+ if (!candidate) {
74
+ sessionManager.sessions.forEach(function (session) {
75
+ if (!candidate && session.cliSessionId === reference) candidate = session;
76
+ });
77
+ }
78
+ return ownsSession(candidate, userId) ? candidate : null;
79
+ }
80
+
81
+ function listHomeSessions(found, userId) {
82
+ var sessionManager = found.ctx.getSessionManager();
83
+ if (!sessionManager) return [];
84
+ var result = [];
85
+ sessionManager.sessions.forEach(function (session) {
86
+ if (!ownsSession(session, userId)) return;
87
+ var cliSessionId = sessionString(session.cliSessionId, 512);
88
+ var createdAt = sessionTimestamp(session.createdAt);
89
+ var lastActivity = sessionTimestamp(session.lastActivity) || createdAt;
90
+ result.push({
91
+ id: cliSessionId || "local:" + session.localId,
92
+ cliSessionId: cliSessionId,
93
+ localId: typeof session.localId === "number" && isFinite(session.localId) ? session.localId : null,
94
+ title: sessionString(session.title, 240) || "New conversation",
95
+ vendor: sessionString(session.vendor, 80),
96
+ model: sessionString(session.model, 240),
97
+ createdAt: createdAt,
98
+ lastActivity: lastActivity,
99
+ isProcessing: !!session.isProcessing,
100
+ });
101
+ if (session.debateSetupMode === true) result[result.length - 1].debatePlanning = true;
102
+ if (session.homeDebatePhase) result[result.length - 1].debatePhase = session.homeDebatePhase;
103
+ if (session.mateCreationMode === true) result[result.length - 1].mateCreation = true;
104
+ if (session.homeMateCreationPhase) result[result.length - 1].mateCreationPhase = session.homeMateCreationPhase;
105
+ });
106
+ result.sort(function (a, b) { return b.lastActivity - a.lastActivity; });
107
+ return result;
108
+ }
109
+ function getLatestHomeSession(found, userId) {
110
+ var sessionManager = found.ctx.getSessionManager();
111
+ if (!sessionManager) return null;
112
+ var best = null;
113
+ sessionManager.sessions.forEach(function (session) {
114
+ if (!ownsSession(session, userId)) return;
115
+ if (!best || (session.lastActivity || 0) > (best.lastActivity || 0)) best = session;
116
+ });
117
+ return best;
118
+ }
119
+ function teardownTap(ws, tapKey) {
120
+ tapKey = tapKey || "_homeChatTap";
121
+ if (ws && ws[tapKey] && typeof ws[tapKey].unsubscribe === "function") {
122
+ try { ws[tapKey].unsubscribe(); } catch (e) {}
123
+ }
124
+ if (ws) ws[tapKey] = null;
125
+ }
126
+ function setupTap(ws, found, sessionId, requestId, tapKey) {
127
+ tapKey = tapKey || "_homeChatTap";
128
+ teardownTap(ws, tapKey);
129
+ var sessionManager = found.ctx.getSessionManager();
130
+ if (!sessionManager || typeof sessionManager.subscribeSession !== "function") return;
131
+ var mateId = found.mate.id;
132
+ var openedSession = sessionManager.sessions.get(sessionId);
133
+ if (openedSession) openedSession._homeRequestId = requestId || null;
134
+ var stableSessionId = openedSession ? sessionReference(openedSession) : null;
135
+ var unsubscribe = sessionManager.subscribeSession(sessionId, function (event) {
136
+ if (ws.readyState !== 1) return;
137
+ var activeSession = sessionManager.sessions.get(sessionId);
138
+ if (event && event.type === "session_id" && event.cliSessionId) {
139
+ var previousSessionId = stableSessionId;
140
+ stableSessionId = event.cliSessionId;
141
+ sendMessage(ws, { type: "home_mate_session_identity", mateId: mateId, previousSessionId: previousSessionId, sessionId: stableSessionId, requestId: requestId || null });
142
+ if (ws[tapKey] && ws[tapKey].sessionId === sessionId) ws[tapKey].sessionReference = stableSessionId;
143
+ var identityUserId = ws._clayUser ? ws._clayUser.id : null;
144
+ sendMessage(ws, { type: "home_mate_sessions_state", mateId: mateId, sessions: listHomeSessions(found, identityUserId) });
145
+ return;
146
+ }
147
+ var transformed = transformEvent(event, mateId, activeSession, requestId, stableSessionId);
148
+ if (!transformed) return;
149
+ try { ws.send(JSON.stringify(transformed)); } catch (e) {}
150
+ if (event.type === "result" || event.type === "done") {
151
+ var userId = ws._clayUser ? ws._clayUser.id : null;
152
+ sendMessage(ws, { type: "home_mate_sessions_state", mateId: mateId, sessions: listHomeSessions(found, userId) });
153
+ }
154
+ });
155
+ if (!unsubscribe) return;
156
+ ws[tapKey] = {
157
+ unsubscribe: unsubscribe,
158
+ sessionId: sessionId,
159
+ sessionReference: stableSessionId,
160
+ mateId: mateId,
161
+ mateSlug: found.slug,
162
+ requestId: requestId || null,
163
+ openedAt: Date.now(),
164
+ };
165
+ }
166
+ function setupSearchTap(ws, found, sessionId, requestId) { setupTap(ws, found, sessionId, requestId, "_searchClayTap"); }
167
+ function messageTap(ws, msg) { return ws._searchClayTap && msg.requestId === ws._searchClayTap.requestId ? ws._searchClayTap : ws._homeChatTap; }
168
+ function sendError(ws, mateId, text, requestId, sessionId, code, details) {
169
+ if (ws.readyState !== 1) return;
170
+ try {
171
+ var payload = {
172
+ type: "home_mate_error",
173
+ mateId: mateId,
174
+ sessionId: sessionId || null,
175
+ requestId: requestId || null,
176
+ code: code || null,
177
+ text: text,
178
+ };
179
+ if (details && typeof details.toolId === "string") payload.toolId = details.toolId;
180
+ ws.send(JSON.stringify(payload));
181
+ } catch (e) {}
182
+ }
183
+
184
+ function sendMessage(ws, payload) {
185
+ if (ws.readyState !== 1) return;
186
+ try { ws.send(JSON.stringify(payload)); } catch (e) {}
187
+ }
188
+
189
+ var homeModels = attachHomeModels({ users: users, mates: mates, projects: projects, sendMessage: sendMessage });
190
+ var homeDebates = attachHomeDebates({ mates: mates, findMateProject: findMateProject, ownsSession: ownsSession, sessionReference: sessionReference, sendMessage: sendMessage });
191
+ var homeClaySessionLinks = attachHomeClaySessionLinks({ projects: projects, workspaceQueryService: deps.workspaceQueryService, sendMessage: sendMessage });
192
+
193
+ function sendHistory(ws, found, session, requestId) {
194
+ if (ws.readyState !== 1) return;
195
+ try {
196
+ var payload = {
197
+ type: "home_mate_history",
198
+ mateId: found.mate.id,
199
+ sessionId: sessionReference(session),
200
+ requestId: requestId || null,
201
+ model: session.model || null,
202
+ vendor: session.vendor || null,
203
+ messages: historyToHomeChat(session.history || [], session.debateSetupMode === true, session.mateCreationMode === true),
204
+ isProcessing: session.isProcessing === true,
205
+ };
206
+ if (session.debateSetupMode === true) payload.debatePlanning = true;
207
+ if (session.homeDebatePhase) payload.debatePhase = session.homeDebatePhase;
208
+ if (session.mateCreationMode === true) payload.mateCreation = true;
209
+ if (session.homeMateCreationPhase) payload.mateCreationPhase = session.homeMateCreationPhase;
210
+ ws.send(JSON.stringify(payload));
211
+ } catch (e) {}
212
+ }
213
+
214
+ async function ensureSessionModel(ws, found, userId, session, resolvedMateModel) {
215
+ if (session && typeof session.model === "string" && session.model.trim()) return session.model;
216
+ if (!session) throw new Error("Conversation not available.");
217
+ var sessionManager = found.ctx.getSessionManager();
218
+ if (!sessionManager || typeof sessionManager.saveSessionFile !== "function") {
219
+ var persistError = new Error("Could not persist the conversation model. Reconnect and try again.");
220
+ persistError.code = "model_unavailable";
221
+ throw persistError;
222
+ }
223
+ var resolved = resolvedMateModel || await homeModels.resolveMateModel(ws, found, userId);
224
+ session.vendor = resolved.vendor;
225
+ session.model = resolved.model;
226
+ sessionManager.saveSessionFile(session);
227
+ return session.model;
228
+ }
229
+
230
+ function sendModelError(ws, found, msg, error, sessionId) {
231
+ var text = error && error.message ? error.message : String(error || "Model unavailable.");
232
+ sendError(ws, found.mate.id, text, msg.requestId || null, sessionId || msg.sessionId || null, error && error.code ? error.code : null);
233
+ }
234
+
235
+ function sendSessionList(ws, found, userId) {
236
+ sendMessage(ws, { type: "home_mate_sessions_state", mateId: found.mate.id, sessions: listHomeSessions(found, userId) });
237
+ }
238
+
239
+ var homeDebate = attachHomeDebatePlanning({
240
+ findMateProject: findMateProject, resolveHomeSession: resolveHomeSession,
241
+ sessionReference: sessionReference, setupTap: setupTap, sendHistory: sendHistory,
242
+ sendSessionList: sendSessionList, sendError: sendError, sendModelError: sendModelError,
243
+ homeModels: homeModels,
244
+ });
245
+ var homeMateCreation = attachHomeMateCreation({ findMateProject: findMateProject, resolveHomeSession: resolveHomeSession, sessionReference: sessionReference, setupTap: setupTap, sendHistory: sendHistory, sendSessionList: sendSessionList, sendError: sendError, sendModelError: sendModelError, homeModels: homeModels });
246
+ var homeClayEntry = attachHomeClayEntry({ findMateProject: findMateProject, ownsSession: ownsSession, sessionReference: sessionReference, setupSearchTap: setupSearchTap, sendHistory: sendHistory, sendSessionList: sendSessionList, sendError: sendError, sendModelError: sendModelError, homeModels: homeModels });
247
+
248
+ async function openExactSession(ws, found, userId, selected, msg) {
249
+ await ensureSessionModel(ws, found, userId, selected, null);
250
+ if (selected.debateSetupMode === true) {
251
+ homeDebate.resume(ws, found, userId, selected, msg);
252
+ return;
253
+ }
254
+ if (selected.mateCreationMode === true) { homeMateCreation.resume(ws, found, userId, selected, msg); return; }
255
+ setupTap(ws, found, selected.localId, msg.requestId || null);
256
+ sendHistory(ws, found, selected, msg.requestId || null);
257
+ sendMessage(ws, { type: "home_mate_sessions_state", mateId: found.mate.id, sessions: listHomeSessions(found, userId) });
258
+ }
259
+
260
+ async function openDefaultSession(ws, found, userId, msg) {
261
+ var sessionManager = found.ctx.getSessionManager();
262
+ if (!sessionManager) throw new Error("Session manager unavailable.");
263
+ var session = getLatestHomeSession(found, userId);
264
+ if (!session) {
265
+ var resolved = await homeModels.resolveMateModel(ws, found, userId);
266
+ session = sessionManager.createSession({ ownerId: userId, vendor: resolved.vendor, model: resolved.model }, null);
267
+ } else {
268
+ await ensureSessionModel(ws, found, userId, session, null);
269
+ }
270
+ setupTap(ws, found, session.localId, msg.requestId || null);
271
+ sendHistory(ws, found, session, msg.requestId || null);
272
+ sendMessage(ws, { type: "home_mate_sessions_state", mateId: found.mate.id, sessions: listHomeSessions(found, userId) });
273
+ }
274
+
275
+ async function openNewSession(ws, found, userId, msg) {
276
+ var sessionManager = found.ctx.getSessionManager();
277
+ if (!sessionManager) throw new Error("Session manager unavailable.");
278
+ var capsuleIntent = homeCapsuleCreation.readCapsuleCreationIntent(msg.intent);
279
+ var resolved = await homeModels.resolveMateModel(ws, found, userId);
280
+ var fresh = sessionManager.createSession({ ownerId: userId, vendor: resolved.vendor, model: resolved.model }, null);
281
+ setupTap(ws, found, fresh.localId, msg.requestId || null);
282
+ if (capsuleIntent) {
283
+ await sendHomeMessage(ws, found, userId, {
284
+ mateId: found.mate.id,
285
+ sessionId: sessionReference(fresh),
286
+ requestId: msg.requestId || null,
287
+ text: capsuleIntent.description,
288
+ }, {
289
+ queryText: homeCapsuleCreation.buildCapsuleCreationPrompt(capsuleIntent.description),
290
+ onRecorded: function (session) {
291
+ sendHistory(ws, found, session, msg.requestId || null);
292
+ sendSessionList(ws, found, userId);
293
+ },
294
+ });
295
+ return;
296
+ }
297
+ sendHistory(ws, found, fresh, msg.requestId || null);
298
+ sendMessage(ws, { type: "home_mate_sessions_state", mateId: found.mate.id, sessions: listHomeSessions(found, userId) });
299
+ }
300
+
301
+ async function sendHomeMessage(ws, found, userId, msg, options) {
302
+ options = options || {};
303
+ var text = (msg.text || "").trim();
304
+ if (!text) return;
305
+ var tap = messageTap(ws, msg);
306
+ var requestId = msg.requestId || (tap && tap.requestId) || null;
307
+ var sessionReferenceValue = msg.sessionId || (tap && tap.sessionReference) || null;
308
+ var sessionManager = found.ctx.getSessionManager();
309
+ if (!sessionManager) throw new Error("Session manager unavailable.");
310
+ var sessionId = tap && tap.mateId === found.mate.id ? tap.sessionId : null;
311
+ var activeSession = sessionId ? sessionManager.sessions.get(sessionId) : null;
312
+ if (!activeSession) {
313
+ var resolved = await homeModels.resolveMateModel(ws, found, userId);
314
+ activeSession = getLatestHomeSession(found, userId);
315
+ if (!activeSession) {
316
+ activeSession = sessionManager.createSession({ ownerId: userId, vendor: resolved.vendor, model: resolved.model }, null);
317
+ } else {
318
+ await ensureSessionModel(ws, found, userId, activeSession, resolved);
319
+ }
320
+ setupTap(ws, found, activeSession.localId, requestId);
321
+ sessionReferenceValue = sessionReference(activeSession);
322
+ } else {
323
+ await ensureSessionModel(ws, found, userId, activeSession, null);
324
+ }
325
+ if (activeSession.mateCreationMode === true) {
326
+ var interviewError = new Error("Answer the current interview question to continue.");
327
+ interviewError.code = "question_required";
328
+ interviewError.sessionId = sessionReference(activeSession);
329
+ throw interviewError;
330
+ }
331
+ if (!activeSession.model) {
332
+ var missingModelError = new Error("Choose a model before sending a message.");
333
+ missingModelError.code = "model_unavailable";
334
+ throw missingModelError;
335
+ }
336
+ var sdk = found.ctx.sdk;
337
+ if (!sdk) {
338
+ var sdkError = new Error("Mate SDK bridge unavailable.");
339
+ sdkError.sessionId = sessionReference(activeSession);
340
+ throw sdkError;
341
+ }
342
+ if (typeof sessionManager.sendAndRecord !== "function") {
343
+ var historyError = new Error("Conversation history is unavailable.");
344
+ historyError.sessionId = sessionReference(activeSession);
345
+ throw historyError;
346
+ }
347
+ sessionManager.sendAndRecord(activeSession, { type: "user_message", text: text });
348
+ try {
349
+ if (!activeSession.isProcessing) {
350
+ activeSession.isProcessing = true;
351
+ activeSession.sentToolResults = {};
352
+ if (typeof options.onRecorded === "function") options.onRecorded(activeSession);
353
+ if (!activeSession.queryInstance && (!activeSession.worker || activeSession.messageQueue !== "worker")) {
354
+ sdk.startQuery(activeSession, options.queryText || text, null, null);
355
+ } else {
356
+ sdk.pushMessage(activeSession, options.queryText || text, null);
357
+ }
358
+ } else {
359
+ sdk.pushMessage(activeSession, options.queryText || text, null);
360
+ }
361
+ } catch (error) {
362
+ error.sessionId = sessionReference(activeSession);
363
+ throw error;
364
+ }
365
+ return sessionReferenceValue;
366
+ }
367
+
368
+ function handleMessage(ws, msg) {
369
+ if (!msg || typeof msg.type !== "string") return false;
370
+ if (msg.type !== "home_debates_list" && msg.type !== "home_clay_ask" && msg.type !== "home_clay_session_resolve" && msg.type !== "home_mate_present" && msg.type !== "home_mate_open" && msg.type !== "home_mate_sessions_list" && msg.type !== "home_mate_session_open" && msg.type !== "home_mate_send" && msg.type !== "home_mate_new_session" && msg.type !== "home_mate_debate_plan" && msg.type !== "home_debate_proposal_response" && msg.type !== "home_debate_question_response" && msg.type !== "home_debate_control" && msg.type !== "home_mate_creation_plan" && msg.type !== "home_mate_creation_question_response" && msg.type !== "home_mate_creation_proposal_response" && msg.type !== "home_mate_close" && msg.type !== "home_mate_memory_list" && msg.type !== "home_mate_knowledge_list" && msg.type !== "home_mate_models_get" && msg.type !== "home_mate_model_set") {
371
+ return false;
372
+ }
373
+
374
+ if (msg.type === "home_mate_present") {
375
+ ws._homeChatPresented = msg.visible === true;
376
+ return true;
377
+ }
378
+
379
+ if (msg.type === "home_mate_close") {
380
+ ws._homeChatPresented = false;
381
+ teardownTap(ws);
382
+ return true;
383
+ }
384
+
385
+ var userId = ws._clayUser ? ws._clayUser.id : null;
386
+ if (users.isMultiUser() && !userId) {
387
+ if (msg.type === "home_debates_list") sendMessage(ws, { type: "home_debates_state", requestId: msg.requestId || null, status: "error", debates: [], error: "Sign in to load your debates." });
388
+ else if (msg.type === "home_clay_session_resolve") sendMessage(ws, { type: "home_clay_session_target", requestId: msg.requestId || null, sessionRef: msg.sessionRef || null, status: "error", error: "Sign in to open this session." });
389
+ else if (msg.type === "home_mate_models_get" || msg.type === "home_mate_model_set") homeModels.sendAccessError(ws, msg, "Not authenticated.");
390
+ else sendError(ws, msg.mateId || null, "Not authenticated.", msg.requestId || null, msg.sessionId || null);
391
+ return true;
392
+ }
393
+
394
+ if (homeDebates.handle(ws, userId, msg)) return true;
395
+ if (msg.type === "home_clay_ask") { homeClayEntry.start(ws, userId, msg); return true; }
396
+ if (msg.type === "home_clay_session_resolve") { homeClaySessionLinks.resolve(ws, msg); return true; }
397
+
398
+ if (msg.type === "home_mate_debate_plan") {
399
+ homeDebate.start(ws, userId, msg);
400
+ return true;
401
+ }
402
+ if (msg.type === "home_debate_proposal_response") {
403
+ homeDebate.respond(ws, userId, msg);
404
+ return true;
405
+ }
406
+ if (msg.type === "home_debate_question_response") {
407
+ homeDebate.respondToQuestion(ws, userId, msg);
408
+ return true;
409
+ }
410
+ if (msg.type === "home_debate_control") {
411
+ homeDebate.control(ws, userId, msg);
412
+ return true;
413
+ }
414
+ if (msg.type === "home_mate_creation_plan") { homeMateCreation.start(ws, userId, msg); return true; }
415
+ if (msg.type === "home_mate_creation_question_response") { homeMateCreation.respondToQuestion(ws, userId, msg); return true; }
416
+ if (msg.type === "home_mate_creation_proposal_response") { homeMateCreation.respondToProposal(ws, userId, msg); return true; }
417
+
418
+ var found = findMateProject(userId, msg.mateId, true);
419
+ if (!found) {
420
+ if (msg.type === "home_mate_models_get" || msg.type === "home_mate_model_set") homeModels.sendAccessError(ws, msg, "Mate not available.");
421
+ else sendError(ws, msg.mateId || null, "Mate not available.", msg.requestId || null, msg.sessionId || null);
422
+ return true;
423
+ }
424
+
425
+ if (msg.type === "home_mate_models_get") {
426
+ homeModels.sendMateModels(ws, found, msg.requestId || null, msg.vendor || null).catch(function (error) {
427
+ sendMessage(ws, { type: "home_mate_models_state", mateId: found.mate.id, requestId: msg.requestId || null, vendor: msg.vendor || found.mate.vendor || "claude", mateVendor: found.mate.vendor || "claude", mateModel: found.mate.model || "", model: "", models: [], vendors: [], status: "error", error: error.message || String(error) });
428
+ });
429
+ return true;
430
+ }
431
+
432
+ if (msg.type === "home_mate_model_set") {
433
+ homeModels.setMateModel(ws, found, userId, msg).then(function (result) {
434
+ var draft = result && result.sessionApplied ? resolveHomeSession(found, userId, result.sessionId) : null;
435
+ if (draft && draft.debateSetupMode === true) homeDebate.resume(ws, found, userId, draft, { requestId: ws._homeChatTap ? ws._homeChatTap.requestId : null });
436
+ if (draft && draft.mateCreationMode === true) homeMateCreation.resume(ws, found, userId, draft, { requestId: ws._homeChatTap ? ws._homeChatTap.requestId : null });
437
+ }).catch(function (error) {
438
+ sendMessage(ws, { type: "home_mate_model_result", mateId: found.mate.id, requestId: msg.requestId || null, ok: false, error: error.message || String(error) });
439
+ });
440
+ return true;
441
+ }
442
+
443
+ if (msg.type === "home_mate_memory_list") {
444
+ var memory = found.ctx.getMemoryState();
445
+ sendMessage(ws, {
446
+ type: "home_mate_memory_state",
447
+ mateId: found.mate.id,
448
+ requestId: msg.requestId || null,
449
+ entries: memory.entries,
450
+ summary: memory.summary,
451
+ });
452
+ return true;
453
+ }
454
+
455
+ if (msg.type === "home_mate_knowledge_list") {
456
+ sendMessage(ws, {
457
+ type: "home_mate_knowledge_state",
458
+ mateId: found.mate.id,
459
+ requestId: msg.requestId || null,
460
+ files: found.ctx.listKnowledgeFiles(),
461
+ });
462
+ return true;
463
+ }
464
+
465
+ if (msg.type === "home_mate_sessions_list") {
466
+ sendMessage(ws, {
467
+ type: "home_mate_sessions_state",
468
+ mateId: found.mate.id,
469
+ sessions: listHomeSessions(found, userId),
470
+ });
471
+ return true;
472
+ }
473
+
474
+ if (msg.type === "home_mate_session_open") {
475
+ var selected = resolveHomeSession(found, userId, msg.sessionId);
476
+ if (!selected) {
477
+ sendError(ws, found.mate.id, "Conversation not available.", msg.requestId || null, msg.sessionId || null, "session_not_found");
478
+ return true;
479
+ }
480
+ openExactSession(ws, found, userId, selected, msg).catch(function (error) {
481
+ sendModelError(ws, found, msg, error, sessionReference(selected));
482
+ });
483
+ return true;
484
+ }
485
+
486
+ if (msg.type === "home_mate_open") {
487
+ openDefaultSession(ws, found, userId, msg).catch(function (error) {
488
+ sendModelError(ws, found, msg, error, null);
489
+ });
490
+ return true;
491
+ }
492
+
493
+ if (msg.type === "home_mate_new_session") {
494
+ openNewSession(ws, found, userId, msg).catch(function (error) {
495
+ sendModelError(ws, found, msg, error, null);
496
+ });
497
+ return true;
498
+ }
499
+
500
+ if (msg.type === "home_mate_send") {
501
+ var sendTap = messageTap(ws, msg);
502
+ var sendRequestId = msg.requestId || (sendTap && sendTap.requestId) || null;
503
+ var sendSessionReference = msg.sessionId || (sendTap && sendTap.sessionReference) || null;
504
+ sendHomeMessage(ws, found, userId, msg).catch(function (error) {
505
+ sendError(ws, found.mate.id, error.message || String(error), sendRequestId, error.sessionId || sendSessionReference, error.code || null);
506
+ });
507
+ return true;
508
+ }
509
+ return false;
510
+ }
511
+ function handleDisconnection(ws) {
512
+ ws._homeChatPresented = false;
513
+ teardownTap(ws);
514
+ teardownTap(ws, "_searchClayTap");
515
+ }
516
+ return { handleMessage: handleMessage, handleDisconnection: handleDisconnection };
517
+ }
518
+ module.exports = { attachHomeChat: attachHomeChat };
@@ -0,0 +1,65 @@
1
+ // Server-owned entry from global search into a fresh builtin Clay session.
2
+
3
+ function attachHomeClayEntry(ctx) {
4
+ function providerPrompt(text) {
5
+ return "This conversation began in Clay Studio's global search. Respond as Clay. When the user is trying to locate prior work, use search_workspace_history before any other investigation. Search by meaning, related terms, and context, using at most three focused search passes. Do not use Bash, filesystem scans, or generic agents to search Clay conversation history. Give the user useful findings promptly, and do not mention tools or internal search mechanics. Do not force a workspace search when the request is a general question or task.\n\nUser request:\n" + text;
6
+ }
7
+
8
+ function findExisting(manager, userId, requestId) {
9
+ var match = null;
10
+ if (!requestId) return null;
11
+ manager.sessions.forEach(function (session) {
12
+ if (!match && ctx.ownsSession(session, userId) && session.homeClayEntryRequestId === requestId) match = session;
13
+ });
14
+ return match;
15
+ }
16
+
17
+ async function start(ws, userId, msg) {
18
+ var text = typeof msg.text === "string" ? msg.text.replace(/[\u0000-\u001f\u007f]/g, " ").trim().slice(0, 12000) : "";
19
+ var requestId = typeof msg.requestId === "string" ? msg.requestId.slice(0, 240) : null;
20
+ var found = ctx.findMateProject(userId, null, true);
21
+ if (!found || found.mate.builtinKey !== "clay") {
22
+ ctx.sendError(ws, null, "Clay is not available.", requestId, null, "mate_unavailable");
23
+ return;
24
+ }
25
+ if (!text || !requestId) {
26
+ ctx.sendError(ws, found.mate.id, "Enter something for Clay to find.", requestId, null, "invalid_request");
27
+ return;
28
+ }
29
+ var manager = found.ctx.getSessionManager();
30
+ if (!manager || typeof manager.sendAndRecord !== "function") {
31
+ ctx.sendError(ws, found.mate.id, "Conversation history is unavailable.", requestId, null, "session_unavailable");
32
+ return;
33
+ }
34
+ var existing = findExisting(manager, userId, requestId);
35
+ if (existing) {
36
+ ctx.setupSearchTap(ws, found, existing.localId, requestId);
37
+ ctx.sendHistory(ws, found, existing, requestId);
38
+ ctx.sendSessionList(ws, found, userId);
39
+ return;
40
+ }
41
+ var session = null;
42
+ try {
43
+ var resolved = await ctx.homeModels.resolveMateModel(ws, found, userId);
44
+ session = manager.createSession({ ownerId: userId, vendor: resolved.vendor, model: resolved.model }, null);
45
+ session.homeClayEntryRequestId = requestId;
46
+ session.homeClayEntryMode = "search";
47
+ manager.sendAndRecord(session, { type: "user_message", text: text });
48
+ session.isProcessing = true;
49
+ session.sentToolResults = {};
50
+ if (typeof manager.saveSessionFile === "function") manager.saveSessionFile(session);
51
+ ctx.setupSearchTap(ws, found, session.localId, requestId);
52
+ ctx.sendHistory(ws, found, session, requestId);
53
+ ctx.sendSessionList(ws, found, userId);
54
+ if (!found.ctx.sdk) throw new Error("Clay SDK bridge unavailable.");
55
+ found.ctx.sdk.startQuery(session, providerPrompt(text), null, null);
56
+ } catch (error) {
57
+ if (session) session.isProcessing = false;
58
+ ctx.sendModelError(ws, found, msg, error, session ? ctx.sessionReference(session) : null);
59
+ }
60
+ }
61
+
62
+ return { start: start };
63
+ }
64
+
65
+ module.exports = { attachHomeClayEntry: attachHomeClayEntry };
@@ -0,0 +1,26 @@
1
+ // Resolve rendered Mate session references through an exact owner-bound source session.
2
+
3
+ function attachHomeClaySessionLinks(ctx) {
4
+ function resolve(ws, msg) {
5
+ var tap = msg.surface === "search" ? ws._searchClayTap : ws._homeChatTap;
6
+ var sourceProject = tap && tap.mateSlug ? ctx.projects.get(tap.mateSlug) : null;
7
+ var sourceManager = sourceProject && sourceProject.getSessionManager ? sourceProject.getSessionManager() : null;
8
+ var sourceSession = sourceManager && tap ? sourceManager.sessions.get(tap.sessionId) : null;
9
+ var bound = ctx.workspaceQueryService && sourceSession
10
+ ? ctx.workspaceQueryService.bindProjectSession({ projectSlug: tap.mateSlug, session: sourceSession })
11
+ : null;
12
+ var payload = { type: "home_clay_session_target", requestId: msg.requestId || null, sessionRef: msg.sessionRef || null, status: "error" };
13
+ try {
14
+ if (!bound) throw new Error("The source conversation is no longer available.");
15
+ payload.target = bound.resolveSessionNavigation({ sessionRef: msg.sessionRef });
16
+ payload.status = "ready";
17
+ } catch (error) {
18
+ payload.error = error.message || "Session not available.";
19
+ }
20
+ ctx.sendMessage(ws, payload);
21
+ }
22
+
23
+ return { resolve: resolve };
24
+ }
25
+
26
+ module.exports = { attachHomeClaySessionLinks: attachHomeClaySessionLinks };