clay-server 2.27.1 → 2.28.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.
@@ -7,8 +7,10 @@
7
7
 
8
8
  import { iconHtml } from './icons.js';
9
9
  import { showToast } from './utils.js';
10
+ import { renderModelList, renderModeList, renderEffortBar, renderThinkingBar } from './settings-defaults.js';
11
+ import { store } from './store.js';
10
12
  import { initSchedulerConfig, setupCreateModal, openCreateModal, openCreateModalWithRecord, closeCreateModal, removePreview, getPreviewEl, showPreviewOnCell, showPreviewOnSlot, showPreviewForCreate, applyDraggedTask, parseCronSimple } from './scheduler-config.js';
11
- import { initSchedulerHistory, renderHistory } from './scheduler-history.js';
13
+ import { initSchedulerHistory, renderHistory, _lastFiles as lastLoopFiles } from './scheduler-history.js';
12
14
  export { handleLoopRegistryUpdated, handleLoopRegistryFiles, handleScheduleRunStarted, handleScheduleRunFinished, handleLoopScheduled } from './scheduler-history.js';
13
15
 
14
16
  var ctx = null;
@@ -671,7 +673,7 @@ function renderDetail() {
671
673
  html += '<div class="scheduler-detail-tabs">';
672
674
  html += '<button class="scheduler-detail-tab active" data-tab="prompt">PROMPT.md</button>';
673
675
  html += '<button class="scheduler-detail-tab" data-tab="judge">JUDGE.md</button>';
674
- html += '<button class="scheduler-detail-tab" data-tab="meta">Info</button>';
676
+ html += '<button class="scheduler-detail-tab" data-tab="model">Model</button>';
675
677
  html += '</div>';
676
678
 
677
679
  html += '<div class="scheduler-detail-body" id="scheduler-detail-body">';
@@ -738,41 +740,83 @@ function renderDetailBody(tab, rec) {
738
740
  var bodyEl2 = document.getElementById("scheduler-detail-body");
739
741
  if (!bodyEl2) return;
740
742
 
741
- if (tab === "meta") {
742
- var isScheduled = !!rec.cron;
743
- var lastRun = rec.runs && rec.runs.length > 0 ? rec.runs[rec.runs.length - 1] : null;
744
- var scheduleStr = isScheduled ? cronToHuman(rec.cron) : "One-off";
745
- var statusStr = isScheduled ? (rec.enabled ? "Enabled" : "Paused") : "One-off";
746
- var createdStr = rec.createdAt ? formatDateTime(new Date(rec.createdAt)) : "—";
747
- var lastRunStr = "Never";
748
- if (lastRun) {
749
- var resultStr = lastRun.result || "?";
750
- var iterStr = (lastRun.iterations || 0) + " iter";
751
- lastRunStr = formatDateTime(new Date(lastRun.finishedAt || lastRun.startedAt)) + " — " + resultStr + " (" + iterStr + ")";
752
- }
753
-
754
- var html = '<div class="scheduler-detail-meta">';
755
- html += '<span class="scheduler-detail-meta-label">Schedule</span>';
756
- html += '<span class="scheduler-detail-meta-value">' + esc(scheduleStr) + '</span>';
757
- html += '<span class="scheduler-detail-meta-label">Status</span>';
758
- html += '<span class="scheduler-detail-meta-value">' + esc(statusStr) + '</span>';
759
- html += '<span class="scheduler-detail-meta-label">Max Iterations</span>';
760
- html += '<span class="scheduler-detail-meta-value">' + (rec.maxIterations || "—") + '</span>';
761
- html += '<span class="scheduler-detail-meta-label">Created</span>';
762
- html += '<span class="scheduler-detail-meta-value">' + esc(createdStr) + '</span>';
763
- html += '<span class="scheduler-detail-meta-label">Last Run</span>';
764
- html += '<span class="scheduler-detail-meta-value">' + esc(lastRunStr) + '</span>';
765
- if (isScheduled && rec.nextRunAt) {
766
- html += '<span class="scheduler-detail-meta-label">Next Run</span>';
767
- html += '<span class="scheduler-detail-meta-value">' + esc(formatDateTime(new Date(rec.nextRunAt))) + '</span>';
768
- }
769
- html += '</div>';
770
- bodyEl2.innerHTML = html;
771
- } else {
772
- // prompt or judge — request files from server
773
- bodyEl2.innerHTML = '<div class="scheduler-detail-loading">Loading...</div>';
774
- send({ type: "loop_registry_files", id: selectedTaskId });
743
+ if (tab === "model") {
744
+ renderModelTab(bodyEl2, rec);
745
+ return;
775
746
  }
747
+
748
+ // prompt or judge — request files from server
749
+ bodyEl2.innerHTML = '<div class="scheduler-detail-loading">Loading...</div>';
750
+ send({ type: "loop_registry_files", id: selectedTaskId });
751
+ }
752
+
753
+ function renderModelTab(bodyEl, rec) {
754
+ var settings = lastLoopFiles.settings || {};
755
+ var loopFilesId = rec.linkedTaskId || rec.id;
756
+
757
+ bodyEl.innerHTML =
758
+ '<div class="scheduler-model-settings">' +
759
+ '<div class="settings-card"><div class="settings-field">' +
760
+ '<label class="settings-label">Model</label>' +
761
+ '<div class="settings-hint">Choose the Claude model for this task.</div>' +
762
+ '<div id="ls-model-list" class="settings-model-list"></div>' +
763
+ '</div></div>' +
764
+ '<div class="settings-card"><div class="settings-field">' +
765
+ '<label class="settings-label">Mode</label>' +
766
+ '<div class="settings-hint">Controls how Claude handles tool use and file edits.</div>' +
767
+ '<div id="ls-mode-list" class="settings-model-list"></div>' +
768
+ '</div></div>' +
769
+ '<div class="settings-card"><div class="settings-field">' +
770
+ '<label class="settings-label">Effort</label>' +
771
+ '<div class="settings-hint">Controls how much thinking effort Claude puts into responses.</div>' +
772
+ '<div class="settings-btn-group" id="ls-effort-bar"></div>' +
773
+ '</div></div>' +
774
+ '<div class="settings-card"><div class="settings-field">' +
775
+ '<label class="settings-label">Thinking</label>' +
776
+ '<div class="settings-hint">Controls whether Claude shows its reasoning process.</div>' +
777
+ '<div class="settings-btn-group" id="ls-thinking-bar"></div>' +
778
+ '<div id="ls-thinking-budget-row" class="settings-budget-row" style="display:none">' +
779
+ '<label class="settings-budget-label">Budget tokens</label>' +
780
+ '<input id="ls-thinking-budget" type="number" class="settings-budget-input" min="1024" max="128000" step="1024" value="10000">' +
781
+ '</div>' +
782
+ '</div></div>' +
783
+ '</div>';
784
+
785
+ function saveLoopSetting(key, value) {
786
+ var updated = Object.assign({}, settings);
787
+ updated[key] = value;
788
+ settings = updated;
789
+ send({ type: "loop_registry_save_files", id: loopFilesId, settings: updated });
790
+ }
791
+
792
+ var opts = {
793
+ models: store.getState().currentModels || [],
794
+ currentModel: settings.model || "",
795
+ currentMode: settings.permissionMode || "default",
796
+ currentEffort: settings.effort || "medium",
797
+ currentThinking: settings.thinking || "adaptive",
798
+ currentThinkingBudget: settings.thinkingBudget || 10000,
799
+ sendMsg: function (msgType, data) {
800
+ if (msgType === "set_model" || msgType === "loop_set_model") {
801
+ saveLoopSetting("model", data.model);
802
+ } else if (msgType === "loop_set_mode") {
803
+ saveLoopSetting("permissionMode", data.mode);
804
+ } else if (msgType === "loop_set_effort") {
805
+ saveLoopSetting("effort", data.effort);
806
+ } else if (msgType === "set_thinking") {
807
+ saveLoopSetting("thinking", data.thinking);
808
+ if (data.budgetTokens) saveLoopSetting("thinkingBudget", data.budgetTokens);
809
+ }
810
+ },
811
+ modelMsgType: "loop_set_model",
812
+ modeMsgType: "loop_set_mode",
813
+ effortMsgType: "loop_set_effort",
814
+ };
815
+
816
+ renderModelList("ls", opts);
817
+ renderModeList("ls", opts);
818
+ renderEffortBar("ls", opts);
819
+ renderThinkingBar("ls", opts);
776
820
  }
777
821
 
778
822
  // --- Chat reparenting ---
package/lib/sdk-bridge.js CHANGED
@@ -554,19 +554,30 @@ function createSDKBridge(opts) {
554
554
  };
555
555
 
556
556
  if (mcpServers) queryOptions.mcpServers = mcpServers;
557
- if (sm.currentModel) queryOptions.model = sm.currentModel;
558
- if (sm.currentEffort) queryOptions.effort = sm.currentEffort;
557
+
558
+ // Per-loop settings override global defaults when present
559
+ var ls2 = session.loopSettings || {};
560
+
561
+ if (ls2.model || sm.currentModel) queryOptions.model = ls2.model || sm.currentModel;
562
+ if (ls2.effort || sm.currentEffort) queryOptions.effort = ls2.effort || sm.currentEffort;
559
563
  if (sm.currentBetas && sm.currentBetas.length > 0) queryOptions.betas = sm.currentBetas;
560
- if (sm.currentThinking === "disabled") {
564
+
565
+ var thinkingMode2 = ls2.thinking || sm.currentThinking;
566
+ if (thinkingMode2 === "disabled") {
561
567
  queryOptions.thinking = { type: "disabled" };
562
- } else if (sm.currentThinking === "budget" && sm.currentThinkingBudget) {
563
- queryOptions.thinking = { type: "enabled", budgetTokens: sm.currentThinkingBudget };
568
+ } else if (thinkingMode2 === "budget") {
569
+ var budgetTokens2 = ls2.thinkingBudget || sm.currentThinkingBudget;
570
+ if (budgetTokens2) queryOptions.thinking = { type: "enabled", budgetTokens: budgetTokens2 };
571
+ }
572
+
573
+ if (ls2.disableAllHooks !== undefined) {
574
+ queryOptions.settings = Object.assign({}, queryOptions.settings || {}, { disableAllHooks: ls2.disableAllHooks });
564
575
  }
565
576
 
566
577
  if (dangerouslySkipPermissions) {
567
578
  queryOptions.allowDangerouslySkipPermissions = true;
568
579
  }
569
- var modeToApply = session.acceptEditsAfterStart ? "acceptEdits" : sm.currentPermissionMode;
580
+ var modeToApply = ls2.permissionMode || (session.acceptEditsAfterStart ? "acceptEdits" : sm.currentPermissionMode);
570
581
  if (session.acceptEditsAfterStart) delete session.acceptEditsAfterStart;
571
582
  if (modeToApply && modeToApply !== "default") {
572
583
  queryOptions.permissionMode = modeToApply;
@@ -1438,29 +1449,44 @@ function createSDKBridge(opts) {
1438
1449
  },
1439
1450
  };
1440
1451
 
1441
- if (sm.currentModel) {
1442
- queryOptions.model = sm.currentModel;
1452
+ // Per-loop settings override global defaults when present
1453
+ var ls = session.loopSettings || {};
1454
+
1455
+ if (ls.model || sm.currentModel) {
1456
+ queryOptions.model = ls.model || sm.currentModel;
1443
1457
  }
1444
1458
 
1445
- if (sm.currentEffort) {
1446
- queryOptions.effort = sm.currentEffort;
1459
+ if (ls.effort || sm.currentEffort) {
1460
+ queryOptions.effort = ls.effort || sm.currentEffort;
1447
1461
  }
1448
1462
 
1449
1463
  if (sm.currentBetas && sm.currentBetas.length > 0) {
1450
1464
  queryOptions.betas = sm.currentBetas;
1451
1465
  }
1452
1466
 
1453
- if (sm.currentThinking === "disabled") {
1467
+ var thinkingMode = ls.thinking || sm.currentThinking;
1468
+ if (thinkingMode === "disabled") {
1454
1469
  queryOptions.thinking = { type: "disabled" };
1455
- } else if (sm.currentThinking === "budget" && sm.currentThinkingBudget) {
1456
- queryOptions.thinking = { type: "enabled", budgetTokens: sm.currentThinkingBudget };
1470
+ } else if (thinkingMode === "budget") {
1471
+ var budgetTokens = ls.thinkingBudget || sm.currentThinkingBudget;
1472
+ if (budgetTokens) queryOptions.thinking = { type: "enabled", budgetTokens: budgetTokens };
1473
+ }
1474
+
1475
+ if (ls.permissionMode) {
1476
+ // Will be applied below, store for later
1477
+ session._loopPermissionMode = ls.permissionMode;
1478
+ }
1479
+
1480
+ // Pass through any extra SDK settings from LOOP.json
1481
+ if (ls.disableAllHooks !== undefined) {
1482
+ queryOptions.settings = Object.assign({}, queryOptions.settings || {}, { disableAllHooks: ls.disableAllHooks });
1457
1483
  }
1458
1484
 
1459
1485
  if (dangerouslySkipPermissions) {
1460
1486
  queryOptions.allowDangerouslySkipPermissions = true;
1461
1487
  }
1462
1488
  // Pass permissionMode in queryOptions at creation time to avoid race condition
1463
- var modeToApply = session.acceptEditsAfterStart ? "acceptEdits" : sm.currentPermissionMode;
1489
+ var modeToApply = session._loopPermissionMode || (session.acceptEditsAfterStart ? "acceptEdits" : sm.currentPermissionMode);
1464
1490
  if (session.acceptEditsAfterStart) delete session.acceptEditsAfterStart;
1465
1491
  if (modeToApply && modeToApply !== "default") {
1466
1492
  queryOptions.permissionMode = modeToApply;
@@ -65,8 +65,13 @@ function attachMates(ctx) {
65
65
  // --- Mate message handlers ---
66
66
 
67
67
  function handleMessage(ws, msg) {
68
- if (!users.isMultiUser() || !ws._clayUser) return false;
69
- var userId = ws._clayUser.id;
68
+ var userId;
69
+ if (users.isMultiUser()) {
70
+ if (!ws._clayUser) return false;
71
+ userId = ws._clayUser.id;
72
+ } else {
73
+ userId = "default";
74
+ }
70
75
 
71
76
  if (msg.type === "mate_create") {
72
77
  if (!msg.seedData) return true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clay-server",
3
- "version": "2.27.1",
3
+ "version": "2.28.0-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",