clay-server 3.8.1 → 4.0.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 (189) hide show
  1. package/lib/ask-user-mcp-server.js +5 -119
  2. package/lib/builtin-mates.js +6 -6
  3. package/lib/claude-hook-installer.js +15 -0
  4. package/lib/clay-history-mcp-server.js +62 -297
  5. package/lib/daemon.js +1 -1
  6. package/lib/debate-model-selection.js +119 -0
  7. package/lib/home-debate-tool-policy.js +164 -0
  8. package/lib/home-response-notifications.js +26 -0
  9. package/lib/mate-creation-mcp-server.js +55 -0
  10. package/lib/mate-ready-creation.js +95 -0
  11. package/lib/mates.js +2 -0
  12. package/lib/notes.js +2 -1
  13. package/lib/project-ask-user.js +64 -0
  14. package/lib/project-capsule-catalog.js +101 -0
  15. package/lib/project-connection.js +4 -0
  16. package/lib/project-debate-proposal.js +133 -17
  17. package/lib/project-debate.js +214 -110
  18. package/lib/project-delegated-follow-up.js +84 -0
  19. package/lib/project-delegated-session.js +53 -0
  20. package/lib/project-filesystem.js +7 -9
  21. package/lib/project-knowledge.js +1 -0
  22. package/lib/project-mate-creation-proposal.js +120 -0
  23. package/lib/project-memory.js +14 -7
  24. package/lib/project-models.js +157 -32
  25. package/lib/project-session-notes.js +2 -2
  26. package/lib/project-session-pair.js +8 -8
  27. package/lib/project-sessions.js +66 -51
  28. package/lib/project-vendor-login.js +397 -0
  29. package/lib/project-worker-proposal.js +25 -25
  30. package/lib/project-workspace-query.js +144 -0
  31. package/lib/project.js +349 -102
  32. package/lib/public/app.js +85 -42
  33. package/lib/public/css/avatar-imprints.css +0 -1
  34. package/lib/public/css/base.css +4 -0
  35. package/lib/public/css/capsule-ui.css +389 -0
  36. package/lib/public/css/command-palette.css +306 -6
  37. package/lib/public/css/home-capsule-library.css +243 -0
  38. package/lib/public/css/home-capsule-source.css +192 -0
  39. package/lib/public/css/home-debate-live.css +146 -0
  40. package/lib/public/css/home-debate-planning.css +125 -0
  41. package/lib/public/css/home-debates-archive.css +281 -0
  42. package/lib/public/css/home-hub.css +896 -405
  43. package/lib/public/css/home-mate-creation.css +34 -0
  44. package/lib/public/css/home-mate-model-picker.css +131 -0
  45. package/lib/public/css/home-mate-settings.css +344 -0
  46. package/lib/public/css/home-session-actions.css +265 -0
  47. package/lib/public/css/home-sidebar.css +667 -0
  48. package/lib/public/css/icon-strip.css +1 -2
  49. package/lib/public/css/mates.css +7 -34
  50. package/lib/public/css/pane.css +1 -1
  51. package/lib/public/css/playbook.css +0 -80
  52. package/lib/public/css/sticky-notes.css +53 -117
  53. package/lib/public/css/tui-attention.css +0 -44
  54. package/lib/public/css/workspace-assignment.css +45 -0
  55. package/lib/public/fonts/source-serif-4/LICENSE.md +93 -0
  56. package/lib/public/fonts/source-serif-4/SourceSerif4Caption-Semibold.ttf.woff2 +0 -0
  57. package/lib/public/index.html +93 -74
  58. package/lib/public/modules/app-connection.js +10 -1
  59. package/lib/public/modules/app-dm.js +43 -160
  60. package/lib/public/modules/app-home-hub.js +328 -585
  61. package/lib/public/modules/app-message-cards.js +242 -0
  62. package/lib/public/modules/app-message-router.js +84 -0
  63. package/lib/public/modules/app-messages.js +102 -37
  64. package/lib/public/modules/app-notifications.js +8 -119
  65. package/lib/public/modules/app-projects.js +9 -1
  66. package/lib/public/modules/app-rendering.js +21 -602
  67. package/lib/public/modules/avatar-imprint.js +11 -8
  68. package/lib/public/modules/chat-bubble-renderer.js +152 -0
  69. package/lib/public/modules/chat-render-runtime.js +198 -0
  70. package/lib/public/modules/command-palette.js +225 -436
  71. package/lib/public/modules/dm-render.js +82 -0
  72. package/lib/public/modules/home-capsule-creation-intent.js +31 -0
  73. package/lib/public/modules/home-capsule-library.js +146 -0
  74. package/lib/public/modules/home-capsule-source.js +383 -0
  75. package/lib/public/modules/home-chat-identity.js +35 -0
  76. package/lib/public/modules/home-chat-scroll.js +77 -0
  77. package/lib/public/modules/home-chat-stream-state.js +34 -0
  78. package/lib/public/modules/home-composer-focus.js +25 -0
  79. package/lib/public/modules/home-conversations-sheet.js +179 -0
  80. package/lib/public/modules/home-debate-activity.js +48 -0
  81. package/lib/public/modules/home-debate-controls.js +295 -0
  82. package/lib/public/modules/home-debate-launch.js +41 -0
  83. package/lib/public/modules/home-debate-live.js +284 -0
  84. package/lib/public/modules/home-debate-models.js +73 -0
  85. package/lib/public/modules/home-debate-planning.js +335 -0
  86. package/lib/public/modules/home-debates-archive.js +279 -0
  87. package/lib/public/modules/home-dock-resize.js +74 -0
  88. package/lib/public/modules/home-dock.js +446 -0
  89. package/lib/public/modules/home-mate-chat.js +496 -0
  90. package/lib/public/modules/home-mate-creation.js +109 -0
  91. package/lib/public/modules/home-mate-model-picker.js +281 -0
  92. package/lib/public/modules/home-mate-selection.js +38 -0
  93. package/lib/public/modules/home-mate-settings-menu.js +131 -0
  94. package/lib/public/modules/home-mate-settings.js +383 -0
  95. package/lib/public/modules/home-session-actions.js +373 -0
  96. package/lib/public/modules/home-session-model-confirmation.js +13 -0
  97. package/lib/public/modules/home-shell.js +9 -0
  98. package/lib/public/modules/home-sidebar-chat-list.js +63 -0
  99. package/lib/public/modules/home-sidebar.js +158 -0
  100. package/lib/public/modules/home-sub-surface.js +19 -0
  101. package/lib/public/modules/home-surface-boot.js +68 -0
  102. package/lib/public/modules/home-surface.js +141 -0
  103. package/lib/public/modules/home-tools.js +271 -0
  104. package/lib/public/modules/markdown.js +50 -2
  105. package/lib/public/modules/mate-management.js +86 -0
  106. package/lib/public/modules/mate-wizard.js +0 -7
  107. package/lib/public/modules/mention.js +17 -19
  108. package/lib/public/modules/pane-bridge.js +9 -0
  109. package/lib/public/modules/profile.js +4 -5
  110. package/lib/public/modules/project-settings.js +34 -28
  111. package/lib/public/modules/project-switcher.js +11 -5
  112. package/lib/public/modules/project-workspace-assignment.js +23 -0
  113. package/lib/public/modules/search-clay-chat.js +402 -0
  114. package/lib/public/modules/server-settings.js +14 -12
  115. package/lib/public/modules/session-actions.js +1 -1
  116. package/lib/public/modules/sidebar-mates.js +56 -552
  117. package/lib/public/modules/sidebar-mobile.js +7 -64
  118. package/lib/public/modules/sidebar-presence.js +37 -0
  119. package/lib/public/modules/split-pair-ui.js +13 -13
  120. package/lib/public/modules/split-view.js +7 -0
  121. package/lib/public/modules/sticky-notes.js +7 -6
  122. package/lib/public/modules/tool-input-composition.js +48 -0
  123. package/lib/public/modules/tool-llm-status.js +167 -0
  124. package/lib/public/modules/tool-renderer-advanced.js +285 -0
  125. package/lib/public/modules/tool-renderer-chart.js +158 -0
  126. package/lib/public/modules/tool-renderer-semantics.js +35 -0
  127. package/lib/public/modules/tool-renderer.js +464 -0
  128. package/lib/public/modules/tool-runtime.js +309 -0
  129. package/lib/public/modules/tool-ui-evaluator.js +97 -0
  130. package/lib/public/modules/tool-ui-tree.js +21 -0
  131. package/lib/public/modules/tui-attention.js +10 -0
  132. package/lib/public/modules/user-settings.js +16 -23
  133. package/lib/public/modules/vendor-login.js +287 -0
  134. package/lib/public/modules/worker-proposal.js +2 -2
  135. package/lib/public/modules/workspace-assignment-card.js +149 -0
  136. package/lib/public/modules/workspace-assignment-routing.js +4 -0
  137. package/lib/public/style.css +13 -2
  138. package/lib/runtime-env.js +65 -0
  139. package/lib/sdk-bridge.js +120 -32
  140. package/lib/sdk-message-processor.js +10 -6
  141. package/lib/server-home-capsule-creation.js +36 -0
  142. package/lib/server-home-chat-events.js +287 -0
  143. package/lib/server-home-chat.js +518 -0
  144. package/lib/server-home-clay-entry.js +65 -0
  145. package/lib/server-home-clay-session-links.js +26 -0
  146. package/lib/server-home-debate-planning.js +214 -0
  147. package/lib/server-home-debates.js +137 -0
  148. package/lib/server-home-mate-creation.js +261 -0
  149. package/lib/server-home-models.js +241 -0
  150. package/lib/server-home-preferences.js +61 -0
  151. package/lib/server-mates.js +37 -0
  152. package/lib/server-tools.js +377 -0
  153. package/lib/server.js +111 -59
  154. package/lib/session-pair-mcp-server.js +6 -6
  155. package/lib/sessions.js +18 -3
  156. package/lib/tool-capsule-source.js +142 -0
  157. package/lib/tool-control-mcp-server.js +126 -0
  158. package/lib/tool-llm.js +48 -0
  159. package/lib/tool-storage.js +77 -0
  160. package/lib/tool-ui-spec-advanced.js +82 -0
  161. package/lib/tool-ui-spec.js +261 -0
  162. package/lib/tools-registry.js +286 -0
  163. package/lib/users-home-dock-preferences.js +77 -0
  164. package/lib/users-home-surface-preferences.js +99 -0
  165. package/lib/users-preferences.js +4 -7
  166. package/lib/users.js +12 -0
  167. package/lib/whats-new-content.js +3 -3
  168. package/lib/workspace-assignment-service.js +420 -0
  169. package/lib/workspace-query-mcp-server.js +154 -0
  170. package/lib/workspace-query-service.js +492 -0
  171. package/lib/ws-schema.js +93 -12
  172. package/lib/yoke/adapters/acp.js +2 -1
  173. package/lib/yoke/adapters/antigravity.js +2 -1
  174. package/lib/yoke/adapters/claude-worker.js +11 -4
  175. package/lib/yoke/adapters/claude.js +73 -43
  176. package/lib/yoke/adapters/codex.js +40 -10
  177. package/lib/yoke/adapters/kiro.js +2 -1
  178. package/lib/yoke/claude-user-input.js +38 -0
  179. package/lib/yoke/codex-app-server.js +20 -2
  180. package/lib/yoke/codex-user-input.js +64 -0
  181. package/lib/yoke/complete-once.js +164 -0
  182. package/lib/yoke/index.js +5 -1
  183. package/lib/yoke/interface.js +19 -0
  184. package/lib/yoke/mcp-bridge-server.js +2 -6
  185. package/lib/yoke/user-input.js +254 -0
  186. package/package.json +3 -2
  187. package/lib/public/css/home-chat.css +0 -380
  188. package/lib/public/modules/home-chat.js +0 -438
  189. package/lib/server-clay-home.js +0 -245
@@ -0,0 +1,287 @@
1
+ // vendor-login.js - Vendor login (auth_required) recovery flow.
2
+ //
3
+ // The server owns the login terminal (see lib/project-vendor-login.js): exactly
4
+ // one per vendor per project, tracked server-side, killed once the login lands.
5
+ // This module is the client half of that contract - it asks for the flow, opens
6
+ // the modal on whatever terminal the server hands back, and reflects the flow's
7
+ // state in a banner.
8
+ //
9
+ // Nothing here spawns a terminal speculatively. Every auth_required after the
10
+ // first is a no-op while a flow is live, which is what stops the login-terminal
11
+ // pile-up and the endless auth_required -> login -> 401 loop.
12
+
13
+ import { store } from './store.js';
14
+ import { getWs } from './ws-ref.js';
15
+ import { refreshIcons, iconHtml } from './icons.js';
16
+ import { openTuiModal, closeTuiModal, getTuiModalTerminalId } from './tui-attention.js';
17
+ import { forwardPaneAuthRequired } from './pane-bridge.js';
18
+
19
+ // vendor -> { terminalId, startedAt } as last broadcast by the server.
20
+ var activeFlows = Object.create(null);
21
+ // vendor -> true between sending vendor_login_start and hearing back.
22
+ var pendingStarts = Object.create(null);
23
+ // Vendor whose login terminal the modal is currently showing.
24
+ var modalVendor = null;
25
+ // Set while this module tears the modal down itself, so the modal's close hook
26
+ // does not read a programmatic close as "the user gave up".
27
+ var closingModalInternally = false;
28
+
29
+ function vendorDisplayName(vendor) {
30
+ var vendors = store.get('vendorInfo') || {};
31
+ var info = vendors[vendor];
32
+ if (info && info.displayName) return info.displayName;
33
+ return vendor === "codex" ? "Codex" : "Claude Code";
34
+ }
35
+
36
+ function currentProjectSlug() {
37
+ var slug = store.get('currentSlug') || "";
38
+ if (slug) return slug;
39
+ // Fallback: derive from the URL (/p/<slug>/...).
40
+ try {
41
+ var m = (window.location.pathname || "").match(/^\/p\/([a-z0-9_-]+)/);
42
+ if (m) return m[1];
43
+ } catch (e) {}
44
+ return "";
45
+ }
46
+
47
+ // The banner surface belongs to the notification center. Query it rather than
48
+ // importing app-notifications so the dependency stays one-directional
49
+ // (app-notifications -> vendor-login).
50
+ function bannerContainer() {
51
+ return document.querySelector(".notif-banner-container");
52
+ }
53
+
54
+ function removeBannerEl(el) {
55
+ if (!el) return;
56
+ el.classList.remove("show");
57
+ setTimeout(function () {
58
+ if (el.parentNode) el.parentNode.removeChild(el);
59
+ }, 300);
60
+ }
61
+
62
+ function clearLoginBanners() {
63
+ var container = bannerContainer();
64
+ if (!container) return;
65
+ var existing = container.querySelectorAll('[data-vendor-login="true"]');
66
+ for (var i = 0; i < existing.length; i++) removeBannerEl(existing[i]);
67
+ }
68
+
69
+ function showLoginBanner(opts) {
70
+ var container = bannerContainer();
71
+ if (!container) return null;
72
+ clearLoginBanners();
73
+
74
+ var banner = document.createElement("div");
75
+ banner.className = "notif-banner notif-banner-update";
76
+ banner.setAttribute("data-notif-id", "_vendor_login");
77
+ banner.setAttribute("data-vendor-login", "true");
78
+ banner.innerHTML =
79
+ '<div class="notif-banner-icon">' + iconHtml(opts.icon || "check-circle") + '</div>' +
80
+ '<div class="notif-banner-body">' +
81
+ '<div class="notif-banner-project">CLAY</div>' +
82
+ '<div class="notif-banner-title">' + opts.title + '</div>' +
83
+ '<div class="notif-banner-text">' + opts.text + '</div>' +
84
+ '</div>' +
85
+ '<button class="notif-banner-close">' + iconHtml("x") + '</button>';
86
+
87
+ container.appendChild(banner);
88
+ refreshIcons();
89
+ requestAnimationFrame(function () { banner.classList.add("show"); });
90
+
91
+ var closeBtn = banner.querySelector(".notif-banner-close");
92
+ if (closeBtn) {
93
+ closeBtn.addEventListener("click", function (e) {
94
+ e.stopPropagation();
95
+ removeBannerEl(banner);
96
+ if (opts.onDismiss) opts.onDismiss();
97
+ });
98
+ }
99
+ if (typeof opts.autoDismissMs === "number") {
100
+ setTimeout(function () { removeBannerEl(banner); }, opts.autoDismissMs);
101
+ }
102
+ return banner;
103
+ }
104
+
105
+ function showLoginStartedBanner(vendor) {
106
+ showLoginBanner({
107
+ icon: "check-circle",
108
+ title: vendorDisplayName(vendor) + " login started",
109
+ text: "Finish the sign-in in the terminal. Clay reloads the credentials on its own, so your sessions keep working - no new session needed.",
110
+ onDismiss: function () { cancelVendorLogin(vendor); },
111
+ });
112
+ }
113
+
114
+ function showLoginCompleteBanner(vendor) {
115
+ showLoginBanner({
116
+ icon: "check-circle",
117
+ title: "Signed in to " + vendorDisplayName(vendor),
118
+ text: "Credentials reloaded. Send your next message to continue.",
119
+ autoDismissMs: 6000,
120
+ });
121
+ }
122
+
123
+ function showLoginErrorBanner(vendor, error) {
124
+ showLoginBanner({
125
+ icon: "alert-triangle",
126
+ title: vendorDisplayName(vendor) + " login could not start",
127
+ text: error || "The login terminal could not be created.",
128
+ autoDismissMs: 8000,
129
+ });
130
+ }
131
+
132
+ function closeModalInternally() {
133
+ modalVendor = null;
134
+ closingModalInternally = true;
135
+ try {
136
+ closeTuiModal();
137
+ } finally {
138
+ closingModalInternally = false;
139
+ }
140
+ }
141
+
142
+ function closeModalForVendor(vendor, terminalId) {
143
+ if (modalVendor !== vendor) return;
144
+ var openId = getTuiModalTerminalId();
145
+ if (typeof terminalId === "number" && openId != null && openId !== terminalId) return;
146
+ closeModalInternally();
147
+ }
148
+
149
+ // ========================================================
150
+ // Outgoing
151
+ // ========================================================
152
+
153
+ /**
154
+ * Ask the server to start (or hand back) the login terminal for a vendor.
155
+ * opts.auto marks a request triggered by an auth_required event; those never
156
+ * open a second prompt while a flow is already running.
157
+ */
158
+ export function requestVendorLogin(vendor, opts) {
159
+ var options = opts || {};
160
+ var target = vendor || "claude";
161
+ if (options.auto && (activeFlows[target] || pendingStarts[target])) return false;
162
+
163
+ var ws = getWs();
164
+ if (!ws || ws.readyState !== 1) return false;
165
+
166
+ pendingStarts[target] = true;
167
+ ws.send(JSON.stringify({
168
+ type: "vendor_login_start",
169
+ vendor: target,
170
+ auto: !!options.auto,
171
+ sessionId: options.sessionId || store.get('activeSessionId') || null,
172
+ }));
173
+ showLoginStartedBanner(target);
174
+ return true;
175
+ }
176
+
177
+ export function cancelVendorLogin(vendor) {
178
+ var target = vendor || modalVendor;
179
+ if (!target) return;
180
+ delete pendingStarts[target];
181
+ delete activeFlows[target];
182
+ clearLoginBanners();
183
+ if (modalVendor === target) closeModalInternally();
184
+ var ws = getWs();
185
+ if (ws && ws.readyState === 1) {
186
+ ws.send(JSON.stringify({ type: "vendor_login_cancel", vendor: target }));
187
+ }
188
+ }
189
+
190
+ /**
191
+ * Auto-open the login flow when the server reports the vendor isn't logged in.
192
+ *
193
+ * Split panes have no banner surface of their own (the parent shell owns the
194
+ * single visible one), so a pane hands the event up instead of running two
195
+ * competing flows.
196
+ */
197
+ export function autoStartLoginIfNeeded(msg) {
198
+ if (!msg) return false;
199
+ if (store.get('paneMode')) {
200
+ return forwardPaneAuthRequired({
201
+ vendor: msg.vendor || "claude",
202
+ sessionId: store.get('activeSessionId') || null,
203
+ });
204
+ }
205
+ var vendor = msg.vendor || "claude";
206
+ if (activeFlows[vendor] || pendingStarts[vendor]) return false;
207
+ return requestVendorLogin(vendor, { auto: true, sessionId: msg.sessionId || null });
208
+ }
209
+
210
+ // ========================================================
211
+ // Incoming
212
+ // ========================================================
213
+
214
+ export function handleVendorLoginReady(msg) {
215
+ if (!msg || typeof msg.terminalId !== "number") return;
216
+ var vendor = msg.vendor || "claude";
217
+ delete pendingStarts[vendor];
218
+ activeFlows[vendor] = { terminalId: msg.terminalId, startedAt: Date.now() };
219
+
220
+ var slug = msg.slug || currentProjectSlug();
221
+ if (!slug) return;
222
+ modalVendor = vendor;
223
+ openTuiModal(msg.terminalId, slug, {
224
+ sessionTitle: vendorDisplayName(vendor) + " login",
225
+ projectName: slug,
226
+ compact: true,
227
+ // Dismissing the modal abandons the login: kill the terminal server-side
228
+ // so it never lingers in the sidebar terminal list.
229
+ onClose: function () {
230
+ if (closingModalInternally) return;
231
+ modalVendor = null;
232
+ cancelVendorLogin(vendor);
233
+ },
234
+ });
235
+ }
236
+
237
+ export function handleVendorLoginState(msg) {
238
+ if (!msg) return;
239
+ var next = Object.create(null);
240
+ var flows = msg.flows || [];
241
+ for (var i = 0; i < flows.length; i++) {
242
+ next[flows[i].vendor] = { terminalId: flows[i].terminalId, startedAt: flows[i].startedAt };
243
+ delete pendingStarts[flows[i].vendor];
244
+ }
245
+
246
+ // A flow the server dropped without an auth_refreshed (cancelled elsewhere,
247
+ // terminal killed from the sidebar) must not leave a stale modal behind.
248
+ var previous = Object.keys(activeFlows);
249
+ for (var p = 0; p < previous.length; p++) {
250
+ if (!next[previous[p]] && modalVendor === previous[p]) {
251
+ closeModalInternally();
252
+ clearLoginBanners();
253
+ }
254
+ }
255
+ activeFlows = next;
256
+ }
257
+
258
+ export function handleAuthRefreshed(msg) {
259
+ if (!msg) return;
260
+ var vendor = msg.vendor || "claude";
261
+ var flow = activeFlows[vendor];
262
+ delete pendingStarts[vendor];
263
+ delete activeFlows[vendor];
264
+ closeModalForVendor(vendor, flow ? flow.terminalId : undefined);
265
+ clearLoginBanners();
266
+ showLoginCompleteBanner(vendor);
267
+ }
268
+
269
+ export function handleVendorLoginError(msg) {
270
+ if (!msg) return;
271
+ var vendor = msg.vendor || "claude";
272
+ delete pendingStarts[vendor];
273
+ delete activeFlows[vendor];
274
+ if (modalVendor === vendor) closeModalInternally();
275
+ clearLoginBanners();
276
+ showLoginErrorBanner(vendor, msg.error);
277
+ }
278
+
279
+ export function requestVendorLoginState() {
280
+ var ws = getWs();
281
+ if (!ws || ws.readyState !== 1) return;
282
+ ws.send(JSON.stringify({ type: "vendor_login_state_request" }));
283
+ }
284
+
285
+ export function getActiveLoginFlow(vendor) {
286
+ return activeFlows[vendor] || null;
287
+ }
@@ -158,7 +158,7 @@ export function renderWorkerProposal(msg) {
158
158
 
159
159
  var summary = document.createElement("p");
160
160
  summary.className = "worker-proposal-summary";
161
- summary.textContent = msg.summary || "This task can be planned here and executed in a visible Worker session.";
161
+ summary.textContent = msg.summary || "This task can be planned here and executed in a visible Split Worker session.";
162
162
  card.appendChild(summary);
163
163
 
164
164
  var steps = planSteps(msg.plan);
@@ -225,7 +225,7 @@ export function renderWorkerProposal(msg) {
225
225
  var accept = document.createElement("button");
226
226
  accept.type = "button";
227
227
  accept.className = "worker-proposal-action primary";
228
- accept.innerHTML = iconHtml("panel-right-open") + "<span>Run with Worker</span>";
228
+ accept.innerHTML = iconHtml("panel-right-open") + "<span>Run with Split Worker</span>";
229
229
  accept.addEventListener("click", function () { sendDecision(card, true); });
230
230
  actions.appendChild(decline);
231
231
  actions.appendChild(accept);
@@ -0,0 +1,149 @@
1
+ import { getWs } from './ws-ref.js';
2
+ import { iconHtml, refreshIcons } from './icons.js';
3
+
4
+ var pendingFocus = {};
5
+
6
+ function textElement(tag, className, text) {
7
+ var element = document.createElement(tag);
8
+ element.className = className;
9
+ element.textContent = text || "";
10
+ return element;
11
+ }
12
+
13
+ function statusLabel(status) {
14
+ if (status === "starting") return "Starting";
15
+ if (status === "running") return "In progress";
16
+ if (status === "completed") return "Completed";
17
+ if (status === "failed") return "Failed";
18
+ if (status === "cancelled") return "Cancelled";
19
+ if (status === "interrupted") return "Interrupted";
20
+ return "Approval required";
21
+ }
22
+
23
+ function sendDecision(assignment, action, surface, context, card, opener) {
24
+ var ws = getWs();
25
+ if (!ws || ws.readyState !== 1 || !assignment || assignment.status !== "proposed") return false;
26
+ pendingFocus[assignment.assignmentId] = opener || null;
27
+ card.setAttribute("aria-busy", "true");
28
+ var buttons = card.querySelectorAll("button");
29
+ for (var i = 0; i < buttons.length; i++) buttons[i].disabled = true;
30
+ ws.send(JSON.stringify({
31
+ type: "project_assignment_response",
32
+ assignmentId: assignment.assignmentId,
33
+ action: action,
34
+ surface: surface,
35
+ sourceProjectSlug: assignment.sourceProjectSlug,
36
+ sourceSessionRef: assignment.sourceSessionRef,
37
+ mateId: context && context.mateId || null,
38
+ sessionId: context && context.sessionId || null,
39
+ requestId: context && context.requestId || null,
40
+ }));
41
+ return true;
42
+ }
43
+
44
+ export function restoreWorkspaceAssignmentFocus(assignmentId, card) {
45
+ if (!assignmentId || !pendingFocus[assignmentId] || !card) return false;
46
+ delete pendingFocus[assignmentId];
47
+ var focusTarget = card.querySelector(".workspace-assignment-error") || card.querySelector('[role="status"]');
48
+ if (!focusTarget) return false;
49
+ focusTarget.tabIndex = -1;
50
+ requestAnimationFrame(function () { focusTarget.focus({ preventScroll: true }); });
51
+ return true;
52
+ }
53
+
54
+ export function createWorkspaceAssignmentCard(assignment, options) {
55
+ var config = options || {};
56
+ var followUp = assignment.delivery === "follow_up";
57
+ var card = document.createElement("section");
58
+ card.className = "workspace-assignment-card" + (config.home ? " home-workspace-assignment" : "");
59
+ card.dataset.assignmentId = assignment.assignmentId || "";
60
+ card.dataset.status = assignment.status || "proposed";
61
+ card.dataset.delivery = followUp ? "follow_up" : "new_session";
62
+ card.setAttribute("aria-label", (followUp ? "Project follow-up: " : "Project assignment: ") + (assignment.title || "Untitled assignment"));
63
+
64
+ var header = document.createElement("div");
65
+ header.className = "workspace-assignment-header";
66
+ var mark = document.createElement("span");
67
+ mark.className = "workspace-assignment-mark";
68
+ mark.innerHTML = iconHtml(followUp ? "message-square-plus" : "send");
69
+ mark.setAttribute("aria-hidden", "true");
70
+ header.appendChild(mark);
71
+ var heading = document.createElement("div");
72
+ heading.className = "workspace-assignment-heading";
73
+ heading.appendChild(textElement("span", "workspace-assignment-kicker", followUp ? "PROJECT FOLLOW-UP" : "PROJECT ASSIGNMENT"));
74
+ heading.appendChild(textElement("h3", "workspace-assignment-title", assignment.title || "Untitled assignment"));
75
+ header.appendChild(heading);
76
+ var status = textElement("span", "workspace-assignment-status", statusLabel(assignment.status));
77
+ status.setAttribute("role", "status");
78
+ header.appendChild(status);
79
+ card.appendChild(header);
80
+
81
+ var targetText = followUp
82
+ ? "Continue “" + (assignment.targetSessionTitle || "Existing session") + "” in " + (assignment.projectTitle || assignment.projectSlug || "project")
83
+ : "New private session in " + (assignment.projectTitle || assignment.projectSlug || "project");
84
+ var target = textElement("p", "workspace-assignment-target", targetText);
85
+ card.appendChild(target);
86
+ card.appendChild(textElement("p", "workspace-assignment-task", assignment.task || ""));
87
+
88
+ if (assignment.error) {
89
+ var error = textElement("p", "workspace-assignment-error", assignment.error);
90
+ error.setAttribute("role", "alert");
91
+ card.appendChild(error);
92
+ }
93
+ if (assignment.resultSummary) card.appendChild(textElement("p", "workspace-assignment-result", assignment.resultSummary));
94
+
95
+ var actions = document.createElement("div");
96
+ actions.className = "workspace-assignment-actions";
97
+ if (assignment.status === "proposed") {
98
+ var cancel = textElement("button", "workspace-assignment-action secondary", "Cancel");
99
+ cancel.type = "button";
100
+ cancel.setAttribute("aria-label", "Cancel project assignment");
101
+ cancel.addEventListener("click", function () { sendDecision(assignment, "cancel", config.home ? "home" : "project", config.context, card, cancel); });
102
+ var approve = textElement("button", "workspace-assignment-action primary", "Approve");
103
+ approve.type = "button";
104
+ approve.setAttribute("aria-label", followUp ? "Approve follow-up in " + (assignment.targetSessionTitle || "the target session") : "Approve new session in " + (assignment.projectTitle || assignment.projectSlug || "the target project"));
105
+ approve.addEventListener("click", function () { sendDecision(assignment, "approve", config.home ? "home" : "project", config.context, card, approve); });
106
+ actions.appendChild(cancel);
107
+ actions.appendChild(approve);
108
+ } else {
109
+ var resolved = textElement("span", "workspace-assignment-resolved", statusLabel(assignment.status));
110
+ resolved.setAttribute("role", "status");
111
+ resolved.tabIndex = -1;
112
+ actions.appendChild(resolved);
113
+ }
114
+ card.appendChild(actions);
115
+ restoreWorkspaceAssignmentFocus(assignment.assignmentId, card);
116
+ refreshIcons();
117
+ return card;
118
+ }
119
+
120
+ export function showWorkspaceAssignmentError(card, msg) {
121
+ if (!card) return false;
122
+ card.removeAttribute("aria-busy");
123
+ var buttons = card.querySelectorAll("button");
124
+ for (var i = 0; i < buttons.length; i++) buttons[i].disabled = false;
125
+ var error = card.querySelector(".workspace-assignment-error");
126
+ if (!error) {
127
+ error = textElement("p", "workspace-assignment-error", "");
128
+ error.setAttribute("role", "alert");
129
+ card.insertBefore(error, card.querySelector(".workspace-assignment-actions"));
130
+ }
131
+ error.textContent = msg.error || "The assignment could not be updated.";
132
+ error.tabIndex = -1;
133
+ error.focus({ preventScroll: true });
134
+ return true;
135
+ }
136
+
137
+ export function applyHomeWorkspaceAssignment(messages, msg) {
138
+ var assignment = msg && msg.assignment;
139
+ if (!assignment || !assignment.assignmentId) return messages;
140
+ var next = (messages || []).slice();
141
+ for (var i = 0; i < next.length; i++) {
142
+ if (next[i].role === "assignment" && next[i].assignment && next[i].assignment.assignmentId === assignment.assignmentId) {
143
+ next[i] = { role: "assignment", assignment: assignment };
144
+ return next;
145
+ }
146
+ }
147
+ next.push({ role: "assignment", assignment: assignment });
148
+ return next;
149
+ }
@@ -0,0 +1,4 @@
1
+ export function isHomeWorkspaceAssignmentMessage(msg) {
2
+ if (!msg) return false;
3
+ return (msg.type === "home_project_assignment_proposal" || msg.type === "home_project_assignment_status") && !!msg.mateId && !!msg.requestId;
4
+ }
@@ -3,6 +3,7 @@
3
3
  @import url("css/title-bar.css?v=20260828-project-header1");
4
4
  @import url("css/pane.css");
5
5
  @import url("css/worker-proposal.css");
6
+ @import url("css/workspace-assignment.css");
6
7
  @import url("css/session-actions.css?v=20260828-contrast1");
7
8
  @import url("css/sidebar.css?v=20260828-session-list3");
8
9
  @import url("css/overlays.css?v=20260827-brand3");
@@ -23,7 +24,13 @@
23
24
  @import url("css/scheduler.css");
24
25
  @import url("css/scheduler-modal.css");
25
26
  @import url("css/home-hub.css");
26
- /* @import url("css/home-chat.css"); */ /* FAB chat disabled */
27
+ @import url("css/home-sidebar.css");
28
+ @import url("css/home-session-actions.css");
29
+ @import url("css/home-capsule-library.css");
30
+ @import url("css/home-capsule-source.css");
31
+ @import url("css/capsule-ui.css?v=20260831-v2");
32
+ @import url("css/home-mate-model-picker.css");
33
+ @import url("css/home-mate-settings.css");
27
34
  @import url("css/playbook.css");
28
35
  @import url("css/stt.css");
29
36
  @import url("css/profile.css");
@@ -32,9 +39,13 @@
32
39
  @import url("css/session-search.css");
33
40
  @import url("css/tooltip.css");
34
41
  @import url("css/mates.css?v=20260407");
35
- @import url("css/command-palette.css");
42
+ @import url("css/command-palette.css?v=20260901-clay-chat2");
36
43
  @import url("css/mention.css");
37
44
  @import url("css/debate.css");
45
+ @import url("css/home-debate-planning.css");
46
+ @import url("css/home-mate-creation.css");
47
+ @import url("css/home-debate-live.css");
48
+ @import url("css/home-debates-archive.css");
38
49
  @import url("css/notifications-center.css");
39
50
  @import url("css/tui-attention.css?v=20260829-terminal-canvas1");
40
51
  @import url("css/pwa-mobile.css?v=20260824f");
@@ -0,0 +1,65 @@
1
+ var SAFE_KEY = /^[A-Za-z_][A-Za-z0-9_]*$/;
2
+ var PROTECTED_KEYS = { HOME: true, USER: true, LOGNAME: true, SHELL: true, PATH: true, XDG_RUNTIME_DIR: true, NODE_OPTIONS: true, TERM: true, COLORFGBG: true };
3
+
4
+ function isProtectedKey(key) {
5
+ return !!PROTECTED_KEYS[key] || key.indexOf("CLAY_") === 0 || key.indexOf("LD_") === 0 || key.indexOf("DYLD_") === 0;
6
+ }
7
+
8
+ function parseQuotedValue(value, lineNumber) {
9
+ var trimmed = value.trim();
10
+ if (!trimmed) return "";
11
+ var quote = trimmed.charAt(0);
12
+ if (quote !== "\"" && quote !== "'") {
13
+ if (/[;`|]|\$\(|&&/.test(trimmed)) throw new Error("Unsupported executable syntax at line " + lineNumber);
14
+ return trimmed;
15
+ }
16
+ if (trimmed.length < 2 || trimmed.charAt(trimmed.length - 1) !== quote) throw new Error("Invalid quoted value at line " + lineNumber);
17
+ var content = trimmed.substring(1, trimmed.length - 1);
18
+ if (quote === "'") return content;
19
+ try {
20
+ var parsed = JSON.parse(trimmed);
21
+ if (typeof parsed !== "string" || parsed.indexOf("\0") !== -1) throw new Error("Invalid");
22
+ return parsed;
23
+ } catch (e) {
24
+ throw new Error("Invalid quoted value at line " + lineNumber);
25
+ }
26
+ }
27
+
28
+ function parseEnvrc(text) {
29
+ var env = {};
30
+ var source = typeof text === "string" ? text : "";
31
+ if (source.indexOf("\0") !== -1) throw new Error("Environment values cannot contain NUL bytes");
32
+ var lines = source.split(/\r?\n/);
33
+ for (var i = 0; i < lines.length; i++) {
34
+ var line = lines[i].trim();
35
+ if (!line || line.charAt(0) === "#") continue;
36
+ if (line.indexOf("export ") === 0) line = line.substring(7).trim();
37
+ var equals = line.indexOf("=");
38
+ if (equals < 1) throw new Error("Unsupported syntax at line " + (i + 1) + ": use KEY=VALUE or export KEY=VALUE");
39
+ var key = line.substring(0, equals).trim();
40
+ if (!SAFE_KEY.test(key)) throw new Error("Invalid variable name at line " + (i + 1) + ": " + key);
41
+ env[key] = parseQuotedValue(line.substring(equals + 1), i + 1);
42
+ }
43
+ return env;
44
+ }
45
+
46
+ function validateEnvString(text) {
47
+ try { parseEnvrc(text); return null; }
48
+ catch (e) { return e.message || String(e); }
49
+ }
50
+
51
+ function applyEnv(target, values) {
52
+ var keys = Object.keys(values || {});
53
+ for (var i = 0; i < keys.length; i++) if (!isProtectedKey(keys[i])) target[keys[i]] = values[keys[i]];
54
+ return target;
55
+ }
56
+
57
+ function resolveRuntimeEnv(opts) {
58
+ opts = opts || {};
59
+ var result = Object.assign({}, opts.baseEnv || process.env);
60
+ applyEnv(result, parseEnvrc(opts.sharedEnvrc || ""));
61
+ applyEnv(result, parseEnvrc(opts.projectEnvrc || ""));
62
+ return result;
63
+ }
64
+
65
+ module.exports = { parseEnvrc: parseEnvrc, validateEnvString: validateEnvString, resolveRuntimeEnv: resolveRuntimeEnv, isProtectedKey: isProtectedKey };