clay-server 5.4.0 → 5.5.0-beta.2

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 (79) hide show
  1. package/lib/agent-behavior-gate.js +223 -0
  2. package/lib/agent-behavior-schema.js +184 -0
  3. package/lib/daemon.js +15 -0
  4. package/lib/issues-mcp-server.js +16 -3
  5. package/lib/migrate-single-user.js +2 -0
  6. package/lib/project-issues.js +9 -2
  7. package/lib/project-log-feedback-delivery.js +3 -0
  8. package/lib/project-logs-mcp-server.js +23 -2
  9. package/lib/project-logs.js +40 -6
  10. package/lib/project-session-document.js +9 -1
  11. package/lib/project-session-notes.js +19 -8
  12. package/lib/project-session-pair-prompt.js +5 -4
  13. package/lib/project-user-knowledge.js +21 -3
  14. package/lib/project-worker-proposal.js +8 -2
  15. package/lib/project.js +108 -61
  16. package/lib/public/app.js +1 -0
  17. package/lib/public/css/agent-behavior.css +101 -0
  18. package/lib/public/modules/agent-behavior-nav.js +90 -0
  19. package/lib/public/modules/agent-behavior-view.js +235 -0
  20. package/lib/public/modules/agent-behavior.js +200 -0
  21. package/lib/public/modules/settings-shell-registry.js +1 -1
  22. package/lib/public/modules/settings-shell-view.js +18 -0
  23. package/lib/public/modules/sidebar-sessions.js +3 -0
  24. package/lib/public/modules/split-view-arrange-menu.js +38 -0
  25. package/lib/public/modules/split-view-create-menu.js +9 -5
  26. package/lib/public/modules/split-view-member-actions.js +42 -0
  27. package/lib/public/modules/split-view-picker-actions.js +2 -2
  28. package/lib/public/modules/split-view-presentation-renderer.js +10 -35
  29. package/lib/public/modules/split-view-presentations.js +9 -8
  30. package/lib/public/modules/split-view-sidebar-menu.js +2 -1
  31. package/lib/public/modules/user-settings-layout.js +8 -1
  32. package/lib/public/modules/user-settings.js +5 -2
  33. package/lib/public/modules/view-workspace-state.js +1 -1
  34. package/lib/public/pane-assets/{app-BDYH6Y7R.js → app-SEMVZ4AY.js} +1 -1
  35. package/lib/public/pane-assets/{chunk-U5GH2UJM.js → chunk-4YE4RU27.js} +1 -1
  36. package/lib/public/pane-assets/{chunk-E6NRPGVH.js → chunk-5UBN6GR7.js} +1 -1
  37. package/lib/public/pane-assets/chunk-6AGSOAOD.js +1 -0
  38. package/lib/public/pane-assets/chunk-AATAIAXL.js +1 -0
  39. package/lib/public/pane-assets/{chunk-42C2CZEP.js → chunk-BNNPJJVG.js} +1 -1
  40. package/lib/public/pane-assets/{chunk-HWLBH6AL.js → chunk-C6P7PICY.js} +1 -1
  41. package/lib/public/pane-assets/{chunk-KB4T5B2R.js → chunk-CQNPEH2M.js} +1 -1
  42. package/lib/public/pane-assets/chunk-D3CE5FZL.js +1 -0
  43. package/lib/public/pane-assets/{chunk-N5SIEIOG.js → chunk-E4VM2IDB.js} +1 -1
  44. package/lib/public/pane-assets/{chunk-VCNLKOVT.js → chunk-FK3YLGAP.js} +1 -1
  45. package/lib/public/pane-assets/chunk-GKSLBGVE.js +168 -0
  46. package/lib/public/pane-assets/chunk-J5PTSKJ6.js +1 -0
  47. package/lib/public/pane-assets/{chunk-XI2DJM4H.js → chunk-K4PAN77B.js} +1 -1
  48. package/lib/public/pane-assets/{chunk-UMIYQYGM.js → chunk-M5CWIIIT.js} +1 -1
  49. package/lib/public/pane-assets/{chunk-KF3LZRJK.js → chunk-QBCM7QEA.js} +1 -1
  50. package/lib/public/pane-assets/chunk-QPBO6ID4.js +1 -0
  51. package/lib/public/pane-assets/{chunk-TMUCEZ7C.js → chunk-QWQB2YRF.js} +1 -1
  52. package/lib/public/pane-assets/chunk-RA6VUJTY.js +1 -0
  53. package/lib/public/pane-assets/{chunk-TT3NECKK.js → chunk-U7EHYRJZ.js} +1 -1
  54. package/lib/public/pane-assets/chunk-XOZUM5ET.js +1 -0
  55. package/lib/public/pane-assets/{chunk-X452YCE5.js → chunk-XYZGVYWW.js} +1 -1
  56. package/lib/public/pane-assets/{chunk-QRVKAVH7.js → chunk-YR5GH3GI.js} +1 -1
  57. package/lib/public/pane-assets/{chunk-YE45RR7W.js → chunk-YVVT3CTV.js} +1 -1
  58. package/lib/public/pane-assets/manifest.json +394 -388
  59. package/lib/public/pane-assets/{style-VDIGFFLR.css → style-DZX6JAMF.css} +1 -1
  60. package/lib/public/style.css +1 -0
  61. package/lib/sdk-bridge.js +3 -2
  62. package/lib/server-agent-behavior-settings.js +72 -0
  63. package/lib/server-settings.js +2 -0
  64. package/lib/server.js +9 -0
  65. package/lib/session-notes-mcp-server.js +27 -7
  66. package/lib/session-skill-scope.js +2 -2
  67. package/lib/users-agent-behavior-preferences.js +38 -0
  68. package/lib/users.js +6 -0
  69. package/lib/yoke/adapters/claude.js +8 -4
  70. package/lib/yoke/skill-discovery.js +46 -1
  71. package/package.json +1 -1
  72. package/lib/public/pane-assets/chunk-46OOGVHU.js +0 -1
  73. package/lib/public/pane-assets/chunk-4OJW4FF4.js +0 -1
  74. package/lib/public/pane-assets/chunk-F7VIE6FJ.js +0 -1
  75. package/lib/public/pane-assets/chunk-FCJYJLVZ.js +0 -1
  76. package/lib/public/pane-assets/chunk-FEJDEXOW.js +0 -1
  77. package/lib/public/pane-assets/chunk-FQQMXMH7.js +0 -168
  78. package/lib/public/pane-assets/chunk-HEMCIILG.js +0 -1
  79. package/lib/public/pane-assets/chunk-RV5O66TO.js +0 -1
@@ -63,7 +63,24 @@ export function renderPageNav(pages, activePage, onSelect) {
63
63
  markActivePage(activePage);
64
64
  }
65
65
 
66
+ // A page may hang a list of in-page links under its own nav item (desktop).
67
+ var subnav = { page: null, node: null };
68
+ var lastActivePage = null;
69
+ export function setPageSubnav(page, node) {
70
+ if (subnav.node && subnav.node.parentNode && subnav.node !== node) subnav.node.parentNode.removeChild(subnav.node);
71
+ subnav = { page: node ? page : null, node: node || null };
72
+ placeSubnav();
73
+ }
74
+ function placeSubnav() {
75
+ var nav = el('settings-page-nav');
76
+ if (!subnav.node) return;
77
+ var active = Array.from(nav.querySelectorAll('button.settings-nav-item')).filter(function (button) { return button.dataset.section === lastActivePage; })[0];
78
+ if (subnav.page === lastActivePage && active) active.after(subnav.node);
79
+ else if (subnav.node.parentNode === nav) nav.removeChild(subnav.node);
80
+ }
81
+
66
82
  export function markActivePage(activePage) {
83
+ lastActivePage = activePage;
67
84
  Array.from(el('settings-page-nav').querySelectorAll('button')).forEach(function (button) {
68
85
  var active = button.dataset.section === activePage;
69
86
  button.classList.toggle('active', active);
@@ -71,6 +88,7 @@ export function markActivePage(activePage) {
71
88
  });
72
89
  var select = el('settings-page-select');
73
90
  if (activePage && select.value !== activePage) select.value = activePage;
91
+ placeSubnav();
74
92
  }
75
93
 
76
94
  // The selector is a plain select; settings-select upgrades it to the shared
@@ -1,3 +1,4 @@
1
+ import { appendRemoveFromView } from './split-view-member-actions.js';
1
2
  import { appendGithubWork } from './session-github.js';
2
3
  // sidebar-sessions.js - Session list, search, presence, countdown, CLI picker
3
4
  // Extracted from sidebar.js (PR-35)
@@ -290,6 +291,8 @@ function showSessionCtxMenu(anchorBtn, sessionId, title, cliSid, sessionData, po
290
291
  menu.appendChild(renameItem);
291
292
 
292
293
 
294
+ appendRemoveFromView(menu, sessionData, currentAnchor);
295
+
293
296
  // Session visibility toggle (only the session owner can change)
294
297
  if (!isMateWorkspace(store.snap()) && store.get('isMultiUserMode') && sessionData && !sessionData.isMate && sessionData.ownerId && sessionData.ownerId === store.get('myUserId')) {
295
298
  var currentVis = (sessionData && sessionData.sessionVisibility) || "private";
@@ -0,0 +1,38 @@
1
+ import { iconHtml } from './icons.js';
2
+ import { appendLayoutOption } from './split-view-layout-icons.js';
3
+ import { SPLIT_SIDES } from './split-view-picker-render.js';
4
+
5
+ export function ensureArrangeMenu(closePresentationArrange) {
6
+ var menu = document.getElementById("split-view-arrange-menu");
7
+ if (menu) return menu;
8
+ menu = document.createElement("div"); menu.id = "split-view-arrange-menu"; menu.className = "split-view-arrange";
9
+ menu.setAttribute("role", "dialog"); menu.setAttribute("aria-label", "Arrange Split view"); menu.hidden = true;
10
+ var header = document.createElement("div"); header.className = "split-view-arrange-header";
11
+ var title = document.createElement("strong"); title.textContent = "Arrange Split view"; header.appendChild(title);
12
+ var close = document.createElement("button"); close.type = "button"; close.setAttribute("aria-label", "Close arrangement menu"); close.innerHTML = iconHtml("x"); header.appendChild(close);
13
+ close.addEventListener("click", function () { closePresentationArrange(); }); menu.appendChild(header);
14
+ var presets = [["balanced", "Balanced"], ["columns", "Columns"], ["rows", "Rows"], ["grid", "Grid"], ["main-left", "Main left"], ["main-right", "Main right"]];
15
+ var presetGrid = document.createElement("div"); presetGrid.className = "split-view-arrange-grid";
16
+ presets.forEach(function (item) { var button = document.createElement("button"); button.type = "button"; button.dataset.preset = item[0]; appendLayoutOption(button, item[1], "preset", item[0]); presetGrid.appendChild(button); });
17
+ menu.appendChild(presetGrid);
18
+ var positions = [["left", "Left half"], ["right", "Right half"], ["top", "Top half"], ["bottom", "Bottom half"], ["top-left", "Top left"], ["top-right", "Top right"], ["bottom-left", "Bottom left"], ["bottom-right", "Bottom right"], ["left-third", "Left third"], ["right-third", "Right third"]];
19
+ var positionGrid = document.createElement("div"); positionGrid.className = "split-view-arrange-grid split-view-arrange-positions";
20
+ positions.forEach(function (item) { var button = document.createElement("button"); button.type = "button"; button.dataset.position = item[0]; appendLayoutOption(button, item[1], "position", item[0]); positionGrid.appendChild(button); });
21
+ menu.appendChild(positionGrid);
22
+ var additions = document.createElement('div'); additions.className = 'split-session-directions';
23
+ SPLIT_SIDES.forEach(function (side) {
24
+ var button = document.createElement('button'); button.type = 'button'; button.dataset.arrangeAddSide = side[0];
25
+ button.innerHTML = iconHtml(side[2]); var label = document.createElement('span'); label.textContent = 'Add ' + side[1] + ' Split'; button.appendChild(label); additions.appendChild(button);
26
+ });
27
+ menu.appendChild(additions);
28
+ var focusButton = document.createElement("button"); focusButton.type = "button"; focusButton.dataset.focusLayout = "true"; menu.appendChild(focusButton);
29
+ var membership = document.createElement("button"); membership.type = "button"; membership.dataset.arrangeMembership = "true";
30
+ membership.textContent = "Choose sessions"; menu.appendChild(membership);
31
+ var context = document.createElement("div"); context.className = "split-view-arrange-context";
32
+ var contextButton = document.createElement("button"); contextButton.type = "button"; contextButton.dataset.arrangeContext = "true";
33
+ contextButton.setAttribute("aria-expanded", "false"); contextButton.setAttribute("aria-controls", "split-view-arrange-context-list");
34
+ var contextTitle = document.createElement("span"); contextTitle.dataset.arrangeContextTitle = "true"; contextButton.appendChild(contextTitle); context.appendChild(contextButton);
35
+ var contextList = document.createElement("div"); contextList.id = "split-view-arrange-context-list"; contextList.hidden = true; contextList.setAttribute("role", "listbox");
36
+ context.appendChild(contextList); menu.insertBefore(context, presetGrid);
37
+ document.body.appendChild(menu); return menu;
38
+ }
@@ -29,8 +29,8 @@ function position() {
29
29
  menu.style.left = Math.max(8, Math.min(rect.right - width, innerWidth - width - 8)) + 'px';
30
30
  menu.style.top = Math.max(8, Math.min(rect.bottom + 5, innerHeight - menu.offsetHeight - 8)) + 'px';
31
31
  }
32
- function open(anchor) {
33
- var receipt = pickerReceipt(anchor);
32
+ function open(anchor, side, origin) {
33
+ var receipt = pickerReceipt(anchor, origin);
34
34
  if (!receipt || !pickerCurrent(receipt)) { showToast('Wait for this View to finish loading, then try again.', 'warn'); return; }
35
35
  var menu = menuElement();
36
36
  if (menu && !menu.hidden && menu.clayCreateAnchor === anchor) { close(true); return; }
@@ -41,8 +41,12 @@ function open(anchor) {
41
41
  }
42
42
  receipt.reservationId = 'split-picker-' + Date.now().toString(36) + '-' + Math.random().toString(36).slice(2);
43
43
  menu.clayCreateAnchor = anchor; anchor.setAttribute('aria-expanded', 'true');
44
- store.set({ splitSessionPicker: { receipt: receipt, side: null, query: '', error: '', priorFocus: store.get('projectSplitViewFocusedOrigin') } });
45
- renderPicker(menu); menu.hidden = false; position(); menu.querySelector('[data-split-side]').focus();
44
+ store.set({ splitSessionPicker: { receipt: receipt, side: side || null, query: '', error: '', priorFocus: store.get('projectSplitViewFocusedOrigin') } });
45
+ if (side) store.set({ projectSplitViewFocusedOrigin: null });
46
+ renderPicker(menu); menu.hidden = false;
47
+ if (side) document.dispatchEvent(new CustomEvent('clay-split-picker-layout'));
48
+ position(); menu.querySelector(side ? 'input' : '[data-split-side]').focus();
49
+ if (side) retryOptions();
46
50
  }
47
51
  function onClick(event) {
48
52
  var menu = menuElement(), state = store.get('splitSessionPicker'); if (!menu || menu.hidden || !state) return;
@@ -76,7 +80,7 @@ function onKey(event) {
76
80
  items[next].focus();
77
81
  }
78
82
  export function initSplitViewCreateMenu() {
79
- document.addEventListener('clay-split-view-open-create', function (event) { if (event.detail && event.detail.anchor) open(event.detail.anchor); });
83
+ document.addEventListener('clay-split-view-open-create', function (event) { if (event.detail && event.detail.anchor) open(event.detail.anchor, event.detail.side, event.detail.origin); });
80
84
  document.addEventListener('click', onClick, true); document.addEventListener('keydown', onKey, true);
81
85
  document.addEventListener('input', function (event) {
82
86
  var state = store.get('splitSessionPicker'); if (!state || !event.target.matches('#split-view-create-menu input')) return;
@@ -0,0 +1,42 @@
1
+ import { store } from './store.js';
2
+ import { getWs } from './ws-ref.js';
3
+ import { mutation } from './view-mutations.js';
4
+ import { remove, validateTree } from './split-view-layout-client.js';
5
+ import { showToast } from './utils.js';
6
+ import { iconHtml } from './icons.js';
7
+ import { closeSessionContextMenu } from './session-context-menu.js';
8
+
9
+ export function removeViewSession(view, origin) {
10
+ var live = (store.get('projectSplitViews') || []).find(function (item) { return item.id === view.id && item.revision === view.revision; });
11
+ if (!live || !live.sessions.some(function (session) { return session.sessionOriginId === origin; })) return false;
12
+ var active = live.id === store.get('projectSplitViewActiveId');
13
+ if (active) {
14
+ try {
15
+ document.querySelectorAll('#split-host .split-pane-frame').forEach(function (frame) { if (frame.clayPane) frame.clayPane.checkDraft(); });
16
+ } catch (error) { showToast(error.message, 'warn'); return false; }
17
+ }
18
+ var origins = live.sessions.map(function (session) { return session.sessionOriginId; }).filter(function (item) { return item !== origin; });
19
+ var tree = remove(live.tree, origin);
20
+ if (!validateTree(tree, origins).ok) return false;
21
+ var nextActive = live.activeSessionOriginId === origin ? origins[0] || null : live.activeSessionOriginId;
22
+ return mutation('project_split_view_update', { id: live.id, sessionOrigins: origins, tree: tree,
23
+ layout: live.layout, activeSessionOriginId: nextActive, active: active }, active ? { closedOrigin: origin, activeOrigin: nextActive } : null);
24
+ }
25
+
26
+ export function appendRemoveFromView(menu, session, currentAnchor) {
27
+ if (!session || session.sessionRole === 'worker') return;
28
+ var view = (store.get('projectSplitViews') || []).find(function (item) {
29
+ return item.sessions.length > 1 && item.sessions.some(function (ref) { return ref.sessionOriginId === session.sessionOriginId; });
30
+ });
31
+ if (!view) return;
32
+ var socket = getWs(), slug = store.get('currentSlug'), viewer = store.get('myUserId'), generation = store.get('projectSplitViewSocketGeneration');
33
+ var button = document.createElement('button'); button.className = 'session-ctx-item';
34
+ button.innerHTML = iconHtml('panel-left-close') + ' <span>Remove from View</span>';
35
+ button.disabled = !!store.get('projectSplitViewPending');
36
+ button.addEventListener('click', function (event) {
37
+ event.stopPropagation(); var anchor = currentAnchor(); closeSessionContextMenu(false);
38
+ if (!anchor || socket !== getWs() || slug !== store.get('currentSlug') || viewer !== store.get('myUserId') || generation !== store.get('projectSplitViewSocketGeneration')) return;
39
+ removeViewSession(view, session.sessionOriginId);
40
+ });
41
+ menu.appendChild(button);
42
+ }
@@ -11,9 +11,9 @@ import { checkComposerDeparture } from './session-rich-drafts.js';
11
11
  import { createOptions } from './session-create.js';
12
12
  import { showToast } from './utils.js';
13
13
 
14
- export function pickerReceipt(anchor) {
14
+ export function pickerReceipt(anchor, targetOrigin) {
15
15
  var view = activeSavedView(), pane = anchor && anchor.closest('.split-pane'), frame = pane && pane.querySelector('.split-pane-frame');
16
- var origin = pane && pane.dataset.presentationOrigin || frame && frame.dataset.sessionOriginId || store.get('sessionOriginId');
16
+ var origin = targetOrigin || pane && pane.dataset.presentationOrigin || frame && frame.dataset.sessionOriginId || store.get('sessionOriginId');
17
17
  return view && view.sessions.some(function (session) { return session.sessionOriginId === origin; }) ? { scope: layoutScope(view), origin: origin } : null;
18
18
  }
19
19
  export function pickerCurrent(receipt) {
@@ -16,7 +16,8 @@ import { createSplitPane, updateSplitPaneMetadata, updateSplitPaneCloseVisibilit
16
16
  import { syncPaneHeaderState } from './split-pane-header-state.js';
17
17
  import { rectangles, resize, validateTree, listIds } from './split-view-layout-client.js';
18
18
  import { iconHtml, refreshIcons } from './icons.js';
19
- import { appendLayoutOption } from './split-view-layout-icons.js';
19
+ import { ensureArrangeMenu as buildArrangeMenu } from './split-view-arrange-menu.js';
20
+ import { pickerCapacity } from './split-view-picker-actions.js';
20
21
  import { reserveEmptySplit, clearEmptySplit } from './split-session-empty-pane.js';
21
22
 
22
23
  var host = null;
@@ -51,7 +52,7 @@ function dispatch(name, detail) {
51
52
  export function initPresentationRenderer(hostElement) {
52
53
  host = hostElement;
53
54
  app = document.getElementById("app");
54
- ensureArrangeMenu();
55
+ buildArrangeMenu(closePresentationArrange);
55
56
  document.addEventListener('clay-split-picker-layout', renderCurrentGeometry);
56
57
  document.addEventListener("click", onClick, true);
57
58
  document.addEventListener("keydown", onKeyDown, true);
@@ -60,7 +61,7 @@ export function initPresentationRenderer(hostElement) {
60
61
  document.addEventListener("pointercancel", onPointerCancel, true);
61
62
  document.addEventListener("lostpointercapture", onPointerCancel, true);
62
63
  window.addEventListener("blur", onWindowBlur);
63
- window.addEventListener("resize", function () { renderCurrentGeometry(); });
64
+ window.addEventListener("resize", function () { closePresentationArrange(false); renderCurrentGeometry(); });
64
65
  window.addEventListener("scroll", closePresentationArrange, true);
65
66
  if (typeof ResizeObserver === "function") {
66
67
  resizeObserver = new ResizeObserver(function () { renderCurrentGeometry(); });
@@ -136,7 +137,7 @@ export function renderPresentationView(view, tree, sessions) {
136
137
  if (app) app.classList.add("split-native-hidden");
137
138
  reconcileSplitPanes(host, panes, function (pane) { return createSplitPane(pane, function (sessionId) { dispatch("clay-split-view-close-pane", { sessionId: sessionId }); }); }, updateSplitPaneMetadata, paneRetention(host));
138
139
  Array.from(host.querySelectorAll(':scope > .split-pane .split-pane-header')).forEach(function (header) { ensureArrangeTrigger(header); });
139
- ensureArrangeMenu();
140
+ buildArrangeMenu(closePresentationArrange);
140
141
  Array.from(host.querySelectorAll(':scope > .split-pane')).forEach(function (node) {
141
142
  var target = panes.find(function (pane) { return node.dataset.splitPaneKey === pane.slug + '#' + pane.sessionId; });
142
143
  var session = target && sessions.find(function (item) { return item.id === target.sessionId; });
@@ -278,34 +279,6 @@ export function ensureArrangeTrigger(header) {
278
279
  if (pane) syncPaneHeaderState(pane);
279
280
  }
280
281
 
281
- function ensureArrangeMenu() {
282
- var menu = document.getElementById("split-view-arrange-menu");
283
- if (menu) return menu;
284
- menu = document.createElement("div"); menu.id = "split-view-arrange-menu"; menu.className = "split-view-arrange";
285
- menu.setAttribute("role", "dialog"); menu.setAttribute("aria-label", "Arrange Split view"); menu.hidden = true;
286
- var header = document.createElement("div"); header.className = "split-view-arrange-header";
287
- var title = document.createElement("strong"); title.textContent = "Arrange Split view"; header.appendChild(title);
288
- var close = document.createElement("button"); close.type = "button"; close.setAttribute("aria-label", "Close arrangement menu"); close.innerHTML = iconHtml("x"); header.appendChild(close);
289
- close.addEventListener("click", function () { closePresentationArrange(); }); menu.appendChild(header);
290
- var presets = [["balanced", "Balanced"], ["columns", "Columns"], ["rows", "Rows"], ["grid", "Grid"], ["main-left", "Main left"], ["main-right", "Main right"]];
291
- var presetGrid = document.createElement("div"); presetGrid.className = "split-view-arrange-grid";
292
- presets.forEach(function (item) { var button = document.createElement("button"); button.type = "button"; button.dataset.preset = item[0]; appendLayoutOption(button, item[1], "preset", item[0]); presetGrid.appendChild(button); });
293
- menu.appendChild(presetGrid);
294
- var positions = [["left", "Left half"], ["right", "Right half"], ["top", "Top half"], ["bottom", "Bottom half"], ["top-left", "Top left"], ["top-right", "Top right"], ["bottom-left", "Bottom left"], ["bottom-right", "Bottom right"], ["left-third", "Left third"], ["right-third", "Right third"]];
295
- var positionGrid = document.createElement("div"); positionGrid.className = "split-view-arrange-grid split-view-arrange-positions";
296
- positions.forEach(function (item) { var button = document.createElement("button"); button.type = "button"; button.dataset.position = item[0]; appendLayoutOption(button, item[1], "position", item[0]); positionGrid.appendChild(button); });
297
- menu.appendChild(positionGrid);
298
- var focusButton = document.createElement("button"); focusButton.type = "button"; focusButton.dataset.focusLayout = "true"; menu.appendChild(focusButton);
299
- var membership = document.createElement("button"); membership.type = "button"; membership.dataset.arrangeMembership = "true";
300
- membership.textContent = "Choose sessions"; menu.appendChild(membership);
301
- var context = document.createElement("div"); context.className = "split-view-arrange-context";
302
- var contextButton = document.createElement("button"); contextButton.type = "button"; contextButton.dataset.arrangeContext = "true";
303
- contextButton.setAttribute("aria-expanded", "false"); contextButton.setAttribute("aria-controls", "split-view-arrange-context-list");
304
- var contextTitle = document.createElement("span"); contextTitle.dataset.arrangeContextTitle = "true"; contextButton.appendChild(contextTitle); context.appendChild(contextButton);
305
- var contextList = document.createElement("div"); contextList.id = "split-view-arrange-context-list"; contextList.hidden = true; contextList.setAttribute("role", "listbox");
306
- context.appendChild(contextList); menu.insertBefore(context, presetGrid);
307
- document.body.appendChild(menu); return menu;
308
- }
309
282
 
310
283
  function openArrangeMenu(trigger) {
311
284
  if (!syncPaneHeaderState(trigger.closest('.split-pane'))) return;
@@ -313,7 +286,7 @@ function openArrangeMenu(trigger) {
313
286
  dispatch("clay-split-view-open-create", { anchor: trigger });
314
287
  return;
315
288
  }
316
- var menu = ensureArrangeMenu();
289
+ var menu = buildArrangeMenu(closePresentationArrange);
317
290
  if (!menu.hidden && menuTrigger === trigger) { closePresentationArrange(); return; }
318
291
  closePresentationArrange(false); menuTrigger = trigger; menu.hidden = false;
319
292
  trigger.setAttribute("aria-expanded", "true");
@@ -340,6 +313,7 @@ function openArrangeMenu(trigger) {
340
313
  Array.from(menu.querySelectorAll('[data-position*="-"]:not([data-position$="-third"])')).forEach(function (button) {
341
314
  button.disabled = count < 3; button.title = count < 3 ? "Quarter layouts require at least three sessions" : "";
342
315
  });
316
+ menu.querySelectorAll('[data-arrange-add-side]').forEach(function (button) { button.disabled = !pickerCapacity() || !!store.get('projectSplitViewPending'); });
343
317
  var rect = trigger.getBoundingClientRect(), width = Math.min(280, window.innerWidth - 16);
344
318
  menu.style.left = Math.max(8, Math.min(rect.left, window.innerWidth - width - 8)) + "px";
345
319
  menu.style.top = Math.max(8, Math.min(rect.bottom + 4, window.innerHeight - menu.offsetHeight - 8)) + "px";
@@ -362,10 +336,11 @@ function onClick(event) {
362
336
  if (trigger) { event.preventDefault(); event.stopImmediatePropagation(); openArrangeMenu(trigger); return; }
363
337
  var menu = event.target.closest('#split-view-arrange-menu');
364
338
  if (menu) {
365
- if (event.target.closest('[data-arrange-membership]')) {
339
+ var addSide = event.target.closest('[data-arrange-add-side]');
340
+ if (addSide || event.target.closest('[data-arrange-membership]')) {
366
341
  event.preventDefault(); event.stopImmediatePropagation();
367
342
  var anchor = menuTrigger; closePresentationArrange(false);
368
- if (anchor && anchor.isConnected) dispatch("clay-split-view-open-create", { anchor: anchor });
343
+ if (anchor && anchor.isConnected) dispatch("clay-split-view-open-create", { anchor: anchor, side: addSide && addSide.dataset.arrangeAddSide, origin: menu.dataset.sourceOrigin });
369
344
  return;
370
345
  }
371
346
  var contextButton = event.target.closest('[data-arrange-context]');
@@ -19,10 +19,11 @@ import { initPresentationDrag, restorePresentationDrag } from './split-view-pres
19
19
  import { detachTuiView } from './session-tui-view.js';
20
20
  import { checkComposerDeparture } from './session-rich-drafts.js';
21
21
  import { showToast } from './utils.js';
22
- import { preset, listIds, validateTree, remove, arrangeRegion } from './split-view-layout-client.js';
22
+ import { preset, listIds, validateTree, arrangeRegion } from './split-view-layout-client.js';
23
23
  import { viewIdForOrigin, updateActiveSession, exactDriverForOrigin, restorablePresentation } from './split-view-presentation-lookup.js';
24
24
  import { groupedSessionIds } from './split-group-helpers.js';
25
25
  import { handleSplitViewCreateEvent } from './split-view-create-action.js';
26
+ import { removeViewSession } from './split-view-member-actions.js';
26
27
  import { initSplitViewCreateMenu } from './split-view-create-menu.js';
27
28
  import { currentSingletonPresentation } from './split-view-singleton.js';
28
29
  var host = null;
@@ -333,6 +334,12 @@ function toggleGroupMenu(group, anchor, point) {
333
334
  }
334
335
  function runGroupAction(view, action, root) {
335
336
  closeGroupMenus(false);
337
+ if (action === 'add-pane') {
338
+ if (!setActiveView(view.id)) return;
339
+ var anchor = host && host.querySelector('.split-pane:not(.split-pane-cached) [data-action="arrange"]');
340
+ if (anchor) document.dispatchEvent(new CustomEvent('clay-split-view-open-create', { detail: { anchor: anchor, side: 'right', origin: view.activeSessionOriginId || view.sessions[0].sessionOriginId } }));
341
+ return;
342
+ }
336
343
  if (action === 'favorite' || action === 'folder') { organizeViewAction(view, action); return; }
337
344
  if (action === "rename") { startRename(root, view); return; }
338
345
  if (action === "separate") {
@@ -418,13 +425,7 @@ function onClosePane(event) {
418
425
  if (!view || !event.detail || !validPresentationClose(event.detail)) return;
419
426
  var session = (getCachedSessions() || []).find(function (item) { return item.id === Number(event.detail.sessionId); });
420
427
  if (!session || !view.sessions.some(function (item) { return item.sessionOriginId === session.sessionOriginId; })) return;
421
- try { checkMountedPaneDrafts(); } catch (error) { showToast(error.message, "warn"); return; }
422
- var origins = view.sessions.map(function (item) { return item.sessionOriginId; }).filter(function (origin) { return origin !== session.sessionOriginId; });
423
- var nextTree = remove(view.tree, session.sessionOriginId);
424
- if (!validateTree(nextTree, origins).ok) return;
425
- var nextActive = view.activeSessionOriginId === session.sessionOriginId ? origins[0] || null : view.activeSessionOriginId;
426
- mutation("project_split_view_update", { id: view.id, sessionOrigins: origins, tree: nextTree,
427
- layout: view.layout, activeSessionOriginId: nextActive }, { closedOrigin: session.sessionOriginId, activeOrigin: nextActive });
428
+ removeViewSession(view, session.sessionOriginId);
428
429
  }
429
430
  function onFocus(event) {
430
431
  var view = viewExists(store.get('projectSplitViews'), store.get('projectSplitViewActiveId'));
@@ -25,8 +25,9 @@ export function openSplitViewMenu(group, anchor, point, onAction) {
25
25
  }
26
26
  closeFolderMenu(false); closeSessionContextMenu(false);
27
27
  var menu = document.createElement('div'); menu.className = 'session-ctx-menu split-view-menu';
28
- [['favorite', view.favorite ? 'Remove from Favorites' : 'Add to Favorites', 'star'], ['folder', 'Move to folder', 'folder-input'], ['rename', 'Rename', 'pencil'], ['separate', 'Separate', 'columns-2']].forEach(function (entry) {
28
+ [['add-pane', 'Add pane', 'panel-right-open'], ['favorite', view.favorite ? 'Remove from Favorites' : 'Add to Favorites', 'star'], ['folder', 'Move to folder', 'folder-input'], ['rename', 'Rename', 'pencil'], ['separate', 'Separate', 'columns-2']].forEach(function (entry) {
29
29
  var button = document.createElement('button'); button.className = 'session-ctx-item'; button.dataset.splitViewAction = entry[0];
30
+ if (entry[0] === 'add-pane') button.disabled = !view.sessions.length || view.sessions.length + (view.workspace && view.workspace.linkedPair ? view.workspace.linkedPair.workerOrigins.length : 0) >= 8 || !!store.get('projectSplitViewPending');
30
31
  button.innerHTML = iconHtml(entry[2]) + ' <span>' + entry[1] + '</span>';
31
32
  button.addEventListener('click', function (event) {
32
33
  event.stopPropagation(); var root = currentRoot(); closeSessionContextMenu(false);
@@ -4,6 +4,8 @@ import { store } from './store.js';
4
4
  import { closeSpeechPicker } from './stt-picker.js';
5
5
  import { loadSpeechSettings } from './stt-settings.js';
6
6
  import { mountDefaultAi, unmountDefaultAi, requestDefaultAi } from './default-ai.js';
7
+ import { mountAgentBehavior, unmountAgentBehavior } from './agent-behavior-view.js';
8
+ import { resetAgentBehavior } from './agent-behavior.js';
7
9
  import { resolvePage } from './settings-shell-registry.js';
8
10
 
9
11
  export function resolveUserSettingsPage(id) { return resolvePage('personal', id); }
@@ -29,6 +31,9 @@ export function buildUserSettingsLayout() {
29
31
  document.getElementById('us-auto-continue').closest('label').querySelector('.settings-hint').textContent = 'When a rate-limited conversation becomes eligible to retry, Clay can send a continuation automatically. This may resume work and consume usage without another message from you.';
30
32
  heading(ai, 'Command permissions', 'These are personal Claude CLI allow rules, separate from model selection and Clay session approval modes. Managed defaults are shown for reference. Server access restrictions still apply. On an OS-user server these install in your OS home; otherwise they install in the shared server process home and can affect other Claude CLI launches there.');
31
33
  ai.appendChild(document.getElementById('us-claude-allow-list').closest('.settings-card'));
34
+ var agent = document.createElement('div'); agent.className = 'us-section'; agent.dataset.section = 'us-agent';
35
+ agent.innerHTML = '<h2>Agent behavior</h2><p class="settings-hint">Choose how your Project Drivers use Clay memory, context, tools and connections. These choices apply to your own Driver conversations.</p><div id="us-agent-behavior"></div>';
36
+ root.querySelector('.us-modal-content').appendChild(agent);
32
37
  section('us-mates').remove();
33
38
  var experiments = document.createElement('details'); experiments.className = 'us-experiments'; experiments.innerHTML = '<summary>Experimental features</summary><p class="settings-hint">Personal opt-ins for features that may change.</p>';
34
39
  experiments.appendChild(document.getElementById('us-capsules-enabled').closest('.settings-card'));chat.appendChild(experiments);section('us-experimental').remove();
@@ -43,9 +48,11 @@ export function buildUserSettingsLayout() {
43
48
  export function loadUserSettingsExtras(page) {
44
49
  if (page === 'us-integrations') loadPersonalIntegrations();
45
50
  if (page === 'us-chat') loadSpeechSettings();
51
+ if (page === 'us-agent') mountAgentBehavior(document.getElementById('us-agent-behavior'));
46
52
  if (page === 'us-ai') { mountDefaultAi(document.getElementById('us-default-ai')); requestDefaultAi(); var ws=getWs();if(ws && ws.readyState===1)ws.send(JSON.stringify({type:'get_claude_allow_list'})); }
47
53
  }
48
- export function finishUserSettingsExtras() {closeSpeechPicker();unmountDefaultAi(document.getElementById('us-default-ai'));}
54
+ export function leaveAgentBehaviorPage() {unmountAgentBehavior();resetAgentBehavior();}
55
+ export function finishUserSettingsExtras() {leaveAgentBehaviorPage();closeSpeechPicker();unmountDefaultAi(document.getElementById('us-default-ai'));}
49
56
  // Returns the draft prompt for leaving one personal page (or every page when
50
57
  // pageId is omitted); the shell shows it with the shared confirmation.
51
58
  export function personalLeavePrompt(pageId) {
@@ -6,7 +6,7 @@ import { initPersonalProfile } from './user-settings-profile.js';
6
6
  import { initPersonalEditors } from './user-settings-editors.js';
7
7
  import { initPersonalPreferences } from './user-settings-preferences.js';
8
8
  import { showPinForm, hidePinForm, populateAccount, savePin, renderEmailSettings } from './user-settings-account.js';
9
- import { buildUserSettingsLayout, resolveUserSettingsPage, loadUserSettingsExtras, finishUserSettingsExtras, personalLeavePrompt } from './user-settings-layout.js';
9
+ import { buildUserSettingsLayout, resolveUserSettingsPage, loadUserSettingsExtras, finishUserSettingsExtras, leaveAgentBehaviorPage, personalLeavePrompt } from './user-settings-layout.js';
10
10
  import { enhanceSettingsSelects } from './settings-select.js';
11
11
  import { loadNotificationSettings } from './notification-settings.js';
12
12
  // user-settings.js — Personal settings pane of the unified settings shell
@@ -37,7 +37,7 @@ export function initUserSettings() {
37
37
  pane: settingsEl,
38
38
  enter: function () { populateAccount(); },
39
39
  activate: function (page) { switchSection(page); },
40
- leave: function () { resetAccountSessions(); finishUserSettingsExtras(); },
40
+ leave: function () { currentPage = null; resetAccountSessions(); finishUserSettingsExtras(); },
41
41
  busy: function () { return store.get('speechPopup') ? 'Finish or close the open voice settings editor first.' : ''; },
42
42
  leavePrompt: function (kind, pageId) { return personalLeavePrompt(kind === 'page' ? pageId : undefined); }
43
43
  });
@@ -159,8 +159,11 @@ export function isUserSettingsOpen() {
159
159
  return isSettingsShellOpen() && activeSettingsScope() === 'personal';
160
160
  }
161
161
 
162
+ var currentPage = null;
162
163
  function switchSection(sectionName) {
163
164
  sectionName = resolveUserSettingsPage(sectionName);
165
+ if (currentPage === 'us-agent' && sectionName !== 'us-agent') leaveAgentBehaviorPage();
166
+ currentPage = sectionName;
164
167
  loadUserSettingsExtras(sectionName);
165
168
  for (var j = 0; j < sections.length; j++) {
166
169
  sections[j].classList.toggle('active', sections[j].dataset.section === sectionName);
@@ -81,7 +81,7 @@ export function prepareViewMutation(type, message) {
81
81
  if (type === 'project_split_view_create' || type === 'project_split_view_update') {
82
82
  if (!message.workspace) message.workspace = recomposeWorkspace(view && view.workspace, message.tree, message.activeSessionOriginId);
83
83
  message.tree = sessionTree(message.workspace);
84
- message.active = true;
84
+ message.active = message.active !== false;
85
85
  }
86
86
  if (type === 'project_split_view_move') {
87
87
  var target = views.find(function (item) { return item.id === message.targetViewId; });