clay-server 2.32.0-beta.1 → 2.32.0-beta.3
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/project-sessions.js
CHANGED
|
@@ -280,6 +280,7 @@ function attachSessions(ctx) {
|
|
|
280
280
|
if (msg.id && sm.sessions.has(msg.id) && msg.title) {
|
|
281
281
|
var s = sm.sessions.get(msg.id);
|
|
282
282
|
s.title = String(msg.title).substring(0, 100);
|
|
283
|
+
s.titleManuallySet = true;
|
|
283
284
|
sm.saveSessionFile(s);
|
|
284
285
|
sm.broadcastSessionList();
|
|
285
286
|
// Sync title to SDK session
|
package/lib/public/app.js
CHANGED
|
@@ -386,7 +386,7 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
386
386
|
openDm: function (userId) { openDm(userId); },
|
|
387
387
|
openMateWizard: function () { requireClayMateInterview(function () { openMateWizard(); }); },
|
|
388
388
|
openAddProjectModal: function () { openAddProjectModal(); },
|
|
389
|
-
sendWs: function (msg) { if (
|
|
389
|
+
sendWs: function (msg) { var _ws = _getWsRef(); if (_ws && _ws.readyState === 1) _ws.send(JSON.stringify(msg)); },
|
|
390
390
|
onDmRemoveUser: function (userId) { var dr = Object.assign({}, store.get('dmRemovedUsers')); dr[userId] = true; store.set({ dmRemovedUsers: dr }); },
|
|
391
391
|
getHistoryFrom: function () { return store.get('historyFrom'); },
|
|
392
392
|
get permissions() { return store.get('permissions'); },
|
|
@@ -399,7 +399,7 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
399
399
|
initMateKnowledge(wsGetter);
|
|
400
400
|
initMateMemory(wsGetter, { onShow: function () { hideKnowledge(); hideNotes(); } });
|
|
401
401
|
initMateWizard(
|
|
402
|
-
function (msg) { if (
|
|
402
|
+
function (msg) { var _ws = _getWsRef(); if (_ws && _ws.readyState === 1) _ws.send(JSON.stringify(msg)); },
|
|
403
403
|
function (mate) { handleMateCreatedInApp(mate); }
|
|
404
404
|
);
|
|
405
405
|
|
|
@@ -420,8 +420,9 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
420
420
|
var stickyBtn = document.getElementById("sticky-notes-sidebar-btn");
|
|
421
421
|
if (stickyBtn) stickyBtn.click();
|
|
422
422
|
}
|
|
423
|
-
|
|
424
|
-
|
|
423
|
+
var _ws = _getWsRef();
|
|
424
|
+
if (_ws && _ws.readyState === 1) {
|
|
425
|
+
_ws.send(JSON.stringify({ type: "switch_session", id: id }));
|
|
425
426
|
}
|
|
426
427
|
},
|
|
427
428
|
openDm: function (userId) { openDm(userId); },
|
|
@@ -700,7 +701,7 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
700
701
|
isDebateFloorMode: function () { return store.get('debateFloorMode'); },
|
|
701
702
|
handleDebateFloorSend: function () { handleDebateFloorSend(); },
|
|
702
703
|
isMateDm: function () { return store.get('dmMode') && store.get('dmTargetUser') && store.get('dmTargetUser').isMate; },
|
|
703
|
-
getDmMateId: function () {
|
|
704
|
+
getDmMateId: function () { var _dmt = store.get('dmTargetUser'); return (store.get('dmMode') && _dmt && _dmt.isMate) ? _dmt.id : null; },
|
|
704
705
|
getMateName: function () { return store.get('dmTargetUser') ? (store.get('dmTargetUser').displayName || "Mate") : "Mate"; },
|
|
705
706
|
getMateAvatarUrl: function () { return document.body.dataset.mateAvatarUrl || ""; },
|
|
706
707
|
showMatePreThinking: function () { showMatePreThinking(); },
|
|
@@ -726,7 +727,7 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
726
727
|
// --- Debate module ---
|
|
727
728
|
initDebate({
|
|
728
729
|
get ws() { return _getWsRef(); },
|
|
729
|
-
sendWs: function (obj) { if (
|
|
730
|
+
sendWs: function (obj) { var _ws = _getWsRef(); if (_ws && _ws.readyState === 1) _ws.send(JSON.stringify(obj)); },
|
|
730
731
|
messagesEl: messagesEl,
|
|
731
732
|
addToMessages: function (el) { addToMessages(el); },
|
|
732
733
|
scrollToBottom: scrollToBottom,
|
|
@@ -18,6 +18,7 @@ var badgeEl = null;
|
|
|
18
18
|
// --- Update available banner state ---
|
|
19
19
|
var pendingUpdateMsg = null;
|
|
20
20
|
var updateReshowTimer = null;
|
|
21
|
+
var updateDismissedAt = 0;
|
|
21
22
|
var UPDATE_RESHOW_INTERVAL = 60 * 60 * 1000; // 1 hour
|
|
22
23
|
|
|
23
24
|
// ========================================================
|
|
@@ -331,6 +332,9 @@ export function showUpdateBanner(msg) {
|
|
|
331
332
|
pendingUpdateMsg = msg;
|
|
332
333
|
if (!bannerContainer) return;
|
|
333
334
|
|
|
335
|
+
// If user dismissed recently, skip until reshow timer fires
|
|
336
|
+
if (updateDismissedAt && (Date.now() - updateDismissedAt) < UPDATE_RESHOW_INTERVAL) return;
|
|
337
|
+
|
|
334
338
|
// Remove any existing update banner
|
|
335
339
|
var existing = bannerContainer.querySelector('[data-notif-id="_update"]');
|
|
336
340
|
if (existing) removeBanner(existing);
|
|
@@ -395,9 +399,11 @@ export function showUpdateBanner(msg) {
|
|
|
395
399
|
}
|
|
396
400
|
|
|
397
401
|
function scheduleUpdateReshow() {
|
|
402
|
+
updateDismissedAt = Date.now();
|
|
398
403
|
if (updateReshowTimer) clearTimeout(updateReshowTimer);
|
|
399
404
|
updateReshowTimer = setTimeout(function () {
|
|
400
405
|
updateReshowTimer = null;
|
|
406
|
+
updateDismissedAt = 0;
|
|
401
407
|
if (pendingUpdateMsg) showUpdateBanner(pendingUpdateMsg);
|
|
402
408
|
}, UPDATE_RESHOW_INTERVAL);
|
|
403
409
|
}
|
package/lib/sdk-bridge.js
CHANGED
|
@@ -9,13 +9,16 @@ function attachMessageProcessor(ctx) {
|
|
|
9
9
|
var pushModule = ctx.pushModule;
|
|
10
10
|
var getNotificationsModule = ctx.getNotificationsModule || function () { return null; };
|
|
11
11
|
var getSDK = ctx.getSDK;
|
|
12
|
-
var adapter = ctx.adapter;
|
|
12
|
+
var adapter = ctx.adapter;
|
|
13
|
+
var cwd = ctx.cwd;
|
|
13
14
|
var onProcessingChanged = ctx.onProcessingChanged;
|
|
14
15
|
var onTurnDone = ctx.onTurnDone;
|
|
15
16
|
var opts = ctx.opts;
|
|
16
17
|
var discoverSkillDirs = ctx.discoverSkillDirs;
|
|
17
18
|
var mergeSkills = ctx.mergeSkills;
|
|
18
19
|
|
|
20
|
+
var AUTO_TITLE_TURN_THRESHOLD = 3;
|
|
21
|
+
|
|
19
22
|
function sendAndRecord(session, obj) {
|
|
20
23
|
sm.sendAndRecord(session, obj);
|
|
21
24
|
}
|
|
@@ -24,6 +27,75 @@ function attachMessageProcessor(ctx) {
|
|
|
24
27
|
sm.sendToSession(session, obj);
|
|
25
28
|
}
|
|
26
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Auto-generate a session title using a lightweight SDK query.
|
|
32
|
+
* Fires after AUTO_TITLE_TURN_THRESHOLD turns and runs async (non-blocking).
|
|
33
|
+
*/
|
|
34
|
+
function autoGenerateTitle(session) {
|
|
35
|
+
// Collect user messages from history for context
|
|
36
|
+
var userMessages = [];
|
|
37
|
+
for (var i = 0; i < session.history.length; i++) {
|
|
38
|
+
var entry = session.history[i];
|
|
39
|
+
if (entry.type === "user_message" && entry.text) {
|
|
40
|
+
userMessages.push(entry.text.substring(0, 200));
|
|
41
|
+
if (userMessages.length >= 5) break;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (userMessages.length === 0) return;
|
|
45
|
+
|
|
46
|
+
var prompt = "Below is a conversation between a user and an AI assistant. Generate a short, descriptive title (3-8 words) that captures the main topic. Reply with ONLY the title, nothing else.\n\n";
|
|
47
|
+
for (var j = 0; j < userMessages.length; j++) {
|
|
48
|
+
prompt += "User message " + (j + 1) + ": " + userMessages[j] + "\n";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
var ac = new AbortController();
|
|
52
|
+
adapter.createQuery({
|
|
53
|
+
cwd: cwd,
|
|
54
|
+
systemPrompt: "You are a title generator. Output only a short title (3-8 words). No quotes, no punctuation at the end, no explanation.",
|
|
55
|
+
model: "claude-haiku-4-5-20250901",
|
|
56
|
+
effort: "low",
|
|
57
|
+
abortController: ac,
|
|
58
|
+
adapterOptions: {
|
|
59
|
+
CLAUDE: {
|
|
60
|
+
permissionMode: "bypassPermissions",
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}).then(function(handle) {
|
|
64
|
+
handle.pushMessage(prompt);
|
|
65
|
+
var title = "";
|
|
66
|
+
(async function() {
|
|
67
|
+
try {
|
|
68
|
+
for await (var msg of handle) {
|
|
69
|
+
if (msg.yokeType === "text_delta" && msg.text) {
|
|
70
|
+
title += msg.text;
|
|
71
|
+
} else if (msg.yokeType === "result") {
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
} finally {
|
|
76
|
+
handle.close();
|
|
77
|
+
}
|
|
78
|
+
title = title.replace(/[\r\n]+/g, " ").replace(/^["'\s]+|["'\s.]+$/g, "").trim();
|
|
79
|
+
if (!title || title.length < 2) return;
|
|
80
|
+
title = title.substring(0, 100);
|
|
81
|
+
|
|
82
|
+
// Only update if user hasn't manually renamed the session
|
|
83
|
+
if (!session.titleManuallySet) {
|
|
84
|
+
session.title = title;
|
|
85
|
+
session.titleAutoGenerated = true;
|
|
86
|
+
sm.saveSessionFile(session);
|
|
87
|
+
sm.broadcastSessionList();
|
|
88
|
+
// Sync to SDK
|
|
89
|
+
if (session.cliSessionId) {
|
|
90
|
+
adapter.renameSession(session.cliSessionId, title, { dir: cwd }).catch(function() {});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
})();
|
|
94
|
+
}).catch(function(e) {
|
|
95
|
+
console.error("[auto-title] Failed to generate title:", e.message || e);
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
27
99
|
function toolActivityTextForSubagent(name, input) {
|
|
28
100
|
if (name === "Bash" && input && input.description) return input.description;
|
|
29
101
|
if (name === "Read" && input && input.file_path) return "Reading " + input.file_path.split("/").pop();
|
|
@@ -405,10 +477,22 @@ function attachMessageProcessor(ctx) {
|
|
|
405
477
|
}
|
|
406
478
|
// Reset for next turn in the same query
|
|
407
479
|
session.lastActivityAt = Date.now();
|
|
480
|
+
session.turnCount = (session.turnCount || 0) + 1;
|
|
408
481
|
var donePreview = session.responsePreview || "";
|
|
409
482
|
session.responsePreview = "";
|
|
410
483
|
session.streamedText = false;
|
|
411
484
|
sm.broadcastSessionList();
|
|
485
|
+
|
|
486
|
+
// Auto-generate title after N turns (skip if loop, mate, or already auto-generated)
|
|
487
|
+
if (session.turnCount === AUTO_TITLE_TURN_THRESHOLD
|
|
488
|
+
&& !session.titleAutoGenerated
|
|
489
|
+
&& !session.titleManuallySet
|
|
490
|
+
&& !session.loop
|
|
491
|
+
&& !isMate
|
|
492
|
+
&& adapter) {
|
|
493
|
+
try { autoGenerateTitle(session); } catch (e) {}
|
|
494
|
+
}
|
|
495
|
+
|
|
412
496
|
if (onTurnDone) {
|
|
413
497
|
try { onTurnDone(session, donePreview); } catch (e) {}
|
|
414
498
|
}
|
package/lib/sessions.js
CHANGED
|
@@ -286,6 +286,8 @@ function createSessionManager(opts) {
|
|
|
286
286
|
allowedTools: {},
|
|
287
287
|
isProcessing: false,
|
|
288
288
|
title: "",
|
|
289
|
+
titleAutoGenerated: false,
|
|
290
|
+
turnCount: 0,
|
|
289
291
|
createdAt: Date.now(),
|
|
290
292
|
lastActivity: Date.now(),
|
|
291
293
|
history: [],
|
|
@@ -314,6 +316,8 @@ function createSessionManager(opts) {
|
|
|
314
316
|
allowedTools: {},
|
|
315
317
|
isProcessing: false,
|
|
316
318
|
title: "",
|
|
319
|
+
titleAutoGenerated: false,
|
|
320
|
+
turnCount: 0,
|
|
317
321
|
createdAt: Date.now(),
|
|
318
322
|
lastActivity: Date.now(),
|
|
319
323
|
history: [],
|