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.
- package/lib/ask-user-mcp-server.js +5 -119
- package/lib/builtin-mates.js +6 -6
- package/lib/claude-hook-installer.js +15 -0
- package/lib/clay-history-mcp-server.js +62 -297
- package/lib/daemon.js +1 -1
- package/lib/debate-model-selection.js +119 -0
- package/lib/home-debate-tool-policy.js +164 -0
- package/lib/home-response-notifications.js +26 -0
- package/lib/mate-creation-mcp-server.js +55 -0
- package/lib/mate-ready-creation.js +95 -0
- package/lib/mates.js +2 -0
- package/lib/notes.js +2 -1
- package/lib/project-ask-user.js +64 -0
- package/lib/project-capsule-catalog.js +101 -0
- package/lib/project-connection.js +4 -0
- package/lib/project-debate-proposal.js +133 -17
- package/lib/project-debate.js +214 -110
- package/lib/project-delegated-follow-up.js +84 -0
- package/lib/project-delegated-session.js +53 -0
- package/lib/project-filesystem.js +7 -9
- package/lib/project-knowledge.js +1 -0
- package/lib/project-mate-creation-proposal.js +120 -0
- package/lib/project-memory.js +14 -7
- package/lib/project-models.js +157 -32
- package/lib/project-session-notes.js +2 -2
- package/lib/project-session-pair.js +8 -8
- package/lib/project-sessions.js +66 -51
- package/lib/project-vendor-login.js +397 -0
- package/lib/project-worker-proposal.js +25 -25
- package/lib/project-workspace-query.js +144 -0
- package/lib/project.js +349 -102
- package/lib/public/app.js +85 -42
- package/lib/public/css/avatar-imprints.css +0 -1
- package/lib/public/css/base.css +4 -0
- package/lib/public/css/capsule-ui.css +389 -0
- package/lib/public/css/command-palette.css +306 -6
- package/lib/public/css/home-capsule-library.css +243 -0
- package/lib/public/css/home-capsule-source.css +192 -0
- package/lib/public/css/home-debate-live.css +146 -0
- package/lib/public/css/home-debate-planning.css +125 -0
- package/lib/public/css/home-debates-archive.css +281 -0
- package/lib/public/css/home-hub.css +896 -405
- package/lib/public/css/home-mate-creation.css +34 -0
- package/lib/public/css/home-mate-model-picker.css +131 -0
- package/lib/public/css/home-mate-settings.css +344 -0
- package/lib/public/css/home-session-actions.css +265 -0
- package/lib/public/css/home-sidebar.css +667 -0
- package/lib/public/css/icon-strip.css +1 -2
- package/lib/public/css/mates.css +7 -34
- package/lib/public/css/pane.css +1 -1
- package/lib/public/css/playbook.css +0 -80
- package/lib/public/css/sticky-notes.css +53 -117
- package/lib/public/css/tui-attention.css +0 -44
- package/lib/public/css/workspace-assignment.css +45 -0
- package/lib/public/fonts/source-serif-4/LICENSE.md +93 -0
- package/lib/public/fonts/source-serif-4/SourceSerif4Caption-Semibold.ttf.woff2 +0 -0
- package/lib/public/index.html +93 -74
- package/lib/public/modules/app-connection.js +10 -1
- package/lib/public/modules/app-dm.js +43 -160
- package/lib/public/modules/app-home-hub.js +328 -585
- package/lib/public/modules/app-message-cards.js +242 -0
- package/lib/public/modules/app-message-router.js +84 -0
- package/lib/public/modules/app-messages.js +102 -37
- package/lib/public/modules/app-notifications.js +8 -119
- package/lib/public/modules/app-projects.js +9 -1
- package/lib/public/modules/app-rendering.js +21 -602
- package/lib/public/modules/avatar-imprint.js +11 -8
- package/lib/public/modules/chat-bubble-renderer.js +152 -0
- package/lib/public/modules/chat-render-runtime.js +198 -0
- package/lib/public/modules/command-palette.js +225 -436
- package/lib/public/modules/dm-render.js +82 -0
- package/lib/public/modules/home-capsule-creation-intent.js +31 -0
- package/lib/public/modules/home-capsule-library.js +146 -0
- package/lib/public/modules/home-capsule-source.js +383 -0
- package/lib/public/modules/home-chat-identity.js +35 -0
- package/lib/public/modules/home-chat-scroll.js +77 -0
- package/lib/public/modules/home-chat-stream-state.js +34 -0
- package/lib/public/modules/home-composer-focus.js +25 -0
- package/lib/public/modules/home-conversations-sheet.js +179 -0
- package/lib/public/modules/home-debate-activity.js +48 -0
- package/lib/public/modules/home-debate-controls.js +295 -0
- package/lib/public/modules/home-debate-launch.js +41 -0
- package/lib/public/modules/home-debate-live.js +284 -0
- package/lib/public/modules/home-debate-models.js +73 -0
- package/lib/public/modules/home-debate-planning.js +335 -0
- package/lib/public/modules/home-debates-archive.js +279 -0
- package/lib/public/modules/home-dock-resize.js +74 -0
- package/lib/public/modules/home-dock.js +446 -0
- package/lib/public/modules/home-mate-chat.js +496 -0
- package/lib/public/modules/home-mate-creation.js +109 -0
- package/lib/public/modules/home-mate-model-picker.js +281 -0
- package/lib/public/modules/home-mate-selection.js +38 -0
- package/lib/public/modules/home-mate-settings-menu.js +131 -0
- package/lib/public/modules/home-mate-settings.js +383 -0
- package/lib/public/modules/home-session-actions.js +373 -0
- package/lib/public/modules/home-session-model-confirmation.js +13 -0
- package/lib/public/modules/home-shell.js +9 -0
- package/lib/public/modules/home-sidebar-chat-list.js +63 -0
- package/lib/public/modules/home-sidebar.js +158 -0
- package/lib/public/modules/home-sub-surface.js +19 -0
- package/lib/public/modules/home-surface-boot.js +68 -0
- package/lib/public/modules/home-surface.js +141 -0
- package/lib/public/modules/home-tools.js +271 -0
- package/lib/public/modules/markdown.js +50 -2
- package/lib/public/modules/mate-management.js +86 -0
- package/lib/public/modules/mate-wizard.js +0 -7
- package/lib/public/modules/mention.js +17 -19
- package/lib/public/modules/pane-bridge.js +9 -0
- package/lib/public/modules/profile.js +4 -5
- package/lib/public/modules/project-settings.js +34 -28
- package/lib/public/modules/project-switcher.js +11 -5
- package/lib/public/modules/project-workspace-assignment.js +23 -0
- package/lib/public/modules/search-clay-chat.js +402 -0
- package/lib/public/modules/server-settings.js +14 -12
- package/lib/public/modules/session-actions.js +1 -1
- package/lib/public/modules/sidebar-mates.js +56 -552
- package/lib/public/modules/sidebar-mobile.js +7 -64
- package/lib/public/modules/sidebar-presence.js +37 -0
- package/lib/public/modules/split-pair-ui.js +13 -13
- package/lib/public/modules/split-view.js +7 -0
- package/lib/public/modules/sticky-notes.js +7 -6
- package/lib/public/modules/tool-input-composition.js +48 -0
- package/lib/public/modules/tool-llm-status.js +167 -0
- package/lib/public/modules/tool-renderer-advanced.js +285 -0
- package/lib/public/modules/tool-renderer-chart.js +158 -0
- package/lib/public/modules/tool-renderer-semantics.js +35 -0
- package/lib/public/modules/tool-renderer.js +464 -0
- package/lib/public/modules/tool-runtime.js +309 -0
- package/lib/public/modules/tool-ui-evaluator.js +97 -0
- package/lib/public/modules/tool-ui-tree.js +21 -0
- package/lib/public/modules/tui-attention.js +10 -0
- package/lib/public/modules/user-settings.js +16 -23
- package/lib/public/modules/vendor-login.js +287 -0
- package/lib/public/modules/worker-proposal.js +2 -2
- package/lib/public/modules/workspace-assignment-card.js +149 -0
- package/lib/public/modules/workspace-assignment-routing.js +4 -0
- package/lib/public/style.css +13 -2
- package/lib/runtime-env.js +65 -0
- package/lib/sdk-bridge.js +120 -32
- package/lib/sdk-message-processor.js +10 -6
- package/lib/server-home-capsule-creation.js +36 -0
- package/lib/server-home-chat-events.js +287 -0
- package/lib/server-home-chat.js +518 -0
- package/lib/server-home-clay-entry.js +65 -0
- package/lib/server-home-clay-session-links.js +26 -0
- package/lib/server-home-debate-planning.js +214 -0
- package/lib/server-home-debates.js +137 -0
- package/lib/server-home-mate-creation.js +261 -0
- package/lib/server-home-models.js +241 -0
- package/lib/server-home-preferences.js +61 -0
- package/lib/server-mates.js +37 -0
- package/lib/server-tools.js +377 -0
- package/lib/server.js +111 -59
- package/lib/session-pair-mcp-server.js +6 -6
- package/lib/sessions.js +18 -3
- package/lib/tool-capsule-source.js +142 -0
- package/lib/tool-control-mcp-server.js +126 -0
- package/lib/tool-llm.js +48 -0
- package/lib/tool-storage.js +77 -0
- package/lib/tool-ui-spec-advanced.js +82 -0
- package/lib/tool-ui-spec.js +261 -0
- package/lib/tools-registry.js +286 -0
- package/lib/users-home-dock-preferences.js +77 -0
- package/lib/users-home-surface-preferences.js +99 -0
- package/lib/users-preferences.js +4 -7
- package/lib/users.js +12 -0
- package/lib/whats-new-content.js +3 -3
- package/lib/workspace-assignment-service.js +420 -0
- package/lib/workspace-query-mcp-server.js +154 -0
- package/lib/workspace-query-service.js +492 -0
- package/lib/ws-schema.js +93 -12
- package/lib/yoke/adapters/acp.js +2 -1
- package/lib/yoke/adapters/antigravity.js +2 -1
- package/lib/yoke/adapters/claude-worker.js +11 -4
- package/lib/yoke/adapters/claude.js +73 -43
- package/lib/yoke/adapters/codex.js +40 -10
- package/lib/yoke/adapters/kiro.js +2 -1
- package/lib/yoke/claude-user-input.js +38 -0
- package/lib/yoke/codex-app-server.js +20 -2
- package/lib/yoke/codex-user-input.js +64 -0
- package/lib/yoke/complete-once.js +164 -0
- package/lib/yoke/index.js +5 -1
- package/lib/yoke/interface.js +19 -0
- package/lib/yoke/mcp-bridge-server.js +2 -6
- package/lib/yoke/user-input.js +254 -0
- package/package.json +3 -2
- package/lib/public/css/home-chat.css +0 -380
- package/lib/public/modules/home-chat.js +0 -438
- package/lib/server-clay-home.js +0 -245
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
var toolsRegistry = require("./tools-registry");
|
|
2
|
+
var toolStorage = require("./tool-storage");
|
|
3
|
+
var crypto = require("crypto");
|
|
4
|
+
var toolLlm = require("./tool-llm");
|
|
5
|
+
|
|
6
|
+
function attachTools(deps) {
|
|
7
|
+
var users = deps.users;
|
|
8
|
+
var projects = deps.projects;
|
|
9
|
+
var serverLogic = Object.create(null);
|
|
10
|
+
var controlTimeoutMs = deps.controlTimeoutMs || 15000;
|
|
11
|
+
var llmControlTimeoutMs = deps.llmControlTimeoutMs || 90000;
|
|
12
|
+
var pendingControl = Object.create(null);
|
|
13
|
+
|
|
14
|
+
function contextFor(userId) {
|
|
15
|
+
var multiUser = users.isMultiUser();
|
|
16
|
+
var linuxUser = null;
|
|
17
|
+
if (multiUser) {
|
|
18
|
+
var user = users.findUserById(userId);
|
|
19
|
+
if (user && user.linuxUser) linuxUser = user.linuxUser;
|
|
20
|
+
}
|
|
21
|
+
return { userId: userId, multiUser: multiUser, linuxUser: linuxUser };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function send(ws, payload) {
|
|
25
|
+
if (ws.readyState !== undefined && ws.readyState !== 1) return;
|
|
26
|
+
ws.send(JSON.stringify(payload));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function broadcastToUser(userId, payload) {
|
|
30
|
+
var sent = new Set();
|
|
31
|
+
projects.forEach(function (projectContext) {
|
|
32
|
+
projectContext.forEachClient(function (otherWs) {
|
|
33
|
+
if (sent.has(otherWs)) return;
|
|
34
|
+
if (users.isMultiUser() && (!otherWs._clayUser || otherWs._clayUser.id !== userId)) return;
|
|
35
|
+
sent.add(otherWs);
|
|
36
|
+
send(otherWs, payload);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function sendError(ws, msg, error) {
|
|
42
|
+
send(ws, {
|
|
43
|
+
type: "tools_error",
|
|
44
|
+
requestType: msg.type,
|
|
45
|
+
toolId: msg.toolId || (msg.manifest && msg.manifest.id) || null,
|
|
46
|
+
seq: msg.seq,
|
|
47
|
+
requestId: msg.requestId,
|
|
48
|
+
message: error && error.message ? error.message : "Tool operation failed.",
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function run(ws, msg, operation) {
|
|
53
|
+
Promise.resolve().then(operation).catch(function (error) { sendError(ws, msg, error); });
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function fullToolList(ctx) {
|
|
57
|
+
var manifests = toolsRegistry.listTools(ctx);
|
|
58
|
+
var tools = [];
|
|
59
|
+
for (var i = 0; i < manifests.length; i++) {
|
|
60
|
+
if (manifests[i].error) {
|
|
61
|
+
tools.push(manifests[i]);
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
var tool = toolsRegistry.getTool(ctx, manifests[i].id);
|
|
65
|
+
if (tool) tools.push(tool);
|
|
66
|
+
}
|
|
67
|
+
return tools;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function installedManifests(userId) {
|
|
71
|
+
var ctx = contextFor(userId);
|
|
72
|
+
return toolsRegistry.listTools(ctx).filter(function (manifest) { return !manifest.error; });
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function refreshMateCatalogs(userId) {
|
|
76
|
+
projects.forEach(function (projectContext) {
|
|
77
|
+
try {
|
|
78
|
+
if (!projectContext || typeof projectContext.getStatus !== "function" || typeof projectContext.refreshCapsuleCatalog !== "function") return;
|
|
79
|
+
var status = projectContext.getStatus();
|
|
80
|
+
if (!status || status.isMate !== true) return;
|
|
81
|
+
if (users.isMultiUser() && status.projectOwnerId !== userId) return;
|
|
82
|
+
projectContext.refreshCapsuleCatalog();
|
|
83
|
+
} catch (error) {
|
|
84
|
+
console.error("[tools] Could not refresh a Mate Capsule catalog:", error && error.message ? error.message : error);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function socketUserId(ws) {
|
|
90
|
+
if (!users.isMultiUser()) return "default";
|
|
91
|
+
return ws._clayUser ? ws._clayUser.id : null;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function latestHomeSocket(userId) {
|
|
95
|
+
var found = null;
|
|
96
|
+
var foundAt = -1;
|
|
97
|
+
var seen = new Set();
|
|
98
|
+
projects.forEach(function (projectContext) {
|
|
99
|
+
projectContext.forEachClient(function (ws) {
|
|
100
|
+
if (seen.has(ws) || socketUserId(ws) !== userId || !ws._homeChatTap) return;
|
|
101
|
+
seen.add(ws);
|
|
102
|
+
if (ws.readyState !== undefined && ws.readyState !== 1) return;
|
|
103
|
+
var openedAt = ws._homeChatTap.openedAt || 0;
|
|
104
|
+
if (!found || openedAt >= foundAt) {
|
|
105
|
+
found = ws;
|
|
106
|
+
foundAt = openedAt;
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
return found;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function requestBrowserControl(userId, mateId, toolId, kind, payload, timeoutMs) {
|
|
114
|
+
var ws = latestHomeSocket(userId);
|
|
115
|
+
if (!ws) return Promise.reject(new Error("The user's home screen is not open."));
|
|
116
|
+
var requestId = "toolctl_" + crypto.randomUUID();
|
|
117
|
+
return new Promise(function (resolve, reject) {
|
|
118
|
+
var timer = setTimeout(function () {
|
|
119
|
+
delete pendingControl[requestId];
|
|
120
|
+
reject(new Error("The tool control request timed out because the home screen did not respond."));
|
|
121
|
+
}, timeoutMs || controlTimeoutMs);
|
|
122
|
+
pendingControl[requestId] = {
|
|
123
|
+
ws: ws,
|
|
124
|
+
userId: userId,
|
|
125
|
+
resolve: resolve,
|
|
126
|
+
reject: reject,
|
|
127
|
+
timer: timer,
|
|
128
|
+
};
|
|
129
|
+
send(ws, {
|
|
130
|
+
type: "tool_control_request",
|
|
131
|
+
requestId: requestId,
|
|
132
|
+
toolId: toolId,
|
|
133
|
+
kind: kind,
|
|
134
|
+
payload: payload || {},
|
|
135
|
+
callerId: mateId,
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function controlForMate(userId, mateId, toolId, kind, payload) {
|
|
141
|
+
payload = payload || {};
|
|
142
|
+
var ctx = contextFor(userId);
|
|
143
|
+
toolsRegistry.listTools(ctx);
|
|
144
|
+
var installed = toolsRegistry.getTool(ctx, toolId);
|
|
145
|
+
if (!installed) return Promise.reject(new Error("Tool is not installed."));
|
|
146
|
+
if (installed.manifest.runtime === "server") {
|
|
147
|
+
var adapter = serverLogic[toolId];
|
|
148
|
+
if (!adapter) return Promise.reject(new Error("No trusted server runtime is registered for this capsule."));
|
|
149
|
+
var context = { userId: userId, callerId: mateId };
|
|
150
|
+
if (kind === "snapshot") return Promise.resolve(adapter.snapshot(context));
|
|
151
|
+
if (kind === "act") return Promise.resolve(adapter.act(context, payload.actionId, payload.args || {}));
|
|
152
|
+
if (kind === "set" && adapter.set) return Promise.resolve(adapter.set(context, payload.controlId, payload.value));
|
|
153
|
+
return Promise.reject(new Error("The server-runtime capsule does not support '" + kind + "'."));
|
|
154
|
+
}
|
|
155
|
+
var permissions = installed.manifest.permissions || [];
|
|
156
|
+
var timeoutMs = kind === "act" && permissions.indexOf("llm") !== -1 ? llmControlTimeoutMs : controlTimeoutMs;
|
|
157
|
+
return requestBrowserControl(userId, mateId, toolId, kind, payload || {}, timeoutMs);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function handleControlResponse(ws, msg, userId) {
|
|
161
|
+
var pending = pendingControl[msg.requestId];
|
|
162
|
+
if (!pending || pending.ws !== ws || pending.userId !== userId) return;
|
|
163
|
+
clearTimeout(pending.timer);
|
|
164
|
+
delete pendingControl[msg.requestId];
|
|
165
|
+
if (msg.error) pending.reject(new Error(msg.error));
|
|
166
|
+
else pending.resolve(msg.data || {});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function storageOperation(ctx, msg) {
|
|
170
|
+
if (!toolsRegistry.getTool(ctx, msg.toolId)) throw new Error("Tool is not installed.");
|
|
171
|
+
var storage = toolStorage.createToolStorage(ctx, msg.toolId);
|
|
172
|
+
var args = msg.args || {};
|
|
173
|
+
if (msg.op === "list") return storage.list();
|
|
174
|
+
if (msg.op === "get") return storage.get(args.id);
|
|
175
|
+
if (msg.op === "put") return storage.put(args.doc);
|
|
176
|
+
if (msg.op === "delete") return storage.delete(args.id);
|
|
177
|
+
if (msg.op === "query") return storage.query(args.query || {});
|
|
178
|
+
throw new Error("Unknown storage operation '" + msg.op + "'.");
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function projectForSocket(ws) {
|
|
182
|
+
var found = null;
|
|
183
|
+
projects.forEach(function (projectContext) {
|
|
184
|
+
if (!found && projectContext.clients && projectContext.clients.has(ws)) found = projectContext;
|
|
185
|
+
});
|
|
186
|
+
return found;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
async function llmOperation(ws, ctx, userId, msg) {
|
|
190
|
+
var tool = toolsRegistry.getTool(ctx, msg.toolId);
|
|
191
|
+
if (!tool) throw new Error("Tool is not installed.");
|
|
192
|
+
var permissions = tool.manifest.permissions || [];
|
|
193
|
+
if (permissions.indexOf("llm") === -1) throw new Error("This capsule does not have the llm permission.");
|
|
194
|
+
var args = toolLlm.validateArgs(msg.args);
|
|
195
|
+
var projectContext = projectForSocket(ws);
|
|
196
|
+
if (!projectContext || typeof projectContext.completeToolLlm !== "function") throw new Error("No project LLM runtime is available.");
|
|
197
|
+
console.debug("[tool-llm] toolId=" + msg.toolId + " callerId=" + (msg.callerId || "user"));
|
|
198
|
+
return projectContext.completeToolLlm(ws, args);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
async function llmConfiguration(ws, alias) {
|
|
202
|
+
var projectContext = projectForSocket(ws);
|
|
203
|
+
if (!projectContext || typeof projectContext.getToolLlmConfig !== "function") {
|
|
204
|
+
return { status: "error", vendor: "", vendorName: "", model: "", modelName: "", error: "No project model configuration is available." };
|
|
205
|
+
}
|
|
206
|
+
try {
|
|
207
|
+
return await projectContext.getToolLlmConfig(ws, alias);
|
|
208
|
+
} catch (error) {
|
|
209
|
+
return { status: "error", vendor: "", vendorName: "", model: "", modelName: "", error: error && error.message ? error.message : "Could not load the model configuration." };
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function installForMate(userId, input) {
|
|
214
|
+
var installed = toolsRegistry.installTool(contextFor(userId), input);
|
|
215
|
+
refreshMateCatalogs(userId);
|
|
216
|
+
broadcastToUser(userId, { type: "tool_installed", tool: installed });
|
|
217
|
+
return installed;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function sourceForMate(userId, toolId) {
|
|
221
|
+
var ctx = contextFor(userId);
|
|
222
|
+
var installed = toolsRegistry.getTool(ctx, toolId);
|
|
223
|
+
if (!installed) throw new Error("Tool not found.");
|
|
224
|
+
if (installed.manifest.runtime !== "worker") throw new Error("Server-managed Capsule source is not available to Mates.");
|
|
225
|
+
if (!installed.metadata || installed.metadata.mateEditingAllowed !== true) {
|
|
226
|
+
throw new Error("The user has not allowed Mate source access for this Capsule.");
|
|
227
|
+
}
|
|
228
|
+
return toolsRegistry.getToolSource(ctx, toolId);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function updateForMate(userId, toolId, input) {
|
|
232
|
+
var ctx = contextFor(userId);
|
|
233
|
+
var installed = toolsRegistry.getTool(ctx, toolId);
|
|
234
|
+
if (!installed) throw new Error("Tool not found.");
|
|
235
|
+
if (!installed.metadata || installed.metadata.mateEditingAllowed !== true) {
|
|
236
|
+
throw new Error("The user has not allowed Mate editing for this Capsule.");
|
|
237
|
+
}
|
|
238
|
+
var updated = toolsRegistry.updateTool(ctx, toolId, input);
|
|
239
|
+
refreshMateCatalogs(userId);
|
|
240
|
+
broadcastToUser(userId, { type: "tool_installed", tool: updated });
|
|
241
|
+
var source = toolsRegistry.getToolSource(ctx, toolId);
|
|
242
|
+
return Object.assign({}, updated, { revision: source.revision });
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function setMateAccess(userId, toolId, allowed, requestId) {
|
|
246
|
+
var ctx = contextFor(userId);
|
|
247
|
+
var tool = toolsRegistry.getTool(ctx, toolId);
|
|
248
|
+
if (!tool) throw new Error("Tool not found.");
|
|
249
|
+
if (tool.manifest.runtime !== "worker" && allowed === true) {
|
|
250
|
+
throw new Error("Server-managed Capsules cannot be edited by Mates.");
|
|
251
|
+
}
|
|
252
|
+
var metadata = toolsRegistry.setMateEditingAllowed(ctx, toolId, allowed);
|
|
253
|
+
broadcastToUser(userId, { type: "tool_mate_access_state", toolId: toolId, requestId: requestId || null, ok: true, metadata: metadata });
|
|
254
|
+
return metadata;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function removeForMate(userId, toolId) {
|
|
258
|
+
var removed = toolsRegistry.removeTool(contextFor(userId), toolId);
|
|
259
|
+
if (!removed) throw new Error("Tool not found.");
|
|
260
|
+
refreshMateCatalogs(userId);
|
|
261
|
+
broadcastToUser(userId, { type: "tool_removed", toolId: toolId });
|
|
262
|
+
return { removed: true, toolId: toolId };
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function handleMessage(ws, msg) {
|
|
266
|
+
var messageTypes = ["tools_list", "tool_get", "tool_install", "tool_remove", "tool_storage_op", "tool_llm_op", "tool_llm_config_get", "tool_control_response", "tool_source_get", "tool_mate_access_set"];
|
|
267
|
+
if (!msg || messageTypes.indexOf(msg.type) === -1) return false;
|
|
268
|
+
var userId;
|
|
269
|
+
if (users.isMultiUser()) {
|
|
270
|
+
if (!ws._clayUser) return false;
|
|
271
|
+
userId = ws._clayUser.id;
|
|
272
|
+
} else {
|
|
273
|
+
userId = "default";
|
|
274
|
+
}
|
|
275
|
+
var ctx = contextFor(userId);
|
|
276
|
+
|
|
277
|
+
if (msg.type === "tool_control_response") {
|
|
278
|
+
handleControlResponse(ws, msg, userId);
|
|
279
|
+
return true;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (msg.type === "tools_list") {
|
|
283
|
+
run(ws, msg, function () {
|
|
284
|
+
send(ws, { type: "tools_state", tools: fullToolList(ctx) });
|
|
285
|
+
});
|
|
286
|
+
return true;
|
|
287
|
+
}
|
|
288
|
+
if (msg.type === "tool_get") {
|
|
289
|
+
run(ws, msg, function () {
|
|
290
|
+
var tool = toolsRegistry.getTool(ctx, msg.toolId);
|
|
291
|
+
if (!tool) throw new Error("Tool not found.");
|
|
292
|
+
send(ws, { type: "tools_state", tools: [tool], requestedToolId: msg.toolId });
|
|
293
|
+
});
|
|
294
|
+
return true;
|
|
295
|
+
}
|
|
296
|
+
if (msg.type === "tool_source_get") {
|
|
297
|
+
Promise.resolve().then(function () {
|
|
298
|
+
return toolsRegistry.getToolSource(ctx, msg.toolId);
|
|
299
|
+
}).then(function (source) {
|
|
300
|
+
send(ws, Object.assign({ type: "tool_source_state", toolId: msg.toolId, requestId: msg.requestId, ok: true }, source));
|
|
301
|
+
}).catch(function (error) {
|
|
302
|
+
send(ws, { type: "tool_source_state", toolId: msg.toolId, requestId: msg.requestId, ok: false, error: error && error.message ? error.message : "Could not read Capsule source." });
|
|
303
|
+
});
|
|
304
|
+
return true;
|
|
305
|
+
}
|
|
306
|
+
if (msg.type === "tool_mate_access_set") {
|
|
307
|
+
Promise.resolve().then(function () {
|
|
308
|
+
if (typeof msg.allowed !== "boolean") throw new Error("Mate editing permission must be true or false.");
|
|
309
|
+
return setMateAccess(userId, msg.toolId, msg.allowed, msg.requestId);
|
|
310
|
+
}).catch(function (error) {
|
|
311
|
+
send(ws, { type: "tool_mate_access_state", toolId: msg.toolId, requestId: msg.requestId, ok: false, error: error && error.message ? error.message : "Could not update Mate editing permission." });
|
|
312
|
+
});
|
|
313
|
+
return true;
|
|
314
|
+
}
|
|
315
|
+
if (msg.type === "tool_install") {
|
|
316
|
+
run(ws, msg, function () {
|
|
317
|
+
installForMate(userId, {
|
|
318
|
+
manifest: msg.manifest,
|
|
319
|
+
logicSource: msg.logicSource,
|
|
320
|
+
uiTree: msg.uiTree,
|
|
321
|
+
});
|
|
322
|
+
});
|
|
323
|
+
return true;
|
|
324
|
+
}
|
|
325
|
+
if (msg.type === "tool_remove") {
|
|
326
|
+
run(ws, msg, function () {
|
|
327
|
+
removeForMate(userId, msg.toolId);
|
|
328
|
+
});
|
|
329
|
+
return true;
|
|
330
|
+
}
|
|
331
|
+
if (msg.type === "tool_storage_op") {
|
|
332
|
+
Promise.resolve().then(function () {
|
|
333
|
+
return storageOperation(ctx, msg);
|
|
334
|
+
}).then(function (data) {
|
|
335
|
+
send(ws, { type: "tool_storage_result", toolId: msg.toolId, op: msg.op, seq: msg.seq, data: data });
|
|
336
|
+
}).catch(function (error) {
|
|
337
|
+
send(ws, {
|
|
338
|
+
type: "tool_storage_result",
|
|
339
|
+
toolId: msg.toolId,
|
|
340
|
+
op: msg.op,
|
|
341
|
+
seq: msg.seq,
|
|
342
|
+
error: error && error.message ? error.message : "Tool storage operation failed.",
|
|
343
|
+
});
|
|
344
|
+
});
|
|
345
|
+
return true;
|
|
346
|
+
}
|
|
347
|
+
if (msg.type === "tool_llm_op") {
|
|
348
|
+
run(ws, msg, async function () {
|
|
349
|
+
var data = await llmOperation(ws, ctx, userId, msg);
|
|
350
|
+
send(ws, { type: "tool_llm_result", toolId: msg.toolId, requestId: msg.requestId, data: data });
|
|
351
|
+
});
|
|
352
|
+
return true;
|
|
353
|
+
}
|
|
354
|
+
if (msg.type === "tool_llm_config_get") {
|
|
355
|
+
run(ws, msg, async function () {
|
|
356
|
+
var alias = toolLlm.MODEL_ALIASES.indexOf(msg.alias) !== -1 ? msg.alias : "standard";
|
|
357
|
+
var state = await llmConfiguration(ws, alias);
|
|
358
|
+
send(ws, Object.assign({ type: "tool_llm_config_state", requestId: msg.requestId || null, alias: alias }, state));
|
|
359
|
+
});
|
|
360
|
+
return true;
|
|
361
|
+
}
|
|
362
|
+
return false;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
return {
|
|
366
|
+
handleMessage: handleMessage,
|
|
367
|
+
installedManifests: installedManifests,
|
|
368
|
+
controlForMate: controlForMate,
|
|
369
|
+
installForMate: installForMate,
|
|
370
|
+
sourceForMate: sourceForMate,
|
|
371
|
+
updateForMate: updateForMate,
|
|
372
|
+
setMateAccess: setMateAccess,
|
|
373
|
+
removeForMate: removeForMate,
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
module.exports = { attachTools: attachTools };
|
package/lib/server.js
CHANGED
|
@@ -13,10 +13,16 @@ var serverSkills = require("./server-skills");
|
|
|
13
13
|
var claudeHookInstaller = require("./claude-hook-installer");
|
|
14
14
|
var serverDm = require("./server-dm");
|
|
15
15
|
var serverMates = require("./server-mates");
|
|
16
|
-
var
|
|
16
|
+
var serverHomeChat = require("./server-home-chat");
|
|
17
|
+
var shouldSuppressHomeResponse = require("./home-response-notifications").shouldSuppressHomeResponse;
|
|
18
|
+
var serverHomePreferences = require("./server-home-preferences");
|
|
19
|
+
var serverTools = require("./server-tools");
|
|
17
20
|
var serverAdmin = require("./server-admin");
|
|
18
21
|
var serverSettings = require("./server-settings");
|
|
19
22
|
var serverPalette = require("./server-palette");
|
|
23
|
+
var workspaceQueryModule = require("./workspace-query-service");
|
|
24
|
+
var { attachWorkspaceQueryService } = workspaceQueryModule;
|
|
25
|
+
var { attachWorkspaceAssignmentService } = require("./workspace-assignment-service");
|
|
20
26
|
var serverEmail = require("./server-email");
|
|
21
27
|
var serverGlobalWs = require("./server-global-ws");
|
|
22
28
|
var { parseWsRequestUrl } = require("./ws-request");
|
|
@@ -291,6 +297,27 @@ function createServer(opts) {
|
|
|
291
297
|
|
|
292
298
|
// --- Project registry ---
|
|
293
299
|
var projects = new Map(); // slug → projectContext
|
|
300
|
+
var assignmentService = attachWorkspaceAssignmentService({
|
|
301
|
+
storageDir: path.join(CONFIG_DIR, "workspace-assignments"),
|
|
302
|
+
getProjects: function () { return projects; },
|
|
303
|
+
isMultiUser: function () { return users.isMultiUser(); },
|
|
304
|
+
sessionRef: workspaceQueryModule.sessionRef,
|
|
305
|
+
});
|
|
306
|
+
var workspaceQueryService = attachWorkspaceQueryService({
|
|
307
|
+
getProjects: function () { return projects; },
|
|
308
|
+
isMultiUser: function () { return users.isMultiUser(); },
|
|
309
|
+
canAccessProject: function (userId, status) {
|
|
310
|
+
return users.canAccessProject(userId, {
|
|
311
|
+
ownerId: status && status.projectOwnerId || null,
|
|
312
|
+
visibility: status && status.visibility || "public",
|
|
313
|
+
allowedUsers: status && status.allowedUsers || [],
|
|
314
|
+
});
|
|
315
|
+
},
|
|
316
|
+
resolveMate: function (userId, mateId) {
|
|
317
|
+
return mates.getMate(mates.buildMateCtx(users.isMultiUser() ? userId : null), mateId);
|
|
318
|
+
},
|
|
319
|
+
assignmentService: assignmentService,
|
|
320
|
+
});
|
|
294
321
|
|
|
295
322
|
// --- Admin module ---
|
|
296
323
|
var admin = serverAdmin.attachAdmin({
|
|
@@ -355,7 +382,7 @@ function createServer(opts) {
|
|
|
355
382
|
// origin. External sites still cannot frame Clay; frame-ancestors below
|
|
356
383
|
// is the modern equivalent for browsers that honor CSP.
|
|
357
384
|
"X-Frame-Options": "SAMEORIGIN",
|
|
358
|
-
"Content-Security-Policy": "default-src 'self'; frame-ancestors 'self'; script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://esm.sh; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://cdn.jsdelivr.net; img-src * data: blob:; connect-src 'self' ws: wss: https://cdn.jsdelivr.net https://esm.sh https://api.open-meteo.com https://ipapi.co; font-src 'self' data: https://fonts.gstatic.com https://cdn.jsdelivr.net;",
|
|
385
|
+
"Content-Security-Policy": "default-src 'self'; frame-ancestors 'self'; script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://esm.sh; worker-src 'self' blob:; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://cdn.jsdelivr.net; img-src * data: blob:; connect-src 'self' ws: wss: https://cdn.jsdelivr.net https://esm.sh https://api.open-meteo.com https://ipapi.co; font-src 'self' data: https://fonts.gstatic.com https://cdn.jsdelivr.net;",
|
|
359
386
|
};
|
|
360
387
|
if (tlsOptions) {
|
|
361
388
|
securityHeaders["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains";
|
|
@@ -655,7 +682,7 @@ function createServer(opts) {
|
|
|
655
682
|
// --- Skills routes (delegated to server-skills) ---
|
|
656
683
|
if (skills.handleRequest(req, res, fullUrl)) return;
|
|
657
684
|
|
|
658
|
-
// Root path —
|
|
685
|
+
// Root path — render home while keeping the last accessible project connected.
|
|
659
686
|
if (fullUrl === "/" && req.method === "GET") {
|
|
660
687
|
if (!isRequestAuthed(req)) {
|
|
661
688
|
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
@@ -665,9 +692,23 @@ function createServer(opts) {
|
|
|
665
692
|
if (projects.size > 0) {
|
|
666
693
|
var targetSlug = null;
|
|
667
694
|
var reqUser = users.isMultiUser() ? getMultiUserFromReq(req) : null;
|
|
695
|
+
var homePreference = typeof users.getHomeSurfacePreference === "function"
|
|
696
|
+
? users.getHomeSurfacePreference(reqUser ? reqUser.id : "default")
|
|
697
|
+
: null;
|
|
698
|
+
var preferredContextSlug = homePreference && homePreference.projectSlug;
|
|
699
|
+
if (preferredContextSlug && projects.has(preferredContextSlug)) {
|
|
700
|
+
if (reqUser && onGetProjectAccess) {
|
|
701
|
+
var preferredAccess = onGetProjectAccess(preferredContextSlug);
|
|
702
|
+
if (preferredAccess && !preferredAccess.error && users.canAccessProject(reqUser.id, preferredAccess)) {
|
|
703
|
+
targetSlug = preferredContextSlug;
|
|
704
|
+
}
|
|
705
|
+
} else {
|
|
706
|
+
targetSlug = preferredContextSlug;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
668
709
|
// Check for last-visited project cookie
|
|
669
710
|
var lastProject = parseCookies(req)["clay_last_project"];
|
|
670
|
-
if (lastProject && projects.has(lastProject)) {
|
|
711
|
+
if (!targetSlug && lastProject && projects.has(lastProject)) {
|
|
671
712
|
if (reqUser && onGetProjectAccess) {
|
|
672
713
|
var lpAccess = onGetProjectAccess(lastProject);
|
|
673
714
|
if (lpAccess && !lpAccess.error && users.canAccessProject(reqUser.id, lpAccess)) {
|
|
@@ -692,8 +733,15 @@ function createServer(opts) {
|
|
|
692
733
|
});
|
|
693
734
|
}
|
|
694
735
|
if (targetSlug) {
|
|
695
|
-
|
|
696
|
-
|
|
736
|
+
try {
|
|
737
|
+
var homeHtml = fs.readFileSync(path.join(publicDir, "index.html"), "utf8");
|
|
738
|
+
homeHtml = homeHtml.replace("<body>", '<body data-home-project-slug="' + targetSlug + '">');
|
|
739
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8", "Cache-Control": "no-cache" });
|
|
740
|
+
res.end(homeHtml);
|
|
741
|
+
} catch (e) {
|
|
742
|
+
res.writeHead(500, { "Content-Type": "text/plain" });
|
|
743
|
+
res.end("App shell missing.");
|
|
744
|
+
}
|
|
697
745
|
return;
|
|
698
746
|
}
|
|
699
747
|
}
|
|
@@ -1028,10 +1076,9 @@ function createServer(opts) {
|
|
|
1028
1076
|
unreadMap[wsSlug] = 0;
|
|
1029
1077
|
}
|
|
1030
1078
|
ctx.handleConnection(ws, wsUser);
|
|
1031
|
-
// Tear down the
|
|
1032
|
-
// we don't leak callbacks against Clay's session manager.
|
|
1079
|
+
// Tear down the embedded mate chat subscription on socket close.
|
|
1033
1080
|
ws.on("close", function () {
|
|
1034
|
-
try {
|
|
1081
|
+
try { homeChatHandler.handleDisconnection(ws); } catch (e) {}
|
|
1035
1082
|
});
|
|
1036
1083
|
});
|
|
1037
1084
|
});
|
|
@@ -1119,8 +1166,20 @@ function createServer(opts) {
|
|
|
1119
1166
|
projectOwnerId: projectOwnerId || null,
|
|
1120
1167
|
worktreeMeta: worktreeMeta || null,
|
|
1121
1168
|
isMate: extra.isMate || false,
|
|
1169
|
+
mateId: extra.mateId || (extra.isMate ? path.basename(cwd) : null),
|
|
1122
1170
|
mateDisplayName: extra.mateDisplayName || "",
|
|
1123
1171
|
isHostAgent: !!extra.isHostAgent,
|
|
1172
|
+
createReadyMate: function (ws, userId, definition) {
|
|
1173
|
+
if (!mateHandler || typeof mateHandler.createReadyMateFromInterview !== "function") throw new Error("Mate creation is unavailable.");
|
|
1174
|
+
return mateHandler.createReadyMateFromInterview(ws, userId, definition);
|
|
1175
|
+
},
|
|
1176
|
+
workspaceQueryService: workspaceQueryService,
|
|
1177
|
+
getProjectAccess: function () {
|
|
1178
|
+
return onGetProjectAccess ? onGetProjectAccess(slug) : { visibility: "public", ownerId: projectOwnerId || null };
|
|
1179
|
+
},
|
|
1180
|
+
shouldSuppressResponseNotification: function (session) {
|
|
1181
|
+
return shouldSuppressHomeResponse(projects, slug, session);
|
|
1182
|
+
},
|
|
1124
1183
|
pushModule: pushModule,
|
|
1125
1184
|
debug: debug,
|
|
1126
1185
|
dangerouslySkipPermissions: dangerouslySkipPermissions,
|
|
@@ -1143,52 +1202,6 @@ function createServer(opts) {
|
|
|
1143
1202
|
});
|
|
1144
1203
|
return list;
|
|
1145
1204
|
},
|
|
1146
|
-
getAllProjectSessions: function () {
|
|
1147
|
-
var allSessions = [];
|
|
1148
|
-
projects.forEach(function (pCtx, pSlug) {
|
|
1149
|
-
if (pSlug === slug) return; // skip self
|
|
1150
|
-
var status = pCtx.getStatus();
|
|
1151
|
-
if (status.isWorktree) return;
|
|
1152
|
-
var pSm = pCtx.getSessionManager();
|
|
1153
|
-
if (!pSm) return;
|
|
1154
|
-
var projectTitle = status.title || status.project || pSlug;
|
|
1155
|
-
pSm.sessions.forEach(function (s) {
|
|
1156
|
-
if (!s.hidden && s.history && s.history.length > 0) {
|
|
1157
|
-
s._projectTitle = projectTitle;
|
|
1158
|
-
allSessions.push(s);
|
|
1159
|
-
}
|
|
1160
|
-
});
|
|
1161
|
-
});
|
|
1162
|
-
return allSessions;
|
|
1163
|
-
},
|
|
1164
|
-
// Like getAllProjectSessions but returns the per-project grouped
|
|
1165
|
-
// shape that session-search.searchPalette expects. Includes self
|
|
1166
|
-
// (so the host agent can search its own past Clay conversations).
|
|
1167
|
-
// Used by clay-history-mcp-server.
|
|
1168
|
-
getAllProjectsWithSessions: function () {
|
|
1169
|
-
var out = [];
|
|
1170
|
-
projects.forEach(function (pCtx, pSlug) {
|
|
1171
|
-
var status = pCtx.getStatus();
|
|
1172
|
-
if (status.isWorktree) return;
|
|
1173
|
-
var pSm = pCtx.getSessionManager();
|
|
1174
|
-
if (!pSm) return;
|
|
1175
|
-
var sessions = [];
|
|
1176
|
-
pSm.sessions.forEach(function (s) {
|
|
1177
|
-
if (s.hidden) return;
|
|
1178
|
-
sessions.push(s);
|
|
1179
|
-
});
|
|
1180
|
-
if (sessions.length === 0) return;
|
|
1181
|
-
out.push({
|
|
1182
|
-
projectSlug: pSlug,
|
|
1183
|
-
projectTitle: status.title || status.project || pSlug,
|
|
1184
|
-
projectIcon: status.icon || null,
|
|
1185
|
-
isMate: !!status.isMate,
|
|
1186
|
-
mateId: status.mateId || null,
|
|
1187
|
-
sessions: sessions,
|
|
1188
|
-
});
|
|
1189
|
-
});
|
|
1190
|
-
return out;
|
|
1191
|
-
},
|
|
1192
1205
|
getHubSchedules: function () {
|
|
1193
1206
|
var allSchedules = [];
|
|
1194
1207
|
projects.forEach(function (ctx, s) {
|
|
@@ -1277,6 +1290,29 @@ function createServer(opts) {
|
|
|
1277
1290
|
onSetProjectDefaultMode: onSetProjectDefaultMode,
|
|
1278
1291
|
onGetProjectLastVendor: onGetProjectLastVendor,
|
|
1279
1292
|
onSetProjectLastVendor: onSetProjectLastVendor,
|
|
1293
|
+
listControllableTools: function (userId) {
|
|
1294
|
+
return toolsHandler ? toolsHandler.installedManifests(userId) : [];
|
|
1295
|
+
},
|
|
1296
|
+
controlToolForMate: function (userId, mateId, toolId, kind, payload) {
|
|
1297
|
+
if (!toolsHandler) return Promise.reject(new Error("Tool control is unavailable."));
|
|
1298
|
+
return toolsHandler.controlForMate(userId, mateId, toolId, kind, payload);
|
|
1299
|
+
},
|
|
1300
|
+
installToolForMate: function (userId, input) {
|
|
1301
|
+
if (!toolsHandler) return Promise.reject(new Error("Tool installation is unavailable."));
|
|
1302
|
+
return toolsHandler.installForMate(userId, input);
|
|
1303
|
+
},
|
|
1304
|
+
sourceToolForMate: function (userId, toolId) {
|
|
1305
|
+
if (!toolsHandler) return Promise.reject(new Error("Capsule source access is unavailable."));
|
|
1306
|
+
return toolsHandler.sourceForMate(userId, toolId);
|
|
1307
|
+
},
|
|
1308
|
+
updateToolForMate: function (userId, toolId, input) {
|
|
1309
|
+
if (!toolsHandler) return Promise.reject(new Error("Capsule source updates are unavailable."));
|
|
1310
|
+
return toolsHandler.updateForMate(userId, toolId, input);
|
|
1311
|
+
},
|
|
1312
|
+
removeToolForMate: function (userId, toolId) {
|
|
1313
|
+
if (!toolsHandler) return Promise.reject(new Error("Tool removal is unavailable."));
|
|
1314
|
+
return toolsHandler.removeForMate(userId, toolId);
|
|
1315
|
+
},
|
|
1280
1316
|
onGetProjectMcpServers: onGetProjectMcpServers,
|
|
1281
1317
|
onSetProjectMcpServers: onSetProjectMcpServers,
|
|
1282
1318
|
onGetDaemonConfig: onGetDaemonConfig,
|
|
@@ -1295,6 +1331,7 @@ function createServer(opts) {
|
|
|
1295
1331
|
isUserOnline: isUserOnline,
|
|
1296
1332
|
});
|
|
1297
1333
|
projects.set(slug, ctx);
|
|
1334
|
+
assignmentService.reconcileProject(slug);
|
|
1298
1335
|
// ctx.warmup() is now deferred to the first websocket connection into
|
|
1299
1336
|
// this project (see project-connection.js handleConnection). Warming
|
|
1300
1337
|
// every project at startup spawned a CodexAppServer and an mcp-bridge
|
|
@@ -1318,12 +1355,24 @@ function createServer(opts) {
|
|
|
1318
1355
|
// --- Email account handler (per-user email account management) ---
|
|
1319
1356
|
var emailHandler = serverEmail.attachEmail({ users: users });
|
|
1320
1357
|
|
|
1321
|
-
// ---
|
|
1322
|
-
var
|
|
1358
|
+
// --- Embedded mate chat handler ---
|
|
1359
|
+
var homeChatHandler = serverHomeChat.attachHomeChat({
|
|
1323
1360
|
users: users,
|
|
1324
1361
|
mates: mates,
|
|
1325
1362
|
projects: projects,
|
|
1326
1363
|
addProject: addProject,
|
|
1364
|
+
workspaceQueryService: workspaceQueryService,
|
|
1365
|
+
});
|
|
1366
|
+
|
|
1367
|
+
var homePreferencesHandler = serverHomePreferences.attachHomePreferences({
|
|
1368
|
+
users: users,
|
|
1369
|
+
projects: projects,
|
|
1370
|
+
});
|
|
1371
|
+
|
|
1372
|
+
// --- User-installed home tools handler ---
|
|
1373
|
+
var toolsHandler = serverTools.attachTools({
|
|
1374
|
+
users: users,
|
|
1375
|
+
projects: projects,
|
|
1327
1376
|
});
|
|
1328
1377
|
|
|
1329
1378
|
// --- Mate handler ---
|
|
@@ -1333,11 +1382,14 @@ function createServer(opts) {
|
|
|
1333
1382
|
if (mateHandler) mateHandler.scheduleRegistryRefresh();
|
|
1334
1383
|
}
|
|
1335
1384
|
|
|
1336
|
-
function handleDmMessage(ws, msg) {
|
|
1385
|
+
function handleDmMessage(ws, msg, routedProjectSlug) {
|
|
1386
|
+
if (assignmentService.handleMessage(ws, msg, routedProjectSlug)) return;
|
|
1337
1387
|
if (dmHandler.handleMessage(ws, msg)) return;
|
|
1338
1388
|
if (mateHandler && mateHandler.handleMessage(ws, msg)) return;
|
|
1339
1389
|
if (emailHandler.handleMessage(ws, msg)) return;
|
|
1340
|
-
if (
|
|
1390
|
+
if (homeChatHandler.handleMessage(ws, msg)) return;
|
|
1391
|
+
if (homePreferencesHandler.handleMessage(ws, msg)) return;
|
|
1392
|
+
if (toolsHandler.handleMessage(ws, msg)) return;
|
|
1341
1393
|
}
|
|
1342
1394
|
|
|
1343
1395
|
function removeProject(slug) {
|