clay-server 2.23.0-beta.2 → 2.23.0-beta.4

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/bin/cli.js CHANGED
@@ -443,20 +443,13 @@ async function restartDaemonFromConfig() {
443
443
  }
444
444
 
445
445
  // Rebuild config (preserve everything except pid)
446
- var newConfig = {
446
+ var newConfig = Object.assign({}, lastConfig, {
447
447
  pid: null,
448
448
  port: targetPort,
449
- pinHash: lastConfig.pinHash || null,
450
- tls: lastConfig.tls !== undefined ? lastConfig.tls : useHttps,
451
- debug: lastConfig.debug || false,
452
- keepAwake: lastConfig.keepAwake || false,
453
- dangerouslySkipPermissions: lastConfig.dangerouslySkipPermissions || false,
454
- osUsers: lastConfig.osUsers || false,
455
449
  projects: (lastConfig.projects || []).filter(function (p) {
456
450
  return fs.existsSync(p.path);
457
- }),
458
- removedProjects: lastConfig.removedProjects || [],
459
- };
451
+ })
452
+ });
460
453
 
461
454
  ensureConfigDir();
462
455
  saveConfig(newConfig);
package/lib/mates.js CHANGED
@@ -409,17 +409,12 @@ function enforceTeamAwareness(filePath) {
409
409
  content = content.substring(0, teamIdx).trimEnd() + content.substring(endOfTeam);
410
410
  }
411
411
 
412
- // Insert before project registry, session memory, or crisis safety section if present, otherwise append
413
- var projRegPos = content.indexOf(PROJECT_REGISTRY_MARKER);
414
- var sessionMemPos = content.indexOf(SESSION_MEMORY_MARKER);
415
- var crisisPos = content.indexOf(crisisSafety.MARKER);
412
+ // Insert before the first subsequent system section (in order)
416
413
  var insertBefore = -1;
417
- if (projRegPos !== -1) {
418
- insertBefore = projRegPos;
419
- } else if (sessionMemPos !== -1) {
420
- insertBefore = sessionMemPos;
421
- } else if (crisisPos !== -1) {
422
- insertBefore = crisisPos;
414
+ var teamInsertCandidates = [PROJECT_REGISTRY_MARKER, SESSION_MEMORY_MARKER, STICKY_NOTES_MARKER, DEBATE_AWARENESS_MARKER, crisisSafety.MARKER];
415
+ for (var ti = 0; ti < teamInsertCandidates.length; ti++) {
416
+ var tip = content.indexOf(teamInsertCandidates[ti]);
417
+ if (tip !== -1) { insertBefore = tip; break; }
423
418
  }
424
419
  if (insertBefore !== -1) {
425
420
  content = content.substring(0, insertBefore).trimEnd() + TEAM_SECTION + "\n\n" + content.substring(insertBefore);
package/lib/project.js CHANGED
@@ -1367,7 +1367,7 @@ function createProjectContext(opts) {
1367
1367
  if (active) {
1368
1368
  userPresence.setPresence(slug, presenceKey, active.localId, storedPresence ? storedPresence.mateDm : null);
1369
1369
  }
1370
- if (storedPresence && storedPresence.mateDm) {
1370
+ if (storedPresence && storedPresence.mateDm && !isMate) {
1371
1371
  sendTo(ws, { type: "restore_mate_dm", mateId: storedPresence.mateDm });
1372
1372
  }
1373
1373
 
@@ -1870,8 +1870,12 @@ function createProjectContext(opts) {
1870
1870
  }
1871
1871
 
1872
1872
  if (msg.type === "set_mate_dm") {
1873
- var dmPresKey = ws._clayUser ? ws._clayUser.id : "_default";
1874
- userPresence.setMateDm(slug, dmPresKey, msg.mateId || null);
1873
+ // Only store mateDm on non-mate projects (main project presence).
1874
+ // Mate projects should never hold mateDm to avoid circular restore loops.
1875
+ if (!isMate) {
1876
+ var dmPresKey = ws._clayUser ? ws._clayUser.id : "_default";
1877
+ userPresence.setMateDm(slug, dmPresKey, msg.mateId || null);
1878
+ }
1875
1879
  return;
1876
1880
  }
1877
1881
 
@@ -7041,11 +7045,6 @@ function createProjectContext(opts) {
7041
7045
  var claudeMdPath = path.join(cwd, "CLAUDE.md");
7042
7046
  // Enforce immediately on startup
7043
7047
  try { matesModule.enforceTeamAwareness(claudeMdPath); } catch (e) {}
7044
- try {
7045
- var _projList = getProjectList();
7046
- var _projData = _projList.filter(function (p) { return !p.isMate && !p.isWorktree; }).map(function (p) { return { slug: p.slug, path: p.path, title: p.title || p.project, icon: p.icon }; });
7047
- matesModule.enforceProjectRegistry(claudeMdPath, _projData);
7048
- } catch (e) {}
7049
7048
  try { matesModule.enforceSessionMemory(claudeMdPath); } catch (e) {}
7050
7049
  try { matesModule.enforceStickyNotes(claudeMdPath); } catch (e) {}
7051
7050
  try { matesModule.enforceDebateAwareness(claudeMdPath); } catch (e) {}
@@ -7069,11 +7068,8 @@ function createProjectContext(opts) {
7069
7068
  crisisDebounce = setTimeout(function () {
7070
7069
  crisisDebounce = null;
7071
7070
  try { matesModule.enforceTeamAwareness(claudeMdPath); } catch (e) {}
7072
- try {
7073
- var _projList2 = getProjectList();
7074
- var _projData2 = _projList2.filter(function (p) { return !p.isMate && !p.isWorktree; }).map(function (p) { return { slug: p.slug, path: p.path, title: p.title || p.project, icon: p.icon }; });
7075
- matesModule.enforceProjectRegistry(claudeMdPath, _projData2);
7076
- } catch (e) {}
7071
+ // Note: project registry is NOT enforced in the watcher to avoid
7072
+ // write cascades (server.js scheduleProjectRegistryRefresh handles updates)
7077
7073
  try { matesModule.enforceSessionMemory(claudeMdPath); } catch (e) {}
7078
7074
  try { matesModule.enforceStickyNotes(claudeMdPath); } catch (e) {}
7079
7075
  try { matesModule.enforceDebateAwareness(claudeMdPath); } catch (e) {}
package/lib/public/app.js CHANGED
@@ -607,8 +607,16 @@ import { initDebate, handleDebateStarted, handleDebateResumed, handleDebateTurn,
607
607
  dmTargetUser = targetUser;
608
608
 
609
609
  // Notify server of active mate DM (server-side presence tracking)
610
- if (targetUser && targetUser.isMate && ws && ws.readyState === 1) {
611
- try { ws.send(JSON.stringify({ type: "set_mate_dm", mateId: targetUser.id })); } catch(e) {}
610
+ // IMPORTANT: set_mate_dm must go to the MAIN project, not a mate project WS.
611
+ // When switching between mates, ws points to the current mate project,
612
+ // so we defer sending set_mate_dm until we reconnect to the main project's context.
613
+ // The server will also receive it via the mate project's onDmMessage handler,
614
+ // but the presence should only be stored on the main project slug.
615
+ if (targetUser && targetUser.isMate) {
616
+ // Send to the current WS only if it's the main project (not another mate)
617
+ if (!mateProjectSlug && ws && ws.readyState === 1) {
618
+ try { ws.send(JSON.stringify({ type: "set_mate_dm", mateId: targetUser.id })); } catch(e) {}
619
+ }
612
620
  }
613
621
 
614
622
  // Clear unread for this user
@@ -972,7 +980,8 @@ import { initDebate, handleDebateStarted, handleDebateResumed, handleDebateTurn,
972
980
 
973
981
  function connectMateProject(slug) {
974
982
  mateProjectSlug = slug;
975
- savedMainSlug = currentSlug;
983
+ // Only save the main slug on the FIRST mate switch (preserve original main project)
984
+ if (!savedMainSlug) savedMainSlug = currentSlug;
976
985
  currentSlug = slug;
977
986
  wsPath = "/p/" + slug + "/ws";
978
987
  resetClientState();
@@ -727,7 +727,7 @@
727
727
  <div class="user-island-profile">
728
728
  <div class="user-island-avatar"><span class="user-island-avatar-letter">?</span></div>
729
729
  <div class="user-island-info">
730
- <span class="user-island-name">Awesome Clay User</span>
730
+ <span class="user-island-name"></span>
731
731
  <span class="user-island-cta hidden">Personalize your name</span>
732
732
  </div>
733
733
  </div>
@@ -64,7 +64,10 @@ export function debouncedSave() {
64
64
  export function applyToIsland() {
65
65
  var avatarWrap = document.querySelector('.user-island-avatar');
66
66
  var nameEl = document.querySelector('.user-island-name');
67
- if (!avatarWrap || !nameEl) return;
67
+ if (!avatarWrap || !nameEl) {
68
+ requestAnimationFrame(applyToIsland);
69
+ return;
70
+ }
68
71
 
69
72
  var displayName = profile.name || 'Awesome Clay User';
70
73
 
package/lib/server.js CHANGED
@@ -2931,28 +2931,9 @@ function createServer(opts) {
2931
2931
  });
2932
2932
  projects.set(slug, ctx);
2933
2933
  ctx.warmup();
2934
- refreshMateProjectRegistries();
2935
2934
  return true;
2936
2935
  }
2937
2936
 
2938
- // --- Refresh project registry on all mate CLAUDE.md files ---
2939
- function refreshMateProjectRegistries() {
2940
- var projList = [];
2941
- projects.forEach(function (ctx) {
2942
- var status = ctx.getStatus();
2943
- if (!status.isMate && !status.isWorktree) {
2944
- projList.push({ slug: status.slug, path: status.path, title: status.title || status.project, icon: status.icon });
2945
- }
2946
- });
2947
- projects.forEach(function (ctx) {
2948
- var status = ctx.getStatus();
2949
- if (status.isMate) {
2950
- var claudeMdPath = path.join(status.path, "CLAUDE.md");
2951
- try { mates.enforceProjectRegistry(claudeMdPath, projList); } catch (e) {}
2952
- }
2953
- });
2954
- }
2955
-
2956
2937
  // --- DM message handler (server-level, cross-project) ---
2957
2938
  function handleDmMessage(ws, msg) {
2958
2939
  if (!users.isMultiUser() || !ws._clayUser) return;
@@ -3335,7 +3316,6 @@ function createServer(opts) {
3335
3316
  if (!ctx) return false;
3336
3317
  ctx.destroy();
3337
3318
  projects.delete(slug);
3338
- refreshMateProjectRegistries();
3339
3319
  return true;
3340
3320
  }
3341
3321
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clay-server",
3
- "version": "2.23.0-beta.2",
3
+ "version": "2.23.0-beta.4",
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",