clay-server 5.2.0-beta.4 → 5.2.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 (171) hide show
  1. package/lib/account-auth-socket.js +1 -0
  2. package/lib/builtin-mates.js +17 -14
  3. package/lib/canonical-clay-record-authority.js +15 -0
  4. package/lib/canonical-clay-scheduling-authority.js +19 -0
  5. package/lib/claude-hook-installer.js +28 -0
  6. package/lib/clay-mate-chat-receipts.js +26 -0
  7. package/lib/clay-mate-chat.js +87 -0
  8. package/lib/clay-mate-knowledge-input.js +26 -0
  9. package/lib/clay-mate-knowledge.js +95 -0
  10. package/lib/clay-mate-lifecycle.js +95 -0
  11. package/lib/clay-product-control-input.js +33 -0
  12. package/lib/clay-product-control-tools.js +51 -0
  13. package/lib/clay-product-control.js +237 -0
  14. package/lib/clay-project-lifecycle.js +154 -0
  15. package/lib/clay-project-record-input.js +44 -0
  16. package/lib/clay-project-records.js +83 -0
  17. package/lib/clay-scheduling-input.js +21 -0
  18. package/lib/clay-scheduling-receipts.js +26 -0
  19. package/lib/clay-scheduling.js +119 -0
  20. package/lib/clay-screen-controls.js +96 -0
  21. package/lib/clay-workspace-controls.js +83 -0
  22. package/lib/clay-workspace-input.js +17 -0
  23. package/lib/clay-workspace-receipts.js +26 -0
  24. package/lib/daemon-project-access.js +11 -0
  25. package/lib/daemon-project-lifecycle-callbacks.js +15 -0
  26. package/lib/daemon-project-lifecycle.js +262 -0
  27. package/lib/daemon-projects.js +1 -1
  28. package/lib/daemon.js +73 -221
  29. package/lib/issues-service.js +29 -15
  30. package/lib/knowledge-db-import.js +5 -4
  31. package/lib/knowledge-db-service.js +9 -6
  32. package/lib/knowledge-document-index.js +2 -1
  33. package/lib/knowledge-product-operations.js +46 -0
  34. package/lib/mate-creation-mcp-server.js +2 -0
  35. package/lib/mate-knowledge-canonical-access.js +10 -0
  36. package/lib/mate-knowledge-service.js +7 -4
  37. package/lib/mate-lifecycle-service.js +94 -0
  38. package/lib/mate-message-delivery.js +43 -0
  39. package/lib/mate-ready-creation.js +9 -1
  40. package/lib/mate-schedule-input.js +29 -0
  41. package/lib/mate-schedule-lifecycle.js +151 -0
  42. package/lib/mate-scheduling.js +103 -0
  43. package/lib/mates.js +4 -3
  44. package/lib/notes-checklist.js +50 -0
  45. package/lib/notes.js +49 -3
  46. package/lib/os-users.js +20 -5
  47. package/lib/project-admin-overview.js +51 -0
  48. package/lib/project-ask-user.js +4 -2
  49. package/lib/project-delegated-follow-up.js +2 -1
  50. package/lib/project-directory-claim.js +65 -0
  51. package/lib/project-file-operations.js +61 -0
  52. package/lib/project-filesystem.js +13 -87
  53. package/lib/project-full-access.js +23 -2
  54. package/lib/project-knowledge.js +32 -18
  55. package/lib/project-lifecycle-operations.js +98 -0
  56. package/lib/project-lifecycle-process-witness.js +32 -0
  57. package/lib/project-lifecycle-runtime.js +50 -0
  58. package/lib/project-linux-access.js +22 -0
  59. package/lib/project-logs-canonical-clay.js +27 -0
  60. package/lib/project-logs-clay-query.js +16 -0
  61. package/lib/project-logs-schema.js +4 -2
  62. package/lib/project-logs-service.js +8 -7
  63. package/lib/project-logs.js +1 -0
  64. package/lib/project-loop.js +1 -0
  65. package/lib/project-mate-chat-control.js +55 -0
  66. package/lib/project-mate-creation-proposal.js +18 -3
  67. package/lib/project-models.js +14 -31
  68. package/lib/project-notifications.js +74 -16
  69. package/lib/project-ownership.js +77 -0
  70. package/lib/project-pair-result-delivery.js +14 -3
  71. package/lib/project-pending-message-queue.js +1 -0
  72. package/lib/project-product-control.js +87 -0
  73. package/lib/project-product-session-queue.js +46 -0
  74. package/lib/project-push-access.js +28 -0
  75. package/lib/project-scheduled-message-queue.js +30 -0
  76. package/lib/project-scheduled-messages.js +63 -64
  77. package/lib/project-scheduled-task-control.js +83 -0
  78. package/lib/project-scheduled-tasks.js +13 -2
  79. package/lib/project-scheduling-access.js +10 -0
  80. package/lib/project-session-actions.js +65 -0
  81. package/lib/project-session-delete.js +8 -1
  82. package/lib/project-session-github.js +47 -12
  83. package/lib/project-session-history-actions.js +165 -0
  84. package/lib/project-session-runtime-actions.js +63 -0
  85. package/lib/project-session-vendor.js +54 -0
  86. package/lib/project-sessions.js +64 -241
  87. package/lib/project-user-message.js +30 -1
  88. package/lib/project-workspace-operations.js +126 -0
  89. package/lib/project-workspace-query.js +9 -2
  90. package/lib/project.js +95 -3
  91. package/lib/public/css/clay-tool-activity.css +51 -0
  92. package/lib/public/css/home-mate-creation.css +5 -0
  93. package/lib/public/css/overlays.css +4 -2
  94. package/lib/public/css/project-admin-overview.css +13 -0
  95. package/lib/public/css/session-folders.css +11 -0
  96. package/lib/public/css/session-github.css +3 -0
  97. package/lib/public/css/sticky-notes.css +61 -1
  98. package/lib/public/index.html +1 -1
  99. package/lib/public/modules/admin-settings-projects.js +6 -2
  100. package/lib/public/modules/app-connection.js +8 -1
  101. package/lib/public/modules/app-message-router.js +1 -1
  102. package/lib/public/modules/app-messages.js +18 -3
  103. package/lib/public/modules/app-projects.js +5 -0
  104. package/lib/public/modules/clay-owner-navigation.js +27 -0
  105. package/lib/public/modules/clay-tool-activity.js +140 -0
  106. package/lib/public/modules/clay-tool-presentation.js +338 -0
  107. package/lib/public/modules/command-palette.js +1 -1
  108. package/lib/public/modules/default-ai.js +11 -3
  109. package/lib/public/modules/home-debate-planning.js +5 -5
  110. package/lib/public/modules/home-mate-chat.js +8 -6
  111. package/lib/public/modules/home-mate-creation.js +73 -1
  112. package/lib/public/modules/personal-settings-dialog.js +7 -1
  113. package/lib/public/modules/project-access-recovery.js +17 -0
  114. package/lib/public/modules/project-admin-overview.js +21 -0
  115. package/lib/public/modules/session-folder-layout.js +1 -1
  116. package/lib/public/modules/session-folder-view-menu.js +9 -0
  117. package/lib/public/modules/session-github.js +65 -8
  118. package/lib/public/modules/sidebar-session-folders-ui.js +1 -1
  119. package/lib/public/modules/sticky-note-markdown.js +72 -6
  120. package/lib/public/modules/sticky-notes-actions.js +30 -0
  121. package/lib/public/modules/sticky-notes-browser.js +9 -2
  122. package/lib/public/modules/sticky-notes-card.js +23 -7
  123. package/lib/public/modules/sticky-notes-checklist.js +71 -0
  124. package/lib/public/modules/sticky-notes-editor.js +108 -44
  125. package/lib/public/modules/sticky-notes-shared.js +50 -2
  126. package/lib/public/modules/sticky-notes-state.js +66 -0
  127. package/lib/public/modules/sticky-notes.js +34 -51
  128. package/lib/public/modules/thinking-lifecycle.js +33 -3
  129. package/lib/public/modules/thinking-view.js +23 -1
  130. package/lib/public/modules/tools.js +34 -6
  131. package/lib/public/modules/user-settings-appearance.js +10 -3
  132. package/lib/public/modules/user-settings-editors.js +1 -1
  133. package/lib/public/modules/user-settings-layout.js +11 -2
  134. package/lib/public/modules/user-settings-preferences.js +6 -2
  135. package/lib/public/modules/user-settings-profile.js +12 -2
  136. package/lib/public/modules/user-settings.js +3 -2
  137. package/lib/public/style.css +2 -0
  138. package/lib/scheduled-task-records.js +3 -1
  139. package/lib/scheduler.js +2 -0
  140. package/lib/sdk-bridge.js +220 -21
  141. package/lib/sdk-message-processor.js +2 -0
  142. package/lib/sdk-response-recovery.js +155 -0
  143. package/lib/server-admin.js +63 -16
  144. package/lib/server-dm.js +1 -1
  145. package/lib/server-global-ws.js +10 -20
  146. package/lib/server-home-chat-events.js +7 -3
  147. package/lib/server-home-chat.js +4 -16
  148. package/lib/server-home-mate-creation.js +1 -0
  149. package/lib/server-home-mcp-consent.js +3 -3
  150. package/lib/server-mates.js +25 -67
  151. package/lib/server-owner-ui.js +77 -0
  152. package/lib/server-path-access.js +38 -0
  153. package/lib/server-project-removal.js +22 -0
  154. package/lib/server.js +99 -29
  155. package/lib/session-config-transition.js +45 -0
  156. package/lib/session-folders.js +7 -2
  157. package/lib/session-github.js +9 -2
  158. package/lib/session-visibility.js +5 -2
  159. package/lib/sessions.js +10 -0
  160. package/lib/terminal-manager.js +52 -0
  161. package/lib/users-permissions.js +2 -8
  162. package/lib/users.js +4 -0
  163. package/lib/workspace-query-access.js +4 -1
  164. package/lib/ws-schema.js +8 -2
  165. package/lib/yoke/adapters/claude.js +47 -8
  166. package/lib/yoke/adapters/codex.js +52 -111
  167. package/lib/yoke/claude-worker-startup.js +28 -0
  168. package/lib/yoke/codex-idle-recovery.js +81 -1
  169. package/lib/yoke/codex-recovery-admission.js +164 -0
  170. package/lib/yoke/session-tool-transport.js +3 -0
  171. package/package.json +1 -1
@@ -1,3 +1,5 @@
1
+ import { clayToolGroupSummary } from './clay-tool-presentation.js';
2
+ import { applyClayToolItem, applyClayToolResult, finishClayToolItem, findEndedClayTool, clayToolResultGroup, bindToolGroupDisclosure, syncToolGroupDisclosure, syncClayToolGroup } from './clay-tool-activity.js';
1
3
  import { escapeHtml, copyToClipboard } from './utils.js';
2
4
  import { iconHtml, refreshIcons } from './icons.js';
3
5
  import { renderMarkdown, highlightCodeBlocks, renderMermaidBlocks } from './markdown.js';
@@ -76,6 +78,8 @@ function findToolGroup(groupId) {
76
78
  }
77
79
 
78
80
  function toolGroupSummary(group) {
81
+ var claySummary = clayToolGroupSummary(group, tools);
82
+ if (claySummary) return claySummary;
79
83
  var names = group.toolNames;
80
84
  var count = names.length;
81
85
  var allDone = group.doneCount >= count;
@@ -167,6 +171,8 @@ function updateToolGroupHeader(group) {
167
171
  header.style.display = "none";
168
172
  group.el.classList.remove("collapsed");
169
173
  }
174
+ syncToolGroupDisclosure(group.el);
175
+ syncClayToolGroup(group);
170
176
  }
171
177
 
172
178
  function isPlanFile(filePath) {
@@ -1628,9 +1634,9 @@ export function createToolItem(id, name) {
1628
1634
  }
1629
1635
 
1630
1636
  applyProjectAssistantIdentity(groupEl, identity);
1631
- groupEl.querySelector(".tool-group-header").addEventListener("click", function () {
1632
- groupEl.classList.toggle("collapsed");
1633
- if (currentToolGroup) currentToolGroup.userToggled = true;
1637
+ bindToolGroupDisclosure(groupEl, function () {
1638
+ var group = findToolGroup(groupEl.dataset.groupId);
1639
+ if (group) group.userToggled = true;
1634
1640
  });
1635
1641
 
1636
1642
  ctx.addToMessages(groupEl);
@@ -1640,6 +1646,8 @@ export function createToolItem(id, name) {
1640
1646
  el: groupEl,
1641
1647
  id: "g" + toolGroupCounter,
1642
1648
  toolNames: [],
1649
+ toolIds: [],
1650
+ toolRecords: {},
1643
1651
  toolCount: 0,
1644
1652
  doneCount: 0,
1645
1653
  errorCount: 0,
@@ -1666,17 +1674,21 @@ export function createToolItem(id, name) {
1666
1674
 
1667
1675
  el.querySelector(".tool-name").textContent = name;
1668
1676
 
1677
+ tools[id] = { el: el, name: name, input: null, done: false, groupId: currentToolGroup.id };
1678
+ applyClayToolItem(tools[id]);
1679
+
1669
1680
  // Append to group instead of messages directly
1670
1681
  currentToolGroup.el.querySelector(".tool-group-items").appendChild(el);
1671
1682
  currentToolGroup.toolNames.push(name);
1683
+ currentToolGroup.toolIds.push(id);
1684
+ currentToolGroup.toolRecords[id] = tools[id];
1672
1685
  currentToolGroup.toolCount++;
1673
1686
  updateToolGroupHeader(currentToolGroup);
1674
1687
 
1675
1688
  refreshIcons();
1676
1689
  maybeScrollToBottom();
1677
1690
 
1678
- tools[id] = { el: el, name: name, input: null, done: false, groupId: currentToolGroup.id };
1679
- ctx.setActivity("Running " + name + "...");
1691
+ ctx.setActivity(tools[id].clayActivity ? tools[id].clayActivity.label : "Running " + name + "...");
1680
1692
  }
1681
1693
 
1682
1694
  export function updateToolExecuting(id, name, input) {
@@ -1684,6 +1696,12 @@ export function updateToolExecuting(id, name, input) {
1684
1696
  if (!tool) return;
1685
1697
 
1686
1698
  tool.input = input;
1699
+ if (applyClayToolItem(tool)) {
1700
+ ctx.setActivity(tool.clayActivity.label);
1701
+ updateToolGroupHeader(findToolGroup(tool.groupId));
1702
+ maybeScrollToBottom();
1703
+ return;
1704
+ }
1687
1705
  var descEl = tool.el.querySelector(".tool-desc");
1688
1706
  descEl.textContent = toolSummary(name, input);
1689
1707
 
@@ -1893,9 +1911,17 @@ function isImagePath(filePath) {
1893
1911
  }
1894
1912
 
1895
1913
  export function updateToolResult(id, content, isError, images) {
1896
- var tool = tools[id];
1914
+ var tool = tools[id] || findEndedClayTool(ctx.messagesEl, id);
1897
1915
  if (!tool) return;
1898
1916
 
1917
+ if (applyClayToolResult(tool, content, isError)) {
1918
+ markToolDone(id, tool.clayActivity.phase === "error" || tool.clayActivity.phase === "cancelled");
1919
+ applyClayToolItem(tool);
1920
+ updateToolGroupHeader(clayToolResultGroup(tool, findToolGroup(tool.groupId)));
1921
+ maybeScrollToBottom();
1922
+ return;
1923
+ }
1924
+
1899
1925
  // Re-render the sublabel from the now-complete input (streaming can have
1900
1926
  // built it from a partial one), but in its terminal form: a result has
1901
1927
  // arrived, so this call is no longer running.
@@ -2026,6 +2052,7 @@ export function markToolDone(id, isError) {
2026
2052
  icon.innerHTML = '<span class="check">' + iconHtml("check") + '</span>';
2027
2053
  }
2028
2054
  refreshIcons();
2055
+ finishClayToolItem(tool, findToolGroup(tool.groupId));
2029
2056
 
2030
2057
  // Update group state
2031
2058
  if (tool.groupId) {
@@ -2244,6 +2271,7 @@ export function removeToolFromGroup(toolId) {
2244
2271
  var group = findToolGroup(tool.groupId);
2245
2272
  if (!group) return;
2246
2273
  group.toolCount--;
2274
+ if (group.toolIds) group.toolIds = group.toolIds.filter(function (id) { return id !== toolId; });
2247
2275
  // Remove tool name from the names array (remove first occurrence)
2248
2276
  var idx = group.toolNames.indexOf(tool.name);
2249
2277
  if (idx !== -1) group.toolNames.splice(idx, 1);
@@ -31,7 +31,11 @@ function addTerminalTypography(section) {
31
31
  var option = document.createElement('option'); option.value = font.family; option.textContent = font.label; family.appendChild(option);
32
32
  });
33
33
  function sync() {
34
- if (store.get('userTerminalDirty')) return;
34
+ if (store.get('userTerminalDirty')) {
35
+ var dirty = family.value !== getTerminalFontFamily() || size.value !== String(getTerminalFontSize());
36
+ store.set({userTerminalDirty:dirty});
37
+ if (dirty) return;
38
+ }
35
39
  var value = getTerminalFontFamily();
36
40
  if (!Array.from(family.options).some(function (option) { return option.value === value; })) {
37
41
  var option = document.createElement('option'); option.value = value; option.textContent = 'Custom font'; family.appendChild(option);
@@ -41,7 +45,10 @@ function addTerminalTypography(section) {
41
45
  family.dispatchEvent(new Event('settings-sync'));
42
46
  }
43
47
  async function save() {
44
- var value = Number(size.value); store.set({ userTerminalDirty: true });
48
+ var dirty = family.value !== getTerminalFontFamily() || size.value !== String(getTerminalFontSize());
49
+ store.set({userTerminalDirty:dirty});
50
+ if (!dirty) {retry.hidden = true;status.textContent = '';status.dataset.state = '';return;}
51
+ var value = Number(size.value);
45
52
  if (!Number.isInteger(value) || value < 9 || value > 32) { status.textContent = 'Choose a whole size from 9 to 32.'; status.dataset.state = 'error'; return; }
46
53
  family.disabled = true; size.disabled = true; retry.hidden = true; status.textContent = 'Saving…'; status.dataset.state = '';
47
54
  try {
@@ -50,6 +57,6 @@ function addTerminalTypography(section) {
50
57
  finally { family.disabled = false; size.disabled = false; family.dispatchEvent(new Event('settings-sync')); }
51
58
  }
52
59
  family.onchange = save; size.onchange = save; retry.onclick = save;
53
- size.oninput = function () { store.set({ userTerminalDirty: true }); };
60
+ size.oninput = function () { store.set({ userTerminalDirty: family.value !== getTerminalFontFamily() || size.value !== String(getTerminalFontSize()) }); };
54
61
  onTerminalFontChange(sync); sync(); enhanceSettingsSelects(content);
55
62
  }
@@ -19,6 +19,6 @@ function wrapEditor(content, title, summary) {
19
19
  }
20
20
  export function initPersonalEditors() {
21
21
  var allow=document.getElementById('us-claude-allow-list');var permission=wrapEditor(allow.closest('.settings-card'),'Personal allow rules',function(){var count=(store.get('userAllowSaved') || []).length;return count+' personal rules. Managed defaults apply separately.';});
22
- permission.done.textContent='Cancel';permission.done.onclick=function(){function cancel(){allow.value=(store.get('userAllowSaved') || []).join('\n');store.set({userAllowDirty:false});permission.content.hidden=true;permission.state.hidden=false;permission.edit.hidden=false;permission.edit.focus();}if(store.get('userAllowDirty'))showConfirmDialog('Discard unsaved command rules?', 'Discard',cancel);else cancel();};
22
+ permission.done.textContent='Cancel';permission.done.onclick=function(){function cancel(){allow.value=(store.get('userAllowSaved') || []).join('\n');store.set({userAllowDirty:false,userAllowNeedsRetry:false});permission.content.hidden=true;permission.state.hidden=false;permission.edit.hidden=false;permission.edit.focus();}if(store.get('userAllowDirty'))showConfirmDialog('Discard unsaved command rules?', 'Discard',cancel);else cancel();};
23
23
  store.subscribe(function(state,previous){if(state.userAllowSaved!==previous.userAllowSaved){permission.state.textContent=(state.userAllowSaved || []).length+' personal rules. Managed defaults apply separately.';if(previous.userAllowDirty && !state.userAllowDirty){permission.content.hidden=true;permission.state.hidden=false;permission.edit.hidden=false;permission.edit.focus();}}});
24
24
  }
@@ -69,12 +69,21 @@ export function loadUserSettingsExtras(page) {
69
69
  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'})); }
70
70
  }
71
71
  export function finishUserSettingsExtras() {closeSpeechPicker();unmountDefaultAi(document.getElementById('us-default-ai'));}
72
- export function guardUserSettingsLeave(done) {
72
+ export function guardUserSettingsLeave(done, destination) {
73
+ var active = document.querySelector('#user-settings .us-section.active');
74
+ var page = active && active.dataset.section;
75
+ if (destination && resolveUserSettingsPage(destination) === page) {done();return;}
76
+ function leaving(id) {return !destination || page === id;}
73
77
  var pin = document.getElementById('us-pin-form');
74
78
  var pinDirty = pin && !pin.classList.contains('hidden') && Array.from(pin.querySelectorAll('input')).some(function (input) {return !!input.value;});
75
79
  var key = document.getElementById('us-linear-key');
76
80
  if(store.get('speechPopup'))return;
77
- var dirty = store.get('personalEmailDirty') || store.get('personalLinearDirty') || store.get('userVoiceDirty') || store.get('notificationDraft') || store.get('appearanceSaveError') || store.get('userProfileDirty') || store.get('userTerminalDirty') || pinDirty || key && key.value || store.get('defaultAiDraftDirty') || store.get('userAllowDirty');
81
+ var dirty = leaving('us-integrations') && (store.get('personalEmailDirty') || store.get('personalLinearDirty') || key && key.value) ||
82
+ leaving('us-chat') && store.get('userVoiceDirty') ||
83
+ leaving('us-notifications') && store.get('notificationDraft') ||
84
+ leaving('us-appearance') && (store.get('appearanceDraft') || store.get('userTerminalDirty')) ||
85
+ leaving('us-account') && (store.get('userProfileDirty') || pinDirty) ||
86
+ leaving('us-ai') && (store.get('defaultAiDraftDirty') || store.get('userAllowDirty'));
78
87
  if (!dirty) {done();return;}
79
88
  showConfirmDialog('Your unsaved changes will remain in this tab. Leave this editor and keep the draft?', 'Keep draft and leave', done);
80
89
  }
@@ -27,7 +27,10 @@ export function initPersonalPreferences() {
27
27
  catch(error){preferenceStatus('us-layout-switcher',error.message+' Try again.',true);}
28
28
  finally{controls.forEach(function(el){el.disabled=false;});}
29
29
  };});
30
- var textarea=document.getElementById('us-claude-allow-list');textarea.oninput=function(){store.set({userAllowDirty:true});};
30
+ var textarea=document.getElementById('us-claude-allow-list');textarea.oninput=function(){
31
+ var patterns=textarea.value.split('\n').map(function(line){return line.trim();}).filter(Boolean);
32
+ store.set({userAllowDirty:!!store.get('userAllowNeedsRetry') || JSON.stringify(patterns)!==JSON.stringify(store.get('userAllowSaved') || [])});
33
+ };
31
34
  document.getElementById('us-claude-allow-save').onclick=function(){
32
35
  var ws=getWs();if(!ws || ws.readyState!==1){preferenceStatus('us-claude-allow-list','Disconnected. Your draft is retained.',true);return;}
33
36
  this.disabled=true;preferenceStatus('us-claude-allow-list','Saving…');ws.send(JSON.stringify({type:'set_claude_user_allow_list',patterns:textarea.value.split('\n').map(function(line){return line.trim();}).filter(Boolean)}));
@@ -37,11 +40,12 @@ export function initPersonalPreferences() {
37
40
  export function handlePersonalSettingsMessage(msg) {
38
41
  if(msg.type==='claude_allow_list') {
39
42
  store.set({userAllowSaved:msg.user || []});
43
+ if(store.get('userAllowDirty'))document.getElementById('us-claude-allow-list').oninput();
40
44
  if(store.get('userAllowDirty'))return;
41
45
  }
42
46
  if(msg.type==='set_claude_user_allow_list_result') {
43
47
  document.getElementById('us-claude-allow-save').disabled=false;
44
- if(msg.ok)store.set({userAllowDirty:msg.effective===false,userAllowSaved:msg.patterns || []});
48
+ if(msg.ok)store.set({userAllowDirty:msg.effective===false,userAllowNeedsRetry:msg.effective===false,userAllowSaved:msg.patterns || []});
45
49
  var message=msg.ok ? (msg.effective===false ? 'Saved to your account, but not installed in Claude. Check filesystem access, then Save to retry.' : 'Saved. Applies on the next Claude invocation.') : msg.error || 'Save failed. Your draft is retained.';
46
50
  preferenceStatus('us-claude-allow-list',message,!msg.ok || msg.effective===false);
47
51
  }
@@ -13,10 +13,20 @@ export function initPersonalProfile() {
13
13
  name.after(form); var input = form.querySelector('input');
14
14
  function close() { form.hidden = true; name.hidden = false; edit.hidden = false; edit.focus(); }
15
15
  edit.onclick = function () {
16
- if (!store.get('userProfileDirty')) input.value = getProfile().name || name.textContent;
16
+ if (!store.get('userProfileDirty')) {
17
+ input.value = name.textContent || getProfile().name;
18
+ store.set({userProfileSavedName:input.value.trim()});
19
+ }
17
20
  form.hidden = false; name.hidden = true; edit.hidden = true; input.focus(); input.select();
18
21
  };
19
- input.oninput = function () { store.set({ userProfileDirty: true }); };
22
+ input.oninput = function () { store.set({ userProfileDirty: input.value.trim() !== store.get('userProfileSavedName') }); };
23
+ store.subscribe(function (state, previous) {
24
+ if (state.userSettingsProfile === previous.userSettingsProfile || !state.userSettingsProfile) return;
25
+ var data = state.userSettingsProfile;
26
+ var saved = data.name || data.displayName || data.username || 'Clay User';
27
+ if (!state.userProfileDirty) input.value = saved;
28
+ store.set({userProfileSavedName:saved.trim(), userProfileDirty:input.value.trim() !== saved.trim()});
29
+ });
20
30
  form.onsubmit = async function (event) {
21
31
  event.preventDefault(); var save = form.querySelector('[type=submit]'); var status = form.querySelector('[role=status]');
22
32
  if (!input.value.trim()) { status.textContent = 'Enter a display name.'; return; }
@@ -75,7 +75,7 @@ export function initUserSettings() {
75
75
  for (var i = 0; i < navItems.length; i++) {
76
76
  navItems[i].addEventListener('click', function () {
77
77
  var section = this.dataset.section;
78
- guardUserSettingsLeave(function () { switchSection(section); });
78
+ guardUserSettingsLeave(function () { switchSection(section); }, section);
79
79
  });
80
80
  }
81
81
 
@@ -83,7 +83,8 @@ export function initUserSettings() {
83
83
  var navDropdown = document.getElementById('user-settings-nav-dropdown');
84
84
  if (navDropdown) {
85
85
  navDropdown.addEventListener('change', function () {
86
- var value = this.value; guardUserSettingsLeave(function () {switchSection(value);});
86
+ var value = this.value; guardUserSettingsLeave(function () {switchSection(value);}, value);
87
+ this.value = settingsEl.querySelector('.us-section.active').dataset.section;
87
88
  });
88
89
  }
89
90
 
@@ -88,3 +88,5 @@
88
88
  @import url("css/server-settings-system.css");
89
89
 
90
90
  @import url("css/project-settings-modal.css");
91
+
92
+ @import url("css/clay-tool-activity.css");
@@ -18,6 +18,7 @@ function createScheduledTaskRecords(ctx) {
18
18
  var runs = Array.isArray(record.runs) ? record.runs : [];
19
19
  var readiness = ctx.runtimeStatus ? ctx.runtimeStatus(record) : null;
20
20
  return {
21
+ createdBy: record.createdBy || null, updatedBy: record.updatedBy || null,
21
22
  id: record.id, revision: Number(record.updatedAt || 0), name: record.name || "",
22
23
  instructions: record.task || record.prompt || record.instructions || "",
23
24
  cron: record.cron || null, date: record.date || null, time: record.time || null,
@@ -49,7 +50,7 @@ function createScheduledTaskRecords(ctx) {
49
50
  return { ok: true, record: publicRecord(record) };
50
51
  }
51
52
 
52
- function update(session, id, revision, patch) {
53
+ function update(session, id, revision, patch, actor) {
53
54
  if (typeof id !== "string" || !ID_PATTERN.test(id)) return { ok: false, error: "Scheduled task id is invalid." };
54
55
  var record = ctx.registry.getById(id);
55
56
  if (!record) return { ok: false, error: "Scheduled task not found." };
@@ -94,6 +95,7 @@ function createScheduledTaskRecords(ctx) {
94
95
  }
95
96
  try {
96
97
  var updateData = Object.assign({}, checked.value);
98
+ if (actor) updateData.updatedBy = actor;
97
99
  if (claimOwner) { updateData.ownerId = session.ownerId; updateData.enabled = true; }
98
100
  if (updateData.cron) {
99
101
  updateData.date = record.cron && !record.scheduleRule ? record.date || null : null;
package/lib/scheduler.js CHANGED
@@ -239,6 +239,7 @@ function createLoopRegistry(opts) {
239
239
 
240
240
  function register(data) {
241
241
  var rec = {
242
+ createdBy: data.createdBy || null, updatedBy: data.updatedBy || null, creationFingerprint: data.creationFingerprint || null,
242
243
  id: data.id || ("loop_" + Date.now() + "_" + require("crypto").randomBytes(3).toString("hex")),
243
244
  name: data.name || "Untitled",
244
245
  task: data.task || "",
@@ -297,6 +298,7 @@ function createLoopRegistry(opts) {
297
298
  if (!rec) return null;
298
299
  var previous = Object.assign({}, rec);
299
300
 
301
+ if (data.updatedBy !== undefined) rec.updatedBy = data.updatedBy;
300
302
  if (data.name !== undefined) rec.name = data.name;
301
303
  if (data.cron !== undefined) rec.cron = data.cron;
302
304
  if (data.enabled !== undefined) rec.enabled = data.enabled;