clay-server 2.36.2-beta.9 → 2.37.0-beta.2

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.
@@ -175,14 +175,37 @@ export function addToMessages(el) {
175
175
 
176
176
  export function scrollToBottom() {
177
177
  if (prependAnchor) return;
178
- var newMsgBtn = document.getElementById("new-msg-btn");
179
- if (isUserScrolledUp) {
180
- newMsgBtn.textContent = NEW_MSG_BTN_ACTIVITY;
181
- newMsgBtn.classList.remove("hidden");
178
+ var messagesEl = getMessagesEl();
179
+ if (!messagesEl) return;
180
+ // Compute the user's current scroll position from the DOM rather than
181
+ // the cached isUserScrolledUp flag. The flag is updated by an async
182
+ // scroll-event listener; if a delta arrives between the user's wheel
183
+ // input and that listener firing, the cached flag is still false and
184
+ // we'd snap them back to the bottom against their intent. Reading
185
+ // scrollTop/scrollHeight here is synchronous and race-free.
186
+ var distFromBottom = messagesEl.scrollHeight - messagesEl.scrollTop - messagesEl.clientHeight;
187
+ if (distFromBottom > 150 || isUserScrolledUp) {
188
+ // Keep the flag and the "New activity" button in sync even when the
189
+ // scroll-listener hasn't fired yet, so other call sites observing
190
+ // isUserScrolledUp see the truth too.
191
+ if (distFromBottom > 150) isUserScrolledUp = true;
192
+ var newMsgBtn = document.getElementById("new-msg-btn");
193
+ if (newMsgBtn) {
194
+ newMsgBtn.textContent = NEW_MSG_BTN_ACTIVITY;
195
+ newMsgBtn.classList.remove("hidden");
196
+ }
182
197
  return;
183
198
  }
184
- var messagesEl = getMessagesEl();
185
199
  requestAnimationFrame(function () {
200
+ // Re-check just before the actual write — the user may have scrolled
201
+ // up during the frame between the synchronous gate above and this rAF
202
+ // firing. Without this re-check the snap-back race re-emerges at a
203
+ // single-frame granularity.
204
+ var dist = messagesEl.scrollHeight - messagesEl.scrollTop - messagesEl.clientHeight;
205
+ if (dist > 150) {
206
+ isUserScrolledUp = true;
207
+ return;
208
+ }
186
209
  messagesEl.scrollTop = messagesEl.scrollHeight;
187
210
  });
188
211
  }
package/lib/sdk-bridge.js CHANGED
@@ -1269,10 +1269,32 @@ function createSDKBridge(opts) {
1269
1269
 
1270
1270
  var codexConfig = getCodexConfig(sm);
1271
1271
  var mergedMcpServers = mergeMcpServers(getMcpServers(), getRemoteMcpServers) || undefined;
1272
+
1273
+ // Derive an explicit session title for fresh queries so the SDK records
1274
+ // it at session creation and skips its own auto-generation. This also
1275
+ // lets us short-circuit autoGenerateTitle below for the common case.
1276
+ // Only applied to NEW sessions (no cliSessionId yet) — when resuming,
1277
+ // the SDK ignores Options.title in favor of the persisted title.
1278
+ var initialTitle = null;
1279
+ if (!session.cliSessionId && !session.titleManuallySet && !session.titleAutoGenerated) {
1280
+ if (session.title) {
1281
+ // Loop / scheduled / mate-seeded sessions arrive with a title already set.
1282
+ initialTitle = session.title;
1283
+ } else if (typeof text === "string") {
1284
+ // Derive a quick first-line snippet from the user's first message.
1285
+ // Skip if too short to be meaningful — fall back to autoGenerateTitle.
1286
+ var firstLine = text.replace(/\s+/g, " ").trim();
1287
+ if (firstLine.length >= 10) {
1288
+ initialTitle = firstLine.length > 60 ? firstLine.substring(0, 60) : firstLine;
1289
+ }
1290
+ }
1291
+ }
1292
+
1272
1293
  var queryOpts = {
1273
1294
  cwd: cwd,
1274
1295
  model: queryModel,
1275
1296
  effort: ls.effort || sm.currentEffort || undefined,
1297
+ title: initialTitle || undefined,
1276
1298
  toolServers: mergedMcpServers,
1277
1299
  toolServerDescriptors: extractMcpDescriptors(mergedMcpServers) || undefined,
1278
1300
  resumeSessionId: session.cliSessionId || undefined,
@@ -1305,6 +1327,18 @@ function createSDKBridge(opts) {
1305
1327
  try {
1306
1328
  handle = await sessionAdapter.createQuery(queryOpts);
1307
1329
  console.log("[sdk-bridge] createQuery returned handle, vendor=" + sessionAdapter.vendor);
1330
+ // SDK accepted the explicit title — adopt it locally so the session
1331
+ // list reflects it immediately and autoGenerateTitle skips this
1332
+ // session (titleAutoGenerated gates re-trigger).
1333
+ if (initialTitle && !session.title) {
1334
+ session.title = initialTitle;
1335
+ session.titleAutoGenerated = true;
1336
+ sm.saveSessionFile(session);
1337
+ sm.broadcastSessionList();
1338
+ } else if (initialTitle && session.title === initialTitle) {
1339
+ session.titleAutoGenerated = true;
1340
+ sm.saveSessionFile(session);
1341
+ }
1308
1342
  } catch (e) {
1309
1343
  console.error("[sdk-bridge] Failed to create query for session " + session.localId + ":", e.message || e);
1310
1344
  console.error("[sdk-bridge] cliSessionId:", session.cliSessionId, "resume:", !!session.cliSessionId);
@@ -1200,7 +1200,13 @@ function createClaudeAdapter(opts) {
1200
1200
  if (queryOpts.resumeSessionId) sdkOptions.resume = queryOpts.resumeSessionId;
1201
1201
 
1202
1202
  // Claude-specific options from adapterOptions.CLAUDE
1203
- if (co.settingSources) sdkOptions.settingSources = co.settingSources;
1203
+ // Always set settingSources explicitly. SDK 0.2.119+ defaults to
1204
+ // loading ALL sources when omitted, but Clay relies on the caller
1205
+ // declaring its scope (e.g. auto-title and mention sub-queries pass
1206
+ // ["user"] only). Falling through to the SDK default would silently
1207
+ // include project/local settings in those isolated paths.
1208
+ sdkOptions.settingSources = co.settingSources || ["user", "project", "local"];
1209
+ if (queryOpts.title) sdkOptions.title = queryOpts.title;
1204
1210
  if (co.includePartialMessages != null) sdkOptions.includePartialMessages = co.includePartialMessages;
1205
1211
  if (co.enableFileCheckpointing != null) sdkOptions.enableFileCheckpointing = co.enableFileCheckpointing;
1206
1212
  if (co.extraArgs) sdkOptions.extraArgs = co.extraArgs;
@@ -1350,7 +1356,10 @@ function createClaudeAdapter(opts) {
1350
1356
  var queryOptions = {
1351
1357
  cwd: workerCwd,
1352
1358
  };
1353
- if (claudeOpts.settingSources) queryOptions.settingSources = claudeOpts.settingSources;
1359
+ // Always set settingSources explicitly. See in-process path comment
1360
+ // above for the SDK 0.2.119+ default-change rationale.
1361
+ queryOptions.settingSources = claudeOpts.settingSources || ["user", "project", "local"];
1362
+ if (queryOpts.title) queryOptions.title = queryOpts.title;
1354
1363
  if (claudeOpts.includePartialMessages != null) queryOptions.includePartialMessages = claudeOpts.includePartialMessages;
1355
1364
  if (claudeOpts.enableFileCheckpointing != null) queryOptions.enableFileCheckpointing = claudeOpts.enableFileCheckpointing;
1356
1365
  if (claudeOpts.extraArgs) queryOptions.extraArgs = claudeOpts.extraArgs;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clay-server",
3
- "version": "2.36.2-beta.9",
3
+ "version": "2.37.0-beta.2",
4
4
  "description": "Self-hosted team workspace for Claude Code and Codex. Multi-user, browser-based, with persistent AI mates.",
5
5
  "bin": {
6
6
  "clay-server": "./bin/cli.js",
@@ -48,7 +48,7 @@
48
48
  "homepage": "https://github.com/chadbyte/clay#readme",
49
49
  "author": "Chad",
50
50
  "dependencies": {
51
- "@anthropic-ai/claude-agent-sdk": "^0.2.112",
51
+ "@anthropic-ai/claude-agent-sdk": "^0.2.132",
52
52
  "@lydell/node-pty": "^1.2.0-beta.3",
53
53
  "@openai/codex": "^0.124.0",
54
54
  "imapflow": "^1.3.1",