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.
- package/lib/project-loop.js +116 -34
- package/lib/project-user-message.js +5 -3
- package/lib/public/css/scheduler-modal.css +156 -1
- package/lib/public/css/scheduler.css +81 -0
- package/lib/public/index.html +83 -59
- package/lib/public/modules/app-loop-ui.js +85 -2
- package/lib/public/modules/app-messages.js +1 -0
- package/lib/public/modules/scheduler-config.js +241 -162
- package/lib/public/modules/scheduler-history.js +57 -5
- package/lib/public/modules/scheduler.js +80 -36
- package/lib/sdk-bridge.js +40 -14
- package/lib/server-mates.js +7 -2
- package/package.json +1 -1
|
@@ -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="
|
|
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 === "
|
|
742
|
-
|
|
743
|
-
|
|
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
|
-
|
|
558
|
-
|
|
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
|
-
|
|
564
|
+
|
|
565
|
+
var thinkingMode2 = ls2.thinking || sm.currentThinking;
|
|
566
|
+
if (thinkingMode2 === "disabled") {
|
|
561
567
|
queryOptions.thinking = { type: "disabled" };
|
|
562
|
-
} else if (
|
|
563
|
-
|
|
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
|
-
|
|
1442
|
-
|
|
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
|
-
|
|
1467
|
+
var thinkingMode = ls.thinking || sm.currentThinking;
|
|
1468
|
+
if (thinkingMode === "disabled") {
|
|
1454
1469
|
queryOptions.thinking = { type: "disabled" };
|
|
1455
|
-
} else if (
|
|
1456
|
-
|
|
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;
|
package/lib/server-mates.js
CHANGED
|
@@ -65,8 +65,13 @@ function attachMates(ctx) {
|
|
|
65
65
|
// --- Mate message handlers ---
|
|
66
66
|
|
|
67
67
|
function handleMessage(ws, msg) {
|
|
68
|
-
|
|
69
|
-
|
|
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;
|