clay-server 4.0.0-beta.2 → 4.0.0-beta.21

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 (177) hide show
  1. package/lib/background-task-timing.js +105 -0
  2. package/lib/capsule-display-floor.js +51 -0
  3. package/lib/capsule-frame-server.js +255 -0
  4. package/lib/capsule-mate-turn.js +105 -0
  5. package/lib/capsule-pig-logic.js +300 -0
  6. package/lib/capsule-server-runtimes.js +39 -0
  7. package/lib/capsules/pig/display.js +198 -0
  8. package/lib/capsules/pig/manifest.json +9 -0
  9. package/lib/capsules/pig/ui.json +84 -0
  10. package/lib/config.js +25 -1
  11. package/lib/daemon-projects.js +13 -8
  12. package/lib/daemon.js +127 -14
  13. package/lib/git-cli.js +95 -1
  14. package/lib/knowledge-import.js +436 -0
  15. package/lib/knowledge-record-store.js +188 -0
  16. package/lib/knowledge-search.js +240 -0
  17. package/lib/mate-knowledge-mcp-server.js +151 -0
  18. package/lib/mate-knowledge-migration.js +283 -0
  19. package/lib/mate-knowledge-service.js +488 -0
  20. package/lib/mate-knowledge-sync.js +482 -0
  21. package/lib/migrate-single-user.js +2 -0
  22. package/lib/notes-lifecycle.js +149 -0
  23. package/lib/notes.js +60 -3
  24. package/lib/os-users.js +3 -1
  25. package/lib/project-capsule-catalog.js +9 -1
  26. package/lib/project-capsule-turn.js +96 -0
  27. package/lib/project-connection.js +14 -1
  28. package/lib/project-http.js +15 -1
  29. package/lib/project-knowledge.js +22 -0
  30. package/lib/project-log-context.js +172 -0
  31. package/lib/project-log-feedback-delivery.js +118 -0
  32. package/lib/project-logs-comments.js +137 -0
  33. package/lib/project-logs-context-state.js +42 -0
  34. package/lib/project-logs-mcp-server.js +328 -0
  35. package/lib/project-logs-query.js +185 -0
  36. package/lib/project-logs-root.js +75 -0
  37. package/lib/project-logs-schema.js +259 -0
  38. package/lib/project-logs-service.js +463 -0
  39. package/lib/project-logs-snapshot.js +205 -0
  40. package/lib/project-logs-store.js +495 -0
  41. package/lib/project-logs-versioning.js +132 -0
  42. package/lib/project-logs.js +400 -0
  43. package/lib/project-mate-interaction.js +14 -0
  44. package/lib/project-mate-knowledge.js +110 -0
  45. package/lib/project-memory.js +22 -0
  46. package/lib/project-message-delivery.js +70 -0
  47. package/lib/project-pair-lifecycle.js +460 -0
  48. package/lib/project-session-handoff.js +1 -1
  49. package/lib/project-session-notes.js +64 -13
  50. package/lib/project-session-pair.js +159 -146
  51. package/lib/project-session-spawn.js +1 -1
  52. package/lib/project-sessions.js +58 -13
  53. package/lib/project-user-message.js +53 -4
  54. package/lib/project-vendor-login.js +20 -3
  55. package/lib/project-worker-permission.js +430 -0
  56. package/lib/project-worker-proposal.js +208 -62
  57. package/lib/project.js +178 -6
  58. package/lib/public/app.js +58 -35
  59. package/lib/public/css/admin.css +2 -2
  60. package/lib/public/css/capsule-ui.css +38 -0
  61. package/lib/public/css/command-palette.css +19 -9
  62. package/lib/public/css/git-placard.css +154 -0
  63. package/lib/public/css/home-hub.css +4 -0
  64. package/lib/public/css/home-session-actions.css +66 -0
  65. package/lib/public/css/home-sidebar.css +66 -0
  66. package/lib/public/css/input.css +163 -28
  67. package/lib/public/css/mobile-nav.css +80 -0
  68. package/lib/public/css/notifications-center.css +67 -0
  69. package/lib/public/css/pane.css +52 -0
  70. package/lib/public/css/project-logs.css +198 -0
  71. package/lib/public/css/scheduler.css +1 -1
  72. package/lib/public/css/session-actions.css +16 -101
  73. package/lib/public/css/sidebar.css +103 -0
  74. package/lib/public/css/sticky-notes.css +237 -229
  75. package/lib/public/css/worker-proposal.css +34 -1
  76. package/lib/public/index.html +45 -11
  77. package/lib/public/modules/admin.js +8 -8
  78. package/lib/public/modules/app-connection.js +109 -4
  79. package/lib/public/modules/app-home-hub.js +19 -7
  80. package/lib/public/modules/app-message-cards.js +2 -1
  81. package/lib/public/modules/app-messages.js +84 -7
  82. package/lib/public/modules/app-misc.js +2 -14
  83. package/lib/public/modules/app-notifications.js +11 -7
  84. package/lib/public/modules/app-panels.js +46 -4
  85. package/lib/public/modules/app-projects.js +13 -8
  86. package/lib/public/modules/app-rendering.js +1 -0
  87. package/lib/public/modules/background-tasks-ui.js +139 -16
  88. package/lib/public/modules/capsule-preference.js +44 -0
  89. package/lib/public/modules/chat-bubble-renderer.js +1 -1
  90. package/lib/public/modules/clay-log-links.js +70 -0
  91. package/lib/public/modules/command-palette.js +52 -15
  92. package/lib/public/modules/context-sources.js +0 -3
  93. package/lib/public/modules/git-agent-sessions.js +85 -0
  94. package/lib/public/modules/git-panel.js +111 -84
  95. package/lib/public/modules/git-placard.js +161 -0
  96. package/lib/public/modules/home-chat-empty-state.js +39 -0
  97. package/lib/public/modules/home-chat-identity.js +9 -0
  98. package/lib/public/modules/home-conversations-sheet.js +102 -48
  99. package/lib/public/modules/home-mate-chat.js +15 -35
  100. package/lib/public/modules/home-session-actions.js +6 -0
  101. package/lib/public/modules/home-sidebar-chat-list.js +101 -41
  102. package/lib/public/modules/home-sidebar.js +7 -0
  103. package/lib/public/modules/home-tool-frame.js +182 -0
  104. package/lib/public/modules/home-tools.js +138 -1
  105. package/lib/public/modules/input.js +38 -7
  106. package/lib/public/modules/markdown.js +2 -0
  107. package/lib/public/modules/mate-sidebar.js +0 -8
  108. package/lib/public/modules/message-delivery.js +97 -0
  109. package/lib/public/modules/paste-modal.js +84 -0
  110. package/lib/public/modules/project-logs-render.js +405 -0
  111. package/lib/public/modules/project-logs.js +461 -0
  112. package/lib/public/modules/project-removal-target.js +26 -0
  113. package/lib/public/modules/scheduler.js +17 -3
  114. package/lib/public/modules/session-actions.js +29 -105
  115. package/lib/public/modules/session-hierarchy.js +54 -0
  116. package/lib/public/modules/sidebar-mobile.js +40 -15
  117. package/lib/public/modules/sidebar-session-hierarchy.js +214 -0
  118. package/lib/public/modules/sidebar-sessions.js +77 -11
  119. package/lib/public/modules/sidebar.js +17 -5
  120. package/lib/public/modules/split-group-helpers.js +8 -0
  121. package/lib/public/modules/split-view.js +5 -2
  122. package/lib/public/modules/sticky-note-markdown.js +5 -0
  123. package/lib/public/modules/sticky-notes-browser.js +326 -0
  124. package/lib/public/modules/sticky-notes-card.js +360 -0
  125. package/lib/public/modules/sticky-notes-editor.js +291 -0
  126. package/lib/public/modules/sticky-notes-shared.js +90 -0
  127. package/lib/public/modules/sticky-notes.js +170 -946
  128. package/lib/public/modules/tool-palette-order.js +143 -0
  129. package/lib/public/modules/tool-palette-overlays.js +159 -0
  130. package/lib/public/modules/tool-palette.js +21 -188
  131. package/lib/public/modules/tools.js +21 -1
  132. package/lib/public/modules/update-snooze.js +169 -0
  133. package/lib/public/modules/user-settings.js +4 -0
  134. package/lib/public/modules/worker-pane-lock.js +176 -0
  135. package/lib/public/modules/worker-proposal-state.js +16 -0
  136. package/lib/public/modules/worker-proposal.js +49 -12
  137. package/lib/public/style.css +3 -1
  138. package/lib/sdk-bridge.js +132 -46
  139. package/lib/sdk-message-processor.js +60 -14
  140. package/lib/server-experimental-settings.js +49 -0
  141. package/lib/server-global-ws.js +11 -0
  142. package/lib/server-home-chat-events.js +31 -1
  143. package/lib/server-home-chat.js +27 -4
  144. package/lib/server-home-clay-entry.js +1 -1
  145. package/lib/server-home-clay-session-links.js +34 -5
  146. package/lib/server-settings.js +5 -0
  147. package/lib/server-tools.js +127 -9
  148. package/lib/server.js +124 -18
  149. package/lib/session-driver-eligibility.js +38 -0
  150. package/lib/session-driver-orchestration.js +31 -0
  151. package/lib/session-hygiene.js +3 -0
  152. package/lib/session-notes-mcp-server.js +25 -5
  153. package/lib/session-pair-factory.js +247 -0
  154. package/lib/session-pair-mcp-server.js +46 -8
  155. package/lib/session-pair-prompts.js +79 -0
  156. package/lib/session-pair-turn-control.js +153 -0
  157. package/lib/session-provenance.js +119 -0
  158. package/lib/session-spawn-mcp-server.js +1 -1
  159. package/lib/session-split-groups.js +4 -1
  160. package/lib/session-title-policy.js +60 -0
  161. package/lib/session-visibility.js +39 -0
  162. package/lib/sessions.js +61 -11
  163. package/lib/tools-registry.js +89 -10
  164. package/lib/update-snooze.js +437 -0
  165. package/lib/users-experimental-preferences.js +30 -0
  166. package/lib/users-permissions.js +2 -2
  167. package/lib/users.js +22 -16
  168. package/lib/workspace-query-access.js +102 -0
  169. package/lib/workspace-query-service.js +24 -17
  170. package/lib/worktree.js +25 -38
  171. package/lib/ws-schema.js +38 -7
  172. package/lib/yoke/adapters/claude-worker.js +16 -6
  173. package/lib/yoke/adapters/claude.js +81 -4
  174. package/lib/yoke/adapters/codex.js +63 -15
  175. package/lib/yoke/codex-background-tasks.js +8 -2
  176. package/lib/yoke/vendor-registry.js +1 -1
  177. package/package.json +3 -3
@@ -0,0 +1,39 @@
1
+ // home-chat-empty-state.js - The Home mate chat's empty conversation stage.
2
+
3
+ import { getHomeMateShortBio } from './home-mate-selection.js';
4
+
5
+ export function renderHomeChatEmptyState(messagesEl, suggestionsEl, mate, mateName, onSuggestion) {
6
+ var empty = document.createElement("div");
7
+ empty.className = "home-mate-chat-empty";
8
+ var brand = document.createElement("div");
9
+ brand.className = "home-mate-chat-brand";
10
+ var symbol = document.createElement("img");
11
+ symbol.src = "/clay-studio-symbol.png";
12
+ symbol.alt = "";
13
+ brand.appendChild(symbol);
14
+ var wordmark = document.createElement("span");
15
+ wordmark.className = "home-sidebar-brand-wordmark home-mate-chat-brand-wordmark";
16
+ wordmark.textContent = "Clay Studio";
17
+ brand.appendChild(wordmark);
18
+ empty.appendChild(brand);
19
+ var greeting = document.createElement("h2");
20
+ greeting.textContent = mate ? "What should we work on, " + mateName + "?" : "Getting Home ready...";
21
+ empty.appendChild(greeting);
22
+ var detail = document.createElement("p");
23
+ detail.textContent = mate ? getHomeMateShortBio(mate) : "Loading your Mate and recent conversation.";
24
+ empty.appendChild(detail);
25
+ messagesEl.appendChild(empty);
26
+ suggestionsEl.innerHTML = "";
27
+ if (!mate) return;
28
+ var suggestions = ["Make me a small tool"];
29
+ for (var i = 0; i < suggestions.length; i++) {
30
+ (function (suggestion) {
31
+ var chip = document.createElement("button");
32
+ chip.type = "button";
33
+ chip.className = "home-mate-chat-suggestion";
34
+ chip.textContent = suggestion;
35
+ chip.addEventListener("click", function () { onSuggestion(suggestion); });
36
+ suggestionsEl.appendChild(chip);
37
+ })(suggestions[i]);
38
+ }
39
+ }
@@ -33,3 +33,12 @@ export function createHomeOrdinaryTyping(mate, mateName) {
33
33
  }
34
34
  return row;
35
35
  }
36
+
37
+ // A Capsule engagement note: a quiet centered system line, not a speech
38
+ // bubble, because no one typed it.
39
+ export function createCapsuleTurnNote(message) {
40
+ var note = document.createElement("div");
41
+ note.className = "home-capsule-turn-note";
42
+ note.textContent = (message && message.text) || "";
43
+ return note;
44
+ }
@@ -3,12 +3,14 @@
3
3
  import { store } from './store.js';
4
4
  import { iconHtml, refreshIcons } from './icons.js';
5
5
  import { getHomeSessionConversations, createHomeSessionActionsTrigger, disposeHomeSessionActionsMenu } from './home-session-actions.js';
6
+ import { buildSessionHierarchy } from './session-hierarchy.js';
6
7
 
7
8
  var overlay = null;
8
9
  var searchInput = null;
9
10
  var listEl = null;
10
11
  var sheetOpener = null;
11
12
  var selectConversation = null;
13
+ var hierarchyExpansion = new Map();
12
14
 
13
15
  function closeSheet(restoreFocus) {
14
16
  if (!overlay) return;
@@ -54,66 +56,118 @@ function handleKeydown(event) {
54
56
  }
55
57
  }
56
58
 
59
+ function conversationMatches(conversation, query) {
60
+ if (!query) return true;
61
+ return conversation.title.toLowerCase().indexOf(query) !== -1 || conversation.mateName.toLowerCase().indexOf(query) !== -1;
62
+ }
63
+
64
+ function createSheetItem(conversation, worker) {
65
+ var item = document.createElement("div");
66
+ item.className = "home-conversations-sheet-item" + (worker ? " home-conversations-worker-item" : "");
67
+ var row = document.createElement("button");
68
+ row.type = "button";
69
+ row.className = "home-conversations-sheet-row";
70
+ var copy = document.createElement("span");
71
+ copy.className = "home-conversations-sheet-copy";
72
+ var title = document.createElement("span");
73
+ title.className = "home-conversations-sheet-row-title";
74
+ title.textContent = conversation.title;
75
+ copy.appendChild(title);
76
+ var mate = document.createElement("span");
77
+ mate.className = "home-conversations-sheet-row-mate";
78
+ mate.textContent = worker && conversation.workerGeneration ? conversation.mateName + " · Worker generation " + conversation.workerGeneration : conversation.mateName;
79
+ copy.appendChild(mate);
80
+ row.appendChild(copy);
81
+ if (conversation.isProcessing) {
82
+ var activity = document.createElement("span");
83
+ activity.className = "home-sidebar-processing";
84
+ activity.setAttribute("aria-label", "Processing");
85
+ row.appendChild(activity);
86
+ }
87
+ row.addEventListener("click", function () {
88
+ var onSelect = selectConversation;
89
+ closeSheet();
90
+ if (onSelect) onSelect(conversation.mateId, conversation.sessionId);
91
+ });
92
+ var active = conversation.mateId === store.get('homeChatMateId') && conversation.sessionId === store.get('homeChatSessionId');
93
+ if (active) {
94
+ item.classList.add("is-active");
95
+ row.setAttribute("aria-current", "page");
96
+ }
97
+ var detailsReturn = sheetOpener;
98
+ var actions = createHomeSessionActionsTrigger(conversation, {
99
+ detailsOpener: detailsReturn,
100
+ beforeOpenDetails: function () { closeSheet(false); },
101
+ });
102
+ item.appendChild(row);
103
+ item.appendChild(actions);
104
+ return item;
105
+ }
106
+
107
+ function createSheetHierarchy(root, query) {
108
+ if (!root.workers.length) return createSheetItem(root.driver, false);
109
+ var key = root.driver.hierarchyId;
110
+ var expanded = hierarchyExpansion.has(key) ? hierarchyExpansion.get(key) : !!query;
111
+ for (var ai = 0; ai < root.workers.length; ai++) {
112
+ if (root.workers[ai].mateId === store.get('homeChatMateId') && root.workers[ai].sessionId === store.get('homeChatSessionId')) expanded = true;
113
+ }
114
+ var wrapper = document.createElement("div");
115
+ wrapper.className = "home-conversations-driver-hierarchy";
116
+ var header = document.createElement("div");
117
+ header.className = "home-conversations-driver-header";
118
+ var toggle = document.createElement("button");
119
+ toggle.type = "button";
120
+ toggle.className = "home-conversations-driver-toggle";
121
+ var childrenId = "home-sheet-workers-" + String(key).replace(/[^a-zA-Z0-9_-]/g, "-");
122
+ toggle.setAttribute("aria-expanded", String(expanded));
123
+ toggle.setAttribute("aria-controls", childrenId);
124
+ toggle.setAttribute("aria-label", (expanded ? "Collapse" : "Expand") + " Workers for " + (root.driver.title || "Unavailable Driver"));
125
+ toggle.innerHTML = iconHtml("chevron-right");
126
+ if (!root.driver.sessionId) toggle.insertAdjacentHTML("beforeend", "<span>Unavailable Driver</span><span>" + root.workers.length + "</span>");
127
+ toggle.addEventListener("click", function () {
128
+ hierarchyExpansion.set(key, !expanded);
129
+ renderRows();
130
+ });
131
+ header.appendChild(toggle);
132
+ if (root.driver.sessionId) header.appendChild(createSheetItem(root.driver, false));
133
+ wrapper.appendChild(header);
134
+ var children = document.createElement("div");
135
+ children.id = childrenId;
136
+ children.className = "home-conversations-worker-children";
137
+ children.setAttribute("role", "group");
138
+ children.setAttribute("aria-label", "Workers for " + (root.driver.title || "Unavailable Driver"));
139
+ children.hidden = !expanded;
140
+ var driverMatched = root.driver.sessionId && conversationMatches(root.driver, query);
141
+ for (var i = 0; i < root.workers.length; i++) {
142
+ if (!query || driverMatched || conversationMatches(root.workers[i], query)) children.appendChild(createSheetItem(root.workers[i], true));
143
+ }
144
+ wrapper.appendChild(children);
145
+ return wrapper;
146
+ }
147
+
57
148
  function renderRows() {
58
149
  if (!listEl) return;
59
150
  var query = searchInput ? searchInput.value.trim().toLowerCase() : "";
60
- var all = getHomeSessionConversations().filter(function (conversation) {
61
- if (!query) return true;
62
- return conversation.title.toLowerCase().indexOf(query) !== -1 || conversation.mateName.toLowerCase().indexOf(query) !== -1;
151
+ var hierarchy = buildSessionHierarchy(getHomeSessionConversations());
152
+ var roots = hierarchy.roots.filter(function (root) {
153
+ if (conversationMatches(root.driver, query)) return true;
154
+ for (var i = 0; i < root.workers.length; i++) if (conversationMatches(root.workers[i], query)) return true;
155
+ return false;
63
156
  });
157
+ var orphans = hierarchy.orphans.filter(function (worker) { return conversationMatches(worker, query); });
64
158
  disposeHomeSessionActionsMenu();
65
159
  listEl.innerHTML = "";
66
- if (!all.length) {
160
+ if (!roots.length && !orphans.length) {
67
161
  var empty = document.createElement("div");
68
162
  empty.className = "home-conversations-sheet-empty";
69
163
  empty.textContent = query ? "No chats match your search." : "No chats yet.";
70
164
  listEl.appendChild(empty);
71
165
  return;
72
166
  }
73
- for (var i = 0; i < all.length; i++) {
74
- (function (conversation) {
75
- var item = document.createElement("div");
76
- item.className = "home-conversations-sheet-item";
77
- var row = document.createElement("button");
78
- row.type = "button";
79
- row.className = "home-conversations-sheet-row";
80
- var copy = document.createElement("span");
81
- copy.className = "home-conversations-sheet-copy";
82
- var title = document.createElement("span");
83
- title.className = "home-conversations-sheet-row-title";
84
- title.textContent = conversation.title;
85
- copy.appendChild(title);
86
- var mate = document.createElement("span");
87
- mate.className = "home-conversations-sheet-row-mate";
88
- mate.textContent = conversation.mateName;
89
- copy.appendChild(mate);
90
- row.appendChild(copy);
91
- if (conversation.isProcessing) {
92
- var activity = document.createElement("span");
93
- activity.className = "home-sidebar-processing";
94
- activity.setAttribute("aria-label", "Processing");
95
- row.appendChild(activity);
96
- }
97
- row.addEventListener("click", function () {
98
- var onSelect = selectConversation;
99
- closeSheet();
100
- if (onSelect) onSelect(conversation.mateId, conversation.sessionId);
101
- });
102
- var active = conversation.mateId === store.get('homeChatMateId') && conversation.sessionId === store.get('homeChatSessionId');
103
- if (active) {
104
- item.classList.add("is-active");
105
- row.setAttribute("aria-current", "page");
106
- }
107
- var detailsReturn = sheetOpener;
108
- var actions = createHomeSessionActionsTrigger(conversation, {
109
- detailsOpener: detailsReturn,
110
- beforeOpenDetails: function () { closeSheet(false); },
111
- });
112
- item.appendChild(row);
113
- item.appendChild(actions);
114
- listEl.appendChild(item);
115
- })(all[i]);
116
- }
167
+ roots.sort(function (a, b) { return b.lastActivity - a.lastActivity; });
168
+ for (var i = 0; i < roots.length; i++) listEl.appendChild(createSheetHierarchy(roots[i], query));
169
+ if (orphans.length) listEl.appendChild(createSheetHierarchy({ driver: { hierarchyId: "orphan-sheet", title: "Unavailable Driver" }, workers: orphans }, query));
170
+ refreshIcons();
117
171
  }
118
172
 
119
173
  export function openHomeConversationsSheet(onSelect, opener) {
@@ -2,7 +2,7 @@
2
2
  import { store } from './store.js';
3
3
  import { getWs } from './ws-ref.js';
4
4
  import { renderAssistantBubbleText, finalizeAssistantBubble, disposeChatBubbleTree } from './chat-bubble-renderer.js';
5
- import { createHomeOrdinaryBubble, createHomeOrdinaryTyping } from './home-chat-identity.js';
5
+ import { createHomeOrdinaryBubble, createHomeOrdinaryTyping, createCapsuleTurnNote } from './home-chat-identity.js';
6
6
  import { openHomeMateSettings, closeHomeMateSettings } from './home-mate-settings.js';
7
7
  import { forgetHomeSession, rememberHomeMate, rememberHomeSession } from './home-surface.js';
8
8
  import { isOwnedHomeSessionMessage, resolveHomeSessionIdentity, appendHomeStreamText, finalizeHomeAssistant } from './home-chat-stream-state.js';
@@ -16,7 +16,8 @@ import { setHomeSubSurface } from './home-sub-surface.js';
16
16
  import { createWorkspaceAssignmentCard, applyHomeWorkspaceAssignment } from './workspace-assignment-card.js';
17
17
  import { markHomeChatActivity, followHomeChatNextRender, captureHomeChatScroll, restoreHomeChatScroll } from './home-chat-scroll.js';
18
18
  import { createHomeMateProposalCard, createHomeMateProposalResponder, applyHomeMateProposal, resolveHomeMateProposal, restoreHomeMateProposalFocus } from './home-mate-creation.js';
19
- import { findHomeMate, getHomeMateName, getHomeMateShortBio } from './home-mate-selection.js';
19
+ import { findHomeMate, getHomeMateName } from './home-mate-selection.js';
20
+ import { renderHomeChatEmptyState } from './home-chat-empty-state.js';
20
21
  var messages = [], streamingText = "", streaming = false, bound = false, animateSwitch = false;
21
22
  var chatEl = null, stageEl = null, messagesEl = null, suggestionsEl = null, inputEl = null, sendBtn = null;
22
23
  var sessionModelEl = null, sessionModelValueEl = null, sessionModelChooseEl = null, sessionRequestSequence = 0, activeSessionRequestId = null, mateCreationActive = false;
@@ -167,6 +168,7 @@ function ensureDom() {
167
168
  }
168
169
  function formatTime(timestamp) { var date = timestamp ? new Date(timestamp) : null; return date ? date.getHours().toString().padStart(2, "0") + ":" + date.getMinutes().toString().padStart(2, "0") : ""; }
169
170
  function appendMessage(message, mate, mateName, finalize) {
171
+ if (message.role === "capsule_turn") return createCapsuleTurnNote(message);
170
172
  if (message.role === "assignment") return createWorkspaceAssignmentCard(message.assignment, { home: true, context: { mateId: store.get('homeChatMateId'), sessionId: store.get('homeChatSessionId'), requestId: activeSessionRequestId } });
171
173
  if (message.role === "mate_proposal") return createHomeMateProposalCard(message, respondToMateProposal, function (created) { openHomeChat(created.mateId); });
172
174
  if (message.role === "proposal" || message.role === "question") return createHomeDebateTranscriptCard(message, respondToDebateTranscript);
@@ -182,39 +184,7 @@ function appendMessage(message, mate, mateName, finalize) {
182
184
  return row;
183
185
  }
184
186
  function renderEmptyState(mate, mateName) {
185
- var empty = document.createElement("div");
186
- empty.className = "home-mate-chat-empty";
187
- var brand = document.createElement("div");
188
- brand.className = "home-mate-chat-brand";
189
- var symbol = document.createElement("img");
190
- symbol.src = "/clay-studio-symbol.png";
191
- symbol.alt = "";
192
- brand.appendChild(symbol);
193
- var wordmark = document.createElement("span");
194
- wordmark.className = "home-sidebar-brand-wordmark home-mate-chat-brand-wordmark";
195
- wordmark.textContent = "Clay Studio";
196
- brand.appendChild(wordmark);
197
- empty.appendChild(brand);
198
- var greeting = document.createElement("h2");
199
- greeting.textContent = mate ? "What should we work on, " + mateName + "?" : "Getting Home ready...";
200
- empty.appendChild(greeting);
201
- var detail = document.createElement("p");
202
- detail.textContent = mate ? getHomeMateShortBio(mate) : "Loading your Mate and recent conversation.";
203
- empty.appendChild(detail);
204
- messagesEl.appendChild(empty);
205
- suggestionsEl.innerHTML = "";
206
- if (!mate) return;
207
- var suggestions = ["Make me a small tool"];
208
- for (var i = 0; i < suggestions.length; i++) {
209
- (function (suggestion) {
210
- var chip = document.createElement("button");
211
- chip.type = "button";
212
- chip.className = "home-mate-chat-suggestion";
213
- chip.textContent = suggestion;
214
- chip.addEventListener("click", function () { submitSuggestion(suggestion); });
215
- suggestionsEl.appendChild(chip);
216
- })(suggestions[i]);
217
- }
187
+ renderHomeChatEmptyState(messagesEl, suggestionsEl, mate, mateName, submitSuggestion);
218
188
  }
219
189
  export function renderHomeChat() {
220
190
  if (!ensureDom()) return;
@@ -396,6 +366,16 @@ export function handleHomeWorkspaceAssignment(msg) {
396
366
  messages = applyHomeWorkspaceAssignment(messages, msg);
397
367
  renderHomeChat();
398
368
  }
369
+ // A tool call ended the mate's spoken burst: seal what has streamed so far
370
+ // into its own bubble; later deltas start a fresh one. Streaming stays on;
371
+ // the turn is not over.
372
+ export function handleHomeMateSegment(msg) {
373
+ if (!isCurrentSessionMessage(msg) || !streamingText) return;
374
+ messages.push({ role: "assistant", text: streamingText, ts: Date.now() });
375
+ streamingText = "";
376
+ renderHomeChat();
377
+ }
378
+
399
379
  export function handleHomeMateDone(msg) {
400
380
  if (!isCurrentSessionMessage(msg)) return;
401
381
  markHomeChatActivity();
@@ -53,6 +53,12 @@ export function getHomeSessionConversations() {
53
53
  createdAt: typeof sessions[j].createdAt === "number" ? sessions[j].createdAt : 0,
54
54
  lastActivity: typeof sessions[j].lastActivity === "number" ? sessions[j].lastActivity : 0,
55
55
  isProcessing: sessions[j].isProcessing === true,
56
+ sessionRole: sessions[j].sessionRole === "worker" ? "worker" : "driver",
57
+ parentSessionId: safeText(sessions[j].parentSessionId, null),
58
+ parentAvailable: sessions[j].parentAvailable === true,
59
+ workerGeneration: typeof sessions[j].workerGeneration === "number" ? sessions[j].workerGeneration : null,
60
+ hierarchyId: mateIds[i] + ":" + sessions[j].id,
61
+ parentHierarchyId: sessions[j].parentSessionId ? mateIds[i] + ":" + sessions[j].parentSessionId : null,
56
62
  });
57
63
  }
58
64
  }
@@ -1,7 +1,98 @@
1
- // Complete, globally sorted Home chat archive rendered in the sidebar.
1
+ // Complete Home chat archive rendered as provenance-preserving session trees.
2
2
 
3
3
  import { store } from './store.js';
4
+ import { iconHtml, refreshIcons } from './icons.js';
4
5
  import { getHomeSessionConversations, createHomeSessionActionsTrigger, disposeHomeSessionActionsMenu } from './home-session-actions.js';
6
+ import { buildSessionHierarchy } from './session-hierarchy.js';
7
+
8
+ var expansion = new Map();
9
+
10
+ function isExpanded(key, workers) {
11
+ if (expansion.has(key)) return expansion.get(key);
12
+ for (var i = 0; i < workers.length; i++) {
13
+ if (workers[i].mateId === store.get('homeChatMateId') && workers[i].sessionId === store.get('homeChatSessionId')) return true;
14
+ }
15
+ return false;
16
+ }
17
+
18
+ function createChatItem(chat, openConversation, worker) {
19
+ var item = document.createElement("div");
20
+ item.className = "home-sidebar-recent-item" + (worker ? " home-sidebar-worker-item" : "");
21
+ var row = document.createElement("button");
22
+ row.type = "button";
23
+ row.className = "home-sidebar-recent-row";
24
+ row.setAttribute("aria-label", chat.title + ", with " + chat.mateName);
25
+ if (chat.mateId === store.get('homeChatMateId') && chat.sessionId === store.get('homeChatSessionId')) {
26
+ item.classList.add("is-active");
27
+ row.setAttribute("aria-current", "page");
28
+ }
29
+ var copy = document.createElement("span");
30
+ copy.className = "home-sidebar-recent-copy";
31
+ var title = document.createElement("span");
32
+ title.className = "home-sidebar-recent-title";
33
+ title.textContent = chat.title;
34
+ var mate = document.createElement("span");
35
+ mate.className = "home-sidebar-recent-mate";
36
+ mate.textContent = worker && chat.workerGeneration ? chat.mateName + " · Worker generation " + chat.workerGeneration : chat.mateName;
37
+ copy.appendChild(title);
38
+ copy.appendChild(mate);
39
+ row.appendChild(copy);
40
+ if (chat.isProcessing) {
41
+ var activity = document.createElement("span");
42
+ activity.className = "home-sidebar-processing";
43
+ activity.setAttribute("aria-label", "Processing");
44
+ row.appendChild(activity);
45
+ }
46
+ row.addEventListener("click", function () {
47
+ if (typeof openConversation === "function") openConversation(chat.mateId, chat.sessionId);
48
+ });
49
+ item.appendChild(row);
50
+ item.appendChild(createHomeSessionActionsTrigger(chat));
51
+ return item;
52
+ }
53
+
54
+ function createHierarchy(root, openConversation) {
55
+ if (!root.workers.length) return createChatItem(root.driver, openConversation, false);
56
+ var wrapper = document.createElement("div");
57
+ wrapper.className = "home-sidebar-driver-hierarchy";
58
+ var key = root.driver.hierarchyId;
59
+ var expanded = isExpanded(key, root.workers);
60
+ var childrenId = "home-sidebar-workers-" + String(key).replace(/[^a-zA-Z0-9_-]/g, "-");
61
+ var header = document.createElement("div");
62
+ header.className = "home-sidebar-driver-header";
63
+ var toggle = document.createElement("button");
64
+ toggle.type = "button";
65
+ toggle.className = "home-sidebar-driver-toggle";
66
+ toggle.setAttribute("aria-expanded", String(expanded));
67
+ toggle.setAttribute("aria-controls", childrenId);
68
+ toggle.setAttribute("aria-label", (expanded ? "Collapse" : "Expand") + " Workers for " + (root.driver.title || "Unavailable Driver"));
69
+ toggle.innerHTML = iconHtml("chevron-right");
70
+ toggle.addEventListener("click", function () {
71
+ expansion.set(key, !expanded);
72
+ renderHomeSidebarChats(openConversation);
73
+ });
74
+ header.appendChild(toggle);
75
+ if (root.driver.sessionId) header.appendChild(createChatItem(root.driver, openConversation, false));
76
+ wrapper.appendChild(header);
77
+ var children = document.createElement("div");
78
+ children.id = childrenId;
79
+ children.className = "home-sidebar-worker-children";
80
+ children.setAttribute("role", "group");
81
+ children.setAttribute("aria-label", "Workers for " + (root.driver.title || "Unavailable Driver"));
82
+ children.hidden = !expanded;
83
+ for (var i = 0; i < root.workers.length; i++) children.appendChild(createChatItem(root.workers[i], openConversation, true));
84
+ wrapper.appendChild(children);
85
+ return wrapper;
86
+ }
87
+
88
+ function createOrphans(workers, openConversation) {
89
+ var wrapper = createHierarchy({ driver: { hierarchyId: "orphan-home", title: "Unavailable Driver" }, workers: workers }, openConversation);
90
+ var toggle = wrapper.querySelector(".home-sidebar-driver-toggle");
91
+ toggle.classList.add("home-sidebar-orphan-toggle");
92
+ toggle.setAttribute("aria-label", "Toggle Workers whose Driver is unavailable");
93
+ toggle.insertAdjacentHTML("beforeend", "<span>Unavailable Driver</span><span>" + workers.length + "</span>");
94
+ return wrapper;
95
+ }
5
96
 
6
97
  export function renderHomeSidebarChats(openConversation) {
7
98
  var list = document.getElementById("home-sidebar-recent-list");
@@ -11,9 +102,7 @@ export function renderHomeSidebarChats(openConversation) {
11
102
  var chats = getHomeSessionConversations();
12
103
  var scope = store.get('homeChatScope') === "current" ? "current" : "all";
13
104
  var activeMateId = store.get('homeChatMateId');
14
- if (scope === "current") {
15
- chats = chats.filter(function (chat) { return chat.mateId === activeMateId; });
16
- }
105
+ if (scope === "current") chats = chats.filter(function (chat) { return chat.mateId === activeMateId; });
17
106
  if (!chats.length) {
18
107
  var empty = document.createElement("div");
19
108
  empty.className = "home-sidebar-recent-empty";
@@ -21,43 +110,14 @@ export function renderHomeSidebarChats(openConversation) {
21
110
  list.appendChild(empty);
22
111
  return 0;
23
112
  }
24
- var activeSessionId = store.get('homeChatSessionId');
25
- for (var i = 0; i < chats.length; i++) {
26
- (function (chat) {
27
- var item = document.createElement("div");
28
- item.className = "home-sidebar-recent-item";
29
- var row = document.createElement("button");
30
- row.type = "button";
31
- row.className = "home-sidebar-recent-row";
32
- row.setAttribute("aria-label", chat.title + ", with " + chat.mateName);
33
- if (chat.mateId === activeMateId && chat.sessionId === activeSessionId) {
34
- item.classList.add("is-active");
35
- row.setAttribute("aria-current", "page");
36
- }
37
- var copy = document.createElement("span");
38
- copy.className = "home-sidebar-recent-copy";
39
- var title = document.createElement("span");
40
- title.className = "home-sidebar-recent-title";
41
- title.textContent = chat.title;
42
- var mate = document.createElement("span");
43
- mate.className = "home-sidebar-recent-mate";
44
- mate.textContent = chat.mateName;
45
- copy.appendChild(title);
46
- copy.appendChild(mate);
47
- row.appendChild(copy);
48
- if (chat.isProcessing) {
49
- var activity = document.createElement("span");
50
- activity.className = "home-sidebar-processing";
51
- activity.setAttribute("aria-label", "Processing");
52
- row.appendChild(activity);
53
- }
54
- row.addEventListener("click", function () {
55
- if (typeof openConversation === "function") openConversation(chat.mateId, chat.sessionId);
56
- });
57
- item.appendChild(row);
58
- item.appendChild(createHomeSessionActionsTrigger(chat));
59
- list.appendChild(item);
60
- })(chats[i]);
113
+ var hierarchy = buildSessionHierarchy(chats);
114
+ var hasHierarchy = hierarchy.orphans.length > 0;
115
+ hierarchy.roots.sort(function (a, b) { return b.lastActivity - a.lastActivity; });
116
+ for (var i = 0; i < hierarchy.roots.length; i++) {
117
+ if (hierarchy.roots[i].workers.length) hasHierarchy = true;
118
+ list.appendChild(createHierarchy(hierarchy.roots[i], openConversation));
61
119
  }
120
+ if (hierarchy.orphans.length) list.appendChild(createOrphans(hierarchy.orphans, openConversation));
121
+ if (hasHierarchy) refreshIcons();
62
122
  return chats.length;
63
123
  }
@@ -10,6 +10,7 @@ import { openHomeConversationsSheet, refreshHomeConversationsSheet } from './hom
10
10
  import { renderHomeSidebarChats } from './home-sidebar-chat-list.js';
11
11
  import { syncHomeSessionDetails } from './home-session-actions.js';
12
12
  import { toggleHomeCapsules } from './home-dock.js';
13
+ import { openHomeScheduler } from './scheduler.js';
13
14
 
14
15
  var initialized = false;
15
16
  var requestedMateIds = {};
@@ -119,6 +120,11 @@ function toggleCapsulesFromSidebar() {
119
120
  closeNarrowDrawer(!opening);
120
121
  }
121
122
 
123
+ function openScheduledTasksFromSidebar() {
124
+ openHomeScheduler();
125
+ closeNarrowDrawer(false);
126
+ }
127
+
122
128
  function handleSidebarKeydown(event) {
123
129
  if (event.key !== "Escape" || event.defaultPrevented) return;
124
130
  if (!document.body.classList.contains("home-active")) return;
@@ -137,6 +143,7 @@ export function initHomeSidebar() {
137
143
  document.getElementById("home-sidebar-new").addEventListener("click", startConversationFromSidebar);
138
144
  document.getElementById("home-sidebar-all").addEventListener("click", openAllConversations);
139
145
  document.getElementById("home-sidebar-debate").addEventListener("click", openDebatesFromSidebar);
146
+ document.getElementById("home-scheduler-btn").addEventListener("click", openScheduledTasksFromSidebar);
140
147
  document.getElementById("home-tools-btn").addEventListener("click", toggleCapsulesFromSidebar);
141
148
  var scopeButtons = document.querySelectorAll("[data-home-chat-scope]");
142
149
  for (var i = 0; i < scopeButtons.length; i++) scopeButtons[i].addEventListener("click", setChatScope);