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.
- package/lib/ask-user-mcp-server.js +120 -0
- package/lib/config.js +9 -13
- package/lib/daemon.js +116 -55
- package/lib/mate-datastore.js +359 -0
- package/lib/mates.js +2 -2
- package/lib/os-users.js +70 -37
- package/lib/project-connection.js +16 -9
- package/lib/project-http.js +3 -4
- package/lib/project-image.js +3 -2
- package/lib/project-mate-datastore.js +232 -0
- package/lib/project-sessions.js +110 -7
- package/lib/project-user-message.js +4 -3
- package/lib/project.js +126 -10
- package/lib/public/app.js +2 -0
- package/lib/public/css/mates.css +228 -11
- package/lib/public/css/messages.css +23 -0
- package/lib/public/css/mobile-nav.css +0 -14
- package/lib/public/css/notifications-center.css +80 -0
- package/lib/public/css/sidebar.css +326 -101
- package/lib/public/index.html +24 -29
- package/lib/public/modules/app-dm.js +0 -2
- package/lib/public/modules/app-messages.js +23 -0
- package/lib/public/modules/app-rendering.js +0 -2
- package/lib/public/modules/diff.js +21 -7
- package/lib/public/modules/mate-datastore-ui.js +280 -0
- package/lib/public/modules/mate-sidebar.js +3 -9
- package/lib/public/modules/mate-wizard.js +15 -15
- package/lib/public/modules/sidebar-mobile.js +10 -20
- package/lib/public/modules/sidebar-sessions.js +490 -113
- package/lib/public/modules/sidebar.js +8 -6
- package/lib/public/modules/tools.js +115 -18
- package/lib/public/sw.js +1 -1
- package/lib/sdk-bridge.js +56 -41
- package/lib/sdk-message-processor.js +21 -4
- package/lib/server.js +28 -72
- package/lib/sessions.js +157 -20
- package/lib/updater.js +2 -2
- package/lib/users.js +2 -2
- package/lib/ws-schema.js +16 -0
- package/lib/yoke/adapters/claude-worker.js +114 -2
- package/lib/yoke/adapters/claude.js +56 -5
- package/lib/yoke/adapters/codex.js +350 -58
- package/lib/yoke/index.js +93 -48
- package/lib/yoke/instructions.js +0 -1
- package/lib/yoke/mcp-bridge-server.js +14 -6
- package/package.json +1 -2
- package/lib/yoke/adapters/gemini.js +0 -709
package/lib/project-http.js
CHANGED
|
@@ -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
|
-
|
|
167
|
-
|
|
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 =
|
|
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
|
});
|
package/lib/project-image.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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 };
|
package/lib/project-sessions.js
CHANGED
|
@@ -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
|
-
|
|
751
|
-
|
|
752
|
-
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
488
|
+
sdk.startQuery(session, finalText, msg.images, ensureProjectAccessForSession(session));
|
|
488
489
|
} else {
|
|
489
490
|
sdk.pushMessage(session, finalText, msg.images);
|
|
490
491
|
}
|