clay-server 2.19.0 → 2.20.0-beta.1
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/README.md +51 -91
- package/bin/cli.js +49 -14
- package/lib/cli-sessions.js +3 -3
- package/lib/config.js +30 -4
- package/lib/daemon.js +28 -5
- package/lib/mates.js +61 -7
- package/lib/notes.js +20 -0
- package/lib/os-users.js +71 -2
- package/lib/project.js +850 -184
- package/lib/public/app.js +160 -16
- package/lib/public/css/mates.css +316 -2
- package/lib/public/css/mention.css +23 -0
- package/lib/public/css/mobile-nav.css +198 -0
- package/lib/public/css/overlays.css +23 -0
- package/lib/public/css/rewind.css +32 -0
- package/lib/public/css/title-bar.css +3 -0
- package/lib/public/css/user-settings.css +2 -2
- package/lib/public/index.html +64 -14
- package/lib/public/modules/command-palette.js +44 -4
- package/lib/public/modules/filebrowser.js +2 -0
- package/lib/public/modules/input.js +11 -3
- package/lib/public/modules/mate-knowledge.js +2 -0
- package/lib/public/modules/mate-memory.js +353 -0
- package/lib/public/modules/mention.js +77 -2
- package/lib/public/modules/notifications.js +0 -8
- package/lib/public/modules/server-settings.js +11 -4
- package/lib/public/modules/sidebar.js +284 -6
- package/lib/public/modules/theme.js +10 -12
- package/lib/public/modules/tools.js +84 -12
- package/lib/public/modules/user-settings.js +45 -12
- package/lib/sdk-bridge.js +114 -35
- package/lib/server.js +84 -6
- package/lib/sessions.js +4 -4
- package/lib/users.js +26 -0
- package/package.json +1 -1
package/lib/sdk-bridge.js
CHANGED
|
@@ -59,14 +59,14 @@ function createSDKBridge(opts) {
|
|
|
59
59
|
var isMate = opts.isMate || (slug.indexOf("mate-") === 0);
|
|
60
60
|
var dangerouslySkipPermissions = opts.dangerouslySkipPermissions || false;
|
|
61
61
|
var onProcessingChanged = opts.onProcessingChanged || function () {};
|
|
62
|
-
|
|
62
|
+
var onTurnDone = opts.onTurnDone || null;
|
|
63
63
|
|
|
64
64
|
// --- Skill discovery helpers ---
|
|
65
65
|
|
|
66
66
|
function discoverSkillDirs() {
|
|
67
67
|
var skills = {};
|
|
68
68
|
var dirs = [
|
|
69
|
-
path.join(
|
|
69
|
+
path.join(require("./config").REAL_HOME, ".claude", "skills"),
|
|
70
70
|
path.join(cwd, ".claude", "skills"),
|
|
71
71
|
];
|
|
72
72
|
for (var d = 0; d < dirs.length; d++) {
|
|
@@ -387,9 +387,13 @@ function createSDKBridge(opts) {
|
|
|
387
387
|
});
|
|
388
388
|
}
|
|
389
389
|
// Reset for next turn in the same query
|
|
390
|
+
var donePreview = session.responsePreview || "";
|
|
390
391
|
session.responsePreview = "";
|
|
391
392
|
session.streamedText = false;
|
|
392
393
|
sm.broadcastSessionList();
|
|
394
|
+
if (onTurnDone) {
|
|
395
|
+
try { onTurnDone(session, donePreview); } catch (e) {}
|
|
396
|
+
}
|
|
393
397
|
|
|
394
398
|
} else if (parsed.type === "system" && parsed.subtype === "status") {
|
|
395
399
|
if (parsed.status === "compacting") {
|
|
@@ -459,6 +463,18 @@ function createSDKBridge(opts) {
|
|
|
459
463
|
|
|
460
464
|
} else if (parsed.type === "rate_limit_event" && parsed.rate_limit_info) {
|
|
461
465
|
var info = parsed.rate_limit_info;
|
|
466
|
+
|
|
467
|
+
// Broadcast reset time for top-bar usage link
|
|
468
|
+
if (info.rateLimitType && info.resetsAt) {
|
|
469
|
+
send({
|
|
470
|
+
type: "rate_limit_usage",
|
|
471
|
+
rateLimitType: info.rateLimitType,
|
|
472
|
+
resetsAt: info.resetsAt * 1000,
|
|
473
|
+
status: info.status,
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// Warning/rejection handling (existing behavior)
|
|
462
478
|
if (info.status === "allowed_warning" || info.status === "rejected") {
|
|
463
479
|
sendAndRecord(session, {
|
|
464
480
|
type: "rate_limit",
|
|
@@ -832,14 +848,12 @@ function createSDKBridge(opts) {
|
|
|
832
848
|
}
|
|
833
849
|
|
|
834
850
|
if (dangerouslySkipPermissions) {
|
|
835
|
-
queryOptions.permissionMode = "bypassPermissions";
|
|
836
851
|
queryOptions.allowDangerouslySkipPermissions = true;
|
|
837
|
-
}
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
}
|
|
852
|
+
}
|
|
853
|
+
var modeToApply = session.acceptEditsAfterStart ? "acceptEdits" : sm.currentPermissionMode;
|
|
854
|
+
if (session.acceptEditsAfterStart) delete session.acceptEditsAfterStart;
|
|
855
|
+
if (modeToApply && modeToApply !== "default") {
|
|
856
|
+
queryOptions.permissionMode = modeToApply;
|
|
843
857
|
}
|
|
844
858
|
|
|
845
859
|
if (session.cliSessionId) {
|
|
@@ -980,10 +994,11 @@ function createSDKBridge(opts) {
|
|
|
980
994
|
sm.broadcastSessionList();
|
|
981
995
|
}
|
|
982
996
|
cleanupSessionWorker(session);
|
|
983
|
-
// Auto-continue
|
|
997
|
+
// Auto-continue on rate limit (scheduler sessions, or user setting)
|
|
984
998
|
var workerDidScheduleAC = false;
|
|
999
|
+
var workerACEnabled = session.onQueryComplete || (typeof opts.getAutoContinueSetting === "function" && opts.getAutoContinueSetting(session));
|
|
985
1000
|
if (session.rateLimitResetsAt && session.rateLimitResetsAt > Date.now()
|
|
986
|
-
&&
|
|
1001
|
+
&& workerACEnabled && !session.destroying) {
|
|
987
1002
|
var wacDelay = session.rateLimitResetsAt - Date.now() + 3000;
|
|
988
1003
|
var wacResetsAt = session.rateLimitResetsAt;
|
|
989
1004
|
session.rateLimitResetsAt = null;
|
|
@@ -1149,6 +1164,52 @@ function createSDKBridge(opts) {
|
|
|
1149
1164
|
if (mateAutoTools[toolName]) {
|
|
1150
1165
|
return Promise.resolve({ behavior: "allow", updatedInput: input });
|
|
1151
1166
|
}
|
|
1167
|
+
// Auto-approve safe Bash commands (read-only, non-destructive)
|
|
1168
|
+
if (toolName === "Bash" && input && input.command) {
|
|
1169
|
+
var cmd = input.command.trim();
|
|
1170
|
+
var firstWord = cmd.split(/[\s;|&]/)[0];
|
|
1171
|
+
var safeBashCommands = {
|
|
1172
|
+
ls: true, cat: true, head: true, tail: true, wc: true, file: true,
|
|
1173
|
+
// File/dir inspection
|
|
1174
|
+
ls: true, cat: true, head: true, tail: true, wc: true, file: true,
|
|
1175
|
+
stat: true, find: true, tree: true, du: true, df: true,
|
|
1176
|
+
readlink: true, realpath: true, basename: true, dirname: true,
|
|
1177
|
+
// Search
|
|
1178
|
+
grep: true, rg: true, ag: true, ack: true, fgrep: true, egrep: true,
|
|
1179
|
+
// Lookup
|
|
1180
|
+
which: true, type: true, whereis: true, command: true, hash: true,
|
|
1181
|
+
// Environment/system info
|
|
1182
|
+
echo: true, printf: true, env: true, printenv: true, pwd: true,
|
|
1183
|
+
whoami: true, id: true, groups: true,
|
|
1184
|
+
date: true, uname: true, hostname: true, uptime: true, arch: true,
|
|
1185
|
+
nproc: true, free: true, lsb_release: true, sw_vers: true,
|
|
1186
|
+
locale: true, timedatectl: true,
|
|
1187
|
+
// Version checks
|
|
1188
|
+
git: true, dotnet: true, ruby: true, java: true, javac: true,
|
|
1189
|
+
rustc: true, gcc: true, clang: true, cmake: true,
|
|
1190
|
+
// Text processing (pure stdin/stdout, no side effects)
|
|
1191
|
+
jq: true, yq: true, sort: true, uniq: true, cut: true, tr: true,
|
|
1192
|
+
awk: true, sed: true, paste: true, column: true, fold: true,
|
|
1193
|
+
rev: true, tac: true, nl: true, expand: true, unexpand: true,
|
|
1194
|
+
fmt: true, pr: true, csplit: true, comm: true, join: true,
|
|
1195
|
+
// Comparison/hashing
|
|
1196
|
+
diff: true, cmp: true, md5sum: true, sha256sum: true, sha1sum: true,
|
|
1197
|
+
shasum: true, cksum: true, sum: true, b2sum: true, base64: true,
|
|
1198
|
+
xxd: true, od: true, hexdump: true,
|
|
1199
|
+
// Misc read-only
|
|
1200
|
+
test: true, true: true, false: true, seq: true, yes: true,
|
|
1201
|
+
sleep: true, tee: true, xargs: true, time: true,
|
|
1202
|
+
man: true, help: true, info: true, apropos: true,
|
|
1203
|
+
cal: true, bc: true, expr: true, factor: true,
|
|
1204
|
+
lsof: true, ps: true, top: true, htop: true, pgrep: true,
|
|
1205
|
+
netstat: true, ss: true, ifconfig: true, ip: true, dig: true,
|
|
1206
|
+
nslookup: true, host: true, ping: true, traceroute: true,
|
|
1207
|
+
curl: true, wget: true, http: true,
|
|
1208
|
+
};
|
|
1209
|
+
if (safeBashCommands[firstWord]) {
|
|
1210
|
+
return Promise.resolve({ behavior: "allow", updatedInput: input });
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1152
1213
|
}
|
|
1153
1214
|
|
|
1154
1215
|
// AskUserQuestion: wait for user answers via WebSocket
|
|
@@ -1373,10 +1434,11 @@ function createSDKBridge(opts) {
|
|
|
1373
1434
|
session.pendingAskUser = {};
|
|
1374
1435
|
session.pendingElicitations = {};
|
|
1375
1436
|
|
|
1376
|
-
// Auto-continue
|
|
1437
|
+
// Auto-continue on rate limit (scheduler sessions, or user setting)
|
|
1377
1438
|
var didScheduleAutoContinue = false;
|
|
1439
|
+
var acEnabled = session.onQueryComplete || (typeof opts.getAutoContinueSetting === "function" && opts.getAutoContinueSetting(session));
|
|
1378
1440
|
if (session.rateLimitResetsAt && session.rateLimitResetsAt > Date.now()
|
|
1379
|
-
&&
|
|
1441
|
+
&& acEnabled && !session.destroying) {
|
|
1380
1442
|
var acDelay = session.rateLimitResetsAt - Date.now() + 3000;
|
|
1381
1443
|
var acResetsAt = session.rateLimitResetsAt;
|
|
1382
1444
|
session.rateLimitResetsAt = null;
|
|
@@ -1527,15 +1589,13 @@ function createSDKBridge(opts) {
|
|
|
1527
1589
|
}
|
|
1528
1590
|
|
|
1529
1591
|
if (dangerouslySkipPermissions) {
|
|
1530
|
-
queryOptions.permissionMode = "bypassPermissions";
|
|
1531
1592
|
queryOptions.allowDangerouslySkipPermissions = true;
|
|
1532
|
-
}
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
}
|
|
1593
|
+
}
|
|
1594
|
+
// Pass permissionMode in queryOptions at creation time to avoid race condition
|
|
1595
|
+
var modeToApply = session.acceptEditsAfterStart ? "acceptEdits" : sm.currentPermissionMode;
|
|
1596
|
+
if (session.acceptEditsAfterStart) delete session.acceptEditsAfterStart;
|
|
1597
|
+
if (modeToApply && modeToApply !== "default") {
|
|
1598
|
+
queryOptions.permissionMode = modeToApply;
|
|
1539
1599
|
}
|
|
1540
1600
|
|
|
1541
1601
|
if (session.cliSessionId) {
|
|
@@ -1745,12 +1805,6 @@ function createSDKBridge(opts) {
|
|
|
1745
1805
|
}
|
|
1746
1806
|
|
|
1747
1807
|
async function setPermissionMode(session, mode) {
|
|
1748
|
-
// When dangerouslySkipPermissions is active, ignore mode changes from UI
|
|
1749
|
-
// to prevent accidentally downgrading from bypassPermissions
|
|
1750
|
-
if (dangerouslySkipPermissions) {
|
|
1751
|
-
send({ type: "config_state", model: sm.currentModel || "", mode: "bypassPermissions", effort: sm.currentEffort || "medium", betas: sm.currentBetas || [] });
|
|
1752
|
-
return;
|
|
1753
|
-
}
|
|
1754
1808
|
if (session.worker) {
|
|
1755
1809
|
session.worker.send({ type: "set_permission_mode", mode: mode });
|
|
1756
1810
|
return;
|
|
@@ -1819,9 +1873,7 @@ function createSDKBridge(opts) {
|
|
|
1819
1873
|
|
|
1820
1874
|
var query;
|
|
1821
1875
|
try {
|
|
1822
|
-
|
|
1823
|
-
prompt: mq,
|
|
1824
|
-
options: {
|
|
1876
|
+
var mentionQueryOptions = {
|
|
1825
1877
|
cwd: cwd,
|
|
1826
1878
|
systemPrompt: opts.claudeMd,
|
|
1827
1879
|
settingSources: ["user"],
|
|
@@ -1837,18 +1889,32 @@ function createSDKBridge(opts) {
|
|
|
1837
1889
|
message: "Read-only access. You cannot make changes via @mention.",
|
|
1838
1890
|
});
|
|
1839
1891
|
},
|
|
1840
|
-
}
|
|
1892
|
+
};
|
|
1893
|
+
if (opts.model) mentionQueryOptions.model = opts.model;
|
|
1894
|
+
query = sdk.query({
|
|
1895
|
+
prompt: mq,
|
|
1896
|
+
options: mentionQueryOptions,
|
|
1841
1897
|
});
|
|
1842
1898
|
} catch (e) {
|
|
1843
1899
|
opts.onError("Failed to create mention query: " + (e.message || e));
|
|
1844
1900
|
return null;
|
|
1845
1901
|
}
|
|
1846
1902
|
|
|
1847
|
-
// Push the initial message (context + question)
|
|
1903
|
+
// Push the initial message (context + question, with optional images)
|
|
1848
1904
|
var initialPrompt = opts.initialContext + "\n\n" + opts.initialMessage;
|
|
1905
|
+
var initialContent = [];
|
|
1906
|
+
if (opts.initialImages && opts.initialImages.length > 0) {
|
|
1907
|
+
for (var ii = 0; ii < opts.initialImages.length; ii++) {
|
|
1908
|
+
initialContent.push({
|
|
1909
|
+
type: "image",
|
|
1910
|
+
source: { type: "base64", media_type: opts.initialImages[ii].mediaType, data: opts.initialImages[ii].data },
|
|
1911
|
+
});
|
|
1912
|
+
}
|
|
1913
|
+
}
|
|
1914
|
+
initialContent.push({ type: "text", text: initialPrompt });
|
|
1849
1915
|
mq.push({
|
|
1850
1916
|
type: "user",
|
|
1851
|
-
message: { role: "user", content:
|
|
1917
|
+
message: { role: "user", content: initialContent },
|
|
1852
1918
|
});
|
|
1853
1919
|
|
|
1854
1920
|
// Background stream processing loop
|
|
@@ -1951,7 +2017,7 @@ function createSDKBridge(opts) {
|
|
|
1951
2017
|
|
|
1952
2018
|
return {
|
|
1953
2019
|
// Push a follow-up message to the existing mention session
|
|
1954
|
-
pushMessage: function (text, callbacks) {
|
|
2020
|
+
pushMessage: function (text, callbacks, images) {
|
|
1955
2021
|
currentOnDelta = callbacks.onDelta;
|
|
1956
2022
|
currentOnDone = callbacks.onDone;
|
|
1957
2023
|
currentOnError = callbacks.onError;
|
|
@@ -1959,11 +2025,24 @@ function createSDKBridge(opts) {
|
|
|
1959
2025
|
mentionBlocks = {};
|
|
1960
2026
|
responseFullText = "";
|
|
1961
2027
|
responseStreamedText = false;
|
|
2028
|
+
var content = [];
|
|
2029
|
+
if (images && images.length > 0) {
|
|
2030
|
+
for (var pi = 0; pi < images.length; pi++) {
|
|
2031
|
+
content.push({
|
|
2032
|
+
type: "image",
|
|
2033
|
+
source: { type: "base64", media_type: images[pi].mediaType, data: images[pi].data },
|
|
2034
|
+
});
|
|
2035
|
+
}
|
|
2036
|
+
}
|
|
2037
|
+
content.push({ type: "text", text: text });
|
|
1962
2038
|
mq.push({
|
|
1963
2039
|
type: "user",
|
|
1964
|
-
message: { role: "user", content:
|
|
2040
|
+
message: { role: "user", content: content },
|
|
1965
2041
|
});
|
|
1966
2042
|
},
|
|
2043
|
+
abort: function () {
|
|
2044
|
+
try { abortController.abort(); } catch (e) {}
|
|
2045
|
+
},
|
|
1967
2046
|
close: function () {
|
|
1968
2047
|
alive = false;
|
|
1969
2048
|
try { mq.end(); } catch (e) {}
|
package/lib/server.js
CHANGED
|
@@ -1128,6 +1128,7 @@ function createServer(opts) {
|
|
|
1128
1128
|
profile.username = mu.username;
|
|
1129
1129
|
profile.userId = mu.id;
|
|
1130
1130
|
profile.role = mu.role;
|
|
1131
|
+
profile.autoContinueOnRateLimit = !!mu.autoContinueOnRateLimit;
|
|
1131
1132
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1132
1133
|
res.end(JSON.stringify(profile));
|
|
1133
1134
|
return;
|
|
@@ -1143,6 +1144,11 @@ function createServer(opts) {
|
|
|
1143
1144
|
if (saved.avatarSeed) profile.avatarSeed = saved.avatarSeed;
|
|
1144
1145
|
if (saved.avatarCustom) profile.avatarCustom = saved.avatarCustom;
|
|
1145
1146
|
} catch (e) { /* file doesn't exist yet */ }
|
|
1147
|
+
// Single-user auto-continue from daemon config
|
|
1148
|
+
if (typeof opts.onGetDaemonConfig === "function") {
|
|
1149
|
+
var dc = opts.onGetDaemonConfig();
|
|
1150
|
+
profile.autoContinueOnRateLimit = !!dc.autoContinueOnRateLimit;
|
|
1151
|
+
}
|
|
1146
1152
|
// Check if custom avatar file exists
|
|
1147
1153
|
try {
|
|
1148
1154
|
var avatarFiles = fs.readdirSync(path.join(CONFIG_DIR, "avatars"));
|
|
@@ -1431,6 +1437,69 @@ function createServer(opts) {
|
|
|
1431
1437
|
return;
|
|
1432
1438
|
}
|
|
1433
1439
|
|
|
1440
|
+
// PUT /api/user/auto-continue
|
|
1441
|
+
if (req.method === "PUT" && fullUrl === "/api/user/auto-continue") {
|
|
1442
|
+
var mu = getMultiUserFromReq(req);
|
|
1443
|
+
if (!mu) {
|
|
1444
|
+
// Single-user: use daemon config fallback
|
|
1445
|
+
var body = "";
|
|
1446
|
+
req.on("data", function (chunk) { body += chunk; });
|
|
1447
|
+
req.on("end", function () {
|
|
1448
|
+
try {
|
|
1449
|
+
var data = JSON.parse(body);
|
|
1450
|
+
if (typeof opts.onSetAutoContinue === "function") {
|
|
1451
|
+
opts.onSetAutoContinue(!!data.enabled);
|
|
1452
|
+
}
|
|
1453
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1454
|
+
res.end(JSON.stringify({ ok: true, autoContinueOnRateLimit: !!data.enabled }));
|
|
1455
|
+
} catch (e) {
|
|
1456
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
1457
|
+
res.end('{"error":"Invalid request"}');
|
|
1458
|
+
}
|
|
1459
|
+
});
|
|
1460
|
+
return;
|
|
1461
|
+
}
|
|
1462
|
+
var body = "";
|
|
1463
|
+
req.on("data", function (chunk) { body += chunk; });
|
|
1464
|
+
req.on("end", function () {
|
|
1465
|
+
try {
|
|
1466
|
+
var data = JSON.parse(body);
|
|
1467
|
+
var result = users.setAutoContinue(mu.id, !!data.enabled);
|
|
1468
|
+
if (result.error) {
|
|
1469
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
1470
|
+
res.end(JSON.stringify({ error: result.error }));
|
|
1471
|
+
return;
|
|
1472
|
+
}
|
|
1473
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1474
|
+
res.end(JSON.stringify({ ok: true, autoContinueOnRateLimit: result.autoContinueOnRateLimit }));
|
|
1475
|
+
} catch (e) {
|
|
1476
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
1477
|
+
res.end('{"error":"Invalid request"}');
|
|
1478
|
+
}
|
|
1479
|
+
});
|
|
1480
|
+
return;
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
// GET /api/user/auto-continue
|
|
1484
|
+
if (req.method === "GET" && fullUrl === "/api/user/auto-continue") {
|
|
1485
|
+
var mu = getMultiUserFromReq(req);
|
|
1486
|
+
if (!mu) {
|
|
1487
|
+
// Single-user: read from daemon config
|
|
1488
|
+
var enabled = false;
|
|
1489
|
+
if (typeof opts.onGetDaemonConfig === "function") {
|
|
1490
|
+
var dc = opts.onGetDaemonConfig();
|
|
1491
|
+
enabled = !!dc.autoContinueOnRateLimit;
|
|
1492
|
+
}
|
|
1493
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1494
|
+
res.end(JSON.stringify({ autoContinueOnRateLimit: enabled }));
|
|
1495
|
+
return;
|
|
1496
|
+
}
|
|
1497
|
+
var val = users.getAutoContinue(mu.id);
|
|
1498
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1499
|
+
res.end(JSON.stringify({ autoContinueOnRateLimit: val }));
|
|
1500
|
+
return;
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1434
1503
|
// --- Admin API endpoints (multi-user mode only) ---
|
|
1435
1504
|
|
|
1436
1505
|
// List all users (admin only)
|
|
@@ -2109,7 +2178,6 @@ function createServer(opts) {
|
|
|
2109
2178
|
var pResults = [];
|
|
2110
2179
|
projects.forEach(function (pCtx, pSlug) {
|
|
2111
2180
|
var status = pCtx.getStatus();
|
|
2112
|
-
if (status.isMate) return;
|
|
2113
2181
|
if (status.isWorktree) return;
|
|
2114
2182
|
// Access check
|
|
2115
2183
|
if (paletteUser && onGetProjectAccess) {
|
|
@@ -2131,7 +2199,7 @@ function createServer(opts) {
|
|
|
2131
2199
|
}
|
|
2132
2200
|
if (!pQuery) {
|
|
2133
2201
|
// Recent mode: return all sessions sorted by lastActivity
|
|
2134
|
-
|
|
2202
|
+
var pItem = {
|
|
2135
2203
|
projectSlug: pSlug,
|
|
2136
2204
|
projectTitle: status.title || status.project,
|
|
2137
2205
|
projectIcon: status.icon || null,
|
|
@@ -2140,7 +2208,12 @@ function createServer(opts) {
|
|
|
2140
2208
|
lastActivity: session.lastActivity || session.createdAt || 0,
|
|
2141
2209
|
matchType: null,
|
|
2142
2210
|
snippet: null
|
|
2143
|
-
}
|
|
2211
|
+
};
|
|
2212
|
+
if (status.isMate) {
|
|
2213
|
+
pItem.isMate = true;
|
|
2214
|
+
pItem.mateId = status.mateId || null;
|
|
2215
|
+
}
|
|
2216
|
+
pResults.push(pItem);
|
|
2144
2217
|
} else {
|
|
2145
2218
|
// Search mode: match title and content
|
|
2146
2219
|
var q = pQuery.toLowerCase();
|
|
@@ -2148,7 +2221,7 @@ function createServer(opts) {
|
|
|
2148
2221
|
var contentSnippet = null;
|
|
2149
2222
|
for (var hi = 0; hi < session.history.length; hi++) {
|
|
2150
2223
|
var entry = session.history[hi];
|
|
2151
|
-
if ((entry.type === "delta" || entry.type === "user_message") && entry.text) {
|
|
2224
|
+
if ((entry.type === "delta" || entry.type === "user_message" || entry.type === "mention_user" || entry.type === "mention_response" || entry.type === "debate_turn_done") && entry.text) {
|
|
2152
2225
|
var lowerText = entry.text.toLowerCase();
|
|
2153
2226
|
var matchIdx = lowerText.indexOf(q);
|
|
2154
2227
|
if (matchIdx !== -1) {
|
|
@@ -2162,7 +2235,7 @@ function createServer(opts) {
|
|
|
2162
2235
|
}
|
|
2163
2236
|
}
|
|
2164
2237
|
if (titleMatch || contentSnippet) {
|
|
2165
|
-
|
|
2238
|
+
var sItem = {
|
|
2166
2239
|
projectSlug: pSlug,
|
|
2167
2240
|
projectTitle: status.title || status.project,
|
|
2168
2241
|
projectIcon: status.icon || null,
|
|
@@ -2171,7 +2244,12 @@ function createServer(opts) {
|
|
|
2171
2244
|
lastActivity: session.lastActivity || session.createdAt || 0,
|
|
2172
2245
|
matchType: titleMatch && contentSnippet ? "both" : titleMatch ? "title" : "content",
|
|
2173
2246
|
snippet: contentSnippet
|
|
2174
|
-
}
|
|
2247
|
+
};
|
|
2248
|
+
if (status.isMate) {
|
|
2249
|
+
sItem.isMate = true;
|
|
2250
|
+
sItem.mateId = status.mateId || null;
|
|
2251
|
+
}
|
|
2252
|
+
pResults.push(sItem);
|
|
2175
2253
|
}
|
|
2176
2254
|
}
|
|
2177
2255
|
});
|
package/lib/sessions.js
CHANGED
|
@@ -31,7 +31,7 @@ function createSessionManager(opts) {
|
|
|
31
31
|
// v2: ~/.claude-relay/sessions/{encoded-cwd}/ (if config.js rename didn't cover it)
|
|
32
32
|
var legacySessionDirs = [
|
|
33
33
|
path.join(cwd, ".claude-relay", "sessions"),
|
|
34
|
-
path.join(require("
|
|
34
|
+
path.join(require("./config").REAL_HOME, ".claude-relay", "sessions", encodedCwd),
|
|
35
35
|
];
|
|
36
36
|
for (var li = 0; li < legacySessionDirs.length; li++) {
|
|
37
37
|
var oldSessionsDir = legacySessionDirs[li];
|
|
@@ -569,7 +569,7 @@ function createSessionManager(opts) {
|
|
|
569
569
|
var contentMatch = false;
|
|
570
570
|
for (var i = 0; i < session.history.length; i++) {
|
|
571
571
|
var entry = session.history[i];
|
|
572
|
-
if ((entry.type === "delta" || entry.type === "user_message") && entry.text) {
|
|
572
|
+
if ((entry.type === "delta" || entry.type === "user_message" || entry.type === "mention_user" || entry.type === "mention_response" || entry.type === "debate_turn_done") && entry.text) {
|
|
573
573
|
if (entry.text.toLowerCase().indexOf(q) !== -1) {
|
|
574
574
|
contentMatch = true;
|
|
575
575
|
break;
|
|
@@ -602,11 +602,11 @@ function createSessionManager(opts) {
|
|
|
602
602
|
var currentTurnStart = -1;
|
|
603
603
|
for (var i = 0; i < history.length; i++) {
|
|
604
604
|
var entry = history[i];
|
|
605
|
-
if (entry.type === "user_message") {
|
|
605
|
+
if (entry.type === "user_message" || entry.type === "mention_user") {
|
|
606
606
|
currentTurnStart = i;
|
|
607
607
|
lastAssistantHitTurn = -1;
|
|
608
608
|
}
|
|
609
|
-
if ((entry.type === "delta" || entry.type === "user_message") && entry.text) {
|
|
609
|
+
if ((entry.type === "delta" || entry.type === "user_message" || entry.type === "mention_user" || entry.type === "mention_response" || entry.type === "debate_turn_done") && entry.text) {
|
|
610
610
|
// Skip duplicate delta hits within the same assistant turn
|
|
611
611
|
if (entry.type === "delta" && currentTurnStart === lastAssistantHitTurn) continue;
|
|
612
612
|
var text = entry.text;
|
package/lib/users.js
CHANGED
|
@@ -641,6 +641,30 @@ function canAccessSession(userId, session, project) {
|
|
|
641
641
|
return false;
|
|
642
642
|
}
|
|
643
643
|
|
|
644
|
+
// --- Per-user auto-continue setting ---
|
|
645
|
+
|
|
646
|
+
function getAutoContinue(userId) {
|
|
647
|
+
var data = loadUsers();
|
|
648
|
+
for (var i = 0; i < data.users.length; i++) {
|
|
649
|
+
if (data.users[i].id === userId) {
|
|
650
|
+
return !!data.users[i].autoContinueOnRateLimit;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
return false;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
function setAutoContinue(userId, enabled) {
|
|
657
|
+
var data = loadUsers();
|
|
658
|
+
for (var i = 0; i < data.users.length; i++) {
|
|
659
|
+
if (data.users[i].id === userId) {
|
|
660
|
+
data.users[i].autoContinueOnRateLimit = !!enabled;
|
|
661
|
+
saveUsers(data);
|
|
662
|
+
return { ok: true, autoContinueOnRateLimit: !!enabled };
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
return { error: "User not found" };
|
|
666
|
+
}
|
|
667
|
+
|
|
644
668
|
module.exports = {
|
|
645
669
|
USERS_FILE: USERS_FILE,
|
|
646
670
|
loadUsers: loadUsers,
|
|
@@ -691,4 +715,6 @@ module.exports = {
|
|
|
691
715
|
getDmHidden: getDmHidden,
|
|
692
716
|
addDmHidden: addDmHidden,
|
|
693
717
|
removeDmHidden: removeDmHidden,
|
|
718
|
+
getAutoContinue: getAutoContinue,
|
|
719
|
+
setAutoContinue: setAutoContinue,
|
|
694
720
|
};
|