clay-server 4.1.0-beta.13 → 4.1.0-beta.14

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.
@@ -2,10 +2,7 @@ import { store } from './store.js';
2
2
  import { getWs } from './ws-ref.js';
3
3
  import { refreshIcons, iconHtml } from './icons.js';
4
4
  import { showToast } from './utils.js';
5
- import { closeProjectLogs } from './project-logs.js';
6
- import { closeNotesBrowser } from './sticky-notes-browser.js';
7
- import { closeFileViewer } from './filebrowser.js';
8
- import { closeTerminal } from './terminal.js';
5
+ import { claimRightWorkbench, registerRightWorkbench, releaseRightWorkbench } from './right-workbench.js';
9
6
  import { humanRecurrence } from './scheduled-task-recurrence.js';
10
7
  import { humanScheduleRule } from './scheduled-task-schedule-rule.js';
11
8
  import { readExecutionRuntime, renderExecutionRuntime } from './scheduled-task-engine.js';
@@ -320,7 +317,7 @@ export function openScheduledTasks() {
320
317
  if (!canOpen()) { closeScheduledTasks(); return; }
321
318
  ensurePanel();
322
319
  if (!panel) return;
323
- closeProjectLogs(); closeNotesBrowser(); closeFileViewer(); closeTerminal();
320
+ claimRightWorkbench("scheduled-tasks");
324
321
  panel.classList.remove("hidden");
325
322
  applyWindowState(store.get('scheduledTasksWide'), false);
326
323
  store.set({ scheduledTasksOpen: true });
@@ -330,6 +327,7 @@ export function openScheduledTasks() {
330
327
  }
331
328
 
332
329
  export function closeScheduledTasks() {
330
+ releaseRightWorkbench("scheduled-tasks");
333
331
  if (panel) panel.classList.add("hidden");
334
332
  setCreateMenu(false);
335
333
  applyWindowState(store.get('scheduledTasksWide'), false);
@@ -430,6 +428,7 @@ export function handleScheduledTaskSessionSwitched(msg) {
430
428
  }
431
429
 
432
430
  export function initScheduledTasks() {
431
+ registerRightWorkbench("scheduled-tasks", closeScheduledTasks);
433
432
  var button = document.getElementById("scheduler-btn");
434
433
  if (button) button.addEventListener("click", function () { if (isScheduledTasksOpen()) closeScheduledTasks(); else openScheduledTasks(); });
435
434
  document.addEventListener("click", handleDocumentClick);
@@ -36,7 +36,7 @@ import { showHomeHub } from './app-home-hub.js';
36
36
  import { openHomeChat } from './home-mate-chat.js';
37
37
  import { openTerminal } from './terminal.js';
38
38
  import { requestKnowledgeList } from './mate-knowledge.js';
39
- import { loadRootDirectory } from './filebrowser.js';
39
+ import { loadRootDirectory, reopenFileViewer } from './filebrowser.js';
40
40
  import {
41
41
  prepareSidebarHierarchy,
42
42
  renderMobileDriverHierarchy,
@@ -91,6 +91,7 @@ export function openMobileSheet(type) {
91
91
  listEl.appendChild(fileTree);
92
92
  fileTree.classList.remove("hidden");
93
93
  }
94
+ reopenFileViewer();
94
95
  loadRootDirectory();
95
96
  } else if (type === "mate-knowledge") {
96
97
  titleEl.textContent = "Knowledge";
@@ -14,6 +14,7 @@ import { renderMiniMarkdown, getTitle } from './sticky-note-markdown.js';
14
14
  import { enhanceClayLogLinks } from './clay-log-links.js';
15
15
  import { send } from './sticky-notes-shared.js';
16
16
  import { listNoteData, focusNoteOnCanvas, hideNotes, showNotes } from './sticky-notes.js';
17
+ import { claimRightWorkbench, registerRightWorkbench, releaseRightWorkbench } from './right-workbench.js';
17
18
 
18
19
  var panel = null;
19
20
  var gridEl = null;
@@ -21,18 +22,6 @@ var tabsEl = null;
21
22
 
22
23
  // Registered by app.js so this module never imports the other right-pane tools
23
24
  // and cannot form an import cycle with them.
24
- var exclusiveClosers = [];
25
-
26
- export function registerExclusiveClosers(fns) {
27
- exclusiveClosers = (fns || []).filter(function (fn) { return typeof fn === "function"; });
28
- }
29
-
30
- function closeOtherRightTools() {
31
- for (var i = 0; i < exclusiveClosers.length; i++) {
32
- try { exclusiveClosers[i](); } catch (e) { /* a missing tool must not block opening */ }
33
- }
34
- }
35
-
36
25
  function isClosed(data) {
37
26
  if (!data) return false;
38
27
  if (data.state === "closed") return true;
@@ -283,7 +272,7 @@ export function renderNotesBrowser() {
283
272
  export function openNotesBrowser() {
284
273
  ensurePanel();
285
274
  if (!panel) return;
286
- closeOtherRightTools();
275
+ claimRightWorkbench("notes-browser");
287
276
  // The floating canvas would otherwise sit on top of the pane.
288
277
  hideNotes();
289
278
  panel.classList.remove("hidden");
@@ -295,6 +284,7 @@ export function openNotesBrowser() {
295
284
  }
296
285
 
297
286
  export function closeNotesBrowser() {
287
+ releaseRightWorkbench("notes-browser");
298
288
  if (!store.get('notesBrowserOpen')) return;
299
289
  if (panel) panel.classList.add("hidden");
300
290
  // Fullscreen is always dropped on close so the next open is a bounded pane.
@@ -311,6 +301,7 @@ export function isNotesBrowserOpen() {
311
301
  }
312
302
 
313
303
  export function initNotesBrowser() {
304
+ registerRightWorkbench("notes-browser", closeNotesBrowser);
314
305
  document.addEventListener("keydown", function (e) {
315
306
  if (e.key !== "Escape") return;
316
307
  if (!store.get('notesBrowserOpen')) return;
@@ -1,11 +1,10 @@
1
1
  import { iconHtml, refreshIcons } from './icons.js';
2
2
  import { closeSidebar } from './sidebar.js';
3
- import { closeFileViewer } from './filebrowser.js';
3
+ import { claimRightWorkbench, registerRightWorkbench, releaseRightWorkbench } from './right-workbench.js';
4
4
  import { copyToClipboard } from './utils.js';
5
5
  import { getTerminalTheme } from './theme.js';
6
6
  import { getTerminalFontFamily, getTerminalFontSize, onTerminalFontChange } from './terminal-prefs.js';
7
7
  import { createKeyToolbar } from './terminal-toolbar.js';
8
- import { closeIssues } from './issues.js';
9
8
 
10
9
  var ctx;
11
10
  var tabs = new Map(); // termId -> { id, title, exited, xterm, fitAddon, bodyEl }
@@ -136,6 +135,7 @@ function createMultiLineLinkProvider(xterm) {
136
135
  // --- Init ---
137
136
  export function initTerminal(_ctx) {
138
137
  ctx = _ctx;
138
+ registerRightWorkbench("terminal", closeTerminal);
139
139
 
140
140
  // Close panel button
141
141
  document.getElementById("terminal-close").addEventListener("click", function () {
@@ -193,8 +193,7 @@ export function openTerminal() {
193
193
  var container = ctx.terminalContainerEl;
194
194
 
195
195
  // Hide file viewer if open (also unwatches)
196
- closeFileViewer();
197
- closeIssues();
196
+ claimRightWorkbench("terminal");
198
197
 
199
198
  container.classList.remove("hidden");
200
199
  isOpen = true;
@@ -224,6 +223,7 @@ export function openTerminal() {
224
223
 
225
224
  // --- Close terminal panel (hide, detach, but keep PTYs alive) ---
226
225
  export function closeTerminal() {
226
+ releaseRightWorkbench("terminal");
227
227
  var container = ctx.terminalContainerEl;
228
228
  container.classList.remove("panel-fullscreen");
229
229
  container.classList.add("hidden");
@@ -778,10 +778,12 @@ function attachMessageProcessor(ctx) {
778
778
  var info = parsed.rateLimitInfo;
779
779
  console.log("[sdk-bridge] rate_limit_event for session " + session.localId + ": status=" + info.status + " resetsAt=" + info.resetsAt + " isUsingOverage=" + info.isUsingOverage + " isProcessing=" + session.isProcessing);
780
780
 
781
- // Broadcast reset time for top-bar usage link
781
+ // Persist the scoped reset snapshot so reconnect/history restore can reproject it.
782
782
  if (info.rateLimitType && info.resetsAt) {
783
- send({
783
+ sendAndRecord(session, {
784
784
  type: "rate_limit_usage",
785
+ sessionId: session.localId,
786
+ vendor: session.vendor || "claude",
785
787
  rateLimitType: info.rateLimitType,
786
788
  resetsAt: info.resetsAt * 1000,
787
789
  status: info.status,
@@ -792,6 +794,8 @@ function attachMessageProcessor(ctx) {
792
794
  if (info.status === "allowed_warning" || info.status === "rejected") {
793
795
  sendAndRecord(session, {
794
796
  type: "rate_limit",
797
+ sessionId: session.localId,
798
+ vendor: session.vendor || "claude",
795
799
  status: info.status,
796
800
  resetsAt: info.resetsAt ? info.resetsAt * 1000 : null,
797
801
  rateLimitType: info.rateLimitType || null,
@@ -4,10 +4,8 @@ var buildShape = require("./session-spawn-mcp-server").buildShape;
4
4
 
5
5
  var MEMORY_CONTRACT =
6
6
  "The sticky-note board persists across sessions and is a user-facing artifact shared with people and Clay agents, not private agent scratch space. " +
7
- "Default to not writing. Create a note proactively only when the user explicitly asks to remember or track something, or when all of these are true: it will remain useful after the current task and session, it is not already adequately recorded in the repository or another note, and the user would likely be glad to find it on the board a week later. " +
8
- "Good notes capture an unresolved commitment, durable product decision, user preference, constraint, or handoff that will materially change future work. " +
9
- "Important exception for deferred defects: while doing code or technical work, actively create a sticky note when you discover a concrete defect, regression risk, security issue, or data-loss risk that is outside the current session goal and will remain unfixed when the turn ends. Do not wait for the user to ask. For a Project Driver session with Issues authority, search, reuse, or create the Issue first and put the primary details there: observable evidence, affected component, impact, and next action. Then make the sticky note only a concise title, one-line actionable cue, and the actual opaque issue: reference; do not duplicate the full defect details in the note. If you lack Issues authority, route through an eligible Project Driver; never invent an issue reference or drop the alert. Check active notes first when a duplicate is plausible. Do not create defect notes for speculation, general cleanup ideas, or problems you fixed in the current session. " +
10
- "Important exception for deferred proposals: when you propose work (a fix, follow-up, improvement, or next step) and the user defers it rather than declining it (\"let's do that later\", \"next time\", \"after this\"), actively create a sticky note before the topic moves on. Do not wait to be asked. Capture what was proposed, why it mattered, and the agreed timing if any. Deferred agreements scroll out of chat history quickly and are hard to track; the board is where they survive. If the user declines the idea outright, do not write a note. " +
7
+ "Default to not writing. Create a note proactively only when the user explicitly asks to remember or track something, or when all of these are true: it will remain useful after the current task and session, it is not already adequately recorded in the repository, an Issue, or another note, and the user would likely be glad to find it on the board a week later. " +
8
+ "Use Sticky Notes for preferences, reminders, undeveloped ideas, or an explicit request to remember something; they are not mandatory duplicates of Project Issues. For concrete actionable bugs, improvements, or deferred implementation, an authorized Project Driver should use Project Issues. If you lack Issues authority, route the work through an eligible Project Driver. Check active notes first when a duplicate is plausible. " +
11
9
  "Never create a note merely because work is important, lengthy, spans agents or restarts, or might help another agent. Do not record completed work, implementation details, test results, investigation logs, transient blockers, conversation summaries, or announcements of your own activity. When uncertain, do not write. " +
12
10
  "Updates are visible too: update only when durable state materially changes. " +
13
11
  "A note is an attention item, so finishing it means closing it, never erasing it: call close_note once the thing it is asking for is done, and the note leaves the active board while the record stays and can be reopened. Do not turn a note into a completion log instead of closing it. " +
@@ -38,7 +38,7 @@ var VENDOR_REGISTRY = {
38
38
  usageDashboard: {
39
39
  icon: "/codex-avatar.png",
40
40
  alt: "Codex",
41
- href: "https://chatgpt.com/admin/usage",
41
+ href: "https://chatgpt.com/codex/settings/usage",
42
42
  title: "Check usage on ChatGPT",
43
43
  },
44
44
  rateLimitTracking: true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clay-server",
3
- "version": "4.1.0-beta.13",
3
+ "version": "4.1.0-beta.14",
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"