clay-server 2.11.0-beta.1 → 2.11.0-beta.3
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.js +134 -24
- package/lib/public/app.js +100 -4
- package/lib/public/css/menus.css +23 -0
- package/lib/public/css/messages.css +8 -0
- package/lib/public/index.html +9 -0
- package/lib/public/modules/tools.js +214 -1
- package/lib/sdk-bridge.js +76 -0
- package/lib/sessions.js +26 -0
- package/package.json +2 -2
package/lib/project.js
CHANGED
|
@@ -979,7 +979,7 @@ function createProjectContext(opts) {
|
|
|
979
979
|
if (sm.currentModel) {
|
|
980
980
|
sendTo(ws, { type: "model_info", model: sm.currentModel, models: sm.availableModels || [] });
|
|
981
981
|
}
|
|
982
|
-
sendTo(ws, { type: "config_state", model: sm.currentModel || "", mode: sm.currentPermissionMode || "default", effort: sm.currentEffort || "medium", betas: sm.currentBetas || [] });
|
|
982
|
+
sendTo(ws, { type: "config_state", model: sm.currentModel || "", mode: sm.currentPermissionMode || "default", effort: sm.currentEffort || "medium", betas: sm.currentBetas || [], thinking: sm.currentThinking || "adaptive", thinkingBudget: sm.currentThinkingBudget || 10000 });
|
|
983
983
|
sendTo(ws, { type: "term_list", terminals: tm.list() });
|
|
984
984
|
sendTo(ws, { type: "notes_list", notes: nm.list() });
|
|
985
985
|
sendTo(ws, { type: "loop_registry_updated", records: getHubSchedules() });
|
|
@@ -1187,17 +1187,31 @@ function createProjectContext(opts) {
|
|
|
1187
1187
|
if (msg.type === "resume_session") {
|
|
1188
1188
|
if (!msg.cliSessionId) return;
|
|
1189
1189
|
var cliSess = require("./cli-sessions");
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1190
|
+
// Try SDK for title first, then fall back to manual parsing
|
|
1191
|
+
var titlePromise = getSDK().then(function(sdkMod) {
|
|
1192
|
+
return sdkMod.getSessionInfo(msg.cliSessionId, { dir: cwd });
|
|
1193
|
+
}).then(function(info) {
|
|
1194
|
+
return (info && info.summary) ? info.summary.substring(0, 100) : null;
|
|
1195
|
+
}).catch(function() { return null; });
|
|
1196
|
+
|
|
1197
|
+
Promise.all([
|
|
1198
|
+
cliSess.readCliSessionHistory(cwd, msg.cliSessionId),
|
|
1199
|
+
titlePromise
|
|
1200
|
+
]).then(function(results) {
|
|
1201
|
+
var history = results[0];
|
|
1202
|
+
var sdkTitle = results[1];
|
|
1203
|
+
var title = sdkTitle || "Resumed session";
|
|
1204
|
+
if (!sdkTitle) {
|
|
1205
|
+
for (var i = 0; i < history.length; i++) {
|
|
1206
|
+
if (history[i].type === "user_message" && history[i].text) {
|
|
1207
|
+
title = history[i].text.substring(0, 50);
|
|
1208
|
+
break;
|
|
1209
|
+
}
|
|
1196
1210
|
}
|
|
1197
1211
|
}
|
|
1198
1212
|
var resumed = sm.resumeSession(msg.cliSessionId, { history: history, title: title }, ws);
|
|
1199
1213
|
if (resumed) ws._clayActiveSession = resumed.localId;
|
|
1200
|
-
}).catch(function
|
|
1214
|
+
}).catch(function() {
|
|
1201
1215
|
var resumed = sm.resumeSession(msg.cliSessionId, undefined, ws);
|
|
1202
1216
|
if (resumed) ws._clayActiveSession = resumed.localId;
|
|
1203
1217
|
});
|
|
@@ -1205,9 +1219,7 @@ function createProjectContext(opts) {
|
|
|
1205
1219
|
}
|
|
1206
1220
|
|
|
1207
1221
|
if (msg.type === "list_cli_sessions") {
|
|
1208
|
-
var cliSessions = require("./cli-sessions");
|
|
1209
1222
|
var _fs = require("fs");
|
|
1210
|
-
var _path = require("path");
|
|
1211
1223
|
// Collect session IDs already in relay (in-memory + persisted on disk)
|
|
1212
1224
|
var relayIds = {};
|
|
1213
1225
|
sm.sessions.forEach(function (s) {
|
|
@@ -1222,13 +1234,34 @@ function createProjectContext(opts) {
|
|
|
1222
1234
|
}
|
|
1223
1235
|
}
|
|
1224
1236
|
} catch (e) {}
|
|
1225
|
-
|
|
1226
|
-
|
|
1237
|
+
|
|
1238
|
+
getSDK().then(function(sdkMod) {
|
|
1239
|
+
return sdkMod.listSessions({ dir: cwd });
|
|
1240
|
+
}).then(function(sdkSessions) {
|
|
1241
|
+
var filtered = sdkSessions.filter(function(s) {
|
|
1227
1242
|
return !relayIds[s.sessionId];
|
|
1243
|
+
}).map(function(s) {
|
|
1244
|
+
return {
|
|
1245
|
+
sessionId: s.sessionId,
|
|
1246
|
+
firstPrompt: s.summary || s.firstPrompt || "",
|
|
1247
|
+
model: null,
|
|
1248
|
+
gitBranch: s.gitBranch || null,
|
|
1249
|
+
startTime: s.createdAt ? new Date(s.createdAt).toISOString() : null,
|
|
1250
|
+
lastActivity: s.lastModified ? new Date(s.lastModified).toISOString() : null,
|
|
1251
|
+
};
|
|
1228
1252
|
});
|
|
1229
1253
|
sendTo(ws, { type: "cli_session_list", sessions: filtered });
|
|
1230
|
-
}).catch(function
|
|
1231
|
-
|
|
1254
|
+
}).catch(function() {
|
|
1255
|
+
// Fallback to manual parsing if SDK fails
|
|
1256
|
+
var cliSessions = require("./cli-sessions");
|
|
1257
|
+
cliSessions.listCliSessions(cwd).then(function(sessions) {
|
|
1258
|
+
var filtered = sessions.filter(function(s) {
|
|
1259
|
+
return !relayIds[s.sessionId];
|
|
1260
|
+
});
|
|
1261
|
+
sendTo(ws, { type: "cli_session_list", sessions: filtered });
|
|
1262
|
+
}).catch(function() {
|
|
1263
|
+
sendTo(ws, { type: "cli_session_list", sessions: [] });
|
|
1264
|
+
});
|
|
1232
1265
|
});
|
|
1233
1266
|
return;
|
|
1234
1267
|
}
|
|
@@ -1264,6 +1297,14 @@ function createProjectContext(opts) {
|
|
|
1264
1297
|
s.title = String(msg.title).substring(0, 100);
|
|
1265
1298
|
sm.saveSessionFile(s);
|
|
1266
1299
|
sm.broadcastSessionList();
|
|
1300
|
+
// Sync title to SDK session
|
|
1301
|
+
if (s.cliSessionId) {
|
|
1302
|
+
getSDK().then(function(sdk) {
|
|
1303
|
+
sdk.renameSession(s.cliSessionId, s.title, { dir: cwd }).catch(function(e) {
|
|
1304
|
+
console.error("[project] SDK renameSession failed:", e.message);
|
|
1305
|
+
});
|
|
1306
|
+
}).catch(function() {});
|
|
1307
|
+
}
|
|
1267
1308
|
}
|
|
1268
1309
|
return;
|
|
1269
1310
|
}
|
|
@@ -1388,7 +1429,7 @@ function createProjectContext(opts) {
|
|
|
1388
1429
|
if (msg.type === "set_permission_mode" && msg.mode) {
|
|
1389
1430
|
// When dangerouslySkipPermissions is active, don't allow UI to change mode
|
|
1390
1431
|
if (dangerouslySkipPermissions) {
|
|
1391
|
-
send({ type: "config_state", model: sm.currentModel || "", mode: "bypassPermissions", effort: sm.currentEffort || "medium", betas: sm.currentBetas || [] });
|
|
1432
|
+
send({ type: "config_state", model: sm.currentModel || "", mode: "bypassPermissions", effort: sm.currentEffort || "medium", betas: sm.currentBetas || [], thinking: sm.currentThinking || "adaptive", thinkingBudget: sm.currentThinkingBudget || 10000 });
|
|
1392
1433
|
return;
|
|
1393
1434
|
}
|
|
1394
1435
|
sm.currentPermissionMode = msg.mode;
|
|
@@ -1396,7 +1437,7 @@ function createProjectContext(opts) {
|
|
|
1396
1437
|
if (session) {
|
|
1397
1438
|
sdk.setPermissionMode(session, msg.mode);
|
|
1398
1439
|
}
|
|
1399
|
-
send({ type: "config_state", model: sm.currentModel || "", mode: sm.currentPermissionMode, effort: sm.currentEffort || "medium", betas: sm.currentBetas || [] });
|
|
1440
|
+
send({ type: "config_state", model: sm.currentModel || "", mode: sm.currentPermissionMode, effort: sm.currentEffort || "medium", betas: sm.currentBetas || [], thinking: sm.currentThinking || "adaptive", thinkingBudget: sm.currentThinkingBudget || 10000 });
|
|
1400
1441
|
return;
|
|
1401
1442
|
}
|
|
1402
1443
|
|
|
@@ -1410,7 +1451,7 @@ function createProjectContext(opts) {
|
|
|
1410
1451
|
if (session) {
|
|
1411
1452
|
sdk.setPermissionMode(session, msg.mode);
|
|
1412
1453
|
}
|
|
1413
|
-
send({ type: "config_state", model: sm.currentModel || "", mode: sm.currentPermissionMode, effort: sm.currentEffort || "medium", betas: sm.currentBetas || [] });
|
|
1454
|
+
send({ type: "config_state", model: sm.currentModel || "", mode: sm.currentPermissionMode, effort: sm.currentEffort || "medium", betas: sm.currentBetas || [], thinking: sm.currentThinking || "adaptive", thinkingBudget: sm.currentThinkingBudget || 10000 });
|
|
1414
1455
|
}
|
|
1415
1456
|
return;
|
|
1416
1457
|
}
|
|
@@ -1425,14 +1466,18 @@ function createProjectContext(opts) {
|
|
|
1425
1466
|
if (session) {
|
|
1426
1467
|
sdk.setPermissionMode(session, msg.mode);
|
|
1427
1468
|
}
|
|
1428
|
-
send({ type: "config_state", model: sm.currentModel || "", mode: sm.currentPermissionMode, effort: sm.currentEffort || "medium", betas: sm.currentBetas || [] });
|
|
1469
|
+
send({ type: "config_state", model: sm.currentModel || "", mode: sm.currentPermissionMode, effort: sm.currentEffort || "medium", betas: sm.currentBetas || [], thinking: sm.currentThinking || "adaptive", thinkingBudget: sm.currentThinkingBudget || 10000 });
|
|
1429
1470
|
}
|
|
1430
1471
|
return;
|
|
1431
1472
|
}
|
|
1432
1473
|
|
|
1433
1474
|
if (msg.type === "set_effort" && msg.effort) {
|
|
1434
1475
|
sm.currentEffort = msg.effort;
|
|
1435
|
-
|
|
1476
|
+
var session = getSessionForWs(ws);
|
|
1477
|
+
if (session) {
|
|
1478
|
+
sdk.setEffort(session, msg.effort);
|
|
1479
|
+
}
|
|
1480
|
+
send({ type: "config_state", model: sm.currentModel || "", mode: sm.currentPermissionMode || "default", effort: sm.currentEffort, betas: sm.currentBetas || [], thinking: sm.currentThinking || "adaptive", thinkingBudget: sm.currentThinkingBudget || 10000 });
|
|
1436
1481
|
return;
|
|
1437
1482
|
}
|
|
1438
1483
|
|
|
@@ -1441,7 +1486,7 @@ function createProjectContext(opts) {
|
|
|
1441
1486
|
opts.onSetServerDefaultEffort(msg.effort);
|
|
1442
1487
|
}
|
|
1443
1488
|
sm.currentEffort = msg.effort;
|
|
1444
|
-
send({ type: "config_state", model: sm.currentModel || "", mode: sm.currentPermissionMode || "default", effort: sm.currentEffort, betas: sm.currentBetas || [] });
|
|
1489
|
+
send({ type: "config_state", model: sm.currentModel || "", mode: sm.currentPermissionMode || "default", effort: sm.currentEffort, betas: sm.currentBetas || [], thinking: sm.currentThinking || "adaptive", thinkingBudget: sm.currentThinkingBudget || 10000 });
|
|
1445
1490
|
return;
|
|
1446
1491
|
}
|
|
1447
1492
|
|
|
@@ -1450,13 +1495,20 @@ function createProjectContext(opts) {
|
|
|
1450
1495
|
opts.onSetProjectDefaultEffort(slug, msg.effort);
|
|
1451
1496
|
}
|
|
1452
1497
|
sm.currentEffort = msg.effort;
|
|
1453
|
-
send({ type: "config_state", model: sm.currentModel || "", mode: sm.currentPermissionMode || "default", effort: sm.currentEffort, betas: sm.currentBetas || [] });
|
|
1498
|
+
send({ type: "config_state", model: sm.currentModel || "", mode: sm.currentPermissionMode || "default", effort: sm.currentEffort, betas: sm.currentBetas || [], thinking: sm.currentThinking || "adaptive", thinkingBudget: sm.currentThinkingBudget || 10000 });
|
|
1454
1499
|
return;
|
|
1455
1500
|
}
|
|
1456
1501
|
|
|
1457
1502
|
if (msg.type === "set_betas") {
|
|
1458
1503
|
sm.currentBetas = msg.betas || [];
|
|
1459
|
-
send({ type: "config_state", model: sm.currentModel || "", mode: sm.currentPermissionMode || "default", effort: sm.currentEffort || "medium", betas: sm.currentBetas });
|
|
1504
|
+
send({ type: "config_state", model: sm.currentModel || "", mode: sm.currentPermissionMode || "default", effort: sm.currentEffort || "medium", betas: sm.currentBetas, thinking: sm.currentThinking || "adaptive", thinkingBudget: sm.currentThinkingBudget || 10000 });
|
|
1505
|
+
return;
|
|
1506
|
+
}
|
|
1507
|
+
|
|
1508
|
+
if (msg.type === "set_thinking") {
|
|
1509
|
+
sm.currentThinking = msg.thinking || "adaptive";
|
|
1510
|
+
if (msg.budgetTokens) sm.currentThinkingBudget = msg.budgetTokens;
|
|
1511
|
+
send({ type: "config_state", model: sm.currentModel || "", mode: sm.currentPermissionMode || "default", effort: sm.currentEffort || "medium", betas: sm.currentBetas || [], thinking: sm.currentThinking || "adaptive", thinkingBudget: sm.currentThinkingBudget || 10000 });
|
|
1460
1512
|
return;
|
|
1461
1513
|
}
|
|
1462
1514
|
|
|
@@ -1557,6 +1609,34 @@ function createProjectContext(opts) {
|
|
|
1557
1609
|
return;
|
|
1558
1610
|
}
|
|
1559
1611
|
|
|
1612
|
+
if (msg.type === "fork_session" && msg.uuid) {
|
|
1613
|
+
var session = getSessionForWs(ws);
|
|
1614
|
+
if (!session || !session.cliSessionId) {
|
|
1615
|
+
sendTo(ws, { type: "error", text: "Cannot fork: no CLI session" });
|
|
1616
|
+
return;
|
|
1617
|
+
}
|
|
1618
|
+
var forkCliId = session.cliSessionId;
|
|
1619
|
+
var forkTitle = (session.title || "New Session") + " (fork)";
|
|
1620
|
+
getSDK().then(function(sdkMod) {
|
|
1621
|
+
return sdkMod.forkSession(forkCliId, {
|
|
1622
|
+
upToMessageId: msg.uuid,
|
|
1623
|
+
dir: cwd,
|
|
1624
|
+
});
|
|
1625
|
+
}).then(function(result) {
|
|
1626
|
+
var cliSess = require("./cli-sessions");
|
|
1627
|
+
return cliSess.readCliSessionHistory(cwd, result.sessionId).then(function(history) {
|
|
1628
|
+
var forked = sm.resumeSession(result.sessionId, { history: history, title: forkTitle }, ws);
|
|
1629
|
+
if (forked) {
|
|
1630
|
+
ws._clayActiveSession = forked.localId;
|
|
1631
|
+
sendTo(ws, { type: "fork_complete", sessionId: forked.localId });
|
|
1632
|
+
}
|
|
1633
|
+
});
|
|
1634
|
+
}).catch(function(e) {
|
|
1635
|
+
sendTo(ws, { type: "error", text: "Fork failed: " + (e.message || e) });
|
|
1636
|
+
});
|
|
1637
|
+
return;
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1560
1640
|
if (msg.type === "ask_user_response") {
|
|
1561
1641
|
var session = getSessionForWs(ws);
|
|
1562
1642
|
if (!session) return;
|
|
@@ -1591,7 +1671,7 @@ function createProjectContext(opts) {
|
|
|
1591
1671
|
if (decision === "allow_accept_edits") {
|
|
1592
1672
|
sdk.setPermissionMode(session, "acceptEdits");
|
|
1593
1673
|
sm.currentPermissionMode = "acceptEdits";
|
|
1594
|
-
send({ type: "config_state", model: sm.currentModel || "", mode: sm.currentPermissionMode, effort: sm.currentEffort || "medium", betas: sm.currentBetas || [] });
|
|
1674
|
+
send({ type: "config_state", model: sm.currentModel || "", mode: sm.currentPermissionMode, effort: sm.currentEffort || "medium", betas: sm.currentBetas || [], thinking: sm.currentThinking || "adaptive", thinkingBudget: sm.currentThinkingBudget || 10000 });
|
|
1595
1675
|
pending.resolve({ behavior: "allow", updatedInput: pending.toolInput });
|
|
1596
1676
|
sm.sendAndRecord(session, { type: "permission_resolved", requestId: requestId, decision: decision });
|
|
1597
1677
|
return;
|
|
@@ -1620,7 +1700,7 @@ function createProjectContext(opts) {
|
|
|
1620
1700
|
|
|
1621
1701
|
// Update permission mode for the new session
|
|
1622
1702
|
sm.currentPermissionMode = "acceptEdits";
|
|
1623
|
-
send({ type: "config_state", model: sm.currentModel || "", mode: sm.currentPermissionMode, effort: sm.currentEffort || "medium", betas: sm.currentBetas || [] });
|
|
1703
|
+
send({ type: "config_state", model: sm.currentModel || "", mode: sm.currentPermissionMode, effort: sm.currentEffort || "medium", betas: sm.currentBetas || [], thinking: sm.currentThinking || "adaptive", thinkingBudget: sm.currentThinkingBudget || 10000 });
|
|
1624
1704
|
|
|
1625
1705
|
// Build prompt from plan content (sent from client) or plan file path
|
|
1626
1706
|
var clientPlanContent = msg.planContent || "";
|
|
@@ -1713,6 +1793,26 @@ function createProjectContext(opts) {
|
|
|
1713
1793
|
return;
|
|
1714
1794
|
}
|
|
1715
1795
|
|
|
1796
|
+
// --- MCP elicitation response ---
|
|
1797
|
+
if (msg.type === "elicitation_response") {
|
|
1798
|
+
var session = getSessionForWs(ws);
|
|
1799
|
+
if (!session) return;
|
|
1800
|
+
var pending = session.pendingElicitations && session.pendingElicitations[msg.requestId];
|
|
1801
|
+
if (!pending) return;
|
|
1802
|
+
delete session.pendingElicitations[msg.requestId];
|
|
1803
|
+
if (msg.action === "accept") {
|
|
1804
|
+
pending.resolve({ action: "accept", content: msg.content || {} });
|
|
1805
|
+
} else {
|
|
1806
|
+
pending.resolve({ action: "reject" });
|
|
1807
|
+
}
|
|
1808
|
+
sm.sendAndRecord(session, {
|
|
1809
|
+
type: "elicitation_resolved",
|
|
1810
|
+
requestId: msg.requestId,
|
|
1811
|
+
action: msg.action,
|
|
1812
|
+
});
|
|
1813
|
+
return;
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1716
1816
|
// --- Browse directories (for add-project autocomplete) ---
|
|
1717
1817
|
if (msg.type === "browse_dir") {
|
|
1718
1818
|
var rawPath = (msg.path || "").replace(/^~/, process.env.HOME || "/");
|
|
@@ -2662,6 +2762,14 @@ function createProjectContext(opts) {
|
|
|
2662
2762
|
session.title = (msg.text || "Image").substring(0, 50);
|
|
2663
2763
|
sm.saveSessionFile(session);
|
|
2664
2764
|
sm.broadcastSessionList();
|
|
2765
|
+
// Sync auto-title to SDK
|
|
2766
|
+
if (session.cliSessionId) {
|
|
2767
|
+
getSDK().then(function(sdk) {
|
|
2768
|
+
sdk.renameSession(session.cliSessionId, session.title, { dir: cwd }).catch(function(e) {
|
|
2769
|
+
console.error("[project] SDK renameSession failed:", e.message);
|
|
2770
|
+
});
|
|
2771
|
+
}).catch(function() {});
|
|
2772
|
+
}
|
|
2665
2773
|
}
|
|
2666
2774
|
|
|
2667
2775
|
var fullText = msg.text || "";
|
|
@@ -3162,6 +3270,8 @@ function createProjectContext(opts) {
|
|
|
3162
3270
|
},
|
|
3163
3271
|
warmup: function () {
|
|
3164
3272
|
sdk.warmup();
|
|
3273
|
+
// Migrate existing relay session titles to SDK format (one-time, async)
|
|
3274
|
+
sm.migrateSessionTitles(getSDK, cwd);
|
|
3165
3275
|
},
|
|
3166
3276
|
destroy: destroy,
|
|
3167
3277
|
};
|
package/lib/public/app.js
CHANGED
|
@@ -10,7 +10,7 @@ import { initFileBrowser, loadRootDirectory, refreshTree, handleFsList, handleFs
|
|
|
10
10
|
import { initTerminal, openTerminal, closeTerminal, resetTerminals, handleTermList, handleTermCreated, handleTermOutput, handleTermExited, handleTermClosed, sendTerminalCommand } from './modules/terminal.js';
|
|
11
11
|
import { initStickyNotes, handleNotesList, handleNoteCreated, handleNoteUpdated, handleNoteDeleted, openArchive, closeArchive, isArchiveOpen } from './modules/sticky-notes.js';
|
|
12
12
|
import { initTheme, getThemeColor, getComputedVar, onThemeChange, getCurrentTheme } from './modules/theme.js';
|
|
13
|
-
import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUserQuestion, markAskUserAnswered, renderPermissionRequest, markPermissionResolved, markPermissionCancelled, renderPlanBanner, renderPlanCard, handleTodoWrite, handleTaskCreate, handleTaskUpdate, startThinking, appendThinking, stopThinking, resetThinkingGroup, createToolItem, updateToolExecuting, updateToolResult, markAllToolsDone, addTurnMeta, enableMainInput, getTools, getPlanContent, setPlanContent, isPlanFilePath, getTodoTools, updateSubagentActivity, addSubagentToolEntry, markSubagentDone, updateSubagentProgress, initSubagentStop, closeToolGroup, removeToolFromGroup } from './modules/tools.js';
|
|
13
|
+
import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUserQuestion, markAskUserAnswered, renderPermissionRequest, markPermissionResolved, markPermissionCancelled, renderElicitationRequest, markElicitationResolved, renderPlanBanner, renderPlanCard, handleTodoWrite, handleTaskCreate, handleTaskUpdate, startThinking, appendThinking, stopThinking, resetThinkingGroup, createToolItem, updateToolExecuting, updateToolResult, markAllToolsDone, addTurnMeta, enableMainInput, getTools, getPlanContent, setPlanContent, isPlanFilePath, getTodoTools, updateSubagentActivity, addSubagentToolEntry, markSubagentDone, updateSubagentProgress, initSubagentStop, closeToolGroup, removeToolFromGroup } from './modules/tools.js';
|
|
14
14
|
import { initServerSettings, updateSettingsStats, updateSettingsModels, updateDaemonConfig, handleSetPinResult, handleKeepAwakeChanged, handleRestartResult, handleShutdownResult, handleSharedEnv, handleSharedEnvSaved, handleGlobalClaudeMdRead, handleGlobalClaudeMdWrite } from './modules/server-settings.js';
|
|
15
15
|
import { initProjectSettings, handleInstructionsRead, handleInstructionsWrite, handleProjectEnv, handleProjectEnvSaved, isProjectSettingsOpen, handleProjectSharedEnv, handleProjectSharedEnvSaved } from './modules/project-settings.js';
|
|
16
16
|
import { initSkills, handleSkillInstalled, handleSkillUninstalled } from './modules/skills.js';
|
|
@@ -1162,9 +1162,11 @@ import { initAdmin, checkAdminAccess } from './modules/admin.js';
|
|
|
1162
1162
|
|
|
1163
1163
|
var confirmCallback = null;
|
|
1164
1164
|
|
|
1165
|
-
function showConfirm(text, onConfirm) {
|
|
1165
|
+
function showConfirm(text, onConfirm, okLabel, destructive) {
|
|
1166
1166
|
confirmText.textContent = text;
|
|
1167
1167
|
confirmCallback = onConfirm;
|
|
1168
|
+
confirmOk.textContent = okLabel || "Delete";
|
|
1169
|
+
confirmOk.className = "confirm-btn " + (destructive === false ? "confirm-ok" : "confirm-delete");
|
|
1168
1170
|
confirmModal.classList.remove("hidden");
|
|
1169
1171
|
}
|
|
1170
1172
|
|
|
@@ -1463,11 +1465,18 @@ import { initAdmin, checkAdminAccess } from './modules/admin.js';
|
|
|
1463
1465
|
var configBetaSection = $("config-beta-section");
|
|
1464
1466
|
var configBeta1mBtn = $("config-beta-1m");
|
|
1465
1467
|
|
|
1468
|
+
var configThinkingSection = $("config-thinking-section");
|
|
1469
|
+
var configThinkingBar = $("config-thinking-bar");
|
|
1470
|
+
var configThinkingBudgetRow = $("config-thinking-budget-row");
|
|
1471
|
+
var configThinkingBudgetInput = $("config-thinking-budget");
|
|
1472
|
+
|
|
1466
1473
|
var currentModels = [];
|
|
1467
1474
|
var currentModel = "";
|
|
1468
1475
|
var currentMode = "default";
|
|
1469
1476
|
var currentEffort = "medium";
|
|
1470
1477
|
var currentBetas = [];
|
|
1478
|
+
var currentThinking = "adaptive";
|
|
1479
|
+
var currentThinkingBudget = 10000;
|
|
1471
1480
|
var skipPermsEnabled = false;
|
|
1472
1481
|
|
|
1473
1482
|
var MODE_OPTIONS = [
|
|
@@ -1478,6 +1487,7 @@ import { initAdmin, checkAdminAccess } from './modules/admin.js';
|
|
|
1478
1487
|
var MODE_FULL_AUTO = { value: "bypassPermissions", label: "Full auto" };
|
|
1479
1488
|
|
|
1480
1489
|
var EFFORT_LEVELS = ["low", "medium", "high", "max"];
|
|
1490
|
+
var THINKING_OPTIONS = ["disabled", "adaptive", "budget"];
|
|
1481
1491
|
|
|
1482
1492
|
function modelDisplayName(value, models) {
|
|
1483
1493
|
if (!value) return "";
|
|
@@ -1503,6 +1513,13 @@ import { initAdmin, checkAdminAccess } from './modules/admin.js';
|
|
|
1503
1513
|
return value.charAt(0).toUpperCase() + value.slice(1);
|
|
1504
1514
|
}
|
|
1505
1515
|
|
|
1516
|
+
function thinkingDisplayName(value) {
|
|
1517
|
+
if (value === "disabled") return "Off";
|
|
1518
|
+
if (value === "adaptive") return "Adaptive";
|
|
1519
|
+
if (value === "budget") return "Budget";
|
|
1520
|
+
return value || "Adaptive";
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1506
1523
|
function isSonnetModel(model) {
|
|
1507
1524
|
if (!model) return false;
|
|
1508
1525
|
var lower = model.toLowerCase();
|
|
@@ -1526,6 +1543,9 @@ import { initAdmin, checkAdminAccess } from './modules/admin.js';
|
|
|
1526
1543
|
if (modelSupportsEffort) {
|
|
1527
1544
|
parts.push(effortDisplayName(currentEffort));
|
|
1528
1545
|
}
|
|
1546
|
+
if (currentThinking && currentThinking !== "adaptive") {
|
|
1547
|
+
parts.push(thinkingDisplayName(currentThinking));
|
|
1548
|
+
}
|
|
1529
1549
|
if (hasBeta("context-1m")) {
|
|
1530
1550
|
parts.push("1M");
|
|
1531
1551
|
}
|
|
@@ -1533,6 +1553,7 @@ import { initAdmin, checkAdminAccess } from './modules/admin.js';
|
|
|
1533
1553
|
rebuildModelList();
|
|
1534
1554
|
rebuildModeList();
|
|
1535
1555
|
rebuildEffortBar();
|
|
1556
|
+
rebuildThinkingSection();
|
|
1536
1557
|
rebuildBetaSection();
|
|
1537
1558
|
}
|
|
1538
1559
|
|
|
@@ -1653,6 +1674,51 @@ import { initAdmin, checkAdminAccess } from './modules/admin.js';
|
|
|
1653
1674
|
configBeta1mBtn.setAttribute("aria-checked", active ? "true" : "false");
|
|
1654
1675
|
}
|
|
1655
1676
|
|
|
1677
|
+
function rebuildThinkingSection() {
|
|
1678
|
+
if (!configThinkingBar || !configThinkingSection) return;
|
|
1679
|
+
configThinkingSection.style.display = "";
|
|
1680
|
+
configThinkingBar.innerHTML = "";
|
|
1681
|
+
for (var i = 0; i < THINKING_OPTIONS.length; i++) {
|
|
1682
|
+
var opt = THINKING_OPTIONS[i];
|
|
1683
|
+
var btn = document.createElement("button");
|
|
1684
|
+
btn.className = "config-segment-btn";
|
|
1685
|
+
if (opt === currentThinking) btn.classList.add("active");
|
|
1686
|
+
btn.dataset.thinking = opt;
|
|
1687
|
+
btn.textContent = thinkingDisplayName(opt);
|
|
1688
|
+
btn.addEventListener("click", function () {
|
|
1689
|
+
var thinking = this.dataset.thinking;
|
|
1690
|
+
var msg = { type: "set_thinking", thinking: thinking };
|
|
1691
|
+
if (thinking === "budget") {
|
|
1692
|
+
msg.budgetTokens = currentThinkingBudget;
|
|
1693
|
+
}
|
|
1694
|
+
if (ws && ws.readyState === 1) {
|
|
1695
|
+
ws.send(JSON.stringify(msg));
|
|
1696
|
+
}
|
|
1697
|
+
});
|
|
1698
|
+
configThinkingBar.appendChild(btn);
|
|
1699
|
+
}
|
|
1700
|
+
// Show/hide budget input
|
|
1701
|
+
if (configThinkingBudgetRow) {
|
|
1702
|
+
configThinkingBudgetRow.style.display = currentThinking === "budget" ? "" : "none";
|
|
1703
|
+
}
|
|
1704
|
+
if (configThinkingBudgetInput) {
|
|
1705
|
+
configThinkingBudgetInput.value = currentThinkingBudget;
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1708
|
+
|
|
1709
|
+
if (configThinkingBudgetInput) {
|
|
1710
|
+
configThinkingBudgetInput.addEventListener("change", function () {
|
|
1711
|
+
var val = parseInt(this.value, 10);
|
|
1712
|
+
if (isNaN(val) || val < 1024) val = 1024;
|
|
1713
|
+
if (val > 128000) val = 128000;
|
|
1714
|
+
currentThinkingBudget = val;
|
|
1715
|
+
this.value = val;
|
|
1716
|
+
if (ws && ws.readyState === 1) {
|
|
1717
|
+
ws.send(JSON.stringify({ type: "set_thinking", thinking: "budget", budgetTokens: val }));
|
|
1718
|
+
}
|
|
1719
|
+
});
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1656
1722
|
configBeta1mBtn.addEventListener("click", function (e) {
|
|
1657
1723
|
e.stopPropagation();
|
|
1658
1724
|
var active = hasBeta("context-1m");
|
|
@@ -2038,6 +2104,20 @@ import { initAdmin, checkAdminAccess } from './modules/admin.js';
|
|
|
2038
2104
|
forceScrollToBottom();
|
|
2039
2105
|
});
|
|
2040
2106
|
|
|
2107
|
+
// Fork session from a user message
|
|
2108
|
+
messagesEl.addEventListener("click", function(e) {
|
|
2109
|
+
var btn = e.target.closest(".msg-action-fork");
|
|
2110
|
+
if (!btn) return;
|
|
2111
|
+
var msgEl = btn.closest("[data-uuid]");
|
|
2112
|
+
if (!msgEl || !msgEl.dataset.uuid) return;
|
|
2113
|
+
var forkUuid = msgEl.dataset.uuid;
|
|
2114
|
+
showConfirm("Fork session from this message?", function() {
|
|
2115
|
+
if (ws && ws.readyState === 1) {
|
|
2116
|
+
ws.send(JSON.stringify({ type: "fork_session", uuid: forkUuid }));
|
|
2117
|
+
}
|
|
2118
|
+
}, "Fork", false);
|
|
2119
|
+
});
|
|
2120
|
+
|
|
2041
2121
|
function scrollToBottom() {
|
|
2042
2122
|
if (prependAnchor) return;
|
|
2043
2123
|
if (isUserScrolledUp) {
|
|
@@ -2145,7 +2225,7 @@ import { initAdmin, checkAdminAccess } from './modules/admin.js';
|
|
|
2145
2225
|
actions.innerHTML =
|
|
2146
2226
|
'<span class="msg-action-time">' + timeStr + '</span>' +
|
|
2147
2227
|
'<button class="msg-action-btn msg-action-copy" type="button" title="Copy">' + iconHtml("copy") + '</button>' +
|
|
2148
|
-
'<button class="msg-action-btn msg-action-
|
|
2228
|
+
'<button class="msg-action-btn msg-action-fork" type="button" title="Fork">' + iconHtml("git-branch") + '</button>' +
|
|
2149
2229
|
'<button class="msg-action-btn msg-action-rewind msg-user-rewind-btn" type="button" title="Rewind">' + iconHtml("rotate-ccw") + '</button>' +
|
|
2150
2230
|
'<button class="msg-action-btn msg-action-hidden msg-action-edit" type="button" title="Edit">' + iconHtml("pencil") + '</button>';
|
|
2151
2231
|
div.appendChild(actions);
|
|
@@ -2893,6 +2973,8 @@ import { initAdmin, checkAdminAccess } from './modules/admin.js';
|
|
|
2893
2973
|
if (msg.mode) currentMode = msg.mode;
|
|
2894
2974
|
if (msg.effort) currentEffort = msg.effort;
|
|
2895
2975
|
if (msg.betas) currentBetas = msg.betas;
|
|
2976
|
+
if (msg.thinking) currentThinking = msg.thinking;
|
|
2977
|
+
if (msg.thinkingBudget) currentThinkingBudget = msg.thinkingBudget;
|
|
2896
2978
|
// Validate effort against current model's supported levels
|
|
2897
2979
|
if (currentModels.length > 0) {
|
|
2898
2980
|
var levels = getModelEffortLevels();
|
|
@@ -3181,6 +3263,16 @@ import { initAdmin, checkAdminAccess } from './modules/admin.js';
|
|
|
3181
3263
|
startUrgentBlink();
|
|
3182
3264
|
break;
|
|
3183
3265
|
|
|
3266
|
+
case "elicitation_request":
|
|
3267
|
+
renderElicitationRequest(msg);
|
|
3268
|
+
startUrgentBlink();
|
|
3269
|
+
break;
|
|
3270
|
+
|
|
3271
|
+
case "elicitation_resolved":
|
|
3272
|
+
markElicitationResolved(msg.requestId, msg.action);
|
|
3273
|
+
stopUrgentBlink();
|
|
3274
|
+
break;
|
|
3275
|
+
|
|
3184
3276
|
case "slash_command_result":
|
|
3185
3277
|
finalizeAssistantBlock();
|
|
3186
3278
|
var cmdBlock = document.createElement("div");
|
|
@@ -3213,7 +3305,7 @@ import { initAdmin, checkAdminAccess } from './modules/admin.js';
|
|
|
3213
3305
|
break;
|
|
3214
3306
|
|
|
3215
3307
|
case "task_progress":
|
|
3216
|
-
updateSubagentProgress(msg.parentToolId, msg.usage, msg.lastToolName);
|
|
3308
|
+
updateSubagentProgress(msg.parentToolId, msg.usage, msg.lastToolName, msg.summary);
|
|
3217
3309
|
break;
|
|
3218
3310
|
|
|
3219
3311
|
case "result":
|
|
@@ -3296,6 +3388,10 @@ import { initAdmin, checkAdminAccess } from './modules/admin.js';
|
|
|
3296
3388
|
addSystemMessage(msg.text || "Rewind failed.", true);
|
|
3297
3389
|
break;
|
|
3298
3390
|
|
|
3391
|
+
case "fork_complete":
|
|
3392
|
+
addSystemMessage("Session forked successfully.");
|
|
3393
|
+
break;
|
|
3394
|
+
|
|
3299
3395
|
case "fs_list_result":
|
|
3300
3396
|
handleFsList(msg);
|
|
3301
3397
|
break;
|
package/lib/public/css/menus.css
CHANGED
|
@@ -617,6 +617,29 @@
|
|
|
617
617
|
background: var(--accent);
|
|
618
618
|
}
|
|
619
619
|
|
|
620
|
+
.config-budget-row {
|
|
621
|
+
padding: 4px 14px 8px;
|
|
622
|
+
display: flex;
|
|
623
|
+
align-items: center;
|
|
624
|
+
gap: 8px;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
.config-budget-label {
|
|
628
|
+
font-size: 11px;
|
|
629
|
+
color: var(--text-muted);
|
|
630
|
+
white-space: nowrap;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
.config-budget-input {
|
|
634
|
+
width: 80px;
|
|
635
|
+
padding: 4px 6px;
|
|
636
|
+
border: 1px solid var(--border);
|
|
637
|
+
border-radius: 4px;
|
|
638
|
+
background: var(--bg);
|
|
639
|
+
color: var(--text);
|
|
640
|
+
font-size: 11px;
|
|
641
|
+
}
|
|
642
|
+
|
|
620
643
|
/* --- Info panels container --- */
|
|
621
644
|
#info-panels {
|
|
622
645
|
position: fixed;
|
|
@@ -984,6 +984,14 @@ pre.mermaid-error {
|
|
|
984
984
|
color: var(--text-dimmer);
|
|
985
985
|
}
|
|
986
986
|
|
|
987
|
+
.subagent-summary {
|
|
988
|
+
margin: 2px 0 2px 24px;
|
|
989
|
+
padding: 2px 12px;
|
|
990
|
+
font-size: 12px;
|
|
991
|
+
color: var(--text-muted);
|
|
992
|
+
font-style: italic;
|
|
993
|
+
}
|
|
994
|
+
|
|
987
995
|
.subagent-stop-btn {
|
|
988
996
|
margin-left: auto;
|
|
989
997
|
padding: 2px 10px;
|
package/lib/public/index.html
CHANGED
|
@@ -336,6 +336,15 @@
|
|
|
336
336
|
<div class="config-section-label">EFFORT</div>
|
|
337
337
|
<div id="config-effort-bar" class="config-segmented"></div>
|
|
338
338
|
</div>
|
|
339
|
+
<div id="config-thinking-section" class="config-section">
|
|
340
|
+
<div class="config-section-label">THINKING</div>
|
|
341
|
+
<div id="config-thinking-bar" class="config-segmented"></div>
|
|
342
|
+
<div id="config-thinking-budget-row" class="config-budget-row" style="display:none">
|
|
343
|
+
<label class="config-budget-label">Budget tokens</label>
|
|
344
|
+
<input id="config-thinking-budget" type="number" class="config-budget-input"
|
|
345
|
+
min="1024" max="128000" step="1024" value="10000">
|
|
346
|
+
</div>
|
|
347
|
+
</div>
|
|
339
348
|
<div id="config-beta-section" class="config-section" style="display:none">
|
|
340
349
|
<div class="config-section-label">BETA</div>
|
|
341
350
|
<div class="config-toggle-row">
|
|
@@ -693,6 +693,207 @@ export function markPermissionCancelled(requestId) {
|
|
|
693
693
|
delete pendingPermissions[requestId];
|
|
694
694
|
}
|
|
695
695
|
|
|
696
|
+
// --- MCP elicitation rendering ---
|
|
697
|
+
|
|
698
|
+
var pendingElicitations = {};
|
|
699
|
+
|
|
700
|
+
export function renderElicitationRequest(msg) {
|
|
701
|
+
if (pendingElicitations[msg.requestId]) return;
|
|
702
|
+
ctx.finalizeAssistantBlock();
|
|
703
|
+
stopThinking();
|
|
704
|
+
closeToolGroup();
|
|
705
|
+
|
|
706
|
+
var container = document.createElement("div");
|
|
707
|
+
container.className = "permission-container elicitation-container";
|
|
708
|
+
container.dataset.requestId = msg.requestId;
|
|
709
|
+
|
|
710
|
+
// Header
|
|
711
|
+
var header = document.createElement("div");
|
|
712
|
+
header.className = "permission-header";
|
|
713
|
+
header.innerHTML =
|
|
714
|
+
'<span class="permission-icon">' + iconHtml("key") + '</span>' +
|
|
715
|
+
'<span class="permission-title">' + escapeHtml(msg.serverName || "MCP Server") + ' requests input</span>';
|
|
716
|
+
|
|
717
|
+
// Body
|
|
718
|
+
var body = document.createElement("div");
|
|
719
|
+
body.className = "permission-body";
|
|
720
|
+
|
|
721
|
+
if (msg.message) {
|
|
722
|
+
var messageEl = document.createElement("div");
|
|
723
|
+
messageEl.className = "permission-reason";
|
|
724
|
+
messageEl.textContent = msg.message;
|
|
725
|
+
body.appendChild(messageEl);
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
// Form fields (form mode) or URL button (url mode)
|
|
729
|
+
var formData = {};
|
|
730
|
+
|
|
731
|
+
if (msg.mode === "url" && msg.url) {
|
|
732
|
+
var urlInfo = document.createElement("div");
|
|
733
|
+
urlInfo.className = "elicitation-url-info";
|
|
734
|
+
urlInfo.style.cssText = "margin-top: 8px; font-size: 12px; color: var(--text-muted);";
|
|
735
|
+
urlInfo.textContent = "Opens: " + msg.url;
|
|
736
|
+
body.appendChild(urlInfo);
|
|
737
|
+
} else if (msg.requestedSchema && msg.requestedSchema.properties) {
|
|
738
|
+
var formEl = document.createElement("div");
|
|
739
|
+
formEl.className = "elicitation-form";
|
|
740
|
+
formEl.style.cssText = "margin-top: 8px; display: flex; flex-direction: column; gap: 8px;";
|
|
741
|
+
|
|
742
|
+
var props = msg.requestedSchema.properties;
|
|
743
|
+
var required = msg.requestedSchema.required || [];
|
|
744
|
+
var propNames = Object.keys(props);
|
|
745
|
+
for (var i = 0; i < propNames.length; i++) {
|
|
746
|
+
var propName = propNames[i];
|
|
747
|
+
var prop = props[propName];
|
|
748
|
+
var isRequired = required.indexOf(propName) !== -1;
|
|
749
|
+
|
|
750
|
+
var fieldWrapper = document.createElement("div");
|
|
751
|
+
fieldWrapper.style.cssText = "display: flex; flex-direction: column; gap: 2px;";
|
|
752
|
+
|
|
753
|
+
var label = document.createElement("label");
|
|
754
|
+
label.style.cssText = "font-size: 12px; font-weight: 500; color: var(--text-secondary);";
|
|
755
|
+
label.textContent = propName + (isRequired ? " *" : "");
|
|
756
|
+
if (prop.description) {
|
|
757
|
+
label.title = prop.description;
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
var input;
|
|
761
|
+
if (prop.type === "boolean") {
|
|
762
|
+
input = document.createElement("input");
|
|
763
|
+
input.type = "checkbox";
|
|
764
|
+
input.dataset.propName = propName;
|
|
765
|
+
input.dataset.propType = "boolean";
|
|
766
|
+
} else if (prop.enum) {
|
|
767
|
+
input = document.createElement("select");
|
|
768
|
+
input.dataset.propName = propName;
|
|
769
|
+
input.dataset.propType = "enum";
|
|
770
|
+
input.style.cssText = "padding: 4px 8px; border-radius: 4px; border: 1px solid var(--border); background: var(--input-bg); color: var(--text-primary); font-size: 13px;";
|
|
771
|
+
for (var ei = 0; ei < prop.enum.length; ei++) {
|
|
772
|
+
var opt = document.createElement("option");
|
|
773
|
+
opt.value = prop.enum[ei];
|
|
774
|
+
opt.textContent = prop.enum[ei];
|
|
775
|
+
input.appendChild(opt);
|
|
776
|
+
}
|
|
777
|
+
} else {
|
|
778
|
+
input = document.createElement("input");
|
|
779
|
+
input.type = prop.type === "number" || prop.type === "integer" ? "number" : "text";
|
|
780
|
+
input.dataset.propName = propName;
|
|
781
|
+
input.dataset.propType = prop.type || "string";
|
|
782
|
+
input.placeholder = prop.description || propName;
|
|
783
|
+
input.style.cssText = "padding: 4px 8px; border-radius: 4px; border: 1px solid var(--border); background: var(--input-bg); color: var(--text-primary); font-size: 13px;";
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
fieldWrapper.appendChild(label);
|
|
787
|
+
fieldWrapper.appendChild(input);
|
|
788
|
+
formEl.appendChild(fieldWrapper);
|
|
789
|
+
}
|
|
790
|
+
body.appendChild(formEl);
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
// Actions
|
|
794
|
+
var actions = document.createElement("div");
|
|
795
|
+
actions.className = "permission-actions";
|
|
796
|
+
|
|
797
|
+
var acceptBtn = document.createElement("button");
|
|
798
|
+
acceptBtn.className = "permission-btn permission-allow";
|
|
799
|
+
|
|
800
|
+
if (msg.mode === "url" && msg.url) {
|
|
801
|
+
acceptBtn.textContent = "Open & Approve";
|
|
802
|
+
acceptBtn.addEventListener("click", function () {
|
|
803
|
+
window.open(msg.url, "_blank");
|
|
804
|
+
sendElicitationResponse(container, msg.requestId, "accept", {});
|
|
805
|
+
});
|
|
806
|
+
} else {
|
|
807
|
+
acceptBtn.textContent = "Submit";
|
|
808
|
+
acceptBtn.addEventListener("click", function () {
|
|
809
|
+
// Collect form values
|
|
810
|
+
var content = {};
|
|
811
|
+
var inputs = container.querySelectorAll("[data-prop-name]");
|
|
812
|
+
for (var j = 0; j < inputs.length; j++) {
|
|
813
|
+
var inp = inputs[j];
|
|
814
|
+
var name = inp.dataset.propName;
|
|
815
|
+
var pType = inp.dataset.propType;
|
|
816
|
+
if (pType === "boolean") {
|
|
817
|
+
content[name] = inp.checked;
|
|
818
|
+
} else if (pType === "number" || pType === "integer") {
|
|
819
|
+
content[name] = Number(inp.value);
|
|
820
|
+
} else {
|
|
821
|
+
content[name] = inp.value;
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
sendElicitationResponse(container, msg.requestId, "accept", content);
|
|
825
|
+
});
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
var denyBtn = document.createElement("button");
|
|
829
|
+
denyBtn.className = "permission-btn permission-deny";
|
|
830
|
+
denyBtn.textContent = "Deny";
|
|
831
|
+
denyBtn.addEventListener("click", function () {
|
|
832
|
+
sendElicitationResponse(container, msg.requestId, "reject", null);
|
|
833
|
+
});
|
|
834
|
+
|
|
835
|
+
actions.appendChild(acceptBtn);
|
|
836
|
+
actions.appendChild(denyBtn);
|
|
837
|
+
|
|
838
|
+
container.appendChild(header);
|
|
839
|
+
container.appendChild(body);
|
|
840
|
+
container.appendChild(actions);
|
|
841
|
+
ctx.addToMessages(container);
|
|
842
|
+
|
|
843
|
+
pendingElicitations[msg.requestId] = container;
|
|
844
|
+
refreshIcons();
|
|
845
|
+
ctx.setActivity(null);
|
|
846
|
+
ctx.scrollToBottom();
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
function sendElicitationResponse(container, requestId, action, content) {
|
|
850
|
+
if (container.classList.contains("resolved")) return;
|
|
851
|
+
container.classList.add("resolved");
|
|
852
|
+
if (ctx.stopUrgentBlink) ctx.stopUrgentBlink();
|
|
853
|
+
|
|
854
|
+
var label = action === "reject" ? "Denied" : "Submitted";
|
|
855
|
+
var resolvedClass = action === "reject" ? "resolved-denied" : "resolved-allowed";
|
|
856
|
+
container.classList.add(resolvedClass);
|
|
857
|
+
|
|
858
|
+
var actions = container.querySelector(".permission-actions");
|
|
859
|
+
if (actions) {
|
|
860
|
+
actions.innerHTML = '<span class="permission-decision-label">' + label + '</span>';
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
if (ctx.ws && ctx.connected) {
|
|
864
|
+
var msg = {
|
|
865
|
+
type: "elicitation_response",
|
|
866
|
+
requestId: requestId,
|
|
867
|
+
action: action,
|
|
868
|
+
};
|
|
869
|
+
if (action === "accept" && content) {
|
|
870
|
+
msg.content = content;
|
|
871
|
+
}
|
|
872
|
+
ctx.ws.send(JSON.stringify(msg));
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
delete pendingElicitations[requestId];
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
export function markElicitationResolved(requestId, action) {
|
|
879
|
+
var container = pendingElicitations[requestId];
|
|
880
|
+
if (!container) {
|
|
881
|
+
container = ctx.messagesEl.querySelector('.elicitation-container[data-request-id="' + requestId + '"]');
|
|
882
|
+
}
|
|
883
|
+
if (!container || container.classList.contains("resolved")) return;
|
|
884
|
+
|
|
885
|
+
container.classList.add("resolved");
|
|
886
|
+
var isDeny = action === "reject";
|
|
887
|
+
container.classList.add(isDeny ? "resolved-denied" : "resolved-allowed");
|
|
888
|
+
|
|
889
|
+
var label = isDeny ? "Denied" : "Submitted";
|
|
890
|
+
var actionsEl = container.querySelector(".permission-actions");
|
|
891
|
+
if (actionsEl) {
|
|
892
|
+
actionsEl.innerHTML = '<span class="permission-decision-label">' + label + '</span>';
|
|
893
|
+
}
|
|
894
|
+
delete pendingElicitations[requestId];
|
|
895
|
+
}
|
|
896
|
+
|
|
696
897
|
// --- Plan mode rendering ---
|
|
697
898
|
export function renderPlanBanner(type) {
|
|
698
899
|
ctx.finalizeAssistantBlock();
|
|
@@ -1504,7 +1705,7 @@ function fmtDuration(ms) {
|
|
|
1504
1705
|
return secs + "s";
|
|
1505
1706
|
}
|
|
1506
1707
|
|
|
1507
|
-
export function updateSubagentProgress(parentToolId, usage, lastToolName) {
|
|
1708
|
+
export function updateSubagentProgress(parentToolId, usage, lastToolName, summary) {
|
|
1508
1709
|
var tool = tools[parentToolId];
|
|
1509
1710
|
if (!tool || !tool.el) return;
|
|
1510
1711
|
var progressEl = tool.el.querySelector(".subagent-progress");
|
|
@@ -1523,6 +1724,17 @@ export function updateSubagentProgress(parentToolId, usage, lastToolName) {
|
|
|
1523
1724
|
}
|
|
1524
1725
|
if (lastToolName) parts.push(lastToolName);
|
|
1525
1726
|
progressEl.textContent = parts.join(" · ");
|
|
1727
|
+
|
|
1728
|
+
// AI-generated progress summary (agentProgressSummaries)
|
|
1729
|
+
if (summary) {
|
|
1730
|
+
var summaryEl = tool.el.querySelector(".subagent-summary");
|
|
1731
|
+
if (!summaryEl) {
|
|
1732
|
+
summaryEl = document.createElement("div");
|
|
1733
|
+
summaryEl.className = "subagent-summary";
|
|
1734
|
+
progressEl.parentNode.insertBefore(summaryEl, progressEl.nextSibling);
|
|
1735
|
+
}
|
|
1736
|
+
summaryEl.textContent = summary;
|
|
1737
|
+
}
|
|
1526
1738
|
}
|
|
1527
1739
|
|
|
1528
1740
|
export function initSubagentStop(parentToolId, taskId) {
|
|
@@ -1640,6 +1852,7 @@ export function resetToolState() {
|
|
|
1640
1852
|
todoWidgetVisible = true;
|
|
1641
1853
|
if (todoObserver) { todoObserver.disconnect(); todoObserver = null; }
|
|
1642
1854
|
pendingPermissions = {};
|
|
1855
|
+
pendingElicitations = {};
|
|
1643
1856
|
currentToolGroup = null;
|
|
1644
1857
|
toolGroupCounter = 0;
|
|
1645
1858
|
toolGroups = {};
|
package/lib/sdk-bridge.js
CHANGED
|
@@ -314,6 +314,7 @@ function createSDKBridge(opts) {
|
|
|
314
314
|
session.blocks = {};
|
|
315
315
|
session.sentToolResults = {};
|
|
316
316
|
session.pendingPermissions = {};
|
|
317
|
+
session.pendingElicitations = {};
|
|
317
318
|
// Record ask_user_answered for any leftover pending questions so replay pairs correctly
|
|
318
319
|
var leftoverAskIds = Object.keys(session.pendingAskUser);
|
|
319
320
|
for (var lai = 0; lai < leftoverAskIds.length; lai++) {
|
|
@@ -386,6 +387,7 @@ function createSDKBridge(opts) {
|
|
|
386
387
|
usage: parsed.usage || null,
|
|
387
388
|
lastToolName: parsed.last_tool_name || null,
|
|
388
389
|
description: parsed.description || "",
|
|
390
|
+
summary: parsed.summary || null,
|
|
389
391
|
});
|
|
390
392
|
}
|
|
391
393
|
|
|
@@ -494,6 +496,51 @@ function createSDKBridge(opts) {
|
|
|
494
496
|
// user messages with parent_tool_use_id contain tool_results — skip silently
|
|
495
497
|
}
|
|
496
498
|
|
|
499
|
+
// --- MCP elicitation ---
|
|
500
|
+
|
|
501
|
+
function handleElicitation(session, request, opts) {
|
|
502
|
+
// Ralph Loop: auto-reject elicitation in autonomous mode
|
|
503
|
+
if (session.loop && session.loop.active && session.loop.role !== "crafting") {
|
|
504
|
+
return Promise.resolve({ action: "reject" });
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
return new Promise(function(resolve) {
|
|
508
|
+
var requestId = crypto.randomUUID();
|
|
509
|
+
if (!session.pendingElicitations) session.pendingElicitations = {};
|
|
510
|
+
session.pendingElicitations[requestId] = {
|
|
511
|
+
resolve: resolve,
|
|
512
|
+
request: request,
|
|
513
|
+
};
|
|
514
|
+
sendAndRecord(session, {
|
|
515
|
+
type: "elicitation_request",
|
|
516
|
+
requestId: requestId,
|
|
517
|
+
serverName: request.serverName,
|
|
518
|
+
message: request.message,
|
|
519
|
+
mode: request.mode || "form",
|
|
520
|
+
url: request.url || null,
|
|
521
|
+
elicitationId: request.elicitationId || null,
|
|
522
|
+
requestedSchema: request.requestedSchema || null,
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
if (pushModule) {
|
|
526
|
+
pushModule.sendPush({
|
|
527
|
+
type: "elicitation",
|
|
528
|
+
slug: slug,
|
|
529
|
+
title: (request.serverName || "MCP Server") + " needs input",
|
|
530
|
+
body: request.message || "Waiting for your response",
|
|
531
|
+
tag: "claude-elicitation",
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
if (opts.signal) {
|
|
536
|
+
opts.signal.addEventListener("abort", function() {
|
|
537
|
+
delete session.pendingElicitations[requestId];
|
|
538
|
+
resolve({ action: "reject" });
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
|
|
497
544
|
// --- SDK query lifecycle ---
|
|
498
545
|
|
|
499
546
|
function handleCanUseTool(session, toolName, input, opts) {
|
|
@@ -710,6 +757,7 @@ function createSDKBridge(opts) {
|
|
|
710
757
|
session.taskStopRequested = false;
|
|
711
758
|
session.pendingPermissions = {};
|
|
712
759
|
session.pendingAskUser = {};
|
|
760
|
+
session.pendingElicitations = {};
|
|
713
761
|
}
|
|
714
762
|
// Ralph Loop: notify completion so loop orchestrator can proceed
|
|
715
763
|
if (session.onQueryComplete) {
|
|
@@ -773,6 +821,7 @@ function createSDKBridge(opts) {
|
|
|
773
821
|
session.blocks = {};
|
|
774
822
|
session.sentToolResults = {};
|
|
775
823
|
session.activeTaskToolIds = {};
|
|
824
|
+
session.pendingElicitations = {};
|
|
776
825
|
session.streamedText = false;
|
|
777
826
|
session.responsePreview = "";
|
|
778
827
|
|
|
@@ -805,9 +854,13 @@ function createSDKBridge(opts) {
|
|
|
805
854
|
extraArgs: { "replay-user-messages": null },
|
|
806
855
|
abortController: session.abortController,
|
|
807
856
|
promptSuggestions: true,
|
|
857
|
+
agentProgressSummaries: true,
|
|
808
858
|
canUseTool: function(toolName, input, toolOpts) {
|
|
809
859
|
return handleCanUseTool(session, toolName, input, toolOpts);
|
|
810
860
|
},
|
|
861
|
+
onElicitation: function(request, elicitOpts) {
|
|
862
|
+
return handleElicitation(session, request, elicitOpts);
|
|
863
|
+
},
|
|
811
864
|
};
|
|
812
865
|
|
|
813
866
|
if (sm.currentModel) {
|
|
@@ -822,6 +875,12 @@ function createSDKBridge(opts) {
|
|
|
822
875
|
queryOptions.betas = sm.currentBetas;
|
|
823
876
|
}
|
|
824
877
|
|
|
878
|
+
if (sm.currentThinking === "disabled") {
|
|
879
|
+
queryOptions.thinking = { type: "disabled" };
|
|
880
|
+
} else if (sm.currentThinking === "budget" && sm.currentThinkingBudget) {
|
|
881
|
+
queryOptions.thinking = { type: "enabled", budgetTokens: sm.currentThinkingBudget };
|
|
882
|
+
}
|
|
883
|
+
|
|
825
884
|
if (dangerouslySkipPermissions) {
|
|
826
885
|
queryOptions.permissionMode = "bypassPermissions";
|
|
827
886
|
queryOptions.allowDangerouslySkipPermissions = true;
|
|
@@ -1009,6 +1068,21 @@ function createSDKBridge(opts) {
|
|
|
1009
1068
|
}
|
|
1010
1069
|
}
|
|
1011
1070
|
|
|
1071
|
+
async function setEffort(session, effort) {
|
|
1072
|
+
if (!session.queryInstance) {
|
|
1073
|
+
sm.currentEffort = effort;
|
|
1074
|
+
send({ type: "config_state", model: sm.currentModel || "", mode: sm.currentPermissionMode || "default", effort: sm.currentEffort, betas: sm.currentBetas || [] });
|
|
1075
|
+
return;
|
|
1076
|
+
}
|
|
1077
|
+
try {
|
|
1078
|
+
await session.queryInstance.setEffort(effort);
|
|
1079
|
+
sm.currentEffort = effort;
|
|
1080
|
+
send({ type: "config_state", model: sm.currentModel || "", mode: sm.currentPermissionMode || "default", effort: sm.currentEffort, betas: sm.currentBetas || [] });
|
|
1081
|
+
} catch (e) {
|
|
1082
|
+
send({ type: "error", text: "Failed to set effort: " + (e.message || e) });
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1012
1086
|
async function setPermissionMode(session, mode) {
|
|
1013
1087
|
// When dangerouslySkipPermissions is active, ignore mode changes from UI
|
|
1014
1088
|
// to prevent accidentally downgrading from bypassPermissions
|
|
@@ -1051,11 +1125,13 @@ function createSDKBridge(opts) {
|
|
|
1051
1125
|
createMessageQueue: createMessageQueue,
|
|
1052
1126
|
processSDKMessage: processSDKMessage,
|
|
1053
1127
|
handleCanUseTool: handleCanUseTool,
|
|
1128
|
+
handleElicitation: handleElicitation,
|
|
1054
1129
|
processQueryStream: processQueryStream,
|
|
1055
1130
|
getOrCreateRewindQuery: getOrCreateRewindQuery,
|
|
1056
1131
|
startQuery: startQuery,
|
|
1057
1132
|
pushMessage: pushMessage,
|
|
1058
1133
|
setModel: setModel,
|
|
1134
|
+
setEffort: setEffort,
|
|
1059
1135
|
setPermissionMode: setPermissionMode,
|
|
1060
1136
|
isClaudeProcess: isClaudeProcess,
|
|
1061
1137
|
permissionPushTitle: permissionPushTitle,
|
package/lib/sessions.js
CHANGED
|
@@ -508,6 +508,31 @@ function createSessionManager(opts) {
|
|
|
508
508
|
return results;
|
|
509
509
|
}
|
|
510
510
|
|
|
511
|
+
function migrateSessionTitles(getSDK, migrateCwd) {
|
|
512
|
+
var toMigrate = [];
|
|
513
|
+
sessions.forEach(function(s) {
|
|
514
|
+
if (s.cliSessionId && s.title && s.title !== "New Session" && s.title !== "Resumed session") {
|
|
515
|
+
toMigrate.push({ cliSessionId: s.cliSessionId, title: s.title });
|
|
516
|
+
}
|
|
517
|
+
});
|
|
518
|
+
if (toMigrate.length === 0) return;
|
|
519
|
+
getSDK().then(function(sdkMod) {
|
|
520
|
+
var chain = Promise.resolve();
|
|
521
|
+
for (var i = 0; i < toMigrate.length; i++) {
|
|
522
|
+
(function(item) {
|
|
523
|
+
chain = chain.then(function() {
|
|
524
|
+
return sdkMod.renameSession(item.cliSessionId, item.title, { dir: migrateCwd }).catch(function(e) {
|
|
525
|
+
console.error("[session] Migration failed for " + item.cliSessionId + ":", e.message);
|
|
526
|
+
});
|
|
527
|
+
});
|
|
528
|
+
})(toMigrate[i]);
|
|
529
|
+
}
|
|
530
|
+
chain.then(function() {
|
|
531
|
+
console.log("[session] Migrated " + toMigrate.length + " session title(s) to SDK format");
|
|
532
|
+
});
|
|
533
|
+
}).catch(function() {});
|
|
534
|
+
}
|
|
535
|
+
|
|
511
536
|
return {
|
|
512
537
|
get activeSessionId() { return activeSessionId; },
|
|
513
538
|
get nextLocalId() { return nextLocalId; },
|
|
@@ -532,6 +557,7 @@ function createSessionManager(opts) {
|
|
|
532
557
|
replayHistory: replayHistory,
|
|
533
558
|
searchSessions: searchSessions,
|
|
534
559
|
setResolveLoopInfo: setResolveLoopInfo,
|
|
560
|
+
migrateSessionTitles: migrateSessionTitles,
|
|
535
561
|
setSessionVisibility: function (localId, visibility) {
|
|
536
562
|
var session = sessions.get(localId);
|
|
537
563
|
if (!session) return { error: "Session not found" };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clay-server",
|
|
3
|
-
"version": "2.11.0-beta.
|
|
3
|
+
"version": "2.11.0-beta.3",
|
|
4
4
|
"description": "Web UI for Claude Code. Any device. Push notifications.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"clay-server": "./bin/cli.js",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"homepage": "https://github.com/chadbyte/claude-relay#readme",
|
|
37
37
|
"author": "Chad",
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@anthropic-ai/claude-agent-sdk": "^0.2.
|
|
39
|
+
"@anthropic-ai/claude-agent-sdk": "^0.2.76",
|
|
40
40
|
"@lydell/node-pty": "^1.2.0-beta.3",
|
|
41
41
|
"nodemailer": "^6.10.1",
|
|
42
42
|
"qrcode-terminal": "^0.12.0",
|