clay-server 3.8.1 → 4.0.0-beta.1

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 (182) 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-debate-proposal.js +133 -17
  16. package/lib/project-debate.js +214 -110
  17. package/lib/project-delegated-follow-up.js +84 -0
  18. package/lib/project-delegated-session.js +53 -0
  19. package/lib/project-filesystem.js +7 -9
  20. package/lib/project-knowledge.js +1 -0
  21. package/lib/project-mate-creation-proposal.js +120 -0
  22. package/lib/project-memory.js +14 -7
  23. package/lib/project-models.js +157 -32
  24. package/lib/project-session-notes.js +2 -2
  25. package/lib/project-session-pair.js +8 -8
  26. package/lib/project-sessions.js +66 -51
  27. package/lib/project-worker-proposal.js +25 -25
  28. package/lib/project-workspace-query.js +144 -0
  29. package/lib/project.js +329 -102
  30. package/lib/public/app.js +85 -42
  31. package/lib/public/css/avatar-imprints.css +0 -1
  32. package/lib/public/css/base.css +4 -0
  33. package/lib/public/css/capsule-ui.css +389 -0
  34. package/lib/public/css/command-palette.css +306 -6
  35. package/lib/public/css/home-capsule-library.css +243 -0
  36. package/lib/public/css/home-capsule-source.css +192 -0
  37. package/lib/public/css/home-debate-live.css +146 -0
  38. package/lib/public/css/home-debate-planning.css +125 -0
  39. package/lib/public/css/home-debates-archive.css +281 -0
  40. package/lib/public/css/home-hub.css +896 -405
  41. package/lib/public/css/home-mate-creation.css +34 -0
  42. package/lib/public/css/home-mate-model-picker.css +131 -0
  43. package/lib/public/css/home-mate-settings.css +344 -0
  44. package/lib/public/css/home-session-actions.css +265 -0
  45. package/lib/public/css/home-sidebar.css +667 -0
  46. package/lib/public/css/icon-strip.css +1 -2
  47. package/lib/public/css/mates.css +7 -34
  48. package/lib/public/css/pane.css +1 -1
  49. package/lib/public/css/playbook.css +0 -80
  50. package/lib/public/css/sticky-notes.css +53 -117
  51. package/lib/public/css/tui-attention.css +0 -44
  52. package/lib/public/css/workspace-assignment.css +45 -0
  53. package/lib/public/fonts/source-serif-4/LICENSE.md +93 -0
  54. package/lib/public/fonts/source-serif-4/SourceSerif4Caption-Semibold.ttf.woff2 +0 -0
  55. package/lib/public/index.html +93 -74
  56. package/lib/public/modules/app-connection.js +10 -1
  57. package/lib/public/modules/app-dm.js +43 -160
  58. package/lib/public/modules/app-home-hub.js +328 -585
  59. package/lib/public/modules/app-message-cards.js +242 -0
  60. package/lib/public/modules/app-message-router.js +84 -0
  61. package/lib/public/modules/app-messages.js +78 -20
  62. package/lib/public/modules/app-notifications.js +4 -2
  63. package/lib/public/modules/app-projects.js +9 -1
  64. package/lib/public/modules/app-rendering.js +21 -602
  65. package/lib/public/modules/avatar-imprint.js +11 -8
  66. package/lib/public/modules/chat-bubble-renderer.js +152 -0
  67. package/lib/public/modules/chat-render-runtime.js +198 -0
  68. package/lib/public/modules/command-palette.js +225 -436
  69. package/lib/public/modules/dm-render.js +82 -0
  70. package/lib/public/modules/home-capsule-creation-intent.js +31 -0
  71. package/lib/public/modules/home-capsule-library.js +146 -0
  72. package/lib/public/modules/home-capsule-source.js +383 -0
  73. package/lib/public/modules/home-chat-identity.js +35 -0
  74. package/lib/public/modules/home-chat-scroll.js +77 -0
  75. package/lib/public/modules/home-chat-stream-state.js +34 -0
  76. package/lib/public/modules/home-composer-focus.js +25 -0
  77. package/lib/public/modules/home-conversations-sheet.js +179 -0
  78. package/lib/public/modules/home-debate-activity.js +48 -0
  79. package/lib/public/modules/home-debate-controls.js +295 -0
  80. package/lib/public/modules/home-debate-launch.js +41 -0
  81. package/lib/public/modules/home-debate-live.js +284 -0
  82. package/lib/public/modules/home-debate-models.js +73 -0
  83. package/lib/public/modules/home-debate-planning.js +335 -0
  84. package/lib/public/modules/home-debates-archive.js +279 -0
  85. package/lib/public/modules/home-dock-resize.js +74 -0
  86. package/lib/public/modules/home-dock.js +446 -0
  87. package/lib/public/modules/home-mate-chat.js +496 -0
  88. package/lib/public/modules/home-mate-creation.js +109 -0
  89. package/lib/public/modules/home-mate-model-picker.js +281 -0
  90. package/lib/public/modules/home-mate-selection.js +38 -0
  91. package/lib/public/modules/home-mate-settings-menu.js +131 -0
  92. package/lib/public/modules/home-mate-settings.js +383 -0
  93. package/lib/public/modules/home-session-actions.js +373 -0
  94. package/lib/public/modules/home-session-model-confirmation.js +13 -0
  95. package/lib/public/modules/home-shell.js +9 -0
  96. package/lib/public/modules/home-sidebar-chat-list.js +63 -0
  97. package/lib/public/modules/home-sidebar.js +158 -0
  98. package/lib/public/modules/home-sub-surface.js +19 -0
  99. package/lib/public/modules/home-surface-boot.js +68 -0
  100. package/lib/public/modules/home-surface.js +141 -0
  101. package/lib/public/modules/home-tools.js +271 -0
  102. package/lib/public/modules/markdown.js +50 -2
  103. package/lib/public/modules/mate-management.js +86 -0
  104. package/lib/public/modules/mate-wizard.js +0 -7
  105. package/lib/public/modules/mention.js +17 -19
  106. package/lib/public/modules/profile.js +4 -5
  107. package/lib/public/modules/project-settings.js +34 -28
  108. package/lib/public/modules/project-switcher.js +11 -5
  109. package/lib/public/modules/project-workspace-assignment.js +23 -0
  110. package/lib/public/modules/search-clay-chat.js +402 -0
  111. package/lib/public/modules/server-settings.js +14 -12
  112. package/lib/public/modules/session-actions.js +1 -1
  113. package/lib/public/modules/sidebar-mates.js +56 -552
  114. package/lib/public/modules/sidebar-mobile.js +7 -64
  115. package/lib/public/modules/sidebar-presence.js +37 -0
  116. package/lib/public/modules/split-pair-ui.js +13 -13
  117. package/lib/public/modules/sticky-notes.js +7 -6
  118. package/lib/public/modules/tool-input-composition.js +48 -0
  119. package/lib/public/modules/tool-llm-status.js +167 -0
  120. package/lib/public/modules/tool-renderer-advanced.js +285 -0
  121. package/lib/public/modules/tool-renderer-chart.js +158 -0
  122. package/lib/public/modules/tool-renderer-semantics.js +35 -0
  123. package/lib/public/modules/tool-renderer.js +464 -0
  124. package/lib/public/modules/tool-runtime.js +309 -0
  125. package/lib/public/modules/tool-ui-evaluator.js +97 -0
  126. package/lib/public/modules/tool-ui-tree.js +21 -0
  127. package/lib/public/modules/user-settings.js +16 -23
  128. package/lib/public/modules/worker-proposal.js +2 -2
  129. package/lib/public/modules/workspace-assignment-card.js +149 -0
  130. package/lib/public/modules/workspace-assignment-routing.js +4 -0
  131. package/lib/public/style.css +13 -2
  132. package/lib/runtime-env.js +65 -0
  133. package/lib/sdk-bridge.js +120 -32
  134. package/lib/sdk-message-processor.js +10 -6
  135. package/lib/server-home-capsule-creation.js +36 -0
  136. package/lib/server-home-chat-events.js +287 -0
  137. package/lib/server-home-chat.js +518 -0
  138. package/lib/server-home-clay-entry.js +65 -0
  139. package/lib/server-home-clay-session-links.js +26 -0
  140. package/lib/server-home-debate-planning.js +214 -0
  141. package/lib/server-home-debates.js +137 -0
  142. package/lib/server-home-mate-creation.js +261 -0
  143. package/lib/server-home-models.js +241 -0
  144. package/lib/server-home-preferences.js +61 -0
  145. package/lib/server-mates.js +37 -0
  146. package/lib/server-tools.js +377 -0
  147. package/lib/server.js +111 -59
  148. package/lib/session-pair-mcp-server.js +6 -6
  149. package/lib/sessions.js +18 -3
  150. package/lib/tool-capsule-source.js +142 -0
  151. package/lib/tool-control-mcp-server.js +126 -0
  152. package/lib/tool-llm.js +48 -0
  153. package/lib/tool-storage.js +77 -0
  154. package/lib/tool-ui-spec-advanced.js +82 -0
  155. package/lib/tool-ui-spec.js +261 -0
  156. package/lib/tools-registry.js +276 -0
  157. package/lib/users-home-dock-preferences.js +77 -0
  158. package/lib/users-home-surface-preferences.js +99 -0
  159. package/lib/users-preferences.js +4 -7
  160. package/lib/users.js +12 -0
  161. package/lib/whats-new-content.js +3 -3
  162. package/lib/workspace-assignment-service.js +420 -0
  163. package/lib/workspace-query-mcp-server.js +154 -0
  164. package/lib/workspace-query-service.js +492 -0
  165. package/lib/ws-schema.js +86 -12
  166. package/lib/yoke/adapters/acp.js +2 -1
  167. package/lib/yoke/adapters/antigravity.js +2 -1
  168. package/lib/yoke/adapters/claude-worker.js +11 -4
  169. package/lib/yoke/adapters/claude.js +73 -43
  170. package/lib/yoke/adapters/codex.js +37 -6
  171. package/lib/yoke/adapters/kiro.js +2 -1
  172. package/lib/yoke/claude-user-input.js +38 -0
  173. package/lib/yoke/codex-user-input.js +64 -0
  174. package/lib/yoke/complete-once.js +164 -0
  175. package/lib/yoke/index.js +5 -1
  176. package/lib/yoke/interface.js +19 -0
  177. package/lib/yoke/mcp-bridge-server.js +2 -6
  178. package/lib/yoke/user-input.js +254 -0
  179. package/package.json +2 -1
  180. package/lib/public/css/home-chat.css +0 -380
  181. package/lib/public/modules/home-chat.js +0 -438
  182. package/lib/server-clay-home.js +0 -245
@@ -0,0 +1,309 @@
1
+ // tool-runtime.js - Web Worker host for user-installed tool logic.
2
+
3
+ import { getWs } from './ws-ref.js';
4
+
5
+ var ACTION_TIMEOUT_MS = 10000;
6
+ var LLM_ACTION_TIMEOUT_MS = 90000;
7
+ var LLM_TIMEOUT_MS = 60000;
8
+ var WS_WAIT_MS = 5000;
9
+ var WS_RETRY_MS = 250;
10
+
11
+ function workerProgram(logicSource, allowLlm) {
12
+ return [
13
+ "self.fetch = undefined; self.XMLHttpRequest = undefined; self.WebSocket = undefined; self.EventSource = undefined; self.importScripts = undefined;",
14
+ "var tool; var currentState = {}; var storageSeq = 0; var storagePending = Object.create(null); var llmSeq = 0; var llmPending = Object.create(null); var actionQueue = Promise.resolve();",
15
+ "function storageCall(op, args) {",
16
+ " return new Promise(function (resolve, reject) {",
17
+ " storageSeq++; storagePending[storageSeq] = { resolve: resolve, reject: reject };",
18
+ " self.postMessage({ type: 'storage', op: op, args: args || {}, seq: storageSeq });",
19
+ " });",
20
+ "}",
21
+ "function llmCall(args, callerId) {",
22
+ allowLlm ? " return new Promise(function (resolve, reject) { llmSeq++; llmPending[llmSeq] = { resolve: resolve, reject: reject }; self.postMessage({ type: 'llm', seq: llmSeq, args: args || {}, callerId: callerId || 'user' }); });" : " return Promise.reject(new Error('This capsule does not have the llm permission.'));",
23
+ "}",
24
+ "var api = { storage: {",
25
+ " list: function () { return storageCall('list', {}); },",
26
+ " get: function (id) { return storageCall('get', { id: id }); },",
27
+ " put: function (doc) { return storageCall('put', { doc: doc }); },",
28
+ " delete: function (id) { return storageCall('delete', { id: id }); },",
29
+ " query: function (query) { return storageCall('query', { query: query }); }",
30
+ "}, llm: { complete: function () { return Promise.reject(new Error('api.llm.complete is only available inside an action.')); } } };",
31
+ logicSource,
32
+ "if (!tool || !tool.actions || typeof tool.actions !== 'object') throw new Error('logic.js must define var tool with an actions object.');",
33
+ "self.onmessage = function (event) {",
34
+ " var msg = event.data || {};",
35
+ " if (msg.type === 'init') {",
36
+ " currentState = msg.state || tool.initialState || {};",
37
+ " self.postMessage({ type: 'state', newState: currentState }); return;",
38
+ " }",
39
+ " if (msg.type === 'storage_result') {",
40
+ " var pending = storagePending[msg.seq]; if (!pending) return; delete storagePending[msg.seq];",
41
+ " if (msg.error) pending.reject(new Error(msg.error)); else pending.resolve(msg.data); return;",
42
+ " }",
43
+ " if (msg.type === 'llm_result') {",
44
+ " var llmRequest = llmPending[msg.seq]; if (!llmRequest) return; delete llmPending[msg.seq];",
45
+ " if (msg.error) llmRequest.reject(new Error(msg.error)); else llmRequest.resolve(msg.data); return;",
46
+ " }",
47
+ " if (msg.type === 'action') {",
48
+ " var action = tool.actions[msg.name];",
49
+ " if (typeof action !== 'function') { self.postMessage({ type: 'error', message: 'Unknown action: ' + msg.name, actionSeq: msg.actionSeq }); return; }",
50
+ " var actionStartState;",
51
+ " actionQueue = actionQueue.then(function () {",
52
+ " actionStartState = currentState;",
53
+ " var actionApi = Object.create(api); actionApi.callerId = msg.callerId || 'user'; actionApi.llm = { complete: function (args) { return llmCall(args, actionApi.callerId); } };",
54
+ " actionApi.setState = function (nextState) { if (!nextState || typeof nextState !== 'object' || Array.isArray(nextState)) throw new Error('api.setState requires a state object.'); currentState = nextState; self.postMessage({ type: 'state', newState: currentState, actionSeq: msg.actionSeq, intermediate: true }); };",
55
+ " return action(currentState, msg.args || {}, actionApi);",
56
+ " }).then(function (nextState) {",
57
+ " if (nextState !== undefined) currentState = nextState;",
58
+ " self.postMessage({ type: 'state', newState: currentState, actionSeq: msg.actionSeq });",
59
+ " }).catch(function (error) { if (actionStartState !== undefined) { currentState = actionStartState; self.postMessage({ type: 'state', newState: currentState, actionSeq: msg.actionSeq, intermediate: true, rollback: true }); } self.postMessage({ type: 'error', message: error && error.message ? error.message : String(error), actionSeq: msg.actionSeq }); });",
60
+ " }",
61
+ "};",
62
+ ].join("\n");
63
+ }
64
+
65
+ export function createToolRuntime(config) {
66
+ var worker = null;
67
+ var state = config.state || null;
68
+ var actionSeq = 0;
69
+ var actionTimeouts = Object.create(null);
70
+ var stopped = false;
71
+ var restartCount = 0;
72
+ var initialActionSent = false;
73
+ var workerGeneration = 0;
74
+ var pendingStorage = Object.create(null);
75
+ var pendingLlm = Object.create(null);
76
+ var pendingActions = Object.create(null);
77
+ var readyWaiters = [];
78
+ var isReady = false;
79
+
80
+ function clearActionTimeout() {
81
+ var keys = Object.keys(actionTimeouts);
82
+ for (var i = 0; i < keys.length; i++) clearTimeout(actionTimeouts[keys[i]]);
83
+ actionTimeouts = Object.create(null);
84
+ }
85
+
86
+ function clearOneActionTimeout(seq) {
87
+ if (seq === undefined || !actionTimeouts[seq]) return;
88
+ clearTimeout(actionTimeouts[seq]);
89
+ delete actionTimeouts[seq];
90
+ }
91
+
92
+ function rejectActions(message) {
93
+ var keys = Object.keys(pendingActions);
94
+ for (var i = 0; i < keys.length; i++) pendingActions[keys[i]].reject(new Error(message));
95
+ pendingActions = Object.create(null);
96
+ }
97
+
98
+ function reportError(message) {
99
+ if (typeof config.onError === "function") config.onError(message);
100
+ }
101
+
102
+ function markReady() {
103
+ isReady = true;
104
+ if (readyWaiters.length === 0) return;
105
+ var waiters = readyWaiters;
106
+ readyWaiters = [];
107
+ for (var i = 0; i < waiters.length; i++) waiters[i].resolve(state);
108
+ }
109
+
110
+ function terminateWorker() {
111
+ clearActionTimeout();
112
+ if (worker) worker.terminate();
113
+ worker = null;
114
+ pendingStorage = Object.create(null);
115
+ var llmKeys = Object.keys(pendingLlm);
116
+ for (var li = 0; li < llmKeys.length; li++) clearTimeout(pendingLlm[llmKeys[li]].timer);
117
+ pendingLlm = Object.create(null);
118
+ }
119
+
120
+ function restartWorker(message) {
121
+ rejectActions(message);
122
+ terminateWorker();
123
+ reportError(message);
124
+ if (stopped) return;
125
+ restartCount++;
126
+ if (restartCount > 3) {
127
+ reportError("Tool worker stopped after repeated failures.");
128
+ var waiters = readyWaiters;
129
+ readyWaiters = [];
130
+ for (var i = 0; i < waiters.length; i++) waiters[i].reject(new Error(message));
131
+ return;
132
+ }
133
+ initialActionSent = false;
134
+ isReady = false;
135
+ setTimeout(function () { if (!stopped) spawnWorker(); }, 100);
136
+ }
137
+
138
+ function withReadySocket(startedAt, generation, onReady, onTimeout) {
139
+ if (stopped || generation !== workerGeneration || !worker) return;
140
+ var ws = getWs();
141
+ if (ws && ws.readyState === 1) {
142
+ try {
143
+ onReady(ws);
144
+ return;
145
+ } catch (error) {}
146
+ }
147
+ var waitMs = config.wsWaitMs === undefined ? WS_WAIT_MS : config.wsWaitMs;
148
+ if (Date.now() - startedAt >= waitMs) {
149
+ onTimeout();
150
+ return;
151
+ }
152
+ var retryMs = config.wsRetryMs === undefined ? WS_RETRY_MS : config.wsRetryMs;
153
+ setTimeout(function () { withReadySocket(startedAt, generation, onReady, onTimeout); }, retryMs);
154
+ }
155
+
156
+ function sendStorageOperation(msg) {
157
+ var generation = workerGeneration;
158
+ withReadySocket(Date.now(), generation, function (ws) {
159
+ var externalSeq = config.toolId + ":" + generation + ":" + msg.seq;
160
+ pendingStorage[externalSeq] = msg.seq;
161
+ try {
162
+ ws.send(JSON.stringify({ type: "tool_storage_op", toolId: config.toolId, op: msg.op, args: msg.args, seq: externalSeq }));
163
+ } catch (error) {
164
+ delete pendingStorage[externalSeq];
165
+ throw error;
166
+ }
167
+ }, function () {
168
+ if (worker && generation === workerGeneration) {
169
+ worker.postMessage({ type: "storage_result", seq: msg.seq, error: "Tool storage is unavailable after waiting 5 seconds for the connection." });
170
+ }
171
+ });
172
+ }
173
+
174
+ function sendLlmOperation(msg) {
175
+ if (!config.allowLlm) {
176
+ worker.postMessage({ type: "llm_result", seq: msg.seq, error: "This capsule does not have the llm permission." });
177
+ return;
178
+ }
179
+ if (typeof config.onLlmRequest === "function") config.onLlmRequest(msg.args && msg.args.model ? msg.args.model : "fast");
180
+ var generation = workerGeneration;
181
+ withReadySocket(Date.now(), generation, function (ws) {
182
+ var requestId = config.toolId + ":llm:" + generation + ":" + msg.seq;
183
+ pendingLlm[requestId] = {
184
+ seq: msg.seq,
185
+ timer: setTimeout(function () {
186
+ if (!pendingLlm[requestId] || !worker) return;
187
+ delete pendingLlm[requestId];
188
+ worker.postMessage({ type: "llm_result", seq: msg.seq, error: "LLM request timed out after 60 seconds." });
189
+ }, LLM_TIMEOUT_MS),
190
+ };
191
+ try {
192
+ ws.send(JSON.stringify({ type: "tool_llm_op", toolId: config.toolId, requestId: requestId, args: msg.args, callerId: msg.callerId || "user" }));
193
+ } catch (error) {
194
+ clearTimeout(pendingLlm[requestId].timer);
195
+ delete pendingLlm[requestId];
196
+ throw error;
197
+ }
198
+ }, function () {
199
+ if (worker && generation === workerGeneration) {
200
+ worker.postMessage({ type: "llm_result", seq: msg.seq, error: "Tool LLM service is unavailable after waiting 5 seconds for the connection." });
201
+ }
202
+ });
203
+ }
204
+
205
+ function handleWorkerMessage(event) {
206
+ var msg = event.data || {};
207
+ if (msg.type === "state") {
208
+ restartCount = 0;
209
+ state = msg.newState || {};
210
+ if (typeof config.onState === "function") config.onState(state);
211
+ if (msg.intermediate) return;
212
+ clearOneActionTimeout(msg.actionSeq);
213
+ if (msg.actionSeq !== undefined && pendingActions[msg.actionSeq]) {
214
+ pendingActions[msg.actionSeq].resolve(state);
215
+ delete pendingActions[msg.actionSeq];
216
+ }
217
+ var shouldInitialize = !initialActionSent && config.initialAction;
218
+ if (shouldInitialize) {
219
+ initialActionSent = true;
220
+ dispatchAction(config.initialAction, {}, "user").catch(function () { markReady(); });
221
+ } else {
222
+ markReady();
223
+ }
224
+ } else if (msg.type === "storage") {
225
+ sendStorageOperation(msg);
226
+ } else if (msg.type === "llm") {
227
+ sendLlmOperation(msg);
228
+ } else if (msg.type === "error") {
229
+ clearOneActionTimeout(msg.actionSeq);
230
+ if (msg.actionSeq !== undefined) {
231
+ if (pendingActions[msg.actionSeq]) {
232
+ pendingActions[msg.actionSeq].reject(new Error(msg.message || "Tool action failed."));
233
+ delete pendingActions[msg.actionSeq];
234
+ }
235
+ reportError(msg.message || "Tool action failed.");
236
+ return;
237
+ }
238
+ restartWorker(msg.message || "Tool worker failed.");
239
+ }
240
+ }
241
+
242
+ function spawnWorker() {
243
+ terminateWorker();
244
+ workerGeneration++;
245
+ var blob = new Blob([workerProgram(config.logicSource, !!config.allowLlm)], { type: "text/javascript" });
246
+ var url = URL.createObjectURL(blob);
247
+ worker = new Worker(url);
248
+ URL.revokeObjectURL(url);
249
+ worker.onmessage = handleWorkerMessage;
250
+ worker.onerror = function (event) {
251
+ if (event.preventDefault) event.preventDefault();
252
+ restartWorker(event.message || "Tool worker crashed.");
253
+ };
254
+ worker.postMessage({ type: "init", state: state });
255
+ }
256
+
257
+ function dispatchAction(name, args, callerId) {
258
+ if (!worker || stopped) return Promise.reject(new Error("Tool worker is not running."));
259
+ actionSeq++;
260
+ var currentActionSeq = actionSeq;
261
+ return new Promise(function (resolve, reject) {
262
+ pendingActions[currentActionSeq] = { resolve: resolve, reject: reject };
263
+ var timeoutMs = config.allowLlm ? LLM_ACTION_TIMEOUT_MS : ACTION_TIMEOUT_MS;
264
+ actionTimeouts[currentActionSeq] = setTimeout(function () {
265
+ restartWorker("Action '" + name + "' timed out after " + Math.round(timeoutMs / 1000) + " seconds.");
266
+ }, timeoutMs);
267
+ worker.postMessage({ type: "action", name: name, args: args || {}, callerId: callerId || "user", actionSeq: currentActionSeq });
268
+ });
269
+ }
270
+
271
+ function handleStorageResult(msg) {
272
+ if (!worker) return;
273
+ var localSeq = pendingStorage[msg.seq];
274
+ if (localSeq === undefined) return;
275
+ delete pendingStorage[msg.seq];
276
+ worker.postMessage({ type: "storage_result", seq: localSeq, data: msg.data, error: msg.error });
277
+ }
278
+
279
+ function handleLlmResult(msg) {
280
+ if (!worker) return;
281
+ var pending = pendingLlm[msg.requestId];
282
+ if (!pending) return;
283
+ clearTimeout(pending.timer);
284
+ delete pendingLlm[msg.requestId];
285
+ worker.postMessage({ type: "llm_result", seq: pending.seq, data: msg.data, error: msg.error });
286
+ }
287
+
288
+ function start() {
289
+ stopped = false;
290
+ isReady = false;
291
+ spawnWorker();
292
+ }
293
+
294
+ function stop() {
295
+ stopped = true;
296
+ rejectActions("Tool worker stopped.");
297
+ var waiters = readyWaiters;
298
+ readyWaiters = [];
299
+ for (var i = 0; i < waiters.length; i++) waiters[i].reject(new Error("Tool worker stopped."));
300
+ terminateWorker();
301
+ }
302
+
303
+ function ready() {
304
+ if (isReady) return Promise.resolve(state);
305
+ return new Promise(function (resolve, reject) { readyWaiters.push({ resolve: resolve, reject: reject }); });
306
+ }
307
+
308
+ return { start: start, ready: ready, action: dispatchAction, handleStorageResult: handleStorageResult, handleLlmResult: handleLlmResult, stop: stop, getState: function () { return state; } };
309
+ }
@@ -0,0 +1,97 @@
1
+ // Pure bounded evaluation for safe declarative Capsule UI.
2
+
3
+ var MAX_COLLECTION_ITEMS = 500;
4
+
5
+ export function normalizeOptions(value) {
6
+ if (!Array.isArray(value)) return [];
7
+ return value.slice(0, 100).reduce(function (result, option) {
8
+ if (typeof option === "string" || typeof option === "number") result.push({ value: option, label: String(option), disabled: false });
9
+ else if (option && typeof option === "object" && (typeof option.value === "string" || typeof option.value === "number") && typeof option.label === "string") result.push({ value: option.value, label: option.label.slice(0, 500), disabled: option.disabled === true });
10
+ return result;
11
+ }, []);
12
+ }
13
+
14
+ export function normalizeColumns(value) {
15
+ if (!Array.isArray(value)) return [];
16
+ return value.slice(0, 20).filter(function (column) {
17
+ return column && typeof column === "object" && typeof column.key === "string" && /^(?:\$(?:state|item)\.)?[A-Za-z0-9_-]+(?:\.[A-Za-z0-9_-]+)*$/.test(column.key) && !/(?:__proto__|constructor|prototype)/.test(column.key);
18
+ }).map(function (column) { return { key: column.key, label: typeof column.label === "string" ? column.label.slice(0, 500) : column.key }; });
19
+ }
20
+
21
+ export function valueAtPath(state, path, item) {
22
+ if (!path) return undefined;
23
+ var source = state;
24
+ var parts = path.split(".");
25
+ if (parts[0] === "$item") { source = item; parts.shift(); }
26
+ else if (parts[0] === "$state") parts.shift();
27
+ for (var i = 0; i < parts.length; i++) {
28
+ if (source === null || source === undefined) return undefined;
29
+ source = source[parts[i]];
30
+ }
31
+ return source;
32
+ }
33
+
34
+ export function resolveValue(value, state, item) {
35
+ if (value && typeof value === "object" && !Array.isArray(value) && value.$bind) {
36
+ var bound = valueAtPath(state, value.$bind, item);
37
+ if (Array.isArray(value.$enum)) return value.$enum.indexOf(bound) !== -1 ? bound : value.fallback !== undefined ? value.fallback : value.$enum[0];
38
+ return bound === undefined ? value.fallback : bound;
39
+ }
40
+ if (typeof value === "string" && (value.indexOf("$item") === 0 || value.indexOf("$state") === 0)) return valueAtPath(state, value, item);
41
+ if (Array.isArray(value)) return value.map(function (entry) { return resolveValue(entry, state, item); });
42
+ if (value && typeof value === "object") {
43
+ var result = {};
44
+ Object.keys(value).forEach(function (key) { result[key] = resolveValue(value[key], state, item); });
45
+ return result;
46
+ }
47
+ return value;
48
+ }
49
+
50
+ export function evaluateCondition(condition, state, item) {
51
+ if (typeof condition === "string") return !!valueAtPath(state, condition, item);
52
+ if (!condition || typeof condition !== "object") return false;
53
+ if (condition.all) return condition.all.every(function (entry) { return evaluateCondition(entry, state, item); });
54
+ if (condition.any) return condition.any.some(function (entry) { return evaluateCondition(entry, state, item); });
55
+ if (condition.not) return !evaluateCondition(condition.not, state, item);
56
+ var operators = ["equals", "notEquals", "in", "gt", "gte", "lt", "lte"];
57
+ for (var i = 0; i < operators.length; i++) {
58
+ var operator = operators[i];
59
+ if (!condition[operator]) continue;
60
+ var rule = condition[operator];
61
+ var actual = valueAtPath(state, rule.path, item);
62
+ if (operator === "equals") return actual === rule.value;
63
+ if (operator === "notEquals") return actual !== rule.value;
64
+ if (operator === "in") return rule.values.indexOf(actual) !== -1;
65
+ if (typeof actual !== "number") return false;
66
+ if (operator === "gt") return actual > rule.value;
67
+ if (operator === "gte") return actual >= rule.value;
68
+ if (operator === "lt") return actual < rule.value;
69
+ if (operator === "lte") return actual <= rule.value;
70
+ }
71
+ return false;
72
+ }
73
+
74
+ export function resolveProps(props, state, item) {
75
+ var result = {};
76
+ Object.keys(props || {}).forEach(function (key) { result[key] = resolveValue(props[key], state, item); });
77
+ return result;
78
+ }
79
+
80
+ export function collectionView(value, props) {
81
+ var items = Array.isArray(value) ? value.slice(0, MAX_COLLECTION_ITEMS) : [];
82
+ var filter = String(props.filter || "").trim().toLocaleLowerCase();
83
+ if (filter) items = items.filter(function (item) {
84
+ var candidate = props.filterKey ? valueAtPath(item, props.filterKey, null) : item;
85
+ return String(candidate === undefined ? "" : candidate).toLocaleLowerCase().indexOf(filter) !== -1;
86
+ });
87
+ if (props.sortKey) items.sort(function (left, right) {
88
+ var a = valueAtPath(left, props.sortKey, null);
89
+ var b = valueAtPath(right, props.sortKey, null);
90
+ if (a === b) return 0;
91
+ var result = a === undefined || a === null ? 1 : b === undefined || b === null ? -1 : a < b ? -1 : 1;
92
+ return props.sortDirection === "desc" ? -result : result;
93
+ });
94
+ var pageSize = Math.max(1, Math.min(100, Number(props.pageSize) || 100));
95
+ var page = Math.max(1, Number(props.page) || 1);
96
+ return items.slice((page - 1) * pageSize, page * pageSize);
97
+ }
@@ -0,0 +1,21 @@
1
+ // Pure helpers for inspecting declarative Capsule UI trees.
2
+
3
+ export function hasToolUiNodeType(node, type) {
4
+ if (!node || typeof node !== "object") return false;
5
+ if (node.type === type) return true;
6
+ var children = Array.isArray(node.children) ? node.children : [];
7
+ for (var i = 0; i < children.length; i++) {
8
+ if (hasToolUiNodeType(children[i], type)) return true;
9
+ }
10
+ return false;
11
+ }
12
+
13
+ export function isToolModelAlias(value) {
14
+ return value === "fast" || value === "standard" || value === "deep";
15
+ }
16
+
17
+ export function shouldInjectToolLlmStatus(definition) {
18
+ var permissions = definition && definition.manifest && Array.isArray(definition.manifest.permissions)
19
+ ? definition.manifest.permissions : [];
20
+ return permissions.indexOf("llm") !== -1 && !hasToolUiNodeType(definition.uiTree, "model-select");
21
+ }
@@ -211,22 +211,20 @@ export function initUserSettings(appCtx) {
211
211
  });
212
212
  }
213
213
 
214
- // Mates UI toggle. Default-on, so flipping off hides every Mates
215
- // surface in the app (sidebar avatars, DM picker entry, home-hub strip)
216
- // via the body.mates-disabled CSS gate. Flipping back on restores the
217
- // surface; existing Mate data is never deleted.
214
+ // This legacy preference now controls only project-local Mate DM entry
215
+ // points. Home, mentions, creation, debates, and Mate data stay available.
218
216
  var matesEnabledToggle = document.getElementById('us-mates-enabled');
219
217
  if (matesEnabledToggle) {
220
218
  matesEnabledToggle.addEventListener('change', function () {
221
219
  var want = !!this.checked;
222
- applyMatesEnabledClass(want);
223
- store.set({ matesEnabled: want });
220
+ applyProjectMateDmsClass(want);
221
+ store.set({ projectMateDmsEnabled: want });
224
222
  fetch('/api/user/mates-enabled', {
225
223
  method: 'PUT',
226
224
  headers: { 'Content-Type': 'application/json' },
227
225
  body: JSON.stringify({ enabled: want }),
228
226
  }).then(function (r) { return r.json(); }).then(function (data) {
229
- if (data.ok) showToast(data.matesEnabled ? 'Mates on' : 'Mates off');
227
+ if (data.ok) showToast(data.matesEnabled ? 'Mate DMs shown in projects' : 'Mate DMs hidden in projects');
230
228
  }).catch(function () {});
231
229
  });
232
230
  }
@@ -317,22 +315,17 @@ function stopProp(e) {
317
315
  e.stopPropagation();
318
316
  }
319
317
 
320
- // Mates UI gate. When the toggle is off we add `mates-disabled` to the
321
- // body so CSS can hide every Mates surface (sidebar avatars, DM picker
322
- // "Create a Mate" entry, home-hub mates strip) without touching the
323
- // underlying data. JS code paths that gate on `store.get('matesEnabled')`
324
- // also short-circuit so we don't, for example, open the mate wizard if
325
- // someone fires it programmatically.
326
- function applyMatesEnabledClass(enabled) {
318
+ // Project-only Mate DM gate. The persisted server field retains its legacy
319
+ // name for migration compatibility, but it no longer disables Mate features.
320
+ function applyProjectMateDmsClass(enabled) {
327
321
  if (!document.body) return;
328
- document.body.classList.toggle('mates-disabled', !enabled);
322
+ document.body.classList.toggle('project-mate-dms-disabled', !enabled);
329
323
  }
330
324
 
331
- export function isMatesEnabled() {
332
- // Default to true if the store hasn't received profile yet populating
333
- // the false state happens after /api/profile resolves, so an undefined
334
- // value at boot should not hide Mates UI.
335
- return store.get('matesEnabled') !== false;
325
+ export function isProjectMateDmsEnabled() {
326
+ // Default to true until the profile arrives so boot does not briefly hide
327
+ // a project shortcut that the persisted preference enables.
328
+ return store.get('projectMateDmsEnabled') !== false;
336
329
  }
337
330
 
338
331
  function showPinForm() {
@@ -467,12 +460,12 @@ function populateAccount() {
467
460
  if (data.mateOnboardingShown) {
468
461
  try { localStorage.setItem("clay-mate-onboarding-shown", "1"); } catch (e) {}
469
462
  }
470
- // Mates UI toggle: default OFF (opt-in); enable only when explicitly true
463
+ // Project Mate DM shortcuts remain opt-in for legacy profiles.
471
464
  var matesOn = data.matesEnabled === true;
472
465
  var matesToggle = document.getElementById('us-mates-enabled');
473
466
  if (matesToggle) matesToggle.checked = matesOn;
474
- store.set({ matesEnabled: matesOn });
475
- applyMatesEnabledClass(matesOn);
467
+ store.set({ projectMateDmsEnabled: matesOn });
468
+ applyProjectMateDmsClass(matesOn);
476
469
  if (data.chatLayout) {
477
470
  setChatLayout(data.chatLayout); // update local cache + CSS
478
471
  }
@@ -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
+ }