clay-server 2.35.0 → 2.35.1-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.
@@ -27,47 +27,65 @@ function attachMateInteraction(ctx) {
27
27
  // it comes from the debate module initialized after this one.
28
28
 
29
29
  // --- @Mention handler ---
30
- var MENTION_WINDOW = 20; // turns to check for session continuity
31
-
32
- function getRecentTurns(session, n) {
30
+ var MENTION_WINDOW = 20; // turns to check for session continuity (recency check only)
31
+ var MENTION_CONTEXT_BUDGET = 32 * 1024; // 32KB total budget for prior turns fed into mention context
32
+ var MENTION_CONTEXT_MAX_TURNS = 200; // hard safety cap so we never walk the entire session
33
+
34
+ // Walk session history backwards collecting full turns (no per-turn truncation)
35
+ // until either maxTurns is reached or byteBudget is exhausted. Each turn keeps
36
+ // its full text; we drop older turns first when the budget runs out.
37
+ function getRecentTurns(session, maxTurns, byteBudget) {
33
38
  var turns = [];
39
+ var totalBytes = 0;
34
40
  var history = session.history;
35
- // Walk backwards through history, collect user/assistant/mention text turns
36
41
  var assistantBuffer = "";
37
- for (var i = history.length - 1; i >= 0 && turns.length < n; i--) {
42
+
43
+ function tryPush(turn) {
44
+ var size = (turn.text || "").length + (turn.role || "").length + 4;
45
+ // Always keep at least one turn so a single long message still gets through.
46
+ if (byteBudget && turns.length > 0 && totalBytes + size > byteBudget) return false;
47
+ turns.push(turn);
48
+ totalBytes += size;
49
+ return true;
50
+ }
51
+
52
+ for (var i = history.length - 1; i >= 0 && turns.length < maxTurns; i--) {
38
53
  var entry = history[i];
39
54
  if (entry.type === "user_message") {
40
55
  if (assistantBuffer) {
41
- turns.push({ role: "assistant", text: assistantBuffer.trim() });
56
+ if (!tryPush({ role: "assistant", text: assistantBuffer.trim() })) { assistantBuffer = ""; break; }
42
57
  assistantBuffer = "";
43
58
  }
44
- turns.push({ role: "user", text: entry.text || "" });
59
+ if (!tryPush({ role: "user", text: entry.text || "" })) break;
45
60
  } else if (entry.type === "delta" || entry.type === "text") {
46
61
  assistantBuffer = (entry.text || "") + assistantBuffer;
47
62
  } else if (entry.type === "mention_response") {
48
63
  if (assistantBuffer) {
49
- turns.push({ role: "assistant", text: assistantBuffer.trim() });
64
+ if (!tryPush({ role: "assistant", text: assistantBuffer.trim() })) { assistantBuffer = ""; break; }
50
65
  assistantBuffer = "";
51
66
  }
52
- turns.push({ role: "@" + (entry.mateName || "Mate"), text: entry.text || "", mateId: entry.mateId });
67
+ if (!tryPush({ role: "@" + (entry.mateName || "Mate"), text: entry.text || "", mateId: entry.mateId })) break;
53
68
  } else if (entry.type === "mention_user") {
54
69
  if (assistantBuffer) {
55
- turns.push({ role: "assistant", text: assistantBuffer.trim() });
70
+ if (!tryPush({ role: "assistant", text: assistantBuffer.trim() })) { assistantBuffer = ""; break; }
56
71
  assistantBuffer = "";
57
72
  }
58
- turns.push({ role: "user", text: "@" + (entry.mateName || "Mate") + " " + (entry.text || ""), mateId: entry.mateId });
73
+ if (!tryPush({ role: "user", text: "@" + (entry.mateName || "Mate") + " " + (entry.text || ""), mateId: entry.mateId })) break;
59
74
  }
60
75
  }
61
- if (assistantBuffer) {
62
- turns.push({ role: "assistant", text: assistantBuffer.trim() });
76
+ if (assistantBuffer && turns.length < maxTurns) {
77
+ tryPush({ role: "assistant", text: assistantBuffer.trim() });
63
78
  }
64
79
  turns.reverse();
65
80
  return turns;
66
81
  }
67
82
 
68
- // Check if the given mate has a mention response in the recent window
69
- function hasMateInWindow(recentTurns, mateId) {
70
- for (var i = 0; i < recentTurns.length; i++) {
83
+ // Check if the given mate has a mention response within the most recent
84
+ // `windowSize` turns. Continuity is recency-based, even when getRecentTurns
85
+ // returns more turns to satisfy the byte budget.
86
+ function hasMateInWindow(recentTurns, mateId, windowSize) {
87
+ var start = windowSize ? Math.max(0, recentTurns.length - windowSize) : 0;
88
+ for (var i = start; i < recentTurns.length; i++) {
71
89
  if (recentTurns[i].mateId === mateId && recentTurns[i].role.charAt(0) === "@") {
72
90
  return true;
73
91
  }
@@ -83,11 +101,11 @@ function attachMateInteraction(ctx) {
83
101
  var t = recentTurns[i];
84
102
  if (t.mateId === mateId && t.role.charAt(0) === "@") { found = true; break; }
85
103
  if (t.role === "user") {
86
- lines.unshift("[User said after your last reply: " + t.text.substring(0, 500) + "]");
104
+ lines.unshift("[User said after your last reply: " + t.text + "]");
87
105
  } else if (t.role === "assistant") {
88
- lines.unshift("[Session agent replied: " + t.text.substring(0, 500) + "]");
106
+ lines.unshift("[Session agent replied: " + t.text + "]");
89
107
  } else if (t.role.charAt(0) === "@") {
90
- lines.unshift("[@" + t.role.substring(1) + " replied: " + t.text.substring(0, 500) + "]");
108
+ lines.unshift("[@" + t.role.substring(1) + " replied: " + t.text + "]");
91
109
  }
92
110
  }
93
111
  if (!found || lines.length === 0) return "";
@@ -101,7 +119,7 @@ function attachMateInteraction(ctx) {
101
119
  for (var i = 0; i < recentTurns.length; i++) {
102
120
  var t = recentTurns[i];
103
121
  var label = t.role === "user" ? userName : t.role === "assistant" ? "Session Agent" : t.role;
104
- lines.push(label + ": " + t.text.substring(0, 500));
122
+ lines.push(label + ": " + t.text);
105
123
  }
106
124
  return lines.join("\n") + "\n\n";
107
125
  }
@@ -512,7 +530,7 @@ function attachMateInteraction(ctx) {
512
530
  sendToSessionOthers(ws, session.localId, hydrateImageRefs(mentionUserEntry));
513
531
 
514
532
  // Extract recent turns for continuity check
515
- var recentTurns = getRecentTurns(session, MENTION_WINDOW);
533
+ var recentTurns = getRecentTurns(session, MENTION_CONTEXT_MAX_TURNS, MENTION_CONTEXT_BUDGET);
516
534
 
517
535
  // Determine user name for context
518
536
  var userName = "User";
@@ -612,7 +630,7 @@ function attachMateInteraction(ctx) {
612
630
  // Session continuity: check if this mate has a response in the recent window
613
631
  var existingSession = session._mentionSessions[msg.mateId];
614
632
  // Don't reuse a session that's still generating a digest (would mix digest output into mention stream)
615
- var canContinue = existingSession && existingSession.isAlive() && !existingSession._digesting && hasMateInWindow(recentTurns, msg.mateId);
633
+ var canContinue = existingSession && existingSession.isAlive() && !existingSession._digesting && hasMateInWindow(recentTurns, msg.mateId, MENTION_WINDOW);
616
634
 
617
635
  if (canContinue) {
618
636
  // Continue existing mention session with middle context
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clay-server",
3
- "version": "2.35.0",
3
+ "version": "2.35.1-beta.1",
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",