clay-server 4.0.0-beta.8 → 4.0.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.
Files changed (185) hide show
  1. package/lib/capsule-display-floor.js +51 -0
  2. package/lib/capsule-frame-server.js +255 -0
  3. package/lib/capsule-mate-turn.js +105 -0
  4. package/lib/capsule-pig-logic.js +300 -0
  5. package/lib/capsule-server-runtimes.js +39 -0
  6. package/lib/capsules/pig/display.js +198 -0
  7. package/lib/capsules/pig/manifest.json +9 -0
  8. package/lib/capsules/pig/ui.json +84 -0
  9. package/lib/config.js +25 -1
  10. package/lib/daemon-projects.js +13 -8
  11. package/lib/daemon.js +127 -14
  12. package/lib/git-cli.js +95 -1
  13. package/lib/knowledge-import.js +436 -0
  14. package/lib/knowledge-record-store.js +188 -0
  15. package/lib/knowledge-search.js +240 -0
  16. package/lib/mate-knowledge-mcp-server.js +151 -0
  17. package/lib/mate-knowledge-migration.js +283 -0
  18. package/lib/mate-knowledge-service.js +488 -0
  19. package/lib/mate-knowledge-sync.js +482 -0
  20. package/lib/migrate-single-user.js +2 -0
  21. package/lib/notes-lifecycle.js +149 -0
  22. package/lib/notes.js +60 -3
  23. package/lib/os-users.js +3 -1
  24. package/lib/project-capsule-catalog.js +9 -1
  25. package/lib/project-capsule-turn.js +96 -0
  26. package/lib/project-connection.js +15 -1
  27. package/lib/project-file-history.js +223 -0
  28. package/lib/project-file-watch.js +77 -49
  29. package/lib/project-filesystem.js +31 -220
  30. package/lib/project-http.js +15 -1
  31. package/lib/project-knowledge.js +22 -0
  32. package/lib/project-log-context.js +172 -0
  33. package/lib/project-log-feedback-delivery.js +118 -0
  34. package/lib/project-logs-comments.js +137 -0
  35. package/lib/project-logs-context-state.js +42 -0
  36. package/lib/project-logs-mcp-server.js +328 -0
  37. package/lib/project-logs-query.js +185 -0
  38. package/lib/project-logs-root.js +75 -0
  39. package/lib/project-logs-schema.js +259 -0
  40. package/lib/project-logs-service.js +463 -0
  41. package/lib/project-logs-snapshot.js +205 -0
  42. package/lib/project-logs-store.js +495 -0
  43. package/lib/project-logs-versioning.js +132 -0
  44. package/lib/project-logs.js +400 -0
  45. package/lib/project-mate-interaction.js +14 -0
  46. package/lib/project-mate-knowledge.js +110 -0
  47. package/lib/project-memory.js +22 -0
  48. package/lib/project-message-delivery.js +70 -0
  49. package/lib/project-pair-lifecycle.js +460 -0
  50. package/lib/project-request-access.js +66 -0
  51. package/lib/project-session-handoff.js +1 -1
  52. package/lib/project-session-notes.js +64 -13
  53. package/lib/project-session-pair.js +159 -146
  54. package/lib/project-session-spawn.js +1 -1
  55. package/lib/project-sessions.js +58 -13
  56. package/lib/project-user-message.js +53 -4
  57. package/lib/project-worker-permission.js +430 -0
  58. package/lib/project-worker-proposal.js +208 -62
  59. package/lib/project.js +185 -6
  60. package/lib/public/app.js +63 -35
  61. package/lib/public/css/admin.css +2 -2
  62. package/lib/public/css/capsule-ui.css +38 -0
  63. package/lib/public/css/command-palette.css +19 -9
  64. package/lib/public/css/git-placard.css +154 -0
  65. package/lib/public/css/home-hub.css +4 -0
  66. package/lib/public/css/home-session-actions.css +66 -0
  67. package/lib/public/css/home-sidebar.css +66 -0
  68. package/lib/public/css/input.css +84 -3
  69. package/lib/public/css/messages.css +40 -1
  70. package/lib/public/css/mobile-nav.css +80 -0
  71. package/lib/public/css/notifications-center.css +67 -0
  72. package/lib/public/css/pane.css +97 -10
  73. package/lib/public/css/project-logs.css +198 -0
  74. package/lib/public/css/scheduler.css +1 -1
  75. package/lib/public/css/session-actions.css +16 -101
  76. package/lib/public/css/sidebar.css +103 -0
  77. package/lib/public/css/sticky-notes.css +237 -229
  78. package/lib/public/css/worker-proposal.css +36 -3
  79. package/lib/public/index.html +43 -11
  80. package/lib/public/modules/admin.js +8 -8
  81. package/lib/public/modules/app-connection.js +124 -6
  82. package/lib/public/modules/app-home-hub.js +12 -8
  83. package/lib/public/modules/app-message-cards.js +2 -1
  84. package/lib/public/modules/app-messages.js +92 -11
  85. package/lib/public/modules/app-misc.js +2 -14
  86. package/lib/public/modules/app-notifications.js +11 -7
  87. package/lib/public/modules/app-panels.js +3 -2
  88. package/lib/public/modules/app-projects.js +56 -18
  89. package/lib/public/modules/app-rendering.js +1 -0
  90. package/lib/public/modules/capsule-preference.js +44 -0
  91. package/lib/public/modules/chat-bubble-renderer.js +1 -1
  92. package/lib/public/modules/clay-log-links.js +70 -0
  93. package/lib/public/modules/command-palette.js +52 -15
  94. package/lib/public/modules/context-sources.js +0 -3
  95. package/lib/public/modules/git-agent-sessions.js +85 -0
  96. package/lib/public/modules/git-panel.js +111 -84
  97. package/lib/public/modules/git-placard.js +161 -0
  98. package/lib/public/modules/home-chat-empty-state.js +39 -0
  99. package/lib/public/modules/home-chat-identity.js +9 -0
  100. package/lib/public/modules/home-conversations-sheet.js +102 -48
  101. package/lib/public/modules/home-mate-chat.js +15 -35
  102. package/lib/public/modules/home-session-actions.js +6 -0
  103. package/lib/public/modules/home-sidebar-chat-list.js +101 -41
  104. package/lib/public/modules/home-sidebar.js +7 -0
  105. package/lib/public/modules/home-surface-boot.js +7 -3
  106. package/lib/public/modules/home-tool-frame.js +182 -0
  107. package/lib/public/modules/home-tools.js +138 -1
  108. package/lib/public/modules/input.js +38 -7
  109. package/lib/public/modules/markdown.js +2 -0
  110. package/lib/public/modules/mate-sidebar.js +0 -8
  111. package/lib/public/modules/message-delivery.js +97 -0
  112. package/lib/public/modules/paste-modal.js +84 -0
  113. package/lib/public/modules/project-activation.js +45 -0
  114. package/lib/public/modules/project-logs-render.js +405 -0
  115. package/lib/public/modules/project-logs.js +461 -0
  116. package/lib/public/modules/project-removal-target.js +26 -0
  117. package/lib/public/modules/scheduler.js +17 -3
  118. package/lib/public/modules/session-actions.js +29 -105
  119. package/lib/public/modules/session-hierarchy.js +54 -0
  120. package/lib/public/modules/sidebar-mobile.js +40 -15
  121. package/lib/public/modules/sidebar-session-hierarchy.js +214 -0
  122. package/lib/public/modules/sidebar-sessions.js +77 -11
  123. package/lib/public/modules/sidebar.js +17 -5
  124. package/lib/public/modules/split-group-helpers.js +8 -0
  125. package/lib/public/modules/split-pair-ui.js +15 -4
  126. package/lib/public/modules/split-view.js +5 -2
  127. package/lib/public/modules/split-worker-runtime.js +24 -0
  128. package/lib/public/modules/sticky-note-markdown.js +5 -0
  129. package/lib/public/modules/sticky-notes-browser.js +326 -0
  130. package/lib/public/modules/sticky-notes-card.js +360 -0
  131. package/lib/public/modules/sticky-notes-editor.js +291 -0
  132. package/lib/public/modules/sticky-notes-shared.js +90 -0
  133. package/lib/public/modules/sticky-notes.js +170 -946
  134. package/lib/public/modules/thinking-lifecycle.js +175 -0
  135. package/lib/public/modules/thinking-summary.js +28 -0
  136. package/lib/public/modules/thinking-view.js +75 -0
  137. package/lib/public/modules/tool-palette-order.js +143 -0
  138. package/lib/public/modules/tool-palette-overlays.js +159 -0
  139. package/lib/public/modules/tool-palette.js +21 -188
  140. package/lib/public/modules/tools.js +31 -123
  141. package/lib/public/modules/update-snooze.js +169 -0
  142. package/lib/public/modules/user-settings.js +4 -0
  143. package/lib/public/modules/worker-pane-lock.js +176 -0
  144. package/lib/public/modules/worker-proposal-state.js +16 -0
  145. package/lib/public/modules/worker-proposal.js +49 -20
  146. package/lib/public/style.css +3 -1
  147. package/lib/sdk-bridge.js +135 -46
  148. package/lib/sdk-message-processor.js +36 -19
  149. package/lib/server-experimental-settings.js +49 -0
  150. package/lib/server-global-ws.js +11 -0
  151. package/lib/server-home-chat-events.js +31 -1
  152. package/lib/server-home-chat.js +27 -4
  153. package/lib/server-home-clay-entry.js +1 -1
  154. package/lib/server-home-clay-session-links.js +34 -5
  155. package/lib/server-settings.js +5 -0
  156. package/lib/server-tools.js +127 -9
  157. package/lib/server.js +137 -22
  158. package/lib/session-driver-eligibility.js +38 -0
  159. package/lib/session-driver-orchestration.js +31 -0
  160. package/lib/session-hygiene.js +3 -0
  161. package/lib/session-notes-mcp-server.js +25 -5
  162. package/lib/session-pair-factory.js +247 -0
  163. package/lib/session-pair-mcp-server.js +46 -8
  164. package/lib/session-pair-prompts.js +79 -0
  165. package/lib/session-pair-turn-control.js +153 -0
  166. package/lib/session-provenance.js +119 -0
  167. package/lib/session-spawn-mcp-server.js +1 -1
  168. package/lib/session-split-groups.js +4 -1
  169. package/lib/session-title-policy.js +60 -0
  170. package/lib/session-visibility.js +41 -0
  171. package/lib/sessions.js +57 -10
  172. package/lib/tools-registry.js +89 -10
  173. package/lib/update-snooze.js +437 -0
  174. package/lib/users-experimental-preferences.js +30 -0
  175. package/lib/users-permissions.js +6 -6
  176. package/lib/users.js +22 -16
  177. package/lib/workspace-query-access.js +102 -0
  178. package/lib/workspace-query-service.js +24 -17
  179. package/lib/worktree.js +25 -38
  180. package/lib/ws-schema.js +38 -7
  181. package/lib/yoke/adapters/claude-worker.js +16 -6
  182. package/lib/yoke/adapters/claude.js +12 -0
  183. package/lib/yoke/adapters/codex.js +24 -14
  184. package/lib/yoke/vendor-registry.js +1 -1
  185. package/package.json +2 -2
@@ -16,6 +16,12 @@ import { VENDOR_AVATARS, VENDOR_NAMES, VENDOR_ORDER, VENDOR_HOMEPAGES, isExperim
16
16
  import { openGroup, separateGroup } from './split-view.js';
17
17
  import { groupedSessionIds } from './split-group-helpers.js';
18
18
  import { openPairDialog } from './split-pair-ui.js';
19
+ import {
20
+ hierarchyItemMatches,
21
+ prepareSidebarHierarchy,
22
+ renderDesktopDriverHierarchy,
23
+ renderDesktopOrphanHierarchy
24
+ } from './sidebar-session-hierarchy.js';
19
25
 
20
26
 
21
27
  // --- Session state ---
@@ -49,8 +55,8 @@ function sendSessionBookmark(sessionId, bookmarked) {
49
55
  }
50
56
 
51
57
  function compareSessionListItems(a, b) {
52
- var aData = a && a.type === "session" ? a.data : a;
53
- var bData = b && b.type === "session" ? b.data : b;
58
+ var aData = a && (a.type === "session" || a.type === "driver-hierarchy") ? a.data : a;
59
+ var bData = b && (b.type === "session" || b.type === "driver-hierarchy") ? b.data : b;
54
60
  var aBookmarked = !!(aData && aData.bookmarked);
55
61
  var bBookmarked = !!(bData && bData.bookmarked);
56
62
  if (aBookmarked !== bBookmarked) return aBookmarked ? -1 : 1;
@@ -236,6 +242,14 @@ function collectItemSessionIds(item) {
236
242
  if (item.type === "split-group" && Array.isArray(item.members)) {
237
243
  return item.members.map(function (session) { return session.id; });
238
244
  }
245
+ if (item.type === "driver-hierarchy" && item.root) {
246
+ var hierarchyIds = [item.root.driver.id];
247
+ for (var j = 0; j < item.root.workers.length; j++) hierarchyIds.push(item.root.workers[j].id);
248
+ return hierarchyIds;
249
+ }
250
+ if (item.type === "orphan-workers" && Array.isArray(item.workers)) {
251
+ return item.workers.map(function (session) { return session.id; });
252
+ }
239
253
  return [];
240
254
  }
241
255
 
@@ -683,7 +697,7 @@ function showSessionCtxMenu(anchorBtn, sessionId, title, cliSid, sessionData) {
683
697
 
684
698
  // Session visibility toggle (only the session owner can change)
685
699
  if (store.get('isMultiUserMode') && sessionData && sessionData.ownerId && sessionData.ownerId === store.get('myUserId')) {
686
- var currentVis = (sessionData && sessionData.sessionVisibility) || "shared";
700
+ var currentVis = (sessionData && sessionData.sessionVisibility) || "private";
687
701
  var isPrivate = currentVis === "private";
688
702
  var visItem = document.createElement("button");
689
703
  visItem.className = "session-ctx-item";
@@ -1246,10 +1260,12 @@ function renderLoopRun(parentGk, startedAtKey, sessions, isRalph) {
1246
1260
 
1247
1261
  // --- Session item rendering ---
1248
1262
 
1249
- function renderSessionItem(s) {
1263
+ function renderSessionItem(s, options) {
1264
+ var itemOptions = options || {};
1250
1265
  var el = document.createElement("div");
1251
1266
  var isMatch = searchMatchIds !== null && searchMatchIds.has(s.id);
1252
1267
  el.className = "session-item" + (s.active ? " active" : "") + (isMatch ? " search-match" : "");
1268
+ if (itemOptions.worker) el.classList.add("session-worker-item");
1253
1269
  el.dataset.sessionId = s.id;
1254
1270
 
1255
1271
  // Keep agent identity separate from the title so each row has a stable
@@ -1274,13 +1290,20 @@ function renderSessionItem(s) {
1274
1290
  if (s.loop && s.loop.source === "debate") {
1275
1291
  textHtml += '<span class="session-debate-icon" title="Debate">' + iconHtml("mic") + '</span>';
1276
1292
  }
1277
- if (store.get('isMultiUserMode') && s.sessionVisibility === "private") {
1278
- textHtml += '<span class="session-private-icon" title="Private session">' + iconHtml("lock") + '</span>';
1293
+ if (store.get('isMultiUserMode') && s.sessionVisibility === "shared") {
1294
+ textHtml += '<span class="session-shared-icon" title="Shared session" aria-label="Shared session">' + iconHtml("users") + '</span>';
1279
1295
  }
1280
1296
  textHtml += highlightMatch(s.title || "New Session", searchQuery);
1281
1297
  textSpan.innerHTML = textHtml;
1282
1298
  el.appendChild(textSpan);
1283
1299
 
1300
+ if (itemOptions.worker) {
1301
+ var workerLabel = document.createElement("span");
1302
+ workerLabel.className = "session-worker-generation" + (itemOptions.current ? " current" : "");
1303
+ workerLabel.textContent = itemOptions.current ? "Current" : (s.workerGeneration ? "Gen " + s.workerGeneration : "Worker");
1304
+ el.appendChild(workerLabel);
1305
+ }
1306
+
1284
1307
  var ageText = relativeTime(s.lastActivity || 0);
1285
1308
  if (ageText) {
1286
1309
  var age = document.createElement("span");
@@ -1325,7 +1348,7 @@ function renderSessionItem(s) {
1325
1348
 
1326
1349
  // Presence avatars (multi-user)
1327
1350
  renderPresenceAvatars(el, String(s.id));
1328
- setupSessionDragHandlers(el, s);
1351
+ if (!itemOptions.worker) setupSessionDragHandlers(el, s);
1329
1352
 
1330
1353
  return el;
1331
1354
  }
@@ -1376,8 +1399,29 @@ function renderSplitGroupItem(group, members) {
1376
1399
 
1377
1400
  // --- Main session list ---
1378
1401
 
1402
+ // Fingerprint of the last completed render: the whole session payload plus the
1403
+ // two store values this render also reads. Nothing is cherry-picked, so the
1404
+ // guard can only skip a frame that is byte-identical to what is already on
1405
+ // screen. A re-render triggered by other state (sessions === null) always
1406
+ // runs and resets the fingerprint afterwards.
1407
+ var lastRenderFingerprint = null;
1408
+
1409
+ function sessionRenderFingerprint(sessions) {
1410
+ try {
1411
+ return JSON.stringify([sessions, store.get('splitGroups') || [], store.get('splitPanes') || null]);
1412
+ } catch (e) {
1413
+ return null;
1414
+ }
1415
+ }
1416
+
1379
1417
  export function renderSessionList(sessions) {
1380
- if (sessions) cachedSessions = sessions;
1418
+ if (sessions) {
1419
+ // A reconnect and a streaming turn both re-send this list unchanged;
1420
+ // rebuilding the sidebar for an identical frame is what made it flicker.
1421
+ var incoming = sessionRenderFingerprint(sessions);
1422
+ if (incoming !== null && incoming === lastRenderFingerprint) return;
1423
+ cachedSessions = sessions;
1424
+ }
1381
1425
 
1382
1426
  // If mobile chat sheet is open, refresh it
1383
1427
  if (refreshMobileChatSheet) refreshMobileChatSheet();
@@ -1388,12 +1432,17 @@ export function renderSessionList(sessions) {
1388
1432
  // Group by loopId + date so all runs of the same task on the same day are merged
1389
1433
  var splitGroups = store.get('splitGroups') || [];
1390
1434
  var groupedIds = groupedSessionIds(splitGroups);
1435
+ var hierarchy = prepareSidebarHierarchy(cachedSessions);
1436
+ var hierarchyByDriver = hierarchy.byDriver;
1437
+ var hierarchySessionIds = hierarchy.sessionIds;
1438
+ hierarchySessionIds.forEach(function (id) { groupedIds.delete(id); });
1391
1439
  var sessionsById = new Map();
1392
1440
  for (var si = 0; si < cachedSessions.length; si++) sessionsById.set(cachedSessions[si].id, cachedSessions[si]);
1393
1441
  var loopGroups = {}; // groupKey -> [sessions]
1394
1442
  var normalSessions = [];
1395
1443
  for (var i = 0; i < cachedSessions.length; i++) {
1396
1444
  var s = cachedSessions[i];
1445
+ if (s.sessionRole === "worker") continue;
1397
1446
  if (groupedIds.has(s.id)) continue;
1398
1447
  if (s.loop && s.loop.loopId && s.loop.role === "crafting" && s.loop.source !== "ralph" && s.loop.source !== "debate") {
1399
1448
  // Task crafting sessions live in the scheduler calendar, not the main list (except debate)
@@ -1412,13 +1461,21 @@ export function renderSessionList(sessions) {
1412
1461
  // Build virtual items: normal sessions + one entry per loop group (using latest child's lastActivity)
1413
1462
  var items = [];
1414
1463
  for (var j = 0; j < normalSessions.length; j++) {
1415
- items.push({ type: "session", data: normalSessions[j], lastActivity: normalSessions[j].lastActivity || 0 });
1464
+ var driverRoot = hierarchyByDriver.get(normalSessions[j].id);
1465
+ if (driverRoot) items.push({ type: "driver-hierarchy", root: driverRoot, data: driverRoot.driver, lastActivity: driverRoot.lastActivity });
1466
+ else items.push({ type: "session", data: normalSessions[j], lastActivity: normalSessions[j].lastActivity || 0 });
1467
+ }
1468
+ if (hierarchy.orphans.length) {
1469
+ var orphanActivity = 0;
1470
+ for (var oi = 0; oi < hierarchy.orphans.length; oi++) orphanActivity = Math.max(orphanActivity, hierarchy.orphans[oi].lastActivity || 0);
1471
+ items.push({ type: "orphan-workers", workers: hierarchy.orphans, lastActivity: orphanActivity });
1416
1472
  }
1417
1473
  for (var sg = 0; sg < splitGroups.length; sg++) {
1418
1474
  var splitGroup = splitGroups[sg];
1419
1475
  var leftMember = sessionsById.get(splitGroup.members[0]);
1420
1476
  var rightMember = sessionsById.get(splitGroup.members[1]);
1421
1477
  if (!leftMember || !rightMember) continue;
1478
+ if (hierarchySessionIds.has(leftMember.id) || hierarchySessionIds.has(rightMember.id)) continue;
1422
1479
  items.push({
1423
1480
  type: "split-group",
1424
1481
  group: splitGroup,
@@ -1449,9 +1506,10 @@ export function renderSessionList(sessions) {
1449
1506
  if (item.type === "session" && item.data && !isSessionVisibleBySearch(item.data.id)) {
1450
1507
  continue;
1451
1508
  }
1509
+ if ((item.type === "driver-hierarchy" || item.type === "orphan-workers") && !hierarchyItemMatches(item, searchMatchIds)) continue;
1452
1510
  if (item.type === "split-group" && searchMatchIds !== null &&
1453
1511
  !searchMatchIds.has(item.members[0].id) && !searchMatchIds.has(item.members[1].id)) continue;
1454
- if (item.type === "session" && item.data && item.data.bookmarked) {
1512
+ if ((item.type === "session" || item.type === "driver-hierarchy") && item.data && item.data.bookmarked) {
1455
1513
  bookmarkedItems.push(item);
1456
1514
  } else {
1457
1515
  regularItems.push(item);
@@ -1468,7 +1526,8 @@ export function renderSessionList(sessions) {
1468
1526
  favoritesContainer.appendChild(emptyHint);
1469
1527
  }
1470
1528
  for (var bi = 0; bi < bookmarkedItems.length; bi++) {
1471
- favoritesContainer.appendChild(renderSessionItem(bookmarkedItems[bi].data));
1529
+ if (bookmarkedItems[bi].type === "driver-hierarchy") favoritesContainer.appendChild(renderDesktopDriverHierarchy(bookmarkedItems[bi].root, renderSessionItem, function () { renderSessionList(null); }, searchMatchIds));
1530
+ else favoritesContainer.appendChild(renderSessionItem(bookmarkedItems[bi].data));
1472
1531
  }
1473
1532
 
1474
1533
  var divider = document.createElement("div");
@@ -1508,6 +1567,10 @@ export function renderSessionList(sessions) {
1508
1567
  }
1509
1568
  } else if (item.type === "split-group") {
1510
1569
  regularContainer.appendChild(renderSplitGroupItem(item.group, item.members));
1570
+ } else if (item.type === "driver-hierarchy") {
1571
+ regularContainer.appendChild(renderDesktopDriverHierarchy(item.root, renderSessionItem, function () { renderSessionList(null); }, searchMatchIds));
1572
+ } else if (item.type === "orphan-workers") {
1573
+ regularContainer.appendChild(renderDesktopOrphanHierarchy(item.workers, renderSessionItem, function () { renderSessionList(null); }, searchMatchIds));
1511
1574
  } else {
1512
1575
  regularContainer.appendChild(renderSessionItem(item.data));
1513
1576
  }
@@ -1516,6 +1579,9 @@ export function renderSessionList(sessions) {
1516
1579
  refreshIcons();
1517
1580
  if (updatePageTitle) updatePageTitle();
1518
1581
  syncHeaderSearchUi();
1582
+ // Recorded from the sessions actually rendered, so a null-argument re-render
1583
+ // driven by other state refreshes the fingerprint rather than stranding it.
1584
+ lastRenderFingerprint = sessionRenderFingerprint(cachedSessions);
1519
1585
  }
1520
1586
 
1521
1587
  // --- Search results ---
@@ -1,4 +1,4 @@
1
- import { closeArchive } from './sticky-notes.js';
1
+ import { closeNotesBrowser } from './sticky-notes-browser.js';
2
2
  import { closeScheduler } from './scheduler.js';
3
3
  import { initSidebarSessions } from './sidebar-sessions.js';
4
4
  import { focusFileTree } from './filebrowser.js';
@@ -23,7 +23,7 @@ export function syncResizeHandles() {
23
23
  }
24
24
 
25
25
  export function dismissOverlayPanels() {
26
- closeArchive();
26
+ closeNotesBrowser();
27
27
  closeScheduler();
28
28
  }
29
29
 
@@ -119,7 +119,10 @@ export function initSidebar(_ctx) {
119
119
 
120
120
  // --- Panel switch (sessions / files / projects) ---
121
121
  var fileBrowserBtn = ctx.$("file-browser-btn");
122
- var gitBtn = ctx.$("git-sidebar-btn");
122
+ // Git has no tool-palette tile. The repository placard's More control is
123
+ // the entry point into the same panel, with the same lifecycle.
124
+ var gitPlacard = ctx.$("git-placard");
125
+ var gitPlacardMore = ctx.$("git-placard-more");
123
126
  var projectsPanel = ctx.$("sidebar-panel-projects");
124
127
  var sessionsPanel = ctx.$("sidebar-panel-sessions");
125
128
  var filesPanel = ctx.$("sidebar-panel-files");
@@ -216,14 +219,23 @@ export function initSidebar(_ctx) {
216
219
  });
217
220
  });
218
221
  }
219
- if (gitBtn) {
220
- gitBtn.addEventListener("click", function () {
222
+ if (gitPlacardMore) {
223
+ gitPlacardMore.addEventListener("click", function () {
221
224
  if (gitPanel && !gitPanel.classList.contains("hidden")) {
222
225
  hideGitPanel(function () { showSessionsPanel(); });
223
226
  }
224
227
  else showGitPanel();
225
228
  });
226
229
  }
230
+ if (gitPlacard && gitPlacardMore) {
231
+ // Clicking the block itself is a pointer convenience only; it forwards to
232
+ // the More control so every listener bound there runs exactly once, and
233
+ // the keyboard path stays the single focusable button.
234
+ gitPlacard.addEventListener("click", function (event) {
235
+ if (event.target.closest("#git-placard-more")) return;
236
+ gitPlacardMore.click();
237
+ });
238
+ }
227
239
  if (gitPanelClose) {
228
240
  gitPanelClose.addEventListener("click", function () {
229
241
  hideGitPanel(function () { showSessionsPanel(); });
@@ -16,3 +16,11 @@ export function findSplitGroup(groups, memberIds) {
16
16
  }
17
17
  return null;
18
18
  }
19
+
20
+ export function isConfiguredWorker(groups, sessionId) {
21
+ var list = groups || [];
22
+ for (var i = 0; i < list.length; i++) {
23
+ if (list[i].pair && list[i].pair.workerId === sessionId) return true;
24
+ }
25
+ return false;
26
+ }
@@ -3,6 +3,8 @@ import { getWs } from './ws-ref.js';
3
3
  import { VENDOR_AVATARS } from './app-rendering.js';
4
4
  import { buildAgentVendorSelect, fillAgentModels, buildAgentEffortSelect, fillAgentEffort } from './agent-config-selects.js';
5
5
  import { showToast } from './utils.js';
6
+ import { getCachedSessions } from './sidebar-sessions.js';
7
+ import { appendWorkerRuntime } from './split-worker-runtime.js';
6
8
 
7
9
  var pairDialog = null;
8
10
  // Set when the dialog was opened via "Add Split Worker" on an existing session:
@@ -190,8 +192,7 @@ export function handlePairCreated(msg) {
190
192
 
191
193
  // --- Worker-pane composer notice -------------------------------------
192
194
  // Shown inside a pane iframe while ITS session is processing a delegated
193
- // turn: typing is allowed (queueing is correct behavior), the bar just
194
- // makes it visible that a new message joins the Driver's turn.
195
+ // turn. Identifies the Driver responsible for the current task.
195
196
  var workerNoticeEl = null;
196
197
 
197
198
  export function showWorkerDelegationNotice(driverTitle) {
@@ -205,7 +206,7 @@ export function showWorkerDelegationNotice(driverTitle) {
205
206
  inputArea.insertBefore(workerNoticeEl, inputWrapper);
206
207
  }
207
208
  var who = driverTitle ? "“" + driverTitle + "”" : "the Driver";
208
- workerNoticeEl.textContent = "Following an instruction from " + who + " — messages you send now join the same turn.";
209
+ workerNoticeEl.textContent = "Working on a task from " + who + ".";
209
210
  workerNoticeEl.classList.add("visible");
210
211
  }
211
212
 
@@ -239,8 +240,10 @@ export function syncPairChrome(host, split) {
239
240
  for (var i = 0; i < groups.length; i++) {
240
241
  if (groups[i].id === split.groupId) { group = groups[i]; break; }
241
242
  }
242
- var oldBadges = host.querySelectorAll(".split-pair-role, .split-pair-live, .split-pair-set-driver");
243
+ var oldBadges = host.querySelectorAll(".split-pair-role, .split-pair-live, .split-pair-set-driver, .split-worker-runtime");
243
244
  for (var bi = 0; bi < oldBadges.length; bi++) oldBadges[bi].remove();
245
+ var runtimeHeaders = host.querySelectorAll(".has-worker-runtime");
246
+ for (var ri = 0; ri < runtimeHeaders.length; ri++) runtimeHeaders[ri].classList.remove("has-worker-runtime");
244
247
  host.classList.remove("split-delegating", "split-direction-right", "split-direction-left");
245
248
  if (!group) return;
246
249
  var headers = host.querySelectorAll(".split-pane-header");
@@ -272,6 +275,14 @@ export function syncPairChrome(host, split) {
272
275
  var title = header.querySelector(".split-pane-title");
273
276
  var access = header.querySelector(".split-pane-full-access");
274
277
  header.insertBefore(badge, access ? access.nextSibling : (title ? title.nextSibling : null));
278
+ if (!isDriver) {
279
+ var sessions = getCachedSessions() || [];
280
+ var workerSession = null;
281
+ for (var wi = 0; wi < sessions.length; wi++) {
282
+ if (sessions[wi].id === sessionId) { workerSession = sessions[wi]; break; }
283
+ }
284
+ appendWorkerRuntime(header, workerSession);
285
+ }
275
286
  })(split.panes[pi].sessionId, headers[pi]);
276
287
  }
277
288
  var active = (store.get('splitDelegations') || {})[group.id];
@@ -7,7 +7,7 @@ import { iconHtml, refreshIcons } from './icons.js';
7
7
  import { detachTuiView } from './session-tui-view.js';
8
8
  import { formatTokens } from './app-panels.js';
9
9
  import { VENDOR_AVATARS, VENDOR_NAMES } from './app-rendering.js';
10
- import { groupedSessionIds, findSplitGroup } from './split-group-helpers.js';
10
+ import { groupedSessionIds, findSplitGroup, isConfiguredWorker } from './split-group-helpers.js';
11
11
  import { showConfirm } from './app-misc.js';
12
12
  import { syncPairChrome } from './split-pair-ui.js';
13
13
  import { presentMarkdownEdit } from './filebrowser.js';
@@ -159,7 +159,10 @@ export function syncPaneTitles() {
159
159
  function updatePaneFullAccessButton(button, session) {
160
160
  if (!button) return;
161
161
  var mode = session && (session.runtimeMode || session.mode || "gui");
162
- var visible = !!session && !store.get('skipPermsEnabled') && mode === "gui";
162
+ var worker = !!session && isConfiguredWorker(store.get('splitGroups'), session.id);
163
+ // Permission policy belongs to the Driver. A configured Worker inherits it
164
+ // server-side and must not present a second, misleading control.
165
+ var visible = !!session && !worker && !store.get('skipPermsEnabled') && mode === "gui";
163
166
  var enabled = visible && session.permissionMode === "bypassPermissions";
164
167
  button.classList.toggle("hidden", !visible);
165
168
  button.classList.toggle("active", enabled);
@@ -0,0 +1,24 @@
1
+ // Read the Worker's own session metadata, never the active Driver's settings.
2
+ export function workerRuntimeLabels(session) {
3
+ var model = session && typeof session.model === "string" ? session.model : "";
4
+ var effort = session && typeof session.effort === "string" ? session.effort : "";
5
+ var level = effort === "xhigh" ? "X-High" : (effort ? effort.charAt(0).toUpperCase() + effort.slice(1) : "Default");
6
+ return { model: model || "Default model", thinking: "Thinking: " + level };
7
+ }
8
+
9
+ export function appendWorkerRuntime(header, session) {
10
+ var labels = workerRuntimeLabels(session);
11
+ var runtime = document.createElement("div");
12
+ runtime.className = "split-worker-runtime";
13
+ var model = document.createElement("span");
14
+ model.className = "split-worker-runtime-model";
15
+ model.textContent = labels.model;
16
+ model.title = "Model: " + labels.model;
17
+ var thinking = document.createElement("span");
18
+ thinking.className = "split-worker-runtime-thinking";
19
+ thinking.textContent = labels.thinking;
20
+ runtime.appendChild(model);
21
+ runtime.appendChild(thinking);
22
+ header.classList.add("has-worker-runtime");
23
+ header.appendChild(runtime);
24
+ }
@@ -58,6 +58,11 @@ function nodeToMarkdown(node) {
58
58
  return "\n" + inner;
59
59
  case "P": return "\n" + inner;
60
60
  case "A": return node.getAttribute("href") || inner;
61
+ case "BUTTON":
62
+ if (node.classList.contains("clayos-log-link") && node.dataset && node.dataset.logRef) {
63
+ return "`" + node.dataset.logRef + "`";
64
+ }
65
+ return inner;
61
66
  case "SPAN":
62
67
  if (node.classList.contains("sn-check")) {
63
68
  return node.classList.contains("checked") ? "- [x]" : "- [ ]";