clay-server 5.3.0-beta.4 → 5.3.0-beta.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/lib/ask-user-request-identity.js +18 -1
  2. package/lib/project-delegated-follow-up.js +18 -9
  3. package/lib/public/css/right-workbench.css +26 -3
  4. package/lib/public/css/shared-browser.css +21 -0
  5. package/lib/public/css/sticky-notes.css +3 -2
  6. package/lib/public/index.html +2 -2
  7. package/lib/public/modules/app-header.js +24 -2
  8. package/lib/public/modules/app-messages.js +1 -1
  9. package/lib/public/modules/clay-session-links.js +1 -1
  10. package/lib/public/modules/filebrowser.js +2 -2
  11. package/lib/public/modules/pane-activation.js +3 -0
  12. package/lib/public/modules/pane-browser-bridge.js +15 -5
  13. package/lib/public/modules/pane-header-runtime.js +18 -0
  14. package/lib/public/modules/right-workbench.js +92 -62
  15. package/lib/public/modules/shared-browser.js +6 -0
  16. package/lib/public/modules/split-pane-renderer.js +6 -8
  17. package/lib/public/modules/split-pane-runtime.js +23 -0
  18. package/lib/public/modules/split-view.js +8 -14
  19. package/lib/public/modules/sticky-notes.js +8 -10
  20. package/lib/public/modules/terminal.js +7 -2
  21. package/lib/public/modules/theme.js +5 -0
  22. package/lib/public/modules/workspace-assignment-card.js +9 -6
  23. package/lib/public/pane-assets/{app-ED2VCGNQ.js → app-6H5S2Y3Z.js} +1 -1
  24. package/lib/public/pane-assets/chunk-3QV5SDC4.js +1 -0
  25. package/lib/public/pane-assets/{chunk-JAEQCN5S.js → chunk-4FM3U3DW.js} +1 -1
  26. package/lib/public/pane-assets/{chunk-6SM2WQJX.js → chunk-7GZH3FZY.js} +1 -1
  27. package/lib/public/pane-assets/chunk-AGJTF5WQ.js +1 -0
  28. package/lib/public/pane-assets/chunk-NPKRTUNG.js +2 -0
  29. package/lib/public/pane-assets/{chunk-IYFILQHZ.js → chunk-VGA2Y3D2.js} +1 -1
  30. package/lib/public/pane-assets/{chunk-IZ2ZANEY.js → chunk-WKO3T75E.js} +89 -89
  31. package/lib/public/pane-assets/manifest.json +361 -359
  32. package/lib/public/pane-assets/{style-JVCOCG3W.css → style-TSRDU54Z.css} +1 -1
  33. package/lib/server.js +1 -0
  34. package/lib/workspace-assignment-service.js +56 -11
  35. package/lib/workspace-follow-up-queue.js +92 -0
  36. package/lib/workspace-query-mcp-server.js +1 -1
  37. package/package.json +1 -1
  38. package/lib/public/pane-assets/chunk-6D4DPE5X.js +0 -1
  39. package/lib/public/pane-assets/chunk-7H36SUZA.js +0 -2
  40. package/lib/public/pane-assets/chunk-ST2Q62ET.js +0 -1
@@ -0,0 +1,23 @@
1
+ import { store } from './store.js';
2
+ import { getCachedSessions } from './sidebar-sessions.js';
3
+ import { livePaneBinding, samePaneBinding, samePaneTarget, paneTarget } from './pane-binding.js';
4
+ import { VENDOR_AVATARS, VENDOR_NAMES } from './app-rendering.js';
5
+ import { modelEntryValue } from './model-picker.js';
6
+
7
+ export function renderPaneRuntime(paneEl, session) {
8
+ var runtime = paneEl.querySelector('.split-view-runtime');
9
+ if (!runtime) return;
10
+ var vendor = session && session.vendor || 'unknown', model = modelEntryValue(session && session.model);
11
+ var frame = paneEl.querySelector('.split-pane-frame'), controller = frame && frame.clayPane;
12
+ var live = controller && controller.identity(), target = session && paneTarget(store.snap(), getCachedSessions(), session.id);
13
+ if (session && live && live.ready && live.sessionId === session.id && !frame.dataset.requestedSessionId &&
14
+ samePaneTarget(target, live.binding) && livePaneBinding(frame) && typeof controller.headerRuntime === 'function') {
15
+ var observed = controller.headerRuntime();
16
+ if (observed && observed.vendor === vendor && samePaneBinding(live.binding, observed.binding)) model = modelEntryValue(observed.model) || model;
17
+ }
18
+ var label = model || (vendor === 'unknown' ? 'Model unavailable' : 'Provider default');
19
+ var icon = runtime.querySelector('.split-view-runtime-icon'), text = runtime.querySelector('.split-view-model');
20
+ if (icon) { icon.hidden = !VENDOR_AVATARS[vendor]; if (!icon.hidden) icon.src = VENDOR_AVATARS[vendor]; icon.title = VENDOR_NAMES[vendor] || vendor; icon.alt = ''; }
21
+ if (text) { text.textContent = label; text.title = (VENDOR_NAMES[vendor] || vendor) + ' · ' + label; }
22
+ runtime.setAttribute('aria-label', (VENDOR_NAMES[vendor] || vendor) + ' · ' + label);
23
+ }
@@ -131,21 +131,15 @@ function closePane(sessionId) {
131
131
  // Called on session_list so pane headers follow renames made elsewhere
132
132
  // (sidebar, in-pane app). Panes being renamed inline are left alone.
133
133
  export function syncPaneTitles() {
134
- var split = store.get('splitPanes');
135
- if (!split || !split.panes || !host) return;
136
- var titleEls = host.querySelectorAll(".split-pane-title");
137
- var paneEls = host.querySelectorAll('.split-pane');
138
- for (var i = 0; i < split.panes.length && i < titleEls.length; i++) {
139
- var session = sessionById(paneEls[i] && Number(paneEls[i].dataset.sessionId) || split.panes[i].sessionId);
140
- if (!session || !session.title) continue;
141
- if (titleEls[i].parentNode) titleEls[i].parentNode.title = session.title;
142
- if (titleEls[i].style.display !== "none") titleEls[i].textContent = session.title;
143
- }
144
- var accessBtns = host.querySelectorAll(".split-pane-full-access");
145
- for (var ai = 0; ai < split.panes.length && ai < accessBtns.length; ai++) {
146
- updatePaneFullAccessButton(accessBtns[ai], sessionById(paneEls[ai] && Number(paneEls[ai].dataset.sessionId) || split.panes[ai].sessionId));
134
+ if (!host) return;
135
+ var panes = host.querySelectorAll('.split-pane:not(.split-pane-cached)');
136
+ for (var i = 0; i < panes.length; i++) {
137
+ var frame = panes[i].querySelector('.split-pane-frame');
138
+ if (frame && frame.dataset.requestedSessionId) continue;
139
+ var session = sessionById(Number(panes[i].dataset.sessionId));
140
+ if (session) updateSplitPaneMetadata(panes[i], { sessionId: session.id, slug: panes[i].dataset.projectSlug, title: session.title });
147
141
  }
148
- syncPairChrome(host, split);
142
+ syncPairChrome(host, store.get('splitPanes'));
149
143
  }
150
144
 
151
145
  function handlePaneMessage(event) {
@@ -104,11 +104,17 @@ function recomputeFlightGeometry() {
104
104
  function syncFlightControls() {
105
105
  var buttons = document.querySelectorAll("#sticky-notes-toggle-btn");
106
106
  var hidden = !!store.get("notesTemporarilyHidden");
107
- var label = hidden ? "Show sticky notes" : "Hide sticky notes";
107
+ var count = openCount();
108
+ var label = hidden ? "Show sticky notes (" + count + ")" : "Hide sticky notes";
108
109
  for (var i = 0; i < buttons.length; i++) {
109
110
  buttons[i].setAttribute("aria-label", label);
110
111
  buttons[i].setAttribute("aria-pressed", hidden ? "true" : "false");
111
112
  buttons[i].title = label;
113
+ var badge = buttons[i].querySelector('.sticky-notes-count');
114
+ if (badge) {
115
+ badge.textContent = String(count);
116
+ badge.classList.toggle('hidden', !hidden || count === 0);
117
+ }
112
118
  }
113
119
  refreshIcons();
114
120
  }
@@ -246,15 +252,7 @@ export function createNote() {
246
252
 
247
253
  function updateBadge() {
248
254
  syncToggleAvailability();
249
- var badge = document.querySelector(".sticky-notes-count");
250
- if (!badge) return;
251
- var count = openCount();
252
- if (count > 0) {
253
- badge.textContent = count;
254
- badge.classList.remove("hidden");
255
- } else {
256
- badge.classList.add("hidden");
257
- }
255
+ syncFlightControls();
258
256
  }
259
257
 
260
258
  // --- WS message handlers ---
@@ -3,7 +3,7 @@ import { iconHtml, refreshIcons } from './icons.js';
3
3
  import { closeSidebar } from './sidebar.js';
4
4
  import { claimRightWorkbench, registerRightWorkbench, releaseRightWorkbench } from './right-workbench.js';
5
5
  import { copyToClipboard } from './utils.js';
6
- import { getTerminalTheme } from './theme.js';
6
+ import { getTerminalTheme, getDockedTerminalTheme } from './theme.js';
7
7
  import { getTerminalFontFamily, getTerminalFontSize, onTerminalFontChange } from './terminal-prefs.js';
8
8
  import { createKeyToolbar } from './terminal-toolbar.js';
9
9
 
@@ -358,7 +358,7 @@ function createXtermForTab(tab) {
358
358
  cursorBlink: true,
359
359
  fontSize: getTerminalFontSize(),
360
360
  fontFamily: getTerminalFontFamily(),
361
- theme: getTerminalTheme(),
361
+ theme: (store.get('rightWorkbenchDockedOwners') || []).indexOf('terminal') !== -1 ? getDockedTerminalTheme() : getTerminalTheme(),
362
362
  });
363
363
 
364
364
  var fitAddon = null;
@@ -843,6 +843,7 @@ export function sendTerminalCommand(command) {
843
843
  }
844
844
 
845
845
  export function setTerminalTheme(xtermTheme) {
846
+ if ((store.get('rightWorkbenchDockedOwners') || []).indexOf('terminal') !== -1) xtermTheme = getDockedTerminalTheme();
846
847
  for (var tab of tabs.values()) {
847
848
  if (tab.xterm) {
848
849
  tab.xterm.options.theme = xtermTheme;
@@ -850,6 +851,10 @@ export function setTerminalTheme(xtermTheme) {
850
851
  }
851
852
  }
852
853
 
854
+ store.subscribe(function (state, previous) {
855
+ if ((state.rightWorkbenchDockedOwners || []).includes('terminal') !== (previous.rightWorkbenchDockedOwners || []).includes('terminal')) setTerminalTheme(getTerminalTheme());
856
+ });
857
+
853
858
  // Live font update: applies family/size to every open shell terminal
854
859
  // and refits each (cell dimensions change with font size, so cols/rows
855
860
  // shift too). Hooked via onTerminalFontChange below.
@@ -267,6 +267,11 @@ export function getTerminalTheme() {
267
267
  return computeTerminalTheme(getTheme(DEFAULT_DARK_THEME_ID) || defaultDarkFallback);
268
268
  }
269
269
 
270
+ export function getDockedTerminalTheme() {
271
+ var theme = getCurrentTheme();
272
+ return Object.assign({}, computeTerminalTheme(theme), { background: '#' + theme.base00 });
273
+ }
274
+
270
275
  export function getMermaidThemeVars() {
271
276
  return computeMermaidVars(getCurrentTheme());
272
277
  }
@@ -11,6 +11,7 @@ function textElement(tag, className, text) {
11
11
  }
12
12
 
13
13
  function statusLabel(status) {
14
+ if (status === "queued") return "Queued for Driver";
14
15
  if (status === "starting") return "Starting";
15
16
  if (status === "running") return "In progress";
16
17
  if (status === "completed") return "Completed";
@@ -22,7 +23,7 @@ function statusLabel(status) {
22
23
 
23
24
  function sendDecision(assignment, action, surface, context, card, opener) {
24
25
  var ws = getWs();
25
- if (!ws || ws.readyState !== 1 || !assignment || assignment.status !== "proposed") return false;
26
+ if (!ws || ws.readyState !== 1 || !assignment || (assignment.status !== "proposed" && !(assignment.status === "queued" && action === "cancel"))) return false;
26
27
  pendingFocus[assignment.assignmentId] = opener || null;
27
28
  card.setAttribute("aria-busy", "true");
28
29
  var buttons = card.querySelectorAll("button");
@@ -79,10 +80,12 @@ export function createWorkspaceAssignmentCard(assignment, options) {
79
80
  card.appendChild(header);
80
81
 
81
82
  var targetText = followUp
82
- ? "Continue “" + (assignment.targetSessionTitle || "Existing session") + "” in " + (assignment.projectTitle || assignment.projectSlug || "project")
83
+ ? "Send instructions to “" + (assignment.targetSessionTitle || "Existing session") + "” in " + (assignment.projectTitle || assignment.projectSlug || "project")
83
84
  : "New private session in " + (assignment.projectTitle || assignment.projectSlug || "project");
84
85
  var target = textElement("p", "workspace-assignment-target", targetText);
85
86
  card.appendChild(target);
87
+ if (assignment.status === 'queued' && assignment.deliveryState === 'queued') card.appendChild(textElement('p', 'workspace-assignment-result', 'Approved. Waiting for the Driver to finish its current turn.'));
88
+ else if (assignment.deliveryState === 'accepted' && assignment.status === 'running') card.appendChild(textElement('p', 'workspace-assignment-result', 'Accepted by the Driver runtime. Completion is still pending.'));
86
89
  card.appendChild(textElement("p", "workspace-assignment-task", assignment.task || ""));
87
90
 
88
91
  if (assignment.error) {
@@ -94,17 +97,17 @@ export function createWorkspaceAssignmentCard(assignment, options) {
94
97
 
95
98
  var actions = document.createElement("div");
96
99
  actions.className = "workspace-assignment-actions";
97
- if (assignment.status === "proposed") {
98
- var cancel = textElement("button", "workspace-assignment-action secondary", "Cancel");
100
+ if (assignment.status === "proposed" || assignment.status === "queued") {
101
+ var cancel = textElement("button", "workspace-assignment-action secondary", assignment.status === "queued" ? "Cancel queued update" : "Cancel");
99
102
  cancel.type = "button";
100
- cancel.setAttribute("aria-label", "Cancel project assignment");
103
+ cancel.setAttribute("aria-label", assignment.status === "queued" ? "Cancel queued update" : "Cancel project assignment");
101
104
  cancel.addEventListener("click", function () { sendDecision(assignment, "cancel", config.home ? "home" : "project", config.context, card, cancel); });
102
105
  var approve = textElement("button", "workspace-assignment-action primary", "Approve");
103
106
  approve.type = "button";
104
107
  approve.setAttribute("aria-label", followUp ? "Approve follow-up in " + (assignment.targetSessionTitle || "the target session") : "Approve new session in " + (assignment.projectTitle || assignment.projectSlug || "the target project"));
105
108
  approve.addEventListener("click", function () { sendDecision(assignment, "approve", config.home ? "home" : "project", config.context, card, approve); });
106
109
  actions.appendChild(cancel);
107
- actions.appendChild(approve);
110
+ if (assignment.status === "proposed") actions.appendChild(approve);
108
111
  } else {
109
112
  var resolved = textElement("span", "workspace-assignment-resolved", statusLabel(assignment.status));
110
113
  resolved.setAttribute("role", "status");