clay-server 2.33.1 → 2.34.0-beta.10

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 (47) hide show
  1. package/lib/ask-user-mcp-server.js +120 -0
  2. package/lib/config.js +9 -13
  3. package/lib/daemon.js +116 -55
  4. package/lib/mate-datastore.js +359 -0
  5. package/lib/mates.js +2 -2
  6. package/lib/os-users.js +70 -37
  7. package/lib/project-connection.js +16 -9
  8. package/lib/project-http.js +3 -4
  9. package/lib/project-image.js +3 -2
  10. package/lib/project-mate-datastore.js +232 -0
  11. package/lib/project-sessions.js +110 -7
  12. package/lib/project-user-message.js +4 -3
  13. package/lib/project.js +126 -10
  14. package/lib/public/app.js +2 -0
  15. package/lib/public/css/mates.css +228 -11
  16. package/lib/public/css/messages.css +23 -0
  17. package/lib/public/css/mobile-nav.css +0 -14
  18. package/lib/public/css/notifications-center.css +80 -0
  19. package/lib/public/css/sidebar.css +326 -101
  20. package/lib/public/index.html +24 -29
  21. package/lib/public/modules/app-dm.js +0 -2
  22. package/lib/public/modules/app-messages.js +23 -0
  23. package/lib/public/modules/app-rendering.js +0 -2
  24. package/lib/public/modules/diff.js +21 -7
  25. package/lib/public/modules/mate-datastore-ui.js +280 -0
  26. package/lib/public/modules/mate-sidebar.js +3 -9
  27. package/lib/public/modules/mate-wizard.js +15 -15
  28. package/lib/public/modules/sidebar-mobile.js +10 -20
  29. package/lib/public/modules/sidebar-sessions.js +490 -113
  30. package/lib/public/modules/sidebar.js +8 -6
  31. package/lib/public/modules/tools.js +115 -18
  32. package/lib/public/sw.js +1 -1
  33. package/lib/sdk-bridge.js +56 -41
  34. package/lib/sdk-message-processor.js +21 -4
  35. package/lib/server.js +28 -72
  36. package/lib/sessions.js +157 -20
  37. package/lib/updater.js +2 -2
  38. package/lib/users.js +2 -2
  39. package/lib/ws-schema.js +16 -0
  40. package/lib/yoke/adapters/claude-worker.js +114 -2
  41. package/lib/yoke/adapters/claude.js +56 -5
  42. package/lib/yoke/adapters/codex.js +350 -58
  43. package/lib/yoke/index.js +93 -48
  44. package/lib/yoke/instructions.js +0 -1
  45. package/lib/yoke/mcp-bridge-server.js +14 -6
  46. package/package.json +1 -2
  47. package/lib/yoke/adapters/gemini.js +0 -709
@@ -163,8 +163,8 @@ function attachHTTP(ctx) {
163
163
  var _osUM = require("./os-users");
164
164
  var _uid = _osUM.getLinuxUserUid(req._clayUser.linuxUser);
165
165
  if (_uid != null) {
166
- require("child_process").execSync("chown " + _uid + " " + JSON.stringify(destPath));
167
- require("child_process").execSync("chown " + _uid + " " + JSON.stringify(tmpDir));
166
+ execFileSync("chown", [String(_uid), destPath]);
167
+ execFileSync("chown", [String(_uid), tmpDir]);
168
168
  }
169
169
  } catch (e2) {}
170
170
  }
@@ -639,9 +639,8 @@ function attachHTTP(ctx) {
639
639
 
640
640
  // Git dirty check
641
641
  if (req.method === "GET" && urlPath === "/api/git-dirty") {
642
- var execSync = require("child_process").execSync;
643
642
  try {
644
- var out = execSync("git status --porcelain", { cwd: cwd, encoding: "utf8", timeout: 5000 });
643
+ var out = execFileSync("git", ["status", "--porcelain"], { cwd: cwd, encoding: "utf8", timeout: 5000 });
645
644
  var lines = out.trim().split("\n").filter(function (line) {
646
645
  return line.trim().length > 0 && !line.startsWith("??");
647
646
  });
@@ -1,6 +1,7 @@
1
1
  var fs = require("fs");
2
2
  var path = require("path");
3
3
  var crypto = require("crypto");
4
+ var execFileSync = require("child_process").execFileSync;
4
5
 
5
6
  /**
6
7
  * Attach image handling to a project context.
@@ -65,12 +66,12 @@ function attachImage(ctx) {
65
66
  var osUsersMod = require("./os-users");
66
67
  var uid = osUsersMod.getLinuxUserUid(ownerLinuxUser);
67
68
  if (uid != null) {
68
- require("child_process").execSync("chown " + uid + " " + JSON.stringify(filePath));
69
+ execFileSync("chown", [String(uid), filePath]);
69
70
  // Also fix parent dirs if root-owned
70
71
  try {
71
72
  var dirStat = fs.statSync(imagesDir);
72
73
  if (dirStat.uid !== uid) {
73
- require("child_process").execSync("chown " + uid + " " + JSON.stringify(imagesDir));
74
+ execFileSync("chown", [String(uid), imagesDir]);
74
75
  }
75
76
  } catch (e2) {}
76
77
  }
@@ -0,0 +1,232 @@
1
+ var z = require("zod");
2
+ var datastore = require("./mate-datastore");
3
+ var FEATURE_ENABLED = false;
4
+
5
+ function attachMateDatastore(ctx) {
6
+ var cwd = ctx.cwd;
7
+ var isMate = ctx.isMate;
8
+ var send = ctx.send;
9
+ var sendTo = ctx.sendTo;
10
+
11
+ function isFeatureAvailable() {
12
+ return FEATURE_ENABLED && datastore.isMateDatastoreAvailable();
13
+ }
14
+
15
+ function ensureProjectDatastore() {
16
+ if (!FEATURE_ENABLED) {
17
+ return { ok: false, code: "MATE_DATASTORE_NOT_ALLOWED", message: "Mate datastore is not enabled." };
18
+ }
19
+ if (!isMate) {
20
+ return { ok: false, code: "MATE_DATASTORE_NOT_ALLOWED", message: "Mate datastore is only available in Mate sessions." };
21
+ }
22
+ try {
23
+ return datastore.ensureMateDatastore({ mateDir: cwd });
24
+ } catch (e) {
25
+ return { ok: false, code: "MATE_DATASTORE_UNAVAILABLE", message: e.message || "Mate datastore is unavailable." };
26
+ }
27
+ }
28
+
29
+ function getDatastoreWarning(handle) {
30
+ if (!handle || !handle.warning) return null;
31
+ return handle.warning;
32
+ }
33
+
34
+ function normalizeParams(input) {
35
+ if (!input) return [];
36
+ if (Array.isArray(input.params)) return input.params;
37
+ return [];
38
+ }
39
+
40
+ function normalizeToolResult(result, requestId) {
41
+ var payload = result || { ok: false, code: "MATE_DATASTORE_UNAVAILABLE", message: "Mate datastore is unavailable." };
42
+ if (typeof requestId !== "undefined") payload.requestId = requestId;
43
+ return payload;
44
+ }
45
+
46
+ function callToolInternal(toolName, input) {
47
+ var handle = ensureProjectDatastore();
48
+ if (!handle || handle.ok === false) {
49
+ return normalizeToolResult(handle, input && input.requestId);
50
+ }
51
+
52
+ var warning = getDatastoreWarning(handle);
53
+ var params = normalizeParams(input);
54
+
55
+ if (toolName === "clay_db_query") {
56
+ var queryResult = datastore.runQuery(handle, input.sql, params, { maxRows: 200, maxBytes: 1024 * 1024, includeSizeInfo: true });
57
+ if (warning && queryResult.ok && !queryResult.warning) queryResult.warning = warning;
58
+ return normalizeToolResult(queryResult, input && input.requestId);
59
+ }
60
+
61
+ if (toolName === "clay_db_exec") {
62
+ var execResult = datastore.runExec(handle, input.sql, params, { maxBytes: 1024 * 1024, includeSizeInfo: true });
63
+ if (warning && execResult.ok && !execResult.warning) execResult.warning = warning;
64
+ if (execResult.ok) {
65
+ broadcastDbChange({ tool: toolName, sql: input.sql, changes: execResult.changes || 0 });
66
+ }
67
+ return normalizeToolResult(execResult, input && input.requestId);
68
+ }
69
+
70
+ if (toolName === "clay_db_tables") {
71
+ var tablesResult = datastore.listSchemaObjects(handle);
72
+ if (warning && tablesResult.ok && !tablesResult.warning) tablesResult.warning = warning;
73
+ return normalizeToolResult(tablesResult, input && input.requestId);
74
+ }
75
+
76
+ if (toolName === "clay_db_describe") {
77
+ var describeResult = datastore.describeTable(handle, input.table);
78
+ if (warning && describeResult.ok && !describeResult.warning) describeResult.warning = warning;
79
+ return normalizeToolResult(describeResult, input && input.requestId);
80
+ }
81
+
82
+ return normalizeToolResult({ ok: false, code: "MATE_DATASTORE_NOT_ALLOWED", message: "Unknown datastore tool: " + toolName }, input && input.requestId);
83
+ }
84
+
85
+ function broadcastDbChange(details) {
86
+ var payload = {
87
+ type: "mate_db_change",
88
+ details: details || {},
89
+ };
90
+ send(payload);
91
+ }
92
+
93
+ function handleMateDatastoreMessage(ws, msg) {
94
+ if (msg.type !== "mate_db_tables" && msg.type !== "mate_db_describe" && msg.type !== "mate_db_query" && msg.type !== "mate_db_exec") {
95
+ return false;
96
+ }
97
+
98
+ if (!FEATURE_ENABLED) {
99
+ return false;
100
+ }
101
+
102
+ if (!isMate) {
103
+ sendTo(ws, {
104
+ type: "mate_db_error",
105
+ requestId: msg.requestId || null,
106
+ code: "MATE_DATASTORE_NOT_ALLOWED",
107
+ message: "Mate datastore is only available in Mate sessions.",
108
+ });
109
+ return true;
110
+ }
111
+
112
+ if (msg.type === "mate_db_tables") {
113
+ sendResult(ws, "mate_db_tables_result", callToolInternal("clay_db_tables", msg));
114
+ return true;
115
+ }
116
+
117
+ if (msg.type === "mate_db_describe") {
118
+ sendResult(ws, "mate_db_describe_result", callToolInternal("clay_db_describe", msg));
119
+ return true;
120
+ }
121
+
122
+ if (msg.type === "mate_db_query") {
123
+ sendResult(ws, "mate_db_query_result", callToolInternal("clay_db_query", msg));
124
+ return true;
125
+ }
126
+
127
+ if (msg.type === "mate_db_exec") {
128
+ sendResult(ws, "mate_db_exec_result", callToolInternal("clay_db_exec", msg));
129
+ return true;
130
+ }
131
+
132
+ return true;
133
+ }
134
+
135
+ function sendResult(ws, type, result) {
136
+ var payload = result || { ok: false, code: "MATE_DATASTORE_UNAVAILABLE", message: "Mate datastore is unavailable." };
137
+ payload.type = type;
138
+ sendTo(ws, payload);
139
+ }
140
+
141
+ function getToolDefinitions() {
142
+ if (!isMate || !isFeatureAvailable()) return [];
143
+ return [
144
+ {
145
+ name: "clay_db_query",
146
+ description: "Execute read-only SQL against the current Mate datastore.",
147
+ inputSchema: {
148
+ sql: z.string().min(1).describe("SQL query"),
149
+ params: z.array(z.any()).optional().describe("Positional parameters"),
150
+ },
151
+ handler: function (input) {
152
+ return callToolInternal("clay_db_query", input || {});
153
+ },
154
+ },
155
+ {
156
+ name: "clay_db_exec",
157
+ description: "Execute schema or write SQL against the current Mate datastore.",
158
+ inputSchema: {
159
+ sql: z.string().min(1).describe("SQL statement"),
160
+ params: z.array(z.any()).optional().describe("Positional parameters"),
161
+ },
162
+ handler: function (input) {
163
+ return callToolInternal("clay_db_exec", input || {});
164
+ },
165
+ },
166
+ {
167
+ name: "clay_db_tables",
168
+ description: "List schema objects in the current Mate datastore.",
169
+ inputSchema: undefined,
170
+ handler: function (input) {
171
+ return callToolInternal("clay_db_tables", input || {});
172
+ },
173
+ },
174
+ {
175
+ name: "clay_db_describe",
176
+ description: "Describe a table or view in the current Mate datastore.",
177
+ inputSchema: {
178
+ table: z.string().min(1).describe("Table name"),
179
+ },
180
+ handler: function (input) {
181
+ return callToolInternal("clay_db_describe", input || {});
182
+ },
183
+ },
184
+ ];
185
+ }
186
+
187
+ function createMcpServer() {
188
+ var defs = getToolDefinitions();
189
+ if (!defs.length) return null;
190
+ var registered = {};
191
+ for (var i = 0; i < defs.length; i++) {
192
+ registered[defs[i].name] = {
193
+ description: defs[i].description,
194
+ inputSchema: defs[i].inputSchema,
195
+ handler: defs[i].handler,
196
+ };
197
+ }
198
+ return {
199
+ name: "clay-datastore",
200
+ version: "1.0.0",
201
+ instance: {
202
+ _registeredTools: registered,
203
+ },
204
+ };
205
+ }
206
+
207
+ function getSessionToolDefinitions() {
208
+ if (!isMate || !isFeatureAvailable()) return null;
209
+ return getToolDefinitions();
210
+ }
211
+
212
+ function callMateTool(session, toolName, input) {
213
+ return callToolInternal(toolName, input || {});
214
+ }
215
+
216
+ function closeAllDatastores() {
217
+ datastore.closeAllMateDatastores();
218
+ }
219
+
220
+ return {
221
+ isFeatureAvailable: isFeatureAvailable,
222
+ handleMateDatastoreMessage: handleMateDatastoreMessage,
223
+ getToolDefinitions: getToolDefinitions,
224
+ createMcpServer: createMcpServer,
225
+ getSessionToolDefinitions: getSessionToolDefinitions,
226
+ callMateTool: callMateTool,
227
+ ensureProjectDatastore: ensureProjectDatastore,
228
+ closeAllDatastores: closeAllDatastores,
229
+ };
230
+ }
231
+
232
+ module.exports = { attachMateDatastore: attachMateDatastore };
@@ -3,6 +3,33 @@ var path = require("path");
3
3
  var { execFileSync } = require("child_process");
4
4
  var { CODEX_DEFAULTS, getCodexConfig } = require("./codex-defaults");
5
5
 
6
+ // Format a user's answer to an ask_user_questions card as a plain user
7
+ // message so the MCP path can feed it back to the agent on the next turn.
8
+ // The agent sees: its own question text, then the selected answer(s).
9
+ function formatAskUserAnswerAsMessage(input, answers) {
10
+ var questions = (input && Array.isArray(input.questions)) ? input.questions : [];
11
+ if (questions.length === 0) {
12
+ // Shouldn't happen, but be defensive.
13
+ try { return "(answered with: " + JSON.stringify(answers || {}) + ")"; }
14
+ catch (e) { return "(answered)"; }
15
+ }
16
+ var lines = [];
17
+ for (var i = 0; i < questions.length; i++) {
18
+ var q = questions[i];
19
+ var qText = (q && q.question) ? q.question : ("Question " + (i + 1));
20
+ var ans = (answers && answers[i] != null) ? String(answers[i]) : "";
21
+ if (!ans) continue;
22
+ if (questions.length === 1) {
23
+ // Single question: keep it tight.
24
+ lines.push(ans);
25
+ } else {
26
+ lines.push("- " + qText + " → " + ans);
27
+ }
28
+ }
29
+ if (lines.length === 0) return "(no answer provided)";
30
+ return lines.join("\n");
31
+ }
32
+
6
33
  /**
7
34
  * Attach session management, config, project management, and mid-section
8
35
  * message handlers to a project context.
@@ -12,7 +39,7 @@ var { CODEX_DEFAULTS, getCodexConfig } = require("./codex-defaults");
12
39
  * sm, sdk, tm, clients,
13
40
  * send, sendTo, sendToAdmins, sendToSession, sendToSessionOthers,
14
41
  * opts, usersModule, userPresence, matesModule, pushModule,
15
- * getSessionForWs, getLinuxUserForSession, getOsUserInfoForWs,
42
+ * getSessionForWs, getLinuxUserForSession, ensureProjectAccessForSession, getOsUserInfoForWs,
16
43
  * hydrateImageRefs, onProcessingChanged, broadcastPresence,
17
44
  * adapter, getProjectList, getProjectCount, getScheduleCount,
18
45
  * moveScheduleToProject, moveAllSchedulesToProject, getHubSchedules,
@@ -43,6 +70,7 @@ function attachSessions(ctx) {
43
70
  var pushModule = ctx.pushModule;
44
71
  var getSessionForWs = ctx.getSessionForWs;
45
72
  var getLinuxUserForSession = ctx.getLinuxUserForSession;
73
+ var ensureProjectAccessForSession = ctx.ensureProjectAccessForSession;
46
74
  var getOsUserInfoForWs = ctx.getOsUserInfoForWs;
47
75
  var hydrateImageRefs = ctx.hydrateImageRefs;
48
76
  var onProcessingChanged = ctx.onProcessingChanged;
@@ -136,6 +164,39 @@ function attachSessions(ctx) {
136
164
  return true;
137
165
  }
138
166
 
167
+ if (msg.type === "reorder_session_bookmarks") {
168
+ if (typeof msg.sourceId === "number" && typeof msg.targetId === "number" && msg.sourceId !== msg.targetId) {
169
+ var source = sm.sessions.get(msg.sourceId);
170
+ var target = sm.sessions.get(msg.targetId);
171
+ if (!source || !target) return true;
172
+ if (usersModule.isMultiUser() && ws._clayUser) {
173
+ if (!usersModule.canAccessSession(ws._clayUser.id, source, { visibility: "public" })) return true;
174
+ if (!usersModule.canAccessSession(ws._clayUser.id, target, { visibility: "public" })) return true;
175
+ }
176
+ sm.reorderBookmarkedSessions(msg.sourceId, msg.targetId, msg.insertBefore !== false);
177
+ }
178
+ return true;
179
+ }
180
+
181
+ if (msg.type === "bulk_delete_sessions") {
182
+ if (!Array.isArray(msg.sessionIds) || msg.sessionIds.length === 0) return true;
183
+ var deletableIds = [];
184
+ for (var di = 0; di < msg.sessionIds.length; di++) {
185
+ var bulkId = msg.sessionIds[di];
186
+ if (typeof bulkId !== "number") continue;
187
+ var bulkTarget = sm.sessions.get(bulkId);
188
+ if (!bulkTarget) continue;
189
+ if (usersModule.isMultiUser() && ws._clayUser) {
190
+ if (!usersModule.canAccessSession(ws._clayUser.id, bulkTarget, { visibility: "public" })) continue;
191
+ }
192
+ deletableIds.push(bulkId);
193
+ }
194
+ if (deletableIds.length > 0) {
195
+ sm.deleteSessionsBulk(deletableIds, ws);
196
+ }
197
+ return true;
198
+ }
199
+
139
200
  if (msg.type === "transfer_project_owner") {
140
201
  // Home directory projects: ownership is permanently locked
141
202
  if (osUsers && osUsers.length > 0 && /^\/home\/[^/]+\//.test(cwd)) {
@@ -243,6 +304,17 @@ function attachSessions(ctx) {
243
304
 
244
305
  if (msg.type === "switch_session") {
245
306
  if (msg.id && sm.sessions.has(msg.id)) {
307
+ // If the target session's vendor doesn't own the currently cached
308
+ // model, clear sm.currentModel so the UI and next query don't leak
309
+ // the previous session's vendor-specific model into this one.
310
+ var switchTargetSess = sm.sessions.get(msg.id);
311
+ if (switchTargetSess && sm.currentModel) {
312
+ var targetVendor = switchTargetSess.vendor || sm.defaultVendor || null;
313
+ var tvModels = (targetVendor && sm.modelsByVendor && sm.modelsByVendor[targetVendor]) || [];
314
+ if (tvModels.length > 0 && tvModels.indexOf(sm.currentModel) === -1) {
315
+ sm.currentModel = "";
316
+ }
317
+ }
246
318
  // Check access in multi-user mode
247
319
  if (usersModule.isMultiUser() && ws._clayUser) {
248
320
  var switchTarget = sm.sessions.get(msg.id);
@@ -747,10 +819,41 @@ function attachSessions(ctx) {
747
819
  if (!pending) return true;
748
820
  delete session.pendingAskUser[toolId];
749
821
  sm.sendAndRecord(session, { type: "ask_user_answered", toolId: toolId, answers: answers });
750
- pending.resolve({
751
- behavior: "allow",
752
- updatedInput: Object.assign({}, pending.input, { answers: answers }),
753
- });
822
+
823
+ if (pending.mode === "mcp") {
824
+ // Stateless MCP path: the tool already returned. Inject the user's
825
+ // answer as a new user message so the conversation continues
826
+ // naturally on the next turn. This matches how the mate would see
827
+ // any other user input.
828
+ var answerText = formatAskUserAnswerAsMessage(pending.input, answers);
829
+ var userMsg = { type: "user_message", text: answerText };
830
+ session.history.push(userMsg);
831
+ sm.appendToSessionFile(session, userMsg);
832
+ sendToSession(session.localId, userMsg);
833
+
834
+ if (!session.isProcessing) {
835
+ session.isProcessing = true;
836
+ onProcessingChanged();
837
+ session.sentToolResults = {};
838
+ sendToSession(session.localId, { type: "status", status: "processing" });
839
+ if (!session.queryInstance && !session.worker) {
840
+ sdk.startQuery(session, answerText, undefined, ensureProjectAccessForSession(session));
841
+ } else {
842
+ sdk.pushMessage(session, answerText);
843
+ }
844
+ } else {
845
+ // Turn is still running; queue for the next turn.
846
+ sdk.pushMessage(session, answerText);
847
+ }
848
+ } else {
849
+ // Claude native AskUserQuestion path (canUseTool). The SDK is
850
+ // synchronously blocked on the permission callback, so we must
851
+ // resolve it with the standard permission shape.
852
+ pending.resolve({
853
+ behavior: "allow",
854
+ updatedInput: Object.assign({}, pending.input, { answers: answers }),
855
+ });
856
+ }
754
857
  return true;
755
858
  }
756
859
 
@@ -863,7 +966,7 @@ function attachSessions(ctx) {
863
966
  newSession.sentToolResults = {};
864
967
  sendToSession(newSession.localId, { type: "status", status: "processing" });
865
968
  newSession.acceptEditsAfterStart = true;
866
- sdk.startQuery(newSession, planPrompt, undefined, getLinuxUserForSession(newSession));
969
+ sdk.startQuery(newSession, planPrompt, undefined, ensureProjectAccessForSession(newSession));
867
970
  } catch (e) {
868
971
  console.error("[project] Error starting plan execution:", e);
869
972
  sendTo(ws, { type: "error", text: "Failed to start plan execution: " + (e.message || e) });
@@ -894,7 +997,7 @@ function attachSessions(ctx) {
894
997
  session.sentToolResults = {};
895
998
  sendToSession(session.localId, { type: "status", status: "processing" });
896
999
  if (!session.queryInstance && !session.worker) {
897
- sdk.startQuery(session, feedback, undefined, getLinuxUserForSession(session));
1000
+ sdk.startQuery(session, feedback, undefined, ensureProjectAccessForSession(session));
898
1001
  } else {
899
1002
  sdk.pushMessage(session, feedback);
900
1003
  }
@@ -13,7 +13,7 @@ var fs = require("fs");
13
13
  * send, sendTo, sendToSession, sendToSessionOthers,
14
14
  * clients, opts,
15
15
  * usersModule, matesModule,
16
- * getSessionForWs, getLinuxUserForSession, getOsUserInfoForWs,
16
+ * getSessionForWs, getLinuxUserForSession, ensureProjectAccessForSession, getOsUserInfoForWs,
17
17
  * hydrateImageRefs, saveImageFile, imagesDir,
18
18
  * onProcessingChanged, onSessionDone,
19
19
  * _loop - { handleLoopMessage: fn(ws, msg) }
@@ -49,6 +49,7 @@ function attachUserMessage(ctx) {
49
49
 
50
50
  var getSessionForWs = ctx.getSessionForWs;
51
51
  var getLinuxUserForSession = ctx.getLinuxUserForSession;
52
+ var ensureProjectAccessForSession = ctx.ensureProjectAccessForSession;
52
53
  var getOsUserInfoForWs = ctx.getOsUserInfoForWs;
53
54
 
54
55
  var hydrateImageRefs = ctx.hydrateImageRefs;
@@ -281,7 +282,7 @@ function attachUserMessage(ctx) {
281
282
  nowSession.isProcessing = true;
282
283
  onProcessingChanged();
283
284
  sendToSession(nowSession.localId, { type: "status", status: "processing" });
284
- sdk.startQuery(nowSession, schedText, null, getLinuxUserForSession(nowSession));
285
+ sdk.startQuery(nowSession, schedText, null, ensureProjectAccessForSession(nowSession));
285
286
  sm.broadcastSessionList();
286
287
  return true;
287
288
  }
@@ -484,7 +485,7 @@ function attachUserMessage(ctx) {
484
485
  // No active query (or worker idle between queries): start a new query
485
486
  session._queryStartTs = Date.now();
486
487
  console.log("[PERF] project.js: startQuery called, localId=" + session.localId + " t=0ms");
487
- sdk.startQuery(session, finalText, msg.images, getLinuxUserForSession(session));
488
+ sdk.startQuery(session, finalText, msg.images, ensureProjectAccessForSession(session));
488
489
  } else {
489
490
  sdk.pushMessage(session, finalText, msg.images);
490
491
  }