clay-server 2.34.1-beta.1 → 2.34.1-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/public/modules/app-connection.js +6 -24
- package/lib/public/modules/app-dm.js +7 -6
- package/lib/public/modules/app-rendering.js +6 -4
- package/lib/public/modules/app-skills-install.js +5 -1
- package/lib/public/modules/input.js +9 -2
- package/lib/users-preferences.js +43 -1
- package/package.json +2 -2
|
@@ -103,30 +103,12 @@ function onConnected() {
|
|
|
103
103
|
|
|
104
104
|
// Session restore is now server-driven (user-presence.json).
|
|
105
105
|
// Mate DM restore is also server-driven via "restore_mate_dm" message.
|
|
106
|
-
//
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
console.log("[dm-restore] Server did not restore DM, using localStorage fallback:", savedDm);
|
|
113
|
-
openDm(savedDm);
|
|
114
|
-
}
|
|
115
|
-
}, 2000);
|
|
116
|
-
// Cancel fallback if server restores DM first
|
|
117
|
-
var patchedOnce = false;
|
|
118
|
-
var checkRestore = function (evt) {
|
|
119
|
-
try {
|
|
120
|
-
var d = JSON.parse(evt.data);
|
|
121
|
-
if (d.type === "restore_mate_dm" && !patchedOnce) {
|
|
122
|
-
patchedOnce = true;
|
|
123
|
-
clearTimeout(dmFallbackTimer);
|
|
124
|
-
}
|
|
125
|
-
} catch (e) {}
|
|
126
|
-
};
|
|
127
|
-
ws.addEventListener("message", checkRestore);
|
|
128
|
-
setTimeout(function () { ws.removeEventListener("message", checkRestore); }, 3000);
|
|
129
|
-
}
|
|
106
|
+
// Previously there was a 2s localStorage fallback that auto-called
|
|
107
|
+
// openDm(savedDm) on every reconnect. That fallback re-opened stale
|
|
108
|
+
// mate DMs on every refresh / project switch and was the root cause
|
|
109
|
+
// of the skill-install modal popping unprompted. Server-driven restore
|
|
110
|
+
// is authoritative — drop the client-side fallback entirely.
|
|
111
|
+
try { localStorage.removeItem("clay-active-dm"); } catch (e) {}
|
|
130
112
|
// Safety: clear returningFromMateDm after initial messages settle
|
|
131
113
|
if (store.get('returningFromMateDm')) {
|
|
132
114
|
setTimeout(function () {
|
|
@@ -21,7 +21,6 @@ import { closeTerminal } from './terminal.js';
|
|
|
21
21
|
import { openMobileSheet, setMobileSheetMateData } from './sidebar-mobile.js';
|
|
22
22
|
import { getProfileLang } from './profile.js';
|
|
23
23
|
import { isSchedulerOpen, closeScheduler } from './scheduler.js';
|
|
24
|
-
import { requireClayMateInterview } from './app-skills-install.js';
|
|
25
24
|
import { syncResizeHandles } from './sidebar.js';
|
|
26
25
|
|
|
27
26
|
var MATE_ONBOARDING_KEY = "clay-mate-onboarding-shown";
|
|
@@ -77,13 +76,15 @@ export function openDm(targetUserId) {
|
|
|
77
76
|
if (!ws || ws.readyState !== 1) return;
|
|
78
77
|
// Persist DM state for refresh recovery
|
|
79
78
|
try { localStorage.setItem("clay-active-dm", targetUserId); } catch (e) {}
|
|
80
|
-
//
|
|
79
|
+
// Opening an existing mate DM does not require the clay-mate-interview
|
|
80
|
+
// skill — that skill is only used during new mate creation / reshaping.
|
|
81
|
+
// Showing onboarding + gating a skill version check here caused the
|
|
82
|
+
// "Skill Installation Required" modal to pop on every refresh / project
|
|
83
|
+
// switch via the localStorage DM-restore fallback in app-connection.js.
|
|
81
84
|
if (typeof targetUserId === "string" && targetUserId.indexOf("mate_") === 0) {
|
|
82
85
|
showMateOnboarding(function () {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
if (ws2) ws2.send(JSON.stringify({ type: "dm_open", targetUserId: targetUserId }));
|
|
86
|
-
});
|
|
86
|
+
var ws2 = getWs();
|
|
87
|
+
if (ws2) ws2.send(JSON.stringify({ type: "dm_open", targetUserId: targetUserId }));
|
|
87
88
|
});
|
|
88
89
|
return;
|
|
89
90
|
}
|
|
@@ -10,7 +10,7 @@ import { iconHtml, refreshIcons } from './icons.js';
|
|
|
10
10
|
import { userAvatarUrl } from './avatar.js';
|
|
11
11
|
import { closeToolGroup } from './tools.js';
|
|
12
12
|
import { showImageModal, showPasteModal } from './app-misc.js';
|
|
13
|
-
import { sendMessage } from './input.js';
|
|
13
|
+
import { sendMessage, hasSendableContent } from './input.js';
|
|
14
14
|
import { getChatLayout } from './theme.js';
|
|
15
15
|
import { getScheduledMsgEl } from './app-rate-limit.js';
|
|
16
16
|
|
|
@@ -547,9 +547,11 @@ export function getGhostSuggestion() {
|
|
|
547
547
|
|
|
548
548
|
export function showSuggestionChips(suggestion) {
|
|
549
549
|
if (!suggestion || store.get('processing')) return;
|
|
550
|
-
|
|
551
|
-
//
|
|
552
|
-
|
|
550
|
+
// Only show ghost text when there is no sendable content — typed text,
|
|
551
|
+
// pending pastes, pending images, or pending files all suppress the
|
|
552
|
+
// suggestion so Enter can't accidentally send it instead of the user's
|
|
553
|
+
// actual attached content.
|
|
554
|
+
if (hasSendableContent()) return;
|
|
553
555
|
_ghostSuggestionText = suggestion;
|
|
554
556
|
var ghostEl = document.getElementById("ghost-suggestion");
|
|
555
557
|
if (!ghostEl) return;
|
|
@@ -197,10 +197,14 @@ export function requireSkills(opts, cb) {
|
|
|
197
197
|
.then(function (res) { return res.json(); })
|
|
198
198
|
.then(function (data) {
|
|
199
199
|
var results = data.results || [];
|
|
200
|
+
// Only "missing" skills block the feature. "outdated" skills already
|
|
201
|
+
// function — an available update should not hard-gate with a modal
|
|
202
|
+
// every time a user opens a DM or refreshes the page. Callers can
|
|
203
|
+
// surface outdated versions elsewhere (e.g. settings / notifications).
|
|
200
204
|
var actionable = [];
|
|
201
205
|
for (var i = 0; i < results.length; i++) {
|
|
202
206
|
var r = results[i];
|
|
203
|
-
if (r.status === "missing"
|
|
207
|
+
if (r.status === "missing") {
|
|
204
208
|
var orig = null;
|
|
205
209
|
for (var j = 0; j < opts.skills.length; j++) {
|
|
206
210
|
if (opts.skills[j].name === r.name) { orig = opts.skills[j]; break; }
|
|
@@ -360,6 +360,9 @@ function renderInputPreviews() {
|
|
|
360
360
|
return;
|
|
361
361
|
}
|
|
362
362
|
bar.classList.add("visible");
|
|
363
|
+
// Hide any ghost suggestion as soon as attached content appears — Enter
|
|
364
|
+
// must not silently swallow the user's paste/image/file.
|
|
365
|
+
if (ctx && ctx.hideSuggestionChips) ctx.hideSuggestionChips();
|
|
363
366
|
|
|
364
367
|
// Image thumbnails
|
|
365
368
|
for (var i = 0; i < pendingImages.length; i++) {
|
|
@@ -1040,9 +1043,13 @@ export function initInput(_ctx) {
|
|
|
1040
1043
|
return;
|
|
1041
1044
|
}
|
|
1042
1045
|
e.preventDefault();
|
|
1043
|
-
// If input
|
|
1046
|
+
// If input has no sendable content but ghost suggestion is showing, adopt it.
|
|
1047
|
+
// Use hasSendableContent() instead of checking inputEl.value alone so that
|
|
1048
|
+
// pending images, pastes, or files block the ghost-text adoption — otherwise
|
|
1049
|
+
// pressing Enter with only a pasted image/block queued would send the
|
|
1050
|
+
// suggestion instead of the user's actual content.
|
|
1044
1051
|
var ghost = ctx.getGhostSuggestion ? ctx.getGhostSuggestion() : "";
|
|
1045
|
-
if (!
|
|
1052
|
+
if (!hasSendableContent() && ghost) {
|
|
1046
1053
|
ctx.inputEl.value = ghost;
|
|
1047
1054
|
if (ctx.hideSuggestionChips) ctx.hideSuggestionChips();
|
|
1048
1055
|
}
|
package/lib/users-preferences.js
CHANGED
|
@@ -87,6 +87,36 @@ function attachPreferences(deps) {
|
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
// --- Deleted built-in mate keys tracking ---
|
|
90
|
+
//
|
|
91
|
+
// In single-user mode there is no users.json, so the user row lookup
|
|
92
|
+
// below returns nothing and the key is silently dropped. That made
|
|
93
|
+
// "Remove mate" in the sidebar picker a no-op: the key was never
|
|
94
|
+
// persisted, ensureBuiltinMates re-created the mate on next mate_list,
|
|
95
|
+
// and the user could not actually get rid of built-in mates.
|
|
96
|
+
//
|
|
97
|
+
// Fallback: when the user record isn't found (single-user mode), read
|
|
98
|
+
// and write deletedBuiltinKeys on daemon.json via lib/config.js. This
|
|
99
|
+
// preserves multi-user behavior (users.json row still wins) while
|
|
100
|
+
// giving single-user deploys a place to persist the setting.
|
|
101
|
+
|
|
102
|
+
function loadSingleUserDeletedKeys() {
|
|
103
|
+
try {
|
|
104
|
+
var config = require("./config");
|
|
105
|
+
var cfg = config.loadConfig() || {};
|
|
106
|
+
return Array.isArray(cfg.deletedBuiltinKeys) ? cfg.deletedBuiltinKeys : [];
|
|
107
|
+
} catch (e) {
|
|
108
|
+
return [];
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function saveSingleUserDeletedKeys(keys) {
|
|
113
|
+
try {
|
|
114
|
+
var config = require("./config");
|
|
115
|
+
var cfg = config.loadConfig() || {};
|
|
116
|
+
cfg.deletedBuiltinKeys = keys;
|
|
117
|
+
config.saveConfig(cfg);
|
|
118
|
+
} catch (e) {}
|
|
119
|
+
}
|
|
90
120
|
|
|
91
121
|
function getDeletedBuiltinKeys(userId) {
|
|
92
122
|
var data = loadUsers();
|
|
@@ -95,7 +125,7 @@ function attachPreferences(deps) {
|
|
|
95
125
|
return data.users[i].deletedBuiltinKeys || [];
|
|
96
126
|
}
|
|
97
127
|
}
|
|
98
|
-
return
|
|
128
|
+
return loadSingleUserDeletedKeys();
|
|
99
129
|
}
|
|
100
130
|
|
|
101
131
|
function addDeletedBuiltinKey(userId, key) {
|
|
@@ -110,6 +140,12 @@ function attachPreferences(deps) {
|
|
|
110
140
|
return;
|
|
111
141
|
}
|
|
112
142
|
}
|
|
143
|
+
// Single-user fallback
|
|
144
|
+
var keys = loadSingleUserDeletedKeys();
|
|
145
|
+
if (keys.indexOf(key) === -1) {
|
|
146
|
+
keys.push(key);
|
|
147
|
+
saveSingleUserDeletedKeys(keys);
|
|
148
|
+
}
|
|
113
149
|
}
|
|
114
150
|
|
|
115
151
|
function removeDeletedBuiltinKey(userId, key) {
|
|
@@ -124,6 +160,12 @@ function attachPreferences(deps) {
|
|
|
124
160
|
return;
|
|
125
161
|
}
|
|
126
162
|
}
|
|
163
|
+
// Single-user fallback
|
|
164
|
+
var keys = loadSingleUserDeletedKeys();
|
|
165
|
+
var filtered = keys.filter(function (k) { return k !== key; });
|
|
166
|
+
if (filtered.length !== keys.length) {
|
|
167
|
+
saveSingleUserDeletedKeys(filtered);
|
|
168
|
+
}
|
|
127
169
|
}
|
|
128
170
|
|
|
129
171
|
// --- Per-user chat layout setting ---
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clay-server",
|
|
3
|
-
"version": "2.34.1-beta.
|
|
3
|
+
"version": "2.34.1-beta.3",
|
|
4
4
|
"description": "Self-hosted Claude Code in your browser. Multi-session, multi-user, push notifications.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"clay-server": "./bin/cli.js",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@anthropic-ai/claude-agent-sdk": "^0.2.112",
|
|
40
40
|
"@lydell/node-pty": "^1.2.0-beta.3",
|
|
41
|
-
"@openai/codex": "^0.
|
|
41
|
+
"@openai/codex": "^0.124.0",
|
|
42
42
|
"imapflow": "^1.3.1",
|
|
43
43
|
"nodemailer": "^6.10.1",
|
|
44
44
|
"qrcode-terminal": "^0.12.0",
|