clay-server 5.1.0-beta.2 → 6.0.0-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.
Files changed (83) hide show
  1. package/bin/cli.js +9 -91
  2. package/lib/daemon-settings.js +11 -0
  3. package/lib/daemon.js +8 -37
  4. package/lib/linear-store.js +35 -8
  5. package/lib/os-users.js +5 -16
  6. package/lib/project-connection.js +0 -7
  7. package/lib/project-filesystem.js +21 -39
  8. package/lib/project-mate-interaction.js +2 -2
  9. package/lib/project-server-settings.js +31 -0
  10. package/lib/project-sessions.js +57 -184
  11. package/lib/public/app.js +6 -37
  12. package/lib/public/css/input.css +3 -1
  13. package/lib/public/css/project-settings-modal.css +56 -0
  14. package/lib/public/css/server-settings-system.css +125 -0
  15. package/lib/public/css/session-github.css +25 -16
  16. package/lib/public/css/settings-ui.css +247 -22
  17. package/lib/public/css/shared-browser.css +2 -0
  18. package/lib/public/css/title-bar.css +5 -1
  19. package/lib/public/index.html +170 -446
  20. package/lib/public/modules/account-island.js +9 -7
  21. package/lib/public/modules/admin-settings-email.js +197 -0
  22. package/lib/public/modules/admin-settings-invitations.js +183 -0
  23. package/lib/public/modules/admin-settings-projects.js +198 -0
  24. package/lib/public/modules/admin-settings-ui.js +100 -0
  25. package/lib/public/modules/admin-settings-users.js +331 -0
  26. package/lib/public/modules/admin.js +19 -949
  27. package/lib/public/modules/app-dm.js +0 -2
  28. package/lib/public/modules/app-messages.js +13 -52
  29. package/lib/public/modules/app-rate-limit.js +70 -12
  30. package/lib/public/modules/context-sources.js +27 -18
  31. package/lib/public/modules/default-ai.js +14 -7
  32. package/lib/public/modules/header-tui-font.js +10 -20
  33. package/lib/public/modules/linear-settings.js +3 -2
  34. package/lib/public/modules/markdown.js +5 -6
  35. package/lib/public/modules/notification-settings.js +81 -0
  36. package/lib/public/modules/notifications.js +44 -28
  37. package/lib/public/modules/personal-settings-dialog.js +51 -0
  38. package/lib/public/modules/profile.js +15 -6
  39. package/lib/public/modules/project-settings-environment.js +106 -0
  40. package/lib/public/modules/project-settings.js +72 -628
  41. package/lib/public/modules/server-settings-controls.js +316 -0
  42. package/lib/public/modules/server-settings-environment.js +194 -0
  43. package/lib/public/modules/server-settings-navigation.js +39 -0
  44. package/lib/public/modules/server-settings-state.js +54 -0
  45. package/lib/public/modules/server-settings.js +116 -788
  46. package/lib/public/modules/server-storage-settings.js +84 -0
  47. package/lib/public/modules/session-github.js +2 -1
  48. package/lib/public/modules/session-presence-ui.js +1 -1
  49. package/lib/public/modules/settings-env-values.js +34 -0
  50. package/lib/public/modules/settings-select.js +121 -0
  51. package/lib/public/modules/shared-browser.js +10 -7
  52. package/lib/public/modules/stt-picker.js +14 -5
  53. package/lib/public/modules/terminal-prefs.js +22 -0
  54. package/lib/public/modules/theme.js +88 -52
  55. package/lib/public/modules/user-settings-account.js +318 -0
  56. package/lib/public/modules/user-settings-appearance.js +55 -0
  57. package/lib/public/modules/user-settings-editors.js +24 -0
  58. package/lib/public/modules/user-settings-integrations.js +104 -0
  59. package/lib/public/modules/user-settings-layout.js +80 -0
  60. package/lib/public/modules/user-settings-preferences.js +48 -0
  61. package/lib/public/modules/user-settings-profile.js +53 -0
  62. package/lib/public/modules/user-settings-themes.js +38 -0
  63. package/lib/public/modules/user-settings-voice.js +75 -0
  64. package/lib/public/modules/user-settings.js +53 -480
  65. package/lib/public/style.css +4 -0
  66. package/lib/sdk-message-processor.js +16 -4
  67. package/lib/sdk-quota-snapshot.js +37 -0
  68. package/lib/server-appearance-settings.js +71 -0
  69. package/lib/server-linear.js +14 -8
  70. package/lib/server-notification-settings.js +67 -0
  71. package/lib/server-settings.js +4 -0
  72. package/lib/server-storage.js +56 -0
  73. package/lib/server.js +10 -2
  74. package/lib/sessions.js +2 -3
  75. package/lib/startup-projects.js +40 -0
  76. package/lib/storage-usage.js +127 -0
  77. package/lib/users-preferences.js +0 -74
  78. package/lib/users.js +0 -4
  79. package/lib/ws-schema.js +2 -4
  80. package/lib/yoke/adapters/codex.js +8 -45
  81. package/lib/yoke/codex-model-catalog.js +40 -0
  82. package/lib/yoke/codex-rate-limits.js +26 -0
  83. package/package.json +3 -3
@@ -218,78 +218,6 @@ function attachPreferences(deps) {
218
218
  return { error: "User not found" };
219
219
  }
220
220
 
221
- // --- Per-user Claude open mode ---
222
- //
223
- // Decides how Claude sessions are rendered when the user clicks into one:
224
- // 'gui' - Clay's custom chat UI driven by the Claude Agent SDK.
225
- // 'tui' - Embedded xterm running the real `claude` CLI. Keeps usage in
226
- // the Interactive billing bucket post Agent SDK split.
227
- //
228
- // Cutover moment: midnight 2026-06-15 in UTC+14 (Line Islands - the
229
- // earliest timezone on Earth in June, 14h ahead of UTC). On that instant
230
- // two things happen at once:
231
- // 1. The default flips from 'gui' to 'tui' (so usage lands in the
232
- // Interactive billing bucket).
233
- // 2. Any previously-stored per-user preference is treated as cleared
234
- // so the new default applies to everyone. Toggles made on or after
235
- // the cutover persist normally.
236
- //
237
- // The preference applies on the next session open. Currently displayed
238
- // sessions are not re-rendered retroactively. The cross-mode click
239
- // logic in project-sessions.js handles both directions in-place
240
- // (prepareTuiSessionForGuiView for tui->gui rendering, claude --resume
241
- // PTY respawn for gui->tui rendering on a born-tui session).
242
-
243
- // Single cutover instant for both default flip and one-time reset:
244
- // 2026-06-15 00:00 in UTC+14 = 2026-06-14 10:00 UTC.
245
- var CLAUDE_OPEN_MODE_CUTOVER_MS = Date.UTC(2026, 5, 14, 10, 0, 0);
246
- var CLAUDE_OPEN_MODE_RESET_MS = CLAUDE_OPEN_MODE_CUTOVER_MS;
247
-
248
- function defaultClaudeOpenMode() {
249
- // Reverted to GUI: the TUI default was a hedge against the (since-cancelled)
250
- // Agent SDK separate-credit billing. Users who explicitly picked TUI after
251
- // the cutover keep it (isStoredPreferenceLive); everyone else gets GUI.
252
- return "gui";
253
- }
254
-
255
- // A stored preference counts only if it was set on or after the reset
256
- // moment (or we haven't reached the reset moment yet). Older preferences
257
- // are ignored so the new default applies once.
258
- function isStoredPreferenceLive(user) {
259
- if (Date.now() < CLAUDE_OPEN_MODE_RESET_MS) return true;
260
- var setAt = user && user.claudeOpenModeSetAt;
261
- return typeof setAt === "number" && setAt >= CLAUDE_OPEN_MODE_RESET_MS;
262
- }
263
-
264
- function getClaudeOpenMode(userId) {
265
- var data = loadUsers();
266
- var fallback = defaultClaudeOpenMode();
267
- for (var i = 0; i < data.users.length; i++) {
268
- if (data.users[i].id === userId) {
269
- var m = data.users[i].claudeOpenMode;
270
- if ((m === "gui" || m === "tui") && isStoredPreferenceLive(data.users[i])) return m;
271
- return fallback;
272
- }
273
- }
274
- return fallback;
275
- }
276
-
277
- function setClaudeOpenMode(userId, mode) {
278
- // GUI is the default in every other resolution path, so an unrecognized
279
- // value must land on GUI too. Only an explicit "tui" opts into terminal.
280
- var normalized = (mode === "tui") ? "tui" : "gui";
281
- var data = loadUsers();
282
- for (var i = 0; i < data.users.length; i++) {
283
- if (data.users[i].id === userId) {
284
- data.users[i].claudeOpenMode = normalized;
285
- data.users[i].claudeOpenModeSetAt = Date.now();
286
- saveUsers(data);
287
- return { ok: true, claudeOpenMode: normalized };
288
- }
289
- }
290
- return { error: "User not found" };
291
- }
292
-
293
221
  // --- Per-user cursor sharing preference ---
294
222
 
295
223
  function getCursorSharing(userId) {
@@ -512,8 +440,6 @@ function attachPreferences(deps) {
512
440
  setChatLayout: setChatLayout,
513
441
  getAutoContinue: getAutoContinue,
514
442
  setAutoContinue: setAutoContinue,
515
- getClaudeOpenMode: getClaudeOpenMode,
516
- setClaudeOpenMode: setClaudeOpenMode,
517
443
  getCursorSharing: getCursorSharing,
518
444
  setCursorSharing: setCursorSharing,
519
445
  getClaudeUserAllowList: getClaudeUserAllowList,
package/lib/users.js CHANGED
@@ -449,8 +449,6 @@ var getAutoContinue = preferences.getAutoContinue;
449
449
  var setAutoContinue = preferences.setAutoContinue;
450
450
  var getProjectMateFilterMode = projectMateFilterPreferences.get;
451
451
  var setProjectMateFilterMode = projectMateFilterPreferences.set;
452
- var getClaudeOpenMode = preferences.getClaudeOpenMode;
453
- var setClaudeOpenMode = preferences.setClaudeOpenMode;
454
452
  var getCursorSharing = preferences.getCursorSharing;
455
453
  var setCursorSharing = preferences.setCursorSharing;
456
454
  var getClaudeUserAllowList = preferences.getClaudeUserAllowList;
@@ -537,8 +535,6 @@ module.exports = {
537
535
  setAutoContinue: setAutoContinue,
538
536
  getProjectMateFilterMode: getProjectMateFilterMode,
539
537
  setProjectMateFilterMode: setProjectMateFilterMode,
540
- getClaudeOpenMode: getClaudeOpenMode,
541
- setClaudeOpenMode: setClaudeOpenMode,
542
538
  getCursorSharing: getCursorSharing,
543
539
  setCursorSharing: setCursorSharing,
544
540
  getClaudeUserAllowList: getClaudeUserAllowList,
package/lib/ws-schema.js CHANGED
@@ -346,6 +346,8 @@ var schema = {
346
346
  // -----------------------------------------------------------------------
347
347
  // Updates
348
348
  // -----------------------------------------------------------------------
349
+ "update_check_failed": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Update check failure" },
350
+ "set_update_channel_result": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Release channel save acknowledgement" },
349
351
  "set_update_channel": { direction: "c2s", handler: "lib/project-sessions.js", description: "Set the update channel (stable/beta)" },
350
352
  "check_update": { direction: "c2s", handler: "lib/project-sessions.js", description: "Check if an update is available" },
351
353
  "update_now": { direction: "c2s", handler: "lib/project-sessions.js", description: "Start the update process" },
@@ -454,10 +456,6 @@ var schema = {
454
456
  "project_env_result": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Project .envrc content" },
455
457
  "set_project_env": { direction: "c2s", handler: "lib/project-filesystem.js", description: "Write the project .envrc" },
456
458
  "set_project_env_result": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Project .envrc write result" },
457
- "read_global_claude_md": { direction: "c2s", handler: "lib/project-filesystem.js", description: "Read the global CLAUDE.md" },
458
- "global_claude_md_result": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Global CLAUDE.md content" },
459
- "write_global_claude_md": { direction: "c2s", handler: "lib/project-filesystem.js", description: "Write the global CLAUDE.md" },
460
- "write_global_claude_md_result": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Global CLAUDE.md write result" },
461
459
  "get_shared_env": { direction: "c2s", handler: "lib/project-filesystem.js", description: "Read the shared .envrc" },
462
460
  "shared_env_result": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Shared .envrc content" },
463
461
  "set_shared_env": { direction: "c2s", handler: "lib/project-filesystem.js", description: "Write the shared .envrc" },
@@ -15,6 +15,8 @@ var backgroundTasks = require("../codex-background-tasks");
15
15
  var codexUserInput = require("../codex-user-input");
16
16
  var createTurnStop = require("../codex-turn-stop").createTurnStop;
17
17
  var idleRecovery = require("../codex-idle-recovery");
18
+ var modelCatalog = require("../codex-model-catalog");
19
+ var normalizeQuotaSnapshot = require("../codex-rate-limits").normalizeSnapshot;
18
20
 
19
21
  // --- Reasoning defaults ---
20
22
  // Reasoning summaries are the only readable thinking channel Codex offers:
@@ -295,30 +297,7 @@ function flattenEvent(notification, state) {
295
297
  // Rate limits from Codex account
296
298
  if (method === "account/rateLimits/updated") {
297
299
  var rl = params.rateLimits;
298
- if (rl) {
299
- var windows = [
300
- { key: "primary", type: "five_hour" },
301
- { key: "secondary", type: "seven_day" },
302
- ];
303
- for (var wi = 0; wi < windows.length; wi++) {
304
- var w = rl[windows[wi].key];
305
- if (!w) continue;
306
- var utilization = (w.usedPercent || 0) / 100;
307
- var status = "allowed";
308
- if (w.usedPercent >= 100) status = "rejected";
309
- else if (w.usedPercent >= 80) status = "allowed_warning";
310
- events.push({
311
- yokeType: "rate_limit",
312
- rateLimitInfo: {
313
- status: status,
314
- resetsAt: w.resetsAt || null,
315
- rateLimitType: windows[wi].type,
316
- utilization: utilization,
317
- isUsingOverage: false,
318
- },
319
- });
320
- }
321
- }
300
+ if (rl) events.push(normalizeQuotaSnapshot(rl));
322
301
  return events;
323
302
  }
324
303
 
@@ -1459,22 +1438,8 @@ function createCodexAdapter(opts) {
1459
1438
  var _createAppServer = (opts && opts.createAppServer) || function(serverOpts) {
1460
1439
  return new CodexAppServer(null, serverOpts);
1461
1440
  };
1462
- // Codex models are a fixed list (the app-server doesn't enumerate them), so
1463
- // model listing must not depend on a successful app-server init — otherwise a
1464
- // slow/failed `initialize` leaves the picker empty and the chip shows the
1465
- // previous vendor's model.
1466
- // Kept in sync with what `model/list` reports for the bundled codex build
1467
- // (0.153.4). Models the CLI no longer offers are rejected at turn/start, so
1468
- // listing them would only surface a runtime failure in the model picker.
1469
- var CODEX_MODELS = [
1470
- "gpt-6-astra",
1471
- "gpt-5.6-sol",
1472
- "gpt-5.6-terra",
1473
- "gpt-5.6-luna",
1474
- "gpt-5.5",
1475
- "gpt-5.2",
1476
- ];
1477
- var _cachedModels = CODEX_MODELS.slice();
1441
+ // Keep the picker available before initialization and during offline recovery.
1442
+ var _cachedModels = modelCatalog.fallback();
1478
1443
  var _appServer = null;
1479
1444
  var _backgroundTaskResetListeners = [];
1480
1445
 
@@ -1921,9 +1886,7 @@ function createCodexAdapter(opts) {
1921
1886
  throw createShutdownError();
1922
1887
  }
1923
1888
 
1924
- console.log("[codex] App-server initialized, models: " + CODEX_MODELS.join(", "));
1925
-
1926
- _cachedModels = CODEX_MODELS.slice();
1889
+ _cachedModels = await modelCatalog.load(_appServer);
1927
1890
 
1928
1891
  // Register foreign vendor roots with Codex so automatic skill
1929
1892
  // triggering and skills/list use the same merged inventory.
@@ -1990,8 +1953,8 @@ function createCodexAdapter(opts) {
1990
1953
  },
1991
1954
 
1992
1955
  supportedModels: function() {
1993
- // Fixed list; return it without requiring a live app-server init.
1994
- return Promise.resolve(CODEX_MODELS.slice());
1956
+ // Never start a process merely to populate a picker.
1957
+ return Promise.resolve(_cachedModels.length ? _cachedModels.slice() : modelCatalog.fallback());
1995
1958
  },
1996
1959
 
1997
1960
  createToolServer: function(def) {
@@ -0,0 +1,40 @@
1
+ // Runtime catalogs are scoped to the adapter's OS user and provider configuration.
2
+ var FALLBACK = ["gpt-6-astra", "gpt-6.1-sol", "gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna", "gpt-5.5", "gpt-5.2"];
3
+
4
+ function fallback() {
5
+ return FALLBACK.slice();
6
+ }
7
+
8
+ async function load(server) {
9
+ var models = [];
10
+ var seen = new Set();
11
+ var cursors = new Set();
12
+ var cursor = null;
13
+ var deadline = Date.now() + 3000;
14
+ try {
15
+ for (var page = 0; page < 4; page++) {
16
+ var remaining = deadline - Date.now();
17
+ if (remaining <= 0) return fallback();
18
+ var result = await server.send("model/list", { limit: 100, includeHidden: false, cursor: cursor }, remaining);
19
+ if (!result || !Array.isArray(result.data)) return fallback();
20
+ result.data.forEach(function (entry) {
21
+ if (!entry || entry.hidden || typeof entry.model !== "string" || !entry.model || seen.has(entry.model)) return;
22
+ seen.add(entry.model);
23
+ var model = { value: entry.model, displayName: entry.displayName || entry.model, description: entry.description || "" };
24
+ if (Array.isArray(entry.supportedReasoningEfforts)) {
25
+ model.supportedEffortLevels = entry.supportedReasoningEfforts.map(function (option) { return option.reasoningEffort; }).filter(function (effort) { return typeof effort === "string" && effort.length > 0; });
26
+ }
27
+ models.push(model);
28
+ });
29
+ cursor = result.nextCursor;
30
+ if (!cursor) return models.length ? models : fallback();
31
+ if (cursors.has(cursor)) return fallback();
32
+ cursors.add(cursor);
33
+ }
34
+ } catch (error) {
35
+ // Catalog failure must not prevent existing conversations from starting.
36
+ }
37
+ return fallback();
38
+ }
39
+
40
+ module.exports = { fallback: fallback, load: load };
@@ -0,0 +1,26 @@
1
+ // Account quota snapshots describe usage, never whether a turn was rejected.
2
+ function normalizeSnapshot(rateLimits) {
3
+ var windows = [];
4
+ ["primary", "secondary"].forEach(function (key) {
5
+ var window = rateLimits[key];
6
+ if (!window || window.windowDurationMins === 0) return;
7
+ var duration = window.windowDurationMins;
8
+ var used = window.usedPercent;
9
+ windows.push({
10
+ key: key,
11
+ windowDurationMins: Number.isFinite(duration) && duration > 0 ? duration : null,
12
+ usedPercent: Number.isFinite(used) && used >= 0 ? used : null,
13
+ resetsAt: Number.isFinite(window.resetsAt) && window.resetsAt > 0 ? window.resetsAt : null,
14
+ });
15
+ });
16
+ return {
17
+ yokeType: "rate_limit",
18
+ rateLimitInfo: {
19
+ source: "account_snapshot",
20
+ limitId: rateLimits.limitId || "codex",
21
+ windows: windows,
22
+ },
23
+ };
24
+ }
25
+
26
+ module.exports = { normalizeSnapshot: normalizeSnapshot };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clay-server",
3
- "version": "5.1.0-beta.2",
3
+ "version": "6.0.0-beta.1",
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"
@@ -47,7 +47,7 @@
47
47
  "homepage": "https://github.com/chadbyte/clay#readme",
48
48
  "author": "Chad",
49
49
  "dependencies": {
50
- "@anthropic-ai/claude-agent-sdk": "^0.3.287",
50
+ "@anthropic-ai/claude-agent-sdk": "^0.3.296",
51
51
  "@dicebear/adventurer": "9.4.3",
52
52
  "@dicebear/bottts": "9.4.3",
53
53
  "@dicebear/core": "9.4.3",
@@ -58,7 +58,7 @@
58
58
  "@dicebear/thumbs": "9.4.3",
59
59
  "@lydell/node-pty": "^1.2.0-beta.3",
60
60
  "@modelcontextprotocol/sdk": "^1.32.1",
61
- "@openai/codex": "^0.160.0",
61
+ "@openai/codex": "^0.162.1",
62
62
  "@seald-io/nedb": "^4.1.2",
63
63
  "codemirror": "^5.65.21",
64
64
  "imapflow": "^1.7.8",