clay-server 2.33.0-beta.2 → 2.33.0
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-connection.js +1 -0
- package/lib/project-sessions.js +12 -0
- package/lib/project.js +38 -5
- package/lib/public/css/debate.css +0 -29
- package/lib/public/css/mobile-nav.css +17 -0
- package/lib/public/css/notifications-center.css +30 -17
- package/lib/public/css/overlays.css +7 -1
- package/lib/public/css/sidebar.css +65 -39
- package/lib/public/index.html +0 -2
- package/lib/public/modules/app-messages.js +11 -1
- package/lib/public/modules/app-panels.js +1 -1
- package/lib/public/modules/app-rate-limit.js +33 -6
- package/lib/public/modules/sidebar-mobile.js +41 -3
- package/lib/public/modules/sidebar-sessions.js +68 -4
- package/lib/public/modules/sidebar.js +7 -4
- package/lib/public/modules/tool-palette.js +3 -1
- package/lib/public/modules/tools.js +47 -26
- package/lib/sdk-bridge.js +58 -6
- package/lib/sdk-message-processor.js +55 -1
- package/lib/sessions.js +15 -0
- package/lib/ws-schema.js +1 -0
- package/lib/yoke/adapters/codex.js +145 -9
- package/lib/yoke/index.js +5 -1
- package/package.json +1 -1
|
@@ -33,6 +33,19 @@ var countdownContainer = null;
|
|
|
33
33
|
var sessionCtxMenu = null;
|
|
34
34
|
var sessionCtxSessionId = null;
|
|
35
35
|
|
|
36
|
+
function sendSessionBookmark(sessionId, bookmarked) {
|
|
37
|
+
if (getWs() && store.get('connected')) {
|
|
38
|
+
getWs().send(JSON.stringify({ type: "set_session_bookmark", sessionId: sessionId, bookmarked: !!bookmarked }));
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function compareSessionListItems(a, b) {
|
|
43
|
+
var aBookmarked = !!a.bookmarked;
|
|
44
|
+
var bBookmarked = !!b.bookmarked;
|
|
45
|
+
if (aBookmarked !== bBookmarked) return aBookmarked ? -1 : 1;
|
|
46
|
+
return (b.lastActivity || 0) - (a.lastActivity || 0);
|
|
47
|
+
}
|
|
48
|
+
|
|
36
49
|
export function initSidebarSessions() {
|
|
37
50
|
|
|
38
51
|
document.addEventListener("click", function () { closeSessionCtxMenu(); });
|
|
@@ -167,6 +180,16 @@ function showSessionCtxMenu(anchorBtn, sessionId, title, cliSid, sessionData) {
|
|
|
167
180
|
var menu = document.createElement("div");
|
|
168
181
|
menu.className = "session-ctx-menu";
|
|
169
182
|
|
|
183
|
+
var bookmarkItem = document.createElement("button");
|
|
184
|
+
bookmarkItem.className = "session-ctx-item";
|
|
185
|
+
bookmarkItem.innerHTML = iconHtml(sessionData && sessionData.bookmarked ? "star-off" : "star") + " <span>" + (sessionData && sessionData.bookmarked ? "Remove Bookmark" : "Bookmark") + "</span>";
|
|
186
|
+
bookmarkItem.addEventListener("click", function (e) {
|
|
187
|
+
e.stopPropagation();
|
|
188
|
+
closeSessionCtxMenu();
|
|
189
|
+
sendSessionBookmark(sessionId, !(sessionData && sessionData.bookmarked));
|
|
190
|
+
});
|
|
191
|
+
menu.appendChild(bookmarkItem);
|
|
192
|
+
|
|
170
193
|
var renameItem = document.createElement("button");
|
|
171
194
|
renameItem.className = "session-ctx-item";
|
|
172
195
|
renameItem.innerHTML = iconHtml("pencil") + " <span>Rename</span>";
|
|
@@ -654,6 +677,23 @@ function renderSessionItem(s) {
|
|
|
654
677
|
el.className = "session-item" + (s.active ? " active" : "") + (isMatch ? " search-match" : "") + (dimmed ? " search-dimmed" : "");
|
|
655
678
|
el.dataset.sessionId = s.id;
|
|
656
679
|
|
|
680
|
+
var bookmarkBtn = document.createElement("button");
|
|
681
|
+
bookmarkBtn.className = "session-bookmark-btn" + (s.bookmarked ? " bookmarked inline" : " hover");
|
|
682
|
+
bookmarkBtn.type = "button";
|
|
683
|
+
bookmarkBtn.title = s.bookmarked ? "Remove bookmark" : "Bookmark";
|
|
684
|
+
bookmarkBtn.setAttribute("aria-label", s.bookmarked ? "Remove bookmark" : "Bookmark");
|
|
685
|
+
bookmarkBtn.innerHTML = iconHtml("star");
|
|
686
|
+
bookmarkBtn.addEventListener("click", (function (id, bookmarked) {
|
|
687
|
+
return function (e) {
|
|
688
|
+
e.preventDefault();
|
|
689
|
+
e.stopPropagation();
|
|
690
|
+
sendSessionBookmark(id, !bookmarked);
|
|
691
|
+
};
|
|
692
|
+
})(s.id, !!s.bookmarked));
|
|
693
|
+
if (s.bookmarked) {
|
|
694
|
+
el.appendChild(bookmarkBtn);
|
|
695
|
+
}
|
|
696
|
+
|
|
657
697
|
var textSpan = document.createElement("span");
|
|
658
698
|
textSpan.className = "session-item-text";
|
|
659
699
|
var textHtml = "";
|
|
@@ -689,6 +729,10 @@ function renderSessionItem(s) {
|
|
|
689
729
|
}
|
|
690
730
|
el.appendChild(unreadBadge);
|
|
691
731
|
|
|
732
|
+
if (!s.bookmarked) {
|
|
733
|
+
el.appendChild(bookmarkBtn);
|
|
734
|
+
}
|
|
735
|
+
|
|
692
736
|
el.addEventListener("click", (function (id) {
|
|
693
737
|
return function () {
|
|
694
738
|
if (getWs() && store.get('connected')) {
|
|
@@ -758,13 +802,33 @@ export function renderSessionList(sessions) {
|
|
|
758
802
|
}
|
|
759
803
|
|
|
760
804
|
// Sort by lastActivity descending
|
|
761
|
-
items.sort(
|
|
762
|
-
return (b.lastActivity || 0) - (a.lastActivity || 0);
|
|
763
|
-
});
|
|
805
|
+
items.sort(compareSessionListItems);
|
|
764
806
|
|
|
765
|
-
var
|
|
807
|
+
var bookmarkedItems = [];
|
|
808
|
+
var regularItems = [];
|
|
766
809
|
for (var n = 0; n < items.length; n++) {
|
|
767
810
|
var item = items[n];
|
|
811
|
+
if (item.type === "session" && item.data && item.data.bookmarked) {
|
|
812
|
+
bookmarkedItems.push(item);
|
|
813
|
+
} else {
|
|
814
|
+
regularItems.push(item);
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
if (bookmarkedItems.length > 0) {
|
|
819
|
+
var bookmarkedHeader = document.createElement("div");
|
|
820
|
+
bookmarkedHeader.className = "session-group-header";
|
|
821
|
+
bookmarkedHeader.textContent = "Bookmarked";
|
|
822
|
+
getSessionListEl().appendChild(bookmarkedHeader);
|
|
823
|
+
|
|
824
|
+
for (var bi = 0; bi < bookmarkedItems.length; bi++) {
|
|
825
|
+
getSessionListEl().appendChild(renderSessionItem(bookmarkedItems[bi].data));
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
var currentGroup = "";
|
|
830
|
+
for (var ri = 0; ri < regularItems.length; ri++) {
|
|
831
|
+
var item = regularItems[ri];
|
|
768
832
|
var group = getDateGroup(item.lastActivity || 0);
|
|
769
833
|
if (group !== currentGroup) {
|
|
770
834
|
currentGroup = group;
|
|
@@ -101,10 +101,13 @@ export function initSidebar(_ctx) {
|
|
|
101
101
|
}
|
|
102
102
|
});
|
|
103
103
|
|
|
104
|
-
// ---
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
104
|
+
// --- Loop (Ralph wizard) tool-palette tile ---
|
|
105
|
+
// The tile is rendered by tool-palette.js at the stable id
|
|
106
|
+
// "loop-tool-btn"; we just wire the click to the wizard opener
|
|
107
|
+
// the same way the old header pill did.
|
|
108
|
+
var loopBtn = ctx.$("loop-tool-btn");
|
|
109
|
+
if (loopBtn) {
|
|
110
|
+
loopBtn.addEventListener("click", function () {
|
|
108
111
|
if (ctx.openRalphWizard) ctx.openRalphWizard();
|
|
109
112
|
});
|
|
110
113
|
}
|
|
@@ -24,6 +24,7 @@ var SESSION_TOOLS = [
|
|
|
24
24
|
{ id: "terminal-sidebar-btn", icon: "square-terminal", label: "Terminal", countId: "terminal-sidebar-count" },
|
|
25
25
|
{ id: "sticky-notes-sidebar-btn", icon: "sticky-note", label: "Sticky Notes", countId: "sticky-notes-sidebar-count" },
|
|
26
26
|
{ id: "scheduler-btn", icon: "calendar-clock", label: "Scheduled Tasks" },
|
|
27
|
+
{ id: "loop-tool-btn", icon: "repeat", label: "Loop" },
|
|
27
28
|
{ id: "email-sidebar-btn", icon: "mail", label: "Email" },
|
|
28
29
|
{ id: "mcp-btn", icon: "cable", label: "MCP Servers", countId: "mcp-sidebar-count" },
|
|
29
30
|
{ id: "skills-btn", icon: "puzzle", label: "Skills" },
|
|
@@ -33,10 +34,11 @@ var MATE_TOOLS = [
|
|
|
33
34
|
{ id: "mate-memory-btn", icon: "brain", label: "Memory", countId: "mate-memory-count" },
|
|
34
35
|
{ id: "mate-knowledge-btn", icon: "book-open", label: "Knowledge", countId: "mate-knowledge-count" },
|
|
35
36
|
{ id: "mate-sticky-notes-btn", icon: "sticky-note", label: "Sticky Notes" },
|
|
37
|
+
{ id: "mate-scheduler-btn", icon: "calendar-clock", label: "Scheduled Tasks" },
|
|
38
|
+
{ id: "mate-debate-btn", icon: "mic", label: "Debate" },
|
|
36
39
|
{ id: "mate-email-btn", icon: "mail", label: "Email" },
|
|
37
40
|
{ id: "mate-mcp-btn", icon: "cable", label: "MCP Servers", countId: "mate-mcp-sidebar-count" },
|
|
38
41
|
{ id: "mate-skills-btn", icon: "puzzle", label: "Skills" },
|
|
39
|
-
{ id: "mate-scheduler-btn", icon: "calendar-clock", label: "Scheduled Tasks" },
|
|
40
42
|
];
|
|
41
43
|
|
|
42
44
|
var PALETTES = {
|
|
@@ -13,6 +13,7 @@ var ctx;
|
|
|
13
13
|
// --- Plan mode state ---
|
|
14
14
|
var inPlanMode = false;
|
|
15
15
|
var planContent = null;
|
|
16
|
+
var currentPlanCardEl = null;
|
|
16
17
|
|
|
17
18
|
// --- Todo state ---
|
|
18
19
|
var todoItems = [];
|
|
@@ -1169,6 +1170,7 @@ export function renderPlanBanner(type) {
|
|
|
1169
1170
|
if (type === "enter") {
|
|
1170
1171
|
inPlanMode = true;
|
|
1171
1172
|
planContent = null;
|
|
1173
|
+
currentPlanCardEl = null;
|
|
1172
1174
|
el.innerHTML =
|
|
1173
1175
|
'<span class="plan-banner-icon">' + iconHtml("map") + '</span>' +
|
|
1174
1176
|
'<span class="plan-banner-text">Entered plan mode</span>' +
|
|
@@ -1191,27 +1193,47 @@ export function renderPlanBanner(type) {
|
|
|
1191
1193
|
export function renderPlanCard(content) {
|
|
1192
1194
|
ctx.finalizeAssistantBlock();
|
|
1193
1195
|
closeToolGroup();
|
|
1196
|
+
planContent = content;
|
|
1197
|
+
|
|
1198
|
+
var el = currentPlanCardEl && currentPlanCardEl.isConnected ? currentPlanCardEl : null;
|
|
1199
|
+
var header;
|
|
1200
|
+
var body;
|
|
1201
|
+
var isNew = !el;
|
|
1202
|
+
if (!el) {
|
|
1203
|
+
el = document.createElement("div");
|
|
1204
|
+
el.className = "plan-card";
|
|
1205
|
+
|
|
1206
|
+
header = document.createElement("div");
|
|
1207
|
+
header.className = "plan-card-header";
|
|
1208
|
+
header.innerHTML =
|
|
1209
|
+
'<span class="plan-card-icon">' + iconHtml("file-text") + '</span>' +
|
|
1210
|
+
'<span class="plan-card-title">Implementation Plan</span>' +
|
|
1211
|
+
'<button class="plan-card-copy" title="Copy plan">' + iconHtml("copy") + '</button>' +
|
|
1212
|
+
'<span class="plan-card-chevron">' + iconHtml("chevron-down") + '</span>';
|
|
1213
|
+
|
|
1214
|
+
body = document.createElement("div");
|
|
1215
|
+
body.className = "plan-card-body";
|
|
1216
|
+
|
|
1217
|
+
header.addEventListener("click", function () {
|
|
1218
|
+
el.classList.toggle("collapsed");
|
|
1219
|
+
});
|
|
1194
1220
|
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
'<button class="plan-card-copy" title="Copy plan">' + iconHtml("copy") + '</button>' +
|
|
1204
|
-
'<span class="plan-card-chevron">' + iconHtml("chevron-down") + '</span>';
|
|
1221
|
+
el.appendChild(header);
|
|
1222
|
+
el.appendChild(body);
|
|
1223
|
+
ctx.addToMessages(el);
|
|
1224
|
+
currentPlanCardEl = el;
|
|
1225
|
+
} else {
|
|
1226
|
+
header = el.querySelector(".plan-card-header");
|
|
1227
|
+
body = el.querySelector(".plan-card-body");
|
|
1228
|
+
}
|
|
1205
1229
|
|
|
1206
|
-
var body = document.createElement("div");
|
|
1207
|
-
body.className = "plan-card-body";
|
|
1208
1230
|
body.innerHTML = renderMarkdown(content);
|
|
1209
1231
|
highlightCodeBlocks(body);
|
|
1210
1232
|
renderMermaidBlocks(body);
|
|
1211
1233
|
|
|
1212
1234
|
var copyBtn = header.querySelector(".plan-card-copy");
|
|
1213
1235
|
if (copyBtn) {
|
|
1214
|
-
copyBtn.
|
|
1236
|
+
copyBtn.onclick = function (e) {
|
|
1215
1237
|
e.stopPropagation();
|
|
1216
1238
|
copyToClipboard(content).then(function () {
|
|
1217
1239
|
copyBtn.innerHTML = iconHtml("check");
|
|
@@ -1221,18 +1243,11 @@ export function renderPlanCard(content) {
|
|
|
1221
1243
|
refreshIcons();
|
|
1222
1244
|
}, 1500);
|
|
1223
1245
|
});
|
|
1224
|
-
}
|
|
1246
|
+
};
|
|
1225
1247
|
}
|
|
1226
1248
|
|
|
1227
|
-
header.addEventListener("click", function () {
|
|
1228
|
-
el.classList.toggle("collapsed");
|
|
1229
|
-
});
|
|
1230
|
-
|
|
1231
|
-
el.appendChild(header);
|
|
1232
|
-
el.appendChild(body);
|
|
1233
|
-
ctx.addToMessages(el);
|
|
1234
1249
|
refreshIcons();
|
|
1235
|
-
ctx.scrollToBottom();
|
|
1250
|
+
if (isNew) ctx.scrollToBottom();
|
|
1236
1251
|
return el;
|
|
1237
1252
|
}
|
|
1238
1253
|
|
|
@@ -1734,14 +1749,17 @@ function renderEditDiff(oldStr, newStr, filePath) {
|
|
|
1734
1749
|
|
|
1735
1750
|
function isDiffContent(text) {
|
|
1736
1751
|
var lines = text.split("\n");
|
|
1737
|
-
var
|
|
1752
|
+
var hasHunkHeader = false;
|
|
1753
|
+
var hasPatchLine = false;
|
|
1738
1754
|
for (var i = 0; i < Math.min(lines.length, 20); i++) {
|
|
1739
1755
|
var l = lines[i];
|
|
1740
|
-
if (l.startsWith("@@")
|
|
1741
|
-
|
|
1756
|
+
if (l.startsWith("@@")) hasHunkHeader = true;
|
|
1757
|
+
if (l.startsWith("---") || l.startsWith("+++")) hasPatchLine = true;
|
|
1758
|
+
if ((l.startsWith("+") && !l.startsWith("+++")) || (l.startsWith("-") && !l.startsWith("---"))) {
|
|
1759
|
+
hasPatchLine = true;
|
|
1742
1760
|
}
|
|
1743
1761
|
}
|
|
1744
|
-
return
|
|
1762
|
+
return (hasHunkHeader && hasPatchLine) || hasPatchLine;
|
|
1745
1763
|
}
|
|
1746
1764
|
|
|
1747
1765
|
function getLanguageFromPath(filePath) {
|
|
@@ -2179,6 +2197,7 @@ export function saveToolState() {
|
|
|
2179
2197
|
todoWidgetEl: todoWidgetEl,
|
|
2180
2198
|
inPlanMode: inPlanMode,
|
|
2181
2199
|
planContent: planContent,
|
|
2200
|
+
currentPlanCardEl: currentPlanCardEl,
|
|
2182
2201
|
currentToolGroup: currentToolGroup,
|
|
2183
2202
|
toolGroupCounter: toolGroupCounter,
|
|
2184
2203
|
toolGroups: toolGroups,
|
|
@@ -2192,6 +2211,7 @@ export function restoreToolState(saved) {
|
|
|
2192
2211
|
todoWidgetEl = saved.todoWidgetEl;
|
|
2193
2212
|
inPlanMode = saved.inPlanMode;
|
|
2194
2213
|
planContent = saved.planContent;
|
|
2214
|
+
currentPlanCardEl = saved.currentPlanCardEl || null;
|
|
2195
2215
|
currentToolGroup = saved.currentToolGroup;
|
|
2196
2216
|
toolGroupCounter = saved.toolGroupCounter;
|
|
2197
2217
|
toolGroups = saved.toolGroups;
|
|
@@ -2207,6 +2227,7 @@ export function resetToolState() {
|
|
|
2207
2227
|
thinkingGroup = null;
|
|
2208
2228
|
inPlanMode = false;
|
|
2209
2229
|
planContent = null;
|
|
2230
|
+
currentPlanCardEl = null;
|
|
2210
2231
|
todoItems = [];
|
|
2211
2232
|
todoWidgetEl = null;
|
|
2212
2233
|
todoWidgetVisible = true;
|
package/lib/sdk-bridge.js
CHANGED
|
@@ -120,6 +120,22 @@ function createSDKBridge(opts) {
|
|
|
120
120
|
var clayTls = opts.clayTls || false;
|
|
121
121
|
var clayAuthToken = opts.clayAuthToken || null;
|
|
122
122
|
var onProcessingChanged = opts.onProcessingChanged || function () {};
|
|
123
|
+
|
|
124
|
+
function getModelsForVendor(vendor) {
|
|
125
|
+
if (vendor && sm.modelsByVendor && sm.modelsByVendor[vendor]) return sm.modelsByVendor[vendor];
|
|
126
|
+
return sm.availableModels || [];
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function sendModelInfoForVendor(vendor, model) {
|
|
130
|
+
send({
|
|
131
|
+
type: "model_info",
|
|
132
|
+
model: model || "",
|
|
133
|
+
models: getModelsForVendor(vendor),
|
|
134
|
+
vendor: vendor || (adapter && adapter.vendor) || "claude",
|
|
135
|
+
availableVendors: sm.availableVendors || [],
|
|
136
|
+
installedVendors: sm.installedVendors || [],
|
|
137
|
+
});
|
|
138
|
+
}
|
|
123
139
|
var onTurnDone = opts.onTurnDone || null;
|
|
124
140
|
|
|
125
141
|
// --- Idle session reaper ---
|
|
@@ -648,7 +664,7 @@ function createSDKBridge(opts) {
|
|
|
648
664
|
break;
|
|
649
665
|
case "model_changed":
|
|
650
666
|
sm.currentModel = metaData.model;
|
|
651
|
-
|
|
667
|
+
sendModelInfoForVendor(session.vendor || (adapter && adapter.vendor) || "claude", metaData.model);
|
|
652
668
|
send({ type: "config_state", model: sm.currentModel, mode: sm.currentPermissionMode || "default", effort: sm.currentEffort || "medium", betas: sm.currentBetas || [] });
|
|
653
669
|
break;
|
|
654
670
|
case "effort_changed":
|
|
@@ -908,13 +924,49 @@ function createSDKBridge(opts) {
|
|
|
908
924
|
}
|
|
909
925
|
|
|
910
926
|
async function startQuery(session, text, images, linuxUser) {
|
|
927
|
+
async function ensureVendorReady(vendor) {
|
|
928
|
+
if (!vendor) return null;
|
|
929
|
+
var vendorAdapter = adapters[vendor] || null;
|
|
930
|
+
if (!vendorAdapter) {
|
|
931
|
+
var yoke = require("./yoke");
|
|
932
|
+
vendorAdapter = await yoke.lazyCreateAdapter(adapters, vendor, {
|
|
933
|
+
cwd: cwd,
|
|
934
|
+
dangerouslySkipPermissions: dangerouslySkipPermissions,
|
|
935
|
+
linuxUser: linuxUser || undefined,
|
|
936
|
+
clayPort: clayPort,
|
|
937
|
+
clayTls: clayTls,
|
|
938
|
+
clayAuthToken: clayAuthToken,
|
|
939
|
+
slug: slug,
|
|
940
|
+
});
|
|
941
|
+
} else if ((!sm.modelsByVendor || !sm.modelsByVendor[vendor]) && typeof vendorAdapter.init === "function") {
|
|
942
|
+
await vendorAdapter.init({
|
|
943
|
+
cwd: cwd,
|
|
944
|
+
dangerouslySkipPermissions: dangerouslySkipPermissions,
|
|
945
|
+
linuxUser: linuxUser || undefined,
|
|
946
|
+
clayPort: clayPort,
|
|
947
|
+
clayTls: clayTls,
|
|
948
|
+
clayAuthToken: clayAuthToken,
|
|
949
|
+
slug: slug,
|
|
950
|
+
});
|
|
951
|
+
}
|
|
952
|
+
if (vendorAdapter) {
|
|
953
|
+
sm.availableVendors = Object.keys(adapters);
|
|
954
|
+
sm.modelsByVendor = sm.modelsByVendor || {};
|
|
955
|
+
if (!sm.modelsByVendor[vendor] && typeof vendorAdapter.supportedModels === "function") {
|
|
956
|
+
sm.modelsByVendor[vendor] = await vendorAdapter.supportedModels();
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
return vendorAdapter;
|
|
960
|
+
}
|
|
961
|
+
|
|
911
962
|
// If vendor is set but adapter not ready, try lazy creation (user may have logged in)
|
|
912
963
|
if (session.vendor && !adapters[session.vendor]) {
|
|
913
|
-
var
|
|
914
|
-
var lazyAdapter = yoke.lazyCreateAdapter(adapters, session.vendor, { cwd: cwd });
|
|
964
|
+
var lazyAdapter = await ensureVendorReady(session.vendor);
|
|
915
965
|
if (lazyAdapter) {
|
|
916
966
|
console.log("[sdk-bridge] Lazy adapter created for " + session.vendor);
|
|
917
967
|
}
|
|
968
|
+
} else if (session.vendor) {
|
|
969
|
+
await ensureVendorReady(session.vendor);
|
|
918
970
|
}
|
|
919
971
|
// If still not available after lazy check, send auth_required
|
|
920
972
|
if (session.vendor && !adapters[session.vendor]) {
|
|
@@ -1299,7 +1351,7 @@ function createSDKBridge(opts) {
|
|
|
1299
1351
|
send({
|
|
1300
1352
|
type: "model_info",
|
|
1301
1353
|
model: sm.currentModel || "",
|
|
1302
|
-
models:
|
|
1354
|
+
models: getModelsForVendor(defaultVendor),
|
|
1303
1355
|
vendor: defaultVendor,
|
|
1304
1356
|
availableVendors: sm.availableVendors,
|
|
1305
1357
|
installedVendors: sm.installedVendors,
|
|
@@ -1311,7 +1363,7 @@ function createSDKBridge(opts) {
|
|
|
1311
1363
|
// No active query — just store the model for next startQuery
|
|
1312
1364
|
sm.currentModel = model;
|
|
1313
1365
|
// Don't send vendor here: session vendor not yet bound, let client keep its selection
|
|
1314
|
-
|
|
1366
|
+
sendModelInfoForVendor(null, model);
|
|
1315
1367
|
send({ type: "config_state", model: sm.currentModel, mode: sm.currentPermissionMode || "default", effort: sm.currentEffort || "medium", betas: sm.currentBetas || [] });
|
|
1316
1368
|
return;
|
|
1317
1369
|
}
|
|
@@ -1319,7 +1371,7 @@ function createSDKBridge(opts) {
|
|
|
1319
1371
|
await session.queryInstance.setModel(model);
|
|
1320
1372
|
sm.currentModel = model;
|
|
1321
1373
|
var sessionVendor = session.vendor || (adapter && adapter.vendor) || "claude";
|
|
1322
|
-
|
|
1374
|
+
sendModelInfoForVendor(sessionVendor, model);
|
|
1323
1375
|
send({ type: "config_state", model: sm.currentModel, mode: sm.currentPermissionMode || "default", effort: sm.currentEffort || "medium", betas: sm.currentBetas || [] });
|
|
1324
1376
|
} catch (e) {
|
|
1325
1377
|
send({ type: "error", text: "Failed to switch model: " + (e.message || e) });
|
|
@@ -36,6 +36,11 @@ function attachMessageProcessor(ctx) {
|
|
|
36
36
|
sm.sendToSession(session, obj);
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
function getModelsForVendor(vendor) {
|
|
40
|
+
if (vendor && sm.modelsByVendor && sm.modelsByVendor[vendor]) return sm.modelsByVendor[vendor];
|
|
41
|
+
return sm.availableModels || [];
|
|
42
|
+
}
|
|
43
|
+
|
|
39
44
|
function toolActivityTextForSubagent(name, input) {
|
|
40
45
|
if (name === "Bash" && input && input.description) return input.description;
|
|
41
46
|
if (name === "Read" && input && input.file_path) return "Reading " + input.file_path.split("/").pop();
|
|
@@ -144,7 +149,15 @@ function attachMessageProcessor(ctx) {
|
|
|
144
149
|
}
|
|
145
150
|
if (parsed.model) {
|
|
146
151
|
sm.currentModel = sm.currentModel || sm._savedDefaultModel || parsed.model;
|
|
147
|
-
|
|
152
|
+
var initVendor = session.vendor || (adapter && adapter.vendor) || "claude";
|
|
153
|
+
send({
|
|
154
|
+
type: "model_info",
|
|
155
|
+
model: sm.currentModel,
|
|
156
|
+
models: getModelsForVendor(initVendor),
|
|
157
|
+
vendor: initVendor,
|
|
158
|
+
availableVendors: sm.availableVendors || [],
|
|
159
|
+
installedVendors: sm.installedVendors || [],
|
|
160
|
+
});
|
|
148
161
|
}
|
|
149
162
|
if (parsed.fastModeState) {
|
|
150
163
|
sendAndRecord(session, { type: "fast_mode_state", state: parsed.fastModeState });
|
|
@@ -189,6 +202,47 @@ function attachMessageProcessor(ctx) {
|
|
|
189
202
|
sendAndRecord(session, { type: "thinking_delta", text: parsed.text });
|
|
190
203
|
}
|
|
191
204
|
|
|
205
|
+
} else if (parsed.yokeType === "tool_executing") {
|
|
206
|
+
sendAndRecord(session, {
|
|
207
|
+
type: "tool_executing",
|
|
208
|
+
id: parsed.toolId,
|
|
209
|
+
name: parsed.toolName,
|
|
210
|
+
input: parsed.input || {},
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
} else if (parsed.yokeType === "tool_result") {
|
|
214
|
+
sendAndRecord(session, {
|
|
215
|
+
type: "tool_result",
|
|
216
|
+
id: parsed.toolId,
|
|
217
|
+
content: parsed.content || "",
|
|
218
|
+
is_error: !!parsed.isError,
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
} else if (parsed.yokeType === "plan_updated") {
|
|
222
|
+
var todos = Array.isArray(parsed.plan) ? parsed.plan.map(function(step, idx) {
|
|
223
|
+
var todo = {
|
|
224
|
+
id: String(idx + 1),
|
|
225
|
+
content: step.step || "",
|
|
226
|
+
status: step.status || "pending",
|
|
227
|
+
};
|
|
228
|
+
if (todo.status === "in_progress" && parsed.explanation) {
|
|
229
|
+
todo.activeForm = parsed.explanation;
|
|
230
|
+
}
|
|
231
|
+
return todo;
|
|
232
|
+
}) : [];
|
|
233
|
+
sendAndRecord(session, {
|
|
234
|
+
type: "tool_executing",
|
|
235
|
+
id: parsed.turnId || "codex-plan",
|
|
236
|
+
name: "TodoWrite",
|
|
237
|
+
input: { todos: todos },
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
} else if (parsed.yokeType === "plan_content") {
|
|
241
|
+
sendAndRecord(session, {
|
|
242
|
+
type: "plan_content",
|
|
243
|
+
content: parsed.content || "",
|
|
244
|
+
});
|
|
245
|
+
|
|
192
246
|
} else if (parsed.yokeType === "block_stop") {
|
|
193
247
|
var idx = parsed.blockId;
|
|
194
248
|
var block = session.blocks[idx];
|
package/lib/sessions.js
CHANGED
|
@@ -95,6 +95,7 @@ function createSessionManager(opts) {
|
|
|
95
95
|
if (session.ownerId) metaObj.ownerId = session.ownerId;
|
|
96
96
|
if (session.vendor) metaObj.vendor = session.vendor;
|
|
97
97
|
if (session.sessionVisibility) metaObj.sessionVisibility = session.sessionVisibility;
|
|
98
|
+
if (session.bookmarked) metaObj.bookmarked = true;
|
|
98
99
|
if (session.lastRewindUuid) metaObj.lastRewindUuid = session.lastRewindUuid;
|
|
99
100
|
if (session.loop) metaObj.loop = session.loop;
|
|
100
101
|
if (session.debateState) metaObj.debateState = session.debateState;
|
|
@@ -200,6 +201,7 @@ function createSessionManager(opts) {
|
|
|
200
201
|
if (m.debateSetupMode) session.debateSetupMode = true;
|
|
201
202
|
if (m.ownerId) session.ownerId = m.ownerId;
|
|
202
203
|
session.sessionVisibility = m.sessionVisibility || "shared";
|
|
204
|
+
session.bookmarked = !!m.bookmarked;
|
|
203
205
|
sessions.set(localId, session);
|
|
204
206
|
}
|
|
205
207
|
}
|
|
@@ -238,6 +240,7 @@ function createSessionManager(opts) {
|
|
|
238
240
|
loop: loop,
|
|
239
241
|
ownerId: s.ownerId || null,
|
|
240
242
|
sessionVisibility: s.sessionVisibility || "shared",
|
|
243
|
+
bookmarked: !!s.bookmarked,
|
|
241
244
|
unread: unreadMap[s.localId] || 0,
|
|
242
245
|
vendor: s.vendor || null,
|
|
243
246
|
};
|
|
@@ -299,6 +302,7 @@ function createSessionManager(opts) {
|
|
|
299
302
|
messageUUIDs: [],
|
|
300
303
|
ownerId: (sessionOpts && sessionOpts.ownerId) || null,
|
|
301
304
|
sessionVisibility: (sessionOpts && sessionOpts.sessionVisibility) || "shared",
|
|
305
|
+
bookmarked: false,
|
|
302
306
|
vendor: (sessionOpts && sessionOpts.vendor) || null,
|
|
303
307
|
};
|
|
304
308
|
sessions.set(localId, session);
|
|
@@ -329,6 +333,7 @@ function createSessionManager(opts) {
|
|
|
329
333
|
messageUUIDs: [],
|
|
330
334
|
ownerId: (sessionOpts && sessionOpts.ownerId) || null,
|
|
331
335
|
sessionVisibility: (sessionOpts && sessionOpts.sessionVisibility) || "shared",
|
|
336
|
+
bookmarked: false,
|
|
332
337
|
vendor: (sessionOpts && sessionOpts.vendor) || null,
|
|
333
338
|
};
|
|
334
339
|
sessions.set(localId, session);
|
|
@@ -613,8 +618,10 @@ function createSessionManager(opts) {
|
|
|
613
618
|
isProcessing: false,
|
|
614
619
|
title: title,
|
|
615
620
|
createdAt: Date.now(),
|
|
621
|
+
lastActivity: Date.now(),
|
|
616
622
|
history: cliHistory,
|
|
617
623
|
messageUUIDs: [],
|
|
624
|
+
bookmarked: false,
|
|
618
625
|
};
|
|
619
626
|
sessions.set(localId, session);
|
|
620
627
|
saveSessionFile(session);
|
|
@@ -818,6 +825,14 @@ function createSessionManager(opts) {
|
|
|
818
825
|
broadcastSessionList();
|
|
819
826
|
return { ok: true };
|
|
820
827
|
},
|
|
828
|
+
setSessionBookmarked: function (localId, bookmarked) {
|
|
829
|
+
var session = sessions.get(localId);
|
|
830
|
+
if (!session) return { error: "Session not found" };
|
|
831
|
+
session.bookmarked = !!bookmarked;
|
|
832
|
+
saveSessionFile(session);
|
|
833
|
+
broadcastSessionList();
|
|
834
|
+
return { ok: true };
|
|
835
|
+
},
|
|
821
836
|
setSessionOwner: function (localId, ownerId) {
|
|
822
837
|
var session = sessions.get(localId);
|
|
823
838
|
if (!session) return { error: "Session not found" };
|
package/lib/ws-schema.js
CHANGED
|
@@ -20,6 +20,7 @@ var schema = {
|
|
|
20
20
|
"new_session": { direction: "c2s", handler: "lib/project-sessions.js", description: "Create a new blank session" },
|
|
21
21
|
"delete_session": { direction: "c2s", handler: "lib/project-sessions.js", description: "Delete a session by ID" },
|
|
22
22
|
"rename_session": { direction: "c2s", handler: "lib/project-sessions.js", description: "Rename a session" },
|
|
23
|
+
"set_session_bookmark": { direction: "c2s", handler: "lib/project-sessions.js", description: "Bookmark or unbookmark a session in the sidebar" },
|
|
23
24
|
"resume_session": { direction: "c2s", handler: "lib/project-sessions.js", description: "Resume a CLI session by its CLI session ID" },
|
|
24
25
|
"set_session_visibility": { direction: "c2s", handler: "lib/project-sessions.js", description: "Show or hide a session in the sidebar" },
|
|
25
26
|
"search_sessions": { direction: "c2s", handler: "lib/project-sessions.js", description: "Search session titles" },
|