clay-server 2.31.0 → 2.32.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.
Files changed (74) hide show
  1. package/lib/browser-mcp-server.js +32 -44
  2. package/lib/debate-mcp-server.js +14 -31
  3. package/lib/mcp-local.js +31 -1
  4. package/lib/project-connection.js +4 -2
  5. package/lib/project-filesystem.js +47 -1
  6. package/lib/project-http.js +75 -8
  7. package/lib/project-mcp.js +4 -0
  8. package/lib/project-sessions.js +88 -51
  9. package/lib/project-user-message.js +12 -7
  10. package/lib/project.js +204 -90
  11. package/lib/public/app.js +123 -448
  12. package/lib/public/codex-avatar.png +0 -0
  13. package/lib/public/css/debate.css +3 -2
  14. package/lib/public/css/filebrowser.css +91 -1
  15. package/lib/public/css/icon-strip.css +21 -5
  16. package/lib/public/css/input.css +181 -100
  17. package/lib/public/css/mates.css +43 -0
  18. package/lib/public/css/mention.css +48 -4
  19. package/lib/public/css/menus.css +1 -1
  20. package/lib/public/css/messages.css +2 -0
  21. package/lib/public/css/notifications-center.css +19 -0
  22. package/lib/public/index.html +46 -24
  23. package/lib/public/modules/app-connection.js +138 -37
  24. package/lib/public/modules/app-cursors.js +18 -17
  25. package/lib/public/modules/app-debate-ui.js +9 -9
  26. package/lib/public/modules/app-dm.js +170 -131
  27. package/lib/public/modules/app-favicon.js +28 -26
  28. package/lib/public/modules/app-header.js +79 -68
  29. package/lib/public/modules/app-home-hub.js +55 -47
  30. package/lib/public/modules/app-loop-ui.js +34 -18
  31. package/lib/public/modules/app-loop-wizard.js +6 -6
  32. package/lib/public/modules/app-messages.js +195 -152
  33. package/lib/public/modules/app-misc.js +23 -12
  34. package/lib/public/modules/app-notifications.js +97 -3
  35. package/lib/public/modules/app-panels.js +203 -49
  36. package/lib/public/modules/app-projects.js +159 -150
  37. package/lib/public/modules/app-rate-limit.js +5 -4
  38. package/lib/public/modules/app-rendering.js +149 -101
  39. package/lib/public/modules/app-skills-install.js +4 -4
  40. package/lib/public/modules/context-sources.js +12 -41
  41. package/lib/public/modules/dom-refs.js +21 -0
  42. package/lib/public/modules/filebrowser.js +173 -2
  43. package/lib/public/modules/input.js +86 -0
  44. package/lib/public/modules/mate-sidebar.js +38 -0
  45. package/lib/public/modules/mention.js +24 -6
  46. package/lib/public/modules/scheduler.js +1 -1
  47. package/lib/public/modules/sidebar-mates.js +66 -34
  48. package/lib/public/modules/sidebar-mobile.js +34 -30
  49. package/lib/public/modules/sidebar-projects.js +60 -57
  50. package/lib/public/modules/sidebar-sessions.js +75 -69
  51. package/lib/public/modules/sidebar.js +12 -20
  52. package/lib/public/modules/skills.js +8 -9
  53. package/lib/public/modules/sticky-notes.js +1 -2
  54. package/lib/public/modules/store.js +9 -2
  55. package/lib/public/modules/stt.js +4 -1
  56. package/lib/public/modules/tools.js +14 -9
  57. package/lib/sdk-bridge.js +511 -1113
  58. package/lib/sdk-message-processor.js +123 -134
  59. package/lib/sdk-worker.js +4 -0
  60. package/lib/server-dm.js +1 -0
  61. package/lib/server.js +86 -1
  62. package/lib/sessions.js +47 -36
  63. package/lib/ws-schema.js +2 -0
  64. package/lib/yoke/adapters/claude-worker.js +559 -0
  65. package/lib/yoke/adapters/claude.js +1418 -0
  66. package/lib/yoke/adapters/codex.js +968 -0
  67. package/lib/yoke/adapters/gemini.js +668 -0
  68. package/lib/yoke/codex-app-server.js +307 -0
  69. package/lib/yoke/index.js +199 -0
  70. package/lib/yoke/instructions.js +62 -0
  71. package/lib/yoke/interface.js +92 -0
  72. package/lib/yoke/mcp-bridge-server.js +294 -0
  73. package/lib/yoke/package.json +7 -0
  74. package/package.json +3 -1
@@ -2,8 +2,10 @@
2
2
  // Extracted from app.js (PR-34)
3
3
 
4
4
  import { refreshIcons } from './icons.js';
5
-
6
- var _ctx = null;
5
+ import { store } from './store.js';
6
+ import { getSendBtn, getStatusDot } from './dom-refs.js';
7
+ import { onThemeChange } from './theme.js';
8
+ import { getActivityEl, setActivityEl, addToMessages, scrollToBottom } from './app-rendering.js';
7
9
 
8
10
  // --- Module-owned state ---
9
11
  var faviconLink, faviconOrigHref, faviconCanvas, faviconCtx, faviconImg, faviconImgReady;
@@ -14,9 +16,7 @@ var ioTimer = null;
14
16
  var sessionIoTimers = {};
15
17
  var crossProjectBlinkTimer = null;
16
18
 
17
- export function initFavicon(ctx) {
18
- _ctx = ctx;
19
-
19
+ export function initFavicon() {
20
20
  faviconLink = document.querySelector('link[rel="icon"]');
21
21
  faviconCanvas = document.createElement("canvas");
22
22
  faviconCanvas.width = 32;
@@ -29,11 +29,11 @@ export function initFavicon(ctx) {
29
29
  (function () {
30
30
  faviconImg = new Image();
31
31
  faviconImg.onload = function () { faviconImgReady = true; };
32
- faviconImg.src = _ctx.basePath + "favicon-banded.png";
32
+ faviconImg.src = (store.get('basePath') || "") + "favicon-banded.png";
33
33
  })();
34
34
 
35
35
  // Reset cached favicon href on theme change
36
- _ctx.onThemeChange(function () { faviconOrigHref = null; });
36
+ onThemeChange(function () { faviconOrigHref = null; });
37
37
  }
38
38
 
39
39
  export function updateFavicon(bgColor) {
@@ -84,21 +84,22 @@ export function drawFaviconAnimFrame() {
84
84
  }
85
85
 
86
86
  export function setSendBtnMode(mode) {
87
+ var sendBtn = getSendBtn();
87
88
  if (mode === "stop") {
88
- _ctx.sendBtn.disabled = false;
89
- _ctx.sendBtn.classList.add("stop");
90
- _ctx.sendBtn.innerHTML = '<i data-lucide="square"></i>';
89
+ sendBtn.disabled = false;
90
+ sendBtn.classList.add("stop");
91
+ sendBtn.innerHTML = '<i data-lucide="square"></i>';
91
92
  } else {
92
- _ctx.sendBtn.disabled = false;
93
- _ctx.sendBtn.classList.remove("stop");
94
- _ctx.sendBtn.innerHTML = '<i data-lucide="arrow-up"></i>';
93
+ sendBtn.disabled = false;
94
+ sendBtn.classList.remove("stop");
95
+ sendBtn.innerHTML = '<i data-lucide="arrow-up"></i>';
95
96
  }
96
97
  refreshIcons();
97
98
  }
98
99
 
99
100
  export function blinkIO() {
100
- if (!_ctx.connected) return;
101
- var dot = _ctx.getStatusDot();
101
+ if (!store.get('connected')) return;
102
+ var dot = getStatusDot();
102
103
  if (dot) dot.classList.add("io");
103
104
  // Also blink the active session's processing dot in sidebar (project or mate)
104
105
  var sessionDot = document.querySelector(".session-item.active .session-processing") ||
@@ -114,17 +115,18 @@ export function blinkIO() {
114
115
  }
115
116
  // Mobile chat chip dot + mobile session dot
116
117
  var mobileChipDot = null;
117
- if (_ctx.dmMode && _ctx.dmTargetUser && _ctx.dmTargetUser.isMate) {
118
- mobileChipDot = document.querySelector('.mobile-chat-chip[data-mate-id="' + _ctx.dmTargetUser.id + '"] .mobile-chat-chip-dot');
118
+ var _s = store.snap();
119
+ if (_s.dmMode && _s.dmTargetUser && _s.dmTargetUser.isMate) {
120
+ mobileChipDot = document.querySelector('.mobile-chat-chip[data-mate-id="' + _s.dmTargetUser.id + '"] .mobile-chat-chip-dot');
119
121
  } else {
120
- mobileChipDot = document.querySelector('.mobile-chat-chip[data-slug="' + _ctx.currentSlug + '"] .mobile-chat-chip-dot');
122
+ mobileChipDot = document.querySelector('.mobile-chat-chip[data-slug="' + _s.currentSlug + '"] .mobile-chat-chip-dot');
121
123
  }
122
124
  if (mobileChipDot) mobileChipDot.classList.add("io");
123
125
  var mobileSessionDot = document.querySelector('.mobile-session-item.active .mobile-session-dot');
124
126
  if (mobileSessionDot) mobileSessionDot.classList.add("io");
125
127
  clearTimeout(ioTimer);
126
128
  ioTimer = setTimeout(function () {
127
- var d = _ctx.getStatusDot();
129
+ var d = getStatusDot();
128
130
  if (d) d.classList.remove("io");
129
131
  var sd = document.querySelector(".session-item.active .session-processing.io") ||
130
132
  document.querySelector(".mate-session-item.active .session-processing.io");
@@ -194,19 +196,19 @@ export function stopUrgentBlink() {
194
196
 
195
197
  export function setActivity(text) {
196
198
  if (text) {
197
- if (!_ctx.getActivityEl()) {
199
+ if (!getActivityEl()) {
198
200
  var _actEl = document.createElement("div");
199
201
  _actEl.className = "activity-inline";
200
202
  _actEl.innerHTML =
201
203
  '<div class="mate-thinking-dots"><span></span><span></span><span></span></div>';
202
- _ctx.setActivityEl(_actEl);
203
- _ctx.addToMessages(_actEl);
204
+ setActivityEl(_actEl);
205
+ addToMessages(_actEl);
204
206
  }
205
- _ctx.scrollToBottom();
207
+ scrollToBottom();
206
208
  } else {
207
- if (_ctx.getActivityEl()) {
208
- _ctx.getActivityEl().remove();
209
- _ctx.setActivityEl(null);
209
+ if (getActivityEl()) {
210
+ getActivityEl().remove();
211
+ setActivityEl(null);
210
212
  }
211
213
  }
212
214
  }
@@ -3,41 +3,49 @@
3
3
 
4
4
  import { refreshIcons, iconHtml } from './icons.js';
5
5
  import { escapeHtml, copyToClipboard } from './utils.js';
6
-
7
- var _ctx = null;
6
+ import { store } from './store.js';
7
+ import { getWs } from './ws-ref.js';
8
+ import { getMessagesEl } from './dom-refs.js';
9
+ import { getActivityEl, setActivityEl, getTurnCounter, setTurnCounter, getPrependAnchor, setPrependAnchor, finalizeAssistantBlock } from './app-rendering.js';
10
+ import { processMessage } from './app-messages.js';
11
+ import { saveToolState, resetToolState, restoreToolState } from './tools.js';
12
+ import { getSessionUsage, setSessionUsage, getContextData, setContextData, updateContextPanel, updateUsagePanel } from './app-panels.js';
13
+ import { onHistoryPrepended as onSessionSearchHistoryPrepended } from './session-search.js';
8
14
 
9
15
  // --- Module-owned state ---
10
16
  var sessionInfoPopover = null;
11
17
  var historySentinelObserver = null;
12
18
 
13
- export function initHeader(ctx) {
14
- _ctx = ctx;
19
+ export function initHeader() {
20
+ var headerRenameBtn = document.getElementById("header-rename-btn");
21
+ var headerTitleEl = document.getElementById("header-title");
22
+ var headerInfoBtn = document.getElementById("header-info-btn");
15
23
 
16
24
  // --- Header session rename ---
17
- if (_ctx.headerRenameBtn) {
18
- _ctx.headerRenameBtn.addEventListener("click", function () {
19
- if (!_ctx.activeSessionId) return;
20
- var currentText = _ctx.headerTitleEl.textContent;
25
+ if (headerRenameBtn) {
26
+ headerRenameBtn.addEventListener("click", function () {
27
+ if (!store.get('activeSessionId')) return;
28
+ var currentText = headerTitleEl.textContent;
21
29
  var input = document.createElement("input");
22
30
  input.type = "text";
23
31
  input.className = "header-rename-input";
24
32
  input.value = currentText;
25
- _ctx.headerTitleEl.style.display = "none";
26
- _ctx.headerRenameBtn.style.display = "none";
27
- _ctx.headerTitleEl.parentNode.insertBefore(input, _ctx.headerTitleEl.nextSibling);
33
+ headerTitleEl.style.display = "none";
34
+ headerRenameBtn.style.display = "none";
35
+ headerTitleEl.parentNode.insertBefore(input, headerTitleEl.nextSibling);
28
36
  input.focus();
29
37
  input.select();
30
38
 
31
39
  function commit() {
32
40
  var newTitle = input.value.trim();
33
- var ws = _ctx.getWs();
41
+ var ws = getWs();
34
42
  if (newTitle && newTitle !== currentText && ws && ws.readyState === 1) {
35
- ws.send(JSON.stringify({ type: "rename_session", id: _ctx.activeSessionId, title: newTitle }));
36
- _ctx.headerTitleEl.textContent = newTitle;
43
+ ws.send(JSON.stringify({ type: "rename_session", id: store.get('activeSessionId'), title: newTitle }));
44
+ headerTitleEl.textContent = newTitle;
37
45
  }
38
46
  input.remove();
39
- _ctx.headerTitleEl.style.display = "";
40
- _ctx.headerRenameBtn.style.display = "";
47
+ headerTitleEl.style.display = "";
48
+ headerRenameBtn.style.display = "";
41
49
  }
42
50
 
43
51
  input.addEventListener("keydown", function (e) {
@@ -45,8 +53,8 @@ export function initHeader(ctx) {
45
53
  if (e.key === "Escape") {
46
54
  e.preventDefault();
47
55
  input.remove();
48
- _ctx.headerTitleEl.style.display = "";
49
- _ctx.headerRenameBtn.style.display = "";
56
+ headerTitleEl.style.display = "";
57
+ headerRenameBtn.style.display = "";
50
58
  }
51
59
  });
52
60
  input.addEventListener("blur", commit);
@@ -54,8 +62,8 @@ export function initHeader(ctx) {
54
62
  }
55
63
 
56
64
  // --- Session info popover ---
57
- if (_ctx.headerInfoBtn) {
58
- _ctx.headerInfoBtn.addEventListener("click", function (e) {
65
+ if (headerInfoBtn) {
66
+ headerInfoBtn.addEventListener("click", function (e) {
59
67
  e.stopPropagation();
60
68
  if (sessionInfoPopover) { closeSessionInfoPopover(); return; }
61
69
 
@@ -81,15 +89,16 @@ export function initHeader(ctx) {
81
89
  pop.appendChild(row);
82
90
  }
83
91
 
84
- if (_ctx.cliSessionId) addRow("Session ID", _ctx.cliSessionId);
85
- if (_ctx.activeSessionId) addRow("Local ID", _ctx.activeSessionId);
86
- if (_ctx.cliSessionId) addRow("Resume", "claude --resume " + _ctx.cliSessionId);
92
+ var s = store.snap();
93
+ if (s.cliSessionId) addRow("Session ID", s.cliSessionId);
94
+ if (s.activeSessionId) addRow("Local ID", s.activeSessionId);
95
+ if (s.cliSessionId) addRow("Resume", "claude --resume " + s.cliSessionId);
87
96
 
88
97
  document.body.appendChild(pop);
89
98
  sessionInfoPopover = pop;
90
99
  refreshIcons();
91
100
 
92
- var btnRect = _ctx.headerInfoBtn.getBoundingClientRect();
101
+ var btnRect = headerInfoBtn.getBoundingClientRect();
93
102
  pop.style.top = (btnRect.bottom + 6) + "px";
94
103
  pop.style.left = btnRect.left + "px";
95
104
  var popRect = pop.getBoundingClientRect();
@@ -114,8 +123,9 @@ export function closeSessionInfoPopover() {
114
123
  }
115
124
 
116
125
  export function updateHistorySentinel() {
117
- var existing = _ctx.messagesEl.querySelector(".history-sentinel");
118
- if (_ctx.historyFrom > 0) {
126
+ var messagesEl = getMessagesEl();
127
+ var existing = messagesEl.querySelector(".history-sentinel");
128
+ if (store.get('historyFrom') > 0) {
119
129
  if (!existing) {
120
130
  var sentinel = document.createElement("div");
121
131
  sentinel.className = "history-sentinel";
@@ -123,15 +133,15 @@ export function updateHistorySentinel() {
123
133
  sentinel.querySelector(".load-more-btn").addEventListener("click", function () {
124
134
  requestMoreHistory();
125
135
  });
126
- _ctx.messagesEl.insertBefore(sentinel, _ctx.messagesEl.firstChild);
136
+ messagesEl.insertBefore(sentinel, messagesEl.firstChild);
127
137
 
128
138
  // Auto-load when sentinel scrolls into view
129
139
  if (historySentinelObserver) historySentinelObserver.disconnect();
130
140
  historySentinelObserver = new IntersectionObserver(function (entries) {
131
- if (entries[0].isIntersecting && !_ctx.loadingMore && _ctx.historyFrom > 0) {
141
+ if (entries[0].isIntersecting && !store.get('loadingMore') && store.get('historyFrom') > 0) {
132
142
  requestMoreHistory();
133
143
  }
134
- }, { root: _ctx.messagesEl, rootMargin: "200px 0px 0px 0px" });
144
+ }, { root: messagesEl, rootMargin: "200px 0px 0px 0px" });
135
145
  historySentinelObserver.observe(sentinel);
136
146
  }
137
147
  } else {
@@ -141,89 +151,90 @@ export function updateHistorySentinel() {
141
151
  }
142
152
 
143
153
  export function requestMoreHistory() {
144
- var ws = _ctx.getWs();
145
- if (_ctx.loadingMore || _ctx.historyFrom <= 0 || !ws || !_ctx.connected) return;
146
- _ctx.loadingMore = true;
147
- var btn = _ctx.messagesEl.querySelector(".load-more-btn");
154
+ var ws = getWs();
155
+ var s = store.snap();
156
+ if (s.loadingMore || s.historyFrom <= 0 || !ws || !s.connected) return;
157
+ store.set({ loadingMore: true });
158
+ var messagesEl = getMessagesEl();
159
+ var btn = messagesEl.querySelector(".load-more-btn");
148
160
  if (btn) btn.classList.add("loading");
149
- ws.send(JSON.stringify({ type: "load_more_history", before: _ctx.historyFrom }));
161
+ ws.send(JSON.stringify({ type: "load_more_history", before: s.historyFrom }));
150
162
  }
151
163
 
152
164
  export function prependOlderHistory(items, meta) {
165
+ var messagesEl = getMessagesEl();
166
+
153
167
  // Save current rendering state
154
- var savedMsgEl = _ctx.getCurrentMsgEl();
155
- var savedActivity = _ctx.getActivityEl();
156
- var savedFullText = _ctx.getCurrentFullText();
157
- var savedTurnCounter = _ctx.getTurnCounter();
158
- var savedToolsState = _ctx.saveToolState();
168
+ var savedMsgEl = store.get('currentMsgEl');
169
+ var savedActivity = getActivityEl();
170
+ var savedFullText = store.get('currentFullText');
171
+ var savedTurnCounter = getTurnCounter();
172
+ var savedToolsState = saveToolState();
159
173
  // Save context & usage so old result messages don't overwrite current values
160
- var savedContext = JSON.parse(JSON.stringify(_ctx.getContextData()));
161
- var savedUsage = JSON.parse(JSON.stringify(_ctx.getSessionUsage()));
174
+ var savedContext = JSON.parse(JSON.stringify(getContextData()));
175
+ var savedUsage = JSON.parse(JSON.stringify(getSessionUsage()));
162
176
 
163
177
  // Reset to initial values for clean rendering
164
- _ctx.setCurrentMsgEl(null);
165
- _ctx.setActivityEl(null);
166
- _ctx.setCurrentFullText("");
167
- _ctx.setTurnCounter(0);
168
- _ctx.resetToolState();
178
+ store.set({ currentMsgEl: null, currentFullText: "" });
179
+ setActivityEl(null);
180
+ setTurnCounter(0);
181
+ resetToolState();
169
182
 
170
183
  // Set prepend anchor to insert before existing content
171
184
  // Skip the sentinel itself when setting anchor
172
- var firstReal = _ctx.messagesEl.querySelector(".history-sentinel");
173
- _ctx.setPrependAnchor(firstReal ? firstReal.nextSibling : _ctx.messagesEl.firstChild);
185
+ var firstReal = messagesEl.querySelector(".history-sentinel");
186
+ setPrependAnchor(firstReal ? firstReal.nextSibling : messagesEl.firstChild);
174
187
 
175
188
  // Remember the first existing content element and its position
176
- var anchorEl = _ctx.getPrependAnchor();
189
+ var anchorEl = getPrependAnchor();
177
190
  var anchorOffset = anchorEl ? anchorEl.getBoundingClientRect().top : 0;
178
191
 
179
192
  // Process each item through the rendering pipeline
180
193
  for (var i = 0; i < items.length; i++) {
181
- _ctx.processMessage(items[i]);
194
+ processMessage(items[i]);
182
195
  }
183
196
 
184
197
  // Finalize any open assistant block from the batch
185
- _ctx.finalizeAssistantBlock();
198
+ finalizeAssistantBlock();
186
199
 
187
200
  // Clear prepend mode
188
- _ctx.setPrependAnchor(null);
201
+ setPrependAnchor(null);
189
202
 
190
203
  // Restore saved state
191
- _ctx.setCurrentMsgEl(savedMsgEl);
192
- _ctx.setActivityEl(savedActivity);
193
- _ctx.setCurrentFullText(savedFullText);
194
- _ctx.setTurnCounter(savedTurnCounter);
195
- _ctx.restoreToolState(savedToolsState);
204
+ store.set({ currentMsgEl: savedMsgEl, currentFullText: savedFullText });
205
+ setActivityEl(savedActivity);
206
+ setTurnCounter(savedTurnCounter);
207
+ restoreToolState(savedToolsState);
196
208
  // Restore context & usage (old result messages must not overwrite current values)
197
- _ctx.setContextData(savedContext);
198
- _ctx.setSessionUsage(savedUsage);
199
- _ctx.updateContextPanel();
200
- _ctx.updateUsagePanel();
209
+ setContextData(savedContext);
210
+ setSessionUsage(savedUsage);
211
+ updateContextPanel();
212
+ updateUsagePanel();
201
213
 
202
214
  // Fix scroll: restore anchor element to same visual position
203
215
  if (anchorEl) {
204
216
  var newTop = anchorEl.getBoundingClientRect().top;
205
- _ctx.messagesEl.scrollTop += (newTop - anchorOffset);
217
+ messagesEl.scrollTop += (newTop - anchorOffset);
206
218
  }
207
219
 
208
220
  // Update state
209
- _ctx.historyFrom = meta.from;
210
- _ctx.loadingMore = false;
221
+ store.set({ historyFrom: meta.from, loadingMore: false });
211
222
 
212
223
  // Renumber data-turn attributes in DOM order
213
- var turnEls = _ctx.messagesEl.querySelectorAll("[data-turn]");
224
+ var turnEls = messagesEl.querySelectorAll("[data-turn]");
214
225
  for (var t = 0; t < turnEls.length; t++) {
215
226
  turnEls[t].dataset.turn = t + 1;
216
227
  }
217
- _ctx.setTurnCounter(turnEls.length);
228
+ setTurnCounter(turnEls.length);
218
229
 
219
230
  // Update sentinel
220
231
  if (meta.hasMore) {
221
- var btn = _ctx.messagesEl.querySelector(".load-more-btn");
232
+ var btn = messagesEl.querySelector(".load-more-btn");
222
233
  if (btn) btn.classList.remove("loading");
223
234
  } else {
224
235
  updateHistorySentinel();
225
236
  }
226
237
 
227
238
  // Notify in-session search that history was prepended (for pending scroll targets)
228
- _ctx.onSessionSearchHistoryPrepended();
239
+ onSessionSearchHistoryPrepended();
229
240
  }