clay-server 2.13.0-beta.4 → 2.13.0-beta.6
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/daemon.js +33 -9
- package/lib/dm.js +2 -2
- package/lib/mates.js +133 -37
- package/lib/project.js +15 -9
- package/lib/public/app.js +16 -2
- package/lib/public/modules/tools.js +8 -14
- package/lib/server.js +37 -11
- package/package.json +1 -1
package/lib/daemon.js
CHANGED
|
@@ -936,16 +936,40 @@ for (var i = 0; i < projects.length; i++) {
|
|
|
936
936
|
}
|
|
937
937
|
}
|
|
938
938
|
|
|
939
|
+
// Migrate legacy mates storage before registration
|
|
940
|
+
mates.migrateLegacyMates();
|
|
941
|
+
|
|
939
942
|
// Register existing mates as projects
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
var
|
|
943
|
-
var
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
943
|
+
if (usersModule.isMultiUser()) {
|
|
944
|
+
// Multi-user mode: iterate over all users and load each user's mates
|
|
945
|
+
var allUsers = usersModule.getAllUsers();
|
|
946
|
+
for (var ui = 0; ui < allUsers.length; ui++) {
|
|
947
|
+
var mateCtx = mates.buildMateCtx(allUsers[ui].id);
|
|
948
|
+
var userMates = mates.getAllMates(mateCtx);
|
|
949
|
+
for (var mi = 0; mi < userMates.length; mi++) {
|
|
950
|
+
var m = userMates[mi];
|
|
951
|
+
var mateDir = mates.getMateDir(mateCtx, m.id);
|
|
952
|
+
var mateSlug = "mate-" + m.id;
|
|
953
|
+
var mateName = (m.profile && m.profile.displayName) || m.name || "New Mate";
|
|
954
|
+
if (fs.existsSync(mateDir)) {
|
|
955
|
+
console.log("[daemon] Adding mate project:", mateSlug);
|
|
956
|
+
relay.addProject(mateDir, mateSlug, mateName, null, m.createdBy, null, { isMate: true });
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
} else {
|
|
961
|
+
// Single-user mode: use null ctx
|
|
962
|
+
var mateCtx = mates.buildMateCtx(null);
|
|
963
|
+
var allMates = mates.getAllMates(mateCtx);
|
|
964
|
+
for (var mi = 0; mi < allMates.length; mi++) {
|
|
965
|
+
var m = allMates[mi];
|
|
966
|
+
var mateDir = mates.getMateDir(mateCtx, m.id);
|
|
967
|
+
var mateSlug = "mate-" + m.id;
|
|
968
|
+
var mateName = (m.profile && m.profile.displayName) || m.name || "New Mate";
|
|
969
|
+
if (fs.existsSync(mateDir)) {
|
|
970
|
+
console.log("[daemon] Adding mate project:", mateSlug);
|
|
971
|
+
relay.addProject(mateDir, mateSlug, mateName, null, m.createdBy, null, { isMate: true });
|
|
972
|
+
}
|
|
949
973
|
}
|
|
950
974
|
}
|
|
951
975
|
|
package/lib/dm.js
CHANGED
|
@@ -118,10 +118,10 @@ function getDmList(userId) {
|
|
|
118
118
|
return dms;
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
// Check if a user
|
|
121
|
+
// Check if a user ID looks like a mate (AI persona) by format only
|
|
122
122
|
function isMate(userId) {
|
|
123
123
|
var mates = require("./mates");
|
|
124
|
-
return mates.
|
|
124
|
+
return mates.isMateIdFormat(userId);
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
module.exports = {
|
package/lib/mates.js
CHANGED
|
@@ -3,8 +3,40 @@ var path = require("path");
|
|
|
3
3
|
var crypto = require("crypto");
|
|
4
4
|
var config = require("./config");
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
// --- Path resolution ---
|
|
7
|
+
|
|
8
|
+
function resolveMatesRoot(ctx) {
|
|
9
|
+
// OS-users mode: per-linuxUser home directory
|
|
10
|
+
if (ctx && ctx.linuxUser) {
|
|
11
|
+
return path.join("/home", ctx.linuxUser, ".clay", "mates");
|
|
12
|
+
}
|
|
13
|
+
// Multi-user mode: per-userId subdirectory
|
|
14
|
+
if (ctx && ctx.multiUser && ctx.userId) {
|
|
15
|
+
return path.join(config.CONFIG_DIR, "mates", ctx.userId);
|
|
16
|
+
}
|
|
17
|
+
// Single-user mode: flat directory
|
|
18
|
+
return path.join(config.CONFIG_DIR, "mates");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function buildMateCtx(userId) {
|
|
22
|
+
if (!userId) return { userId: null, multiUser: false, linuxUser: null };
|
|
23
|
+
// Lazy require to avoid circular dependency
|
|
24
|
+
var users = require("./users");
|
|
25
|
+
var multiUser = users.isMultiUser();
|
|
26
|
+
var linuxUser = null;
|
|
27
|
+
if (multiUser && userId) {
|
|
28
|
+
var user = users.findUserById(userId);
|
|
29
|
+
if (user && user.linuxUser) {
|
|
30
|
+
linuxUser = user.linuxUser;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return { userId: userId, multiUser: multiUser, linuxUser: linuxUser };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function isMateIdFormat(id) {
|
|
37
|
+
if (!id) return false;
|
|
38
|
+
return typeof id === "string" && id.indexOf("mate_") === 0;
|
|
39
|
+
}
|
|
8
40
|
|
|
9
41
|
// --- Default data ---
|
|
10
42
|
|
|
@@ -14,9 +46,13 @@ function defaultData() {
|
|
|
14
46
|
|
|
15
47
|
// --- Load / Save ---
|
|
16
48
|
|
|
17
|
-
function
|
|
49
|
+
function matesFilePath(ctx) {
|
|
50
|
+
return path.join(resolveMatesRoot(ctx), "mates.json");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function loadMates(ctx) {
|
|
18
54
|
try {
|
|
19
|
-
var raw = fs.readFileSync(
|
|
55
|
+
var raw = fs.readFileSync(matesFilePath(ctx), "utf8");
|
|
20
56
|
var data = JSON.parse(raw);
|
|
21
57
|
if (!data.mates) data.mates = [];
|
|
22
58
|
return data;
|
|
@@ -25,11 +61,12 @@ function loadMates() {
|
|
|
25
61
|
}
|
|
26
62
|
}
|
|
27
63
|
|
|
28
|
-
function saveMates(data) {
|
|
29
|
-
|
|
30
|
-
|
|
64
|
+
function saveMates(ctx, data) {
|
|
65
|
+
var filePath = matesFilePath(ctx);
|
|
66
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
67
|
+
var tmpPath = filePath + ".tmp";
|
|
31
68
|
fs.writeFileSync(tmpPath, JSON.stringify(data, null, 2));
|
|
32
|
-
fs.renameSync(tmpPath,
|
|
69
|
+
fs.renameSync(tmpPath, filePath);
|
|
33
70
|
}
|
|
34
71
|
|
|
35
72
|
// --- CRUD ---
|
|
@@ -38,9 +75,10 @@ function generateMateId() {
|
|
|
38
75
|
return "mate_" + crypto.randomUUID();
|
|
39
76
|
}
|
|
40
77
|
|
|
41
|
-
function createMate(
|
|
42
|
-
var data = loadMates();
|
|
78
|
+
function createMate(ctx, seedData) {
|
|
79
|
+
var data = loadMates(ctx);
|
|
43
80
|
var id = generateMateId();
|
|
81
|
+
var userId = ctx ? ctx.userId : null;
|
|
44
82
|
|
|
45
83
|
// Pick a random avatar color from a pleasant palette
|
|
46
84
|
var colors = ["#6c5ce7", "#00b894", "#e17055", "#0984e3", "#fdcb6e", "#e84393", "#00cec9", "#ff7675"];
|
|
@@ -63,10 +101,10 @@ function createMate(seedData, userId) {
|
|
|
63
101
|
};
|
|
64
102
|
|
|
65
103
|
data.mates.push(mate);
|
|
66
|
-
saveMates(data);
|
|
104
|
+
saveMates(ctx, data);
|
|
67
105
|
|
|
68
106
|
// Create the mate's identity directory
|
|
69
|
-
var mateDir =
|
|
107
|
+
var mateDir = getMateDir(ctx, id);
|
|
70
108
|
fs.mkdirSync(mateDir, { recursive: true });
|
|
71
109
|
|
|
72
110
|
// Write initial mate.yaml
|
|
@@ -98,40 +136,40 @@ function createMate(seedData, userId) {
|
|
|
98
136
|
return mate;
|
|
99
137
|
}
|
|
100
138
|
|
|
101
|
-
function getMate(id) {
|
|
102
|
-
var data = loadMates();
|
|
139
|
+
function getMate(ctx, id) {
|
|
140
|
+
var data = loadMates(ctx);
|
|
103
141
|
for (var i = 0; i < data.mates.length; i++) {
|
|
104
142
|
if (data.mates[i].id === id) return data.mates[i];
|
|
105
143
|
}
|
|
106
144
|
return null;
|
|
107
145
|
}
|
|
108
146
|
|
|
109
|
-
function updateMate(id, updates) {
|
|
110
|
-
var data = loadMates();
|
|
147
|
+
function updateMate(ctx, id, updates) {
|
|
148
|
+
var data = loadMates(ctx);
|
|
111
149
|
for (var i = 0; i < data.mates.length; i++) {
|
|
112
150
|
if (data.mates[i].id === id) {
|
|
113
151
|
var keys = Object.keys(updates);
|
|
114
152
|
for (var j = 0; j < keys.length; j++) {
|
|
115
153
|
data.mates[i][keys[j]] = updates[keys[j]];
|
|
116
154
|
}
|
|
117
|
-
saveMates(data);
|
|
155
|
+
saveMates(ctx, data);
|
|
118
156
|
return data.mates[i];
|
|
119
157
|
}
|
|
120
158
|
}
|
|
121
159
|
return null;
|
|
122
160
|
}
|
|
123
161
|
|
|
124
|
-
function deleteMate(id) {
|
|
125
|
-
var data = loadMates();
|
|
162
|
+
function deleteMate(ctx, id) {
|
|
163
|
+
var data = loadMates(ctx);
|
|
126
164
|
var before = data.mates.length;
|
|
127
165
|
data.mates = data.mates.filter(function (m) {
|
|
128
166
|
return m.id !== id;
|
|
129
167
|
});
|
|
130
168
|
if (data.mates.length === before) return { error: "Mate not found" };
|
|
131
|
-
saveMates(data);
|
|
169
|
+
saveMates(ctx, data);
|
|
132
170
|
|
|
133
171
|
// Remove mate directory
|
|
134
|
-
var mateDir =
|
|
172
|
+
var mateDir = getMateDir(ctx, id);
|
|
135
173
|
try {
|
|
136
174
|
fs.rmSync(mateDir, { recursive: true, force: true });
|
|
137
175
|
} catch (e) {
|
|
@@ -141,29 +179,86 @@ function deleteMate(id) {
|
|
|
141
179
|
return { ok: true };
|
|
142
180
|
}
|
|
143
181
|
|
|
144
|
-
function getAllMates() {
|
|
145
|
-
var data = loadMates();
|
|
182
|
+
function getAllMates(ctx) {
|
|
183
|
+
var data = loadMates(ctx);
|
|
146
184
|
return data.mates;
|
|
147
185
|
}
|
|
148
186
|
|
|
149
|
-
function
|
|
150
|
-
var data = loadMates();
|
|
151
|
-
return data.mates.filter(function (m) {
|
|
152
|
-
return m.createdBy === userId;
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
function isMate(id) {
|
|
187
|
+
function isMate(ctx, id) {
|
|
157
188
|
if (!id) return false;
|
|
158
189
|
if (typeof id === "string" && id.indexOf("mate_") === 0) {
|
|
159
190
|
// Double check it exists in registry
|
|
160
|
-
return !!getMate(id);
|
|
191
|
+
return !!getMate(ctx, id);
|
|
161
192
|
}
|
|
162
193
|
return false;
|
|
163
194
|
}
|
|
164
195
|
|
|
165
|
-
function getMateDir(id) {
|
|
166
|
-
return path.join(
|
|
196
|
+
function getMateDir(ctx, id) {
|
|
197
|
+
return path.join(resolveMatesRoot(ctx), id);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// --- Migration ---
|
|
201
|
+
|
|
202
|
+
function migrateLegacyMates() {
|
|
203
|
+
var legacyFile = path.join(config.CONFIG_DIR, "mates.json");
|
|
204
|
+
if (!fs.existsSync(legacyFile)) return;
|
|
205
|
+
|
|
206
|
+
// Check if already migrated
|
|
207
|
+
var migratedMarker = legacyFile + ".migrated";
|
|
208
|
+
if (fs.existsSync(migratedMarker)) return;
|
|
209
|
+
|
|
210
|
+
try {
|
|
211
|
+
var raw = fs.readFileSync(legacyFile, "utf8");
|
|
212
|
+
var data = JSON.parse(raw);
|
|
213
|
+
if (!data.mates || data.mates.length === 0) {
|
|
214
|
+
// Nothing to migrate, just mark as done
|
|
215
|
+
fs.renameSync(legacyFile, migratedMarker);
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Group mates by createdBy
|
|
220
|
+
var byUser = {};
|
|
221
|
+
for (var i = 0; i < data.mates.length; i++) {
|
|
222
|
+
var m = data.mates[i];
|
|
223
|
+
var key = m.createdBy || "__null__";
|
|
224
|
+
if (!byUser[key]) byUser[key] = [];
|
|
225
|
+
byUser[key].push(m);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Write each user's mates to their own storage path
|
|
229
|
+
var keys = Object.keys(byUser);
|
|
230
|
+
for (var k = 0; k < keys.length; k++) {
|
|
231
|
+
var userId = keys[k] === "__null__" ? null : keys[k];
|
|
232
|
+
var ctx = buildMateCtx(userId);
|
|
233
|
+
var userData = { mates: byUser[keys[k]] };
|
|
234
|
+
saveMates(ctx, userData);
|
|
235
|
+
|
|
236
|
+
// Move mate identity directories to new location
|
|
237
|
+
var legacyMatesDir = path.join(config.CONFIG_DIR, "mates");
|
|
238
|
+
var newRoot = resolveMatesRoot(ctx);
|
|
239
|
+
for (var mi = 0; mi < byUser[keys[k]].length; mi++) {
|
|
240
|
+
var mateId = byUser[keys[k]][mi].id;
|
|
241
|
+
var oldDir = path.join(legacyMatesDir, mateId);
|
|
242
|
+
var newDir = path.join(newRoot, mateId);
|
|
243
|
+
if (fs.existsSync(oldDir) && oldDir !== newDir) {
|
|
244
|
+
fs.mkdirSync(path.dirname(newDir), { recursive: true });
|
|
245
|
+
try {
|
|
246
|
+
fs.renameSync(oldDir, newDir);
|
|
247
|
+
} catch (e) {
|
|
248
|
+
// Cross-device or other issue, copy instead
|
|
249
|
+
fs.cpSync(oldDir, newDir, { recursive: true });
|
|
250
|
+
fs.rmSync(oldDir, { recursive: true, force: true });
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Mark legacy file as migrated
|
|
257
|
+
fs.renameSync(legacyFile, migratedMarker);
|
|
258
|
+
console.log("[mates] Migrated legacy mates.json to per-user storage");
|
|
259
|
+
} catch (e) {
|
|
260
|
+
console.error("[mates] Legacy migration failed:", e.message);
|
|
261
|
+
}
|
|
167
262
|
}
|
|
168
263
|
|
|
169
264
|
// Format seed data as a human-readable context string
|
|
@@ -206,8 +301,9 @@ function formatSeedContext(seedData) {
|
|
|
206
301
|
}
|
|
207
302
|
|
|
208
303
|
module.exports = {
|
|
209
|
-
|
|
210
|
-
|
|
304
|
+
resolveMatesRoot: resolveMatesRoot,
|
|
305
|
+
buildMateCtx: buildMateCtx,
|
|
306
|
+
isMateIdFormat: isMateIdFormat,
|
|
211
307
|
loadMates: loadMates,
|
|
212
308
|
saveMates: saveMates,
|
|
213
309
|
createMate: createMate,
|
|
@@ -215,8 +311,8 @@ module.exports = {
|
|
|
215
311
|
updateMate: updateMate,
|
|
216
312
|
deleteMate: deleteMate,
|
|
217
313
|
getAllMates: getAllMates,
|
|
218
|
-
getMatesByUser: getMatesByUser,
|
|
219
314
|
isMate: isMate,
|
|
220
315
|
getMateDir: getMateDir,
|
|
316
|
+
migrateLegacyMates: migrateLegacyMates,
|
|
221
317
|
formatSeedContext: formatSeedContext,
|
|
222
318
|
};
|
package/lib/project.js
CHANGED
|
@@ -626,13 +626,12 @@ function createProjectContext(opts) {
|
|
|
626
626
|
console.log("[loop-registry] Schedule triggered: " + record.name + " → linked task " + loopId);
|
|
627
627
|
}
|
|
628
628
|
|
|
629
|
-
// Verify the loop directory and
|
|
629
|
+
// Verify the loop directory and PROMPT.md exist
|
|
630
630
|
var recDir = path.join(cwd, ".claude", "loops", loopId);
|
|
631
631
|
try {
|
|
632
632
|
fs.accessSync(path.join(recDir, "PROMPT.md"));
|
|
633
|
-
fs.accessSync(path.join(recDir, "JUDGE.md"));
|
|
634
633
|
} catch (e) {
|
|
635
|
-
console.error("[loop-registry]
|
|
634
|
+
console.error("[loop-registry] PROMPT.md missing for " + loopId);
|
|
636
635
|
return;
|
|
637
636
|
}
|
|
638
637
|
// Set the loopId and start
|
|
@@ -675,8 +674,7 @@ function createProjectContext(opts) {
|
|
|
675
674
|
try {
|
|
676
675
|
judgeText = fs.readFileSync(judgePath, "utf8");
|
|
677
676
|
} catch (e) {
|
|
678
|
-
|
|
679
|
-
return;
|
|
677
|
+
judgeText = null;
|
|
680
678
|
}
|
|
681
679
|
|
|
682
680
|
var baseCommit;
|
|
@@ -700,7 +698,7 @@ function createProjectContext(opts) {
|
|
|
700
698
|
loopState.promptText = promptText;
|
|
701
699
|
loopState.judgeText = judgeText;
|
|
702
700
|
loopState.iteration = 0;
|
|
703
|
-
loopState.maxIterations = loopConfig.maxIterations || loopOpts.maxIterations || 20;
|
|
701
|
+
loopState.maxIterations = judgeText ? (loopConfig.maxIterations || loopOpts.maxIterations || 20) : 1;
|
|
704
702
|
loopState.baseCommit = baseCommit;
|
|
705
703
|
loopState.currentSessionId = null;
|
|
706
704
|
loopState.judgeSessionId = null;
|
|
@@ -778,7 +776,11 @@ function createProjectContext(opts) {
|
|
|
778
776
|
setTimeout(function() { runNextIteration(); }, 2000);
|
|
779
777
|
return;
|
|
780
778
|
}
|
|
781
|
-
|
|
779
|
+
if (loopState.judgeText) {
|
|
780
|
+
runJudge();
|
|
781
|
+
} else {
|
|
782
|
+
finishLoop("pass");
|
|
783
|
+
}
|
|
782
784
|
};
|
|
783
785
|
|
|
784
786
|
// Watchdog: if onQueryComplete hasn't fired after 10 minutes, force error and retry
|
|
@@ -2975,6 +2977,11 @@ function createProjectContext(opts) {
|
|
|
2975
2977
|
var tmpJudge = judgePath + ".tmp";
|
|
2976
2978
|
fs.writeFileSync(tmpJudge, wData.judgeText);
|
|
2977
2979
|
fs.renameSync(tmpJudge, judgePath);
|
|
2980
|
+
} else {
|
|
2981
|
+
// No judge: force single iteration
|
|
2982
|
+
var singleJson = loopJsonPath + ".tmp2";
|
|
2983
|
+
fs.writeFileSync(singleJson, JSON.stringify({ maxIterations: 1 }, null, 2));
|
|
2984
|
+
fs.renameSync(singleJson, loopJsonPath);
|
|
2978
2985
|
}
|
|
2979
2986
|
|
|
2980
2987
|
// Go straight to approval (no crafting needed)
|
|
@@ -3190,9 +3197,8 @@ function createProjectContext(opts) {
|
|
|
3190
3197
|
var rerunDir = path.join(cwd, ".claude", "loops", rerunRec.id);
|
|
3191
3198
|
try {
|
|
3192
3199
|
fs.accessSync(path.join(rerunDir, "PROMPT.md"));
|
|
3193
|
-
fs.accessSync(path.join(rerunDir, "JUDGE.md"));
|
|
3194
3200
|
} catch (e) {
|
|
3195
|
-
sendTo(ws, { type: "loop_registry_error", text: "
|
|
3201
|
+
sendTo(ws, { type: "loop_registry_error", text: "PROMPT.md missing for " + rerunRec.id });
|
|
3196
3202
|
return;
|
|
3197
3203
|
}
|
|
3198
3204
|
loopState.loopId = rerunRec.id;
|
package/lib/public/app.js
CHANGED
|
@@ -859,6 +859,10 @@ import { initCommandPalette, handlePaletteSessionSwitch, setPaletteVersion } fro
|
|
|
859
859
|
mateWs = null;
|
|
860
860
|
}
|
|
861
861
|
mateProjectSlug = null;
|
|
862
|
+
// If main WS was disconnected while in mate DM, reconnect now
|
|
863
|
+
if (ws && ws.readyState !== 1) {
|
|
864
|
+
connect();
|
|
865
|
+
}
|
|
862
866
|
}
|
|
863
867
|
|
|
864
868
|
function appendDmMessage(msg) {
|
|
@@ -3122,9 +3126,9 @@ import { initCommandPalette, handlePaletteSessionSwitch, setPaletteVersion } fro
|
|
|
3122
3126
|
ws = new WebSocket(protocol + "//" + location.host + wsPath);
|
|
3123
3127
|
|
|
3124
3128
|
|
|
3125
|
-
// If not connected within 3s, force retry
|
|
3129
|
+
// If not connected within 3s, force retry (skip if stashed during mate DM)
|
|
3126
3130
|
connectTimeoutId = setTimeout(function () {
|
|
3127
|
-
if (!connected) {
|
|
3131
|
+
if (!connected && !savedMainWs) {
|
|
3128
3132
|
ws.onclose = null;
|
|
3129
3133
|
ws.onerror = null;
|
|
3130
3134
|
ws.close();
|
|
@@ -3183,6 +3187,9 @@ import { initCommandPalette, handlePaletteSessionSwitch, setPaletteVersion } fro
|
|
|
3183
3187
|
};
|
|
3184
3188
|
|
|
3185
3189
|
ws.onclose = function (e) {
|
|
3190
|
+
// If this WS is stashed while in mate DM, ignore close events
|
|
3191
|
+
if (savedMainWs === this) return;
|
|
3192
|
+
|
|
3186
3193
|
if (connectTimeoutId) { clearTimeout(connectTimeoutId); connectTimeoutId = null; }
|
|
3187
3194
|
setStatus("disconnected");
|
|
3188
3195
|
processing = false;
|
|
@@ -3208,9 +3215,14 @@ import { initCommandPalette, handlePaletteSessionSwitch, setPaletteVersion } fro
|
|
|
3208
3215
|
};
|
|
3209
3216
|
|
|
3210
3217
|
ws.onerror = function () {
|
|
3218
|
+
// If this WS is stashed while in mate DM, ignore error events
|
|
3219
|
+
if (savedMainWs === this) return;
|
|
3211
3220
|
};
|
|
3212
3221
|
|
|
3213
3222
|
ws.onmessage = function (event) {
|
|
3223
|
+
// If this WS is stashed while in mate DM, ignore its messages
|
|
3224
|
+
if (savedMainWs === this) return;
|
|
3225
|
+
|
|
3214
3226
|
// Backup: if we're receiving messages, we're connected
|
|
3215
3227
|
if (!connected) {
|
|
3216
3228
|
setStatus("connected");
|
|
@@ -4372,6 +4384,8 @@ import { initCommandPalette, handlePaletteSessionSwitch, setPaletteVersion } fro
|
|
|
4372
4384
|
|
|
4373
4385
|
function scheduleReconnect() {
|
|
4374
4386
|
if (reconnectTimer) return;
|
|
4387
|
+
// Don't reconnect main WS while in mate DM
|
|
4388
|
+
if (savedMainWs) return;
|
|
4375
4389
|
reconnectTimer = setTimeout(function () {
|
|
4376
4390
|
reconnectTimer = null;
|
|
4377
4391
|
// Check if auth is still valid before reconnecting
|
|
@@ -242,9 +242,6 @@ export function renderAskUserQuestion(toolId, input) {
|
|
|
242
242
|
answers[qIdx] = opt.label;
|
|
243
243
|
var otherInput = qDiv.querySelector(".ask-user-other input");
|
|
244
244
|
if (otherInput) otherInput.value = "";
|
|
245
|
-
if (questions.length === 1) {
|
|
246
|
-
submitAskUserAnswer(container, toolId, questions, answers, multiSelections);
|
|
247
|
-
}
|
|
248
245
|
}
|
|
249
246
|
});
|
|
250
247
|
|
|
@@ -280,17 +277,14 @@ export function renderAskUserQuestion(toolId, input) {
|
|
|
280
277
|
container.appendChild(qDiv);
|
|
281
278
|
});
|
|
282
279
|
|
|
283
|
-
// Submit button: show
|
|
284
|
-
var
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
});
|
|
292
|
-
container.appendChild(submitBtn);
|
|
293
|
-
}
|
|
280
|
+
// Submit button: always show
|
|
281
|
+
var submitBtn = document.createElement("button");
|
|
282
|
+
submitBtn.className = "ask-user-submit";
|
|
283
|
+
submitBtn.textContent = "Submit";
|
|
284
|
+
submitBtn.addEventListener("click", function () {
|
|
285
|
+
submitAskUserAnswer(container, toolId, questions, answers, multiSelections);
|
|
286
|
+
});
|
|
287
|
+
container.appendChild(submitBtn);
|
|
294
288
|
|
|
295
289
|
// Skip button
|
|
296
290
|
var skipBtn = document.createElement("button");
|
package/lib/server.js
CHANGED
|
@@ -2614,7 +2614,8 @@ function createServer(opts) {
|
|
|
2614
2614
|
}
|
|
2615
2615
|
}
|
|
2616
2616
|
// Include mates in the list
|
|
2617
|
-
var
|
|
2617
|
+
var mateCtx = mates.buildMateCtx(userId);
|
|
2618
|
+
var mateList = mates.getAllMates(mateCtx);
|
|
2618
2619
|
ws.send(JSON.stringify({ type: "dm_list", dms: dmList, mates: mateList }));
|
|
2619
2620
|
return;
|
|
2620
2621
|
}
|
|
@@ -2623,16 +2624,25 @@ function createServer(opts) {
|
|
|
2623
2624
|
if (!msg.targetUserId) return;
|
|
2624
2625
|
|
|
2625
2626
|
// Check if target is a mate
|
|
2626
|
-
|
|
2627
|
-
|
|
2627
|
+
var mateCtx2 = mates.buildMateCtx(userId);
|
|
2628
|
+
if (mates.isMate(mateCtx2, msg.targetUserId)) {
|
|
2629
|
+
var mate = mates.getMate(mateCtx2, msg.targetUserId);
|
|
2628
2630
|
if (!mate) return;
|
|
2631
|
+
// Ensure mate project is registered (survives server restarts)
|
|
2632
|
+
var mateSlug2 = "mate-" + mate.id;
|
|
2633
|
+
if (!projects.has(mateSlug2)) {
|
|
2634
|
+
var mateDir2 = mates.getMateDir(mateCtx2, mate.id);
|
|
2635
|
+
fs.mkdirSync(mateDir2, { recursive: true });
|
|
2636
|
+
var mateName2 = (mate.profile && mate.profile.displayName) || mate.name || "New Mate";
|
|
2637
|
+
addProject(mateDir2, mateSlug2, mateName2, null, mate.createdBy || userId, null, { isMate: true });
|
|
2638
|
+
}
|
|
2629
2639
|
var mp = mate.profile || {};
|
|
2630
2640
|
ws.send(JSON.stringify({
|
|
2631
2641
|
type: "dm_history",
|
|
2632
2642
|
dmKey: "mate:" + mate.id,
|
|
2633
2643
|
messages: dm.loadHistory("mate:" + mate.id),
|
|
2634
2644
|
isMate: true,
|
|
2635
|
-
projectSlug:
|
|
2645
|
+
projectSlug: mateSlug2,
|
|
2636
2646
|
targetUser: {
|
|
2637
2647
|
id: mate.id,
|
|
2638
2648
|
displayName: mp.displayName || mate.name || "New Mate",
|
|
@@ -2690,8 +2700,9 @@ function createServer(opts) {
|
|
|
2690
2700
|
var parts = msg.dmKey.split(":");
|
|
2691
2701
|
|
|
2692
2702
|
// Handle mate DM: dmKey is "mate:mate_xxx"
|
|
2693
|
-
|
|
2694
|
-
|
|
2703
|
+
var mateCtx3 = mates.buildMateCtx(userId);
|
|
2704
|
+
if (parts[0] === "mate" && mates.isMate(mateCtx3, parts[1])) {
|
|
2705
|
+
var mate = mates.getMate(mateCtx3, parts[1]);
|
|
2695
2706
|
if (!mate) return;
|
|
2696
2707
|
// Verify sender is the mate's creator
|
|
2697
2708
|
if (mate.createdBy !== userId) return;
|
|
@@ -2758,9 +2769,10 @@ function createServer(opts) {
|
|
|
2758
2769
|
if (msg.type === "mate_create") {
|
|
2759
2770
|
if (!msg.seedData) return;
|
|
2760
2771
|
try {
|
|
2761
|
-
var
|
|
2772
|
+
var mateCtx4 = mates.buildMateCtx(userId);
|
|
2773
|
+
var mate = mates.createMate(mateCtx4, msg.seedData);
|
|
2762
2774
|
// Register mate as a project
|
|
2763
|
-
var mateDir =
|
|
2775
|
+
var mateDir = mates.getMateDir(mateCtx4, mate.id);
|
|
2764
2776
|
var mateSlug = "mate-" + mate.id;
|
|
2765
2777
|
var mateName = (mate.profile && mate.profile.displayName) || mate.name || "New Mate";
|
|
2766
2778
|
addProject(mateDir, mateSlug, mateName, null, mate.createdBy, null, { isMate: true });
|
|
@@ -2772,14 +2784,27 @@ function createServer(opts) {
|
|
|
2772
2784
|
}
|
|
2773
2785
|
|
|
2774
2786
|
if (msg.type === "mate_list") {
|
|
2775
|
-
var
|
|
2787
|
+
var mateCtx5 = mates.buildMateCtx(userId);
|
|
2788
|
+
var mateList = mates.getAllMates(mateCtx5);
|
|
2789
|
+
// Ensure all mate projects are registered (survives server restarts)
|
|
2790
|
+
for (var mi = 0; mi < mateList.length; mi++) {
|
|
2791
|
+
var m = mateList[mi];
|
|
2792
|
+
var mSlug = "mate-" + m.id;
|
|
2793
|
+
if (!projects.has(mSlug)) {
|
|
2794
|
+
var mDir = mates.getMateDir(mateCtx5, m.id);
|
|
2795
|
+
fs.mkdirSync(mDir, { recursive: true });
|
|
2796
|
+
var mName = (m.profile && m.profile.displayName) || m.name || "New Mate";
|
|
2797
|
+
addProject(mDir, mSlug, mName, null, m.createdBy || userId, null, { isMate: true });
|
|
2798
|
+
}
|
|
2799
|
+
}
|
|
2776
2800
|
ws.send(JSON.stringify({ type: "mate_list", mates: mateList }));
|
|
2777
2801
|
return;
|
|
2778
2802
|
}
|
|
2779
2803
|
|
|
2780
2804
|
if (msg.type === "mate_delete") {
|
|
2781
2805
|
if (!msg.mateId) return;
|
|
2782
|
-
var
|
|
2806
|
+
var mateCtx6 = mates.buildMateCtx(userId);
|
|
2807
|
+
var result = mates.deleteMate(mateCtx6, msg.mateId);
|
|
2783
2808
|
if (result.error) {
|
|
2784
2809
|
ws.send(JSON.stringify({ type: "mate_error", error: result.error }));
|
|
2785
2810
|
} else {
|
|
@@ -2799,7 +2824,8 @@ function createServer(opts) {
|
|
|
2799
2824
|
|
|
2800
2825
|
if (msg.type === "mate_update") {
|
|
2801
2826
|
if (!msg.mateId || !msg.updates) return;
|
|
2802
|
-
var
|
|
2827
|
+
var mateCtx7 = mates.buildMateCtx(userId);
|
|
2828
|
+
var updated = mates.updateMate(mateCtx7, msg.mateId, msg.updates);
|
|
2803
2829
|
if (updated) {
|
|
2804
2830
|
ws.send(JSON.stringify({ type: "mate_updated", mate: updated }));
|
|
2805
2831
|
// Broadcast update
|