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,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
+ }
@@ -29,6 +29,10 @@ var modalWs = null;
29
29
  var modalResizeObserver = null;
30
30
  var modalKeyHandler = null;
31
31
  var modalResizeDebounce = null;
32
+ // Optional per-open callback fired once the modal is torn down, whatever
33
+ // dismissed it (Esc, backdrop click, term_closed). The login flow uses it to
34
+ // cancel a login the user walked away from.
35
+ var modalCloseHook = null;
32
36
  // Debounced fit+redraw for the modal xterm. Same rationale as
33
37
  // session-tui-view.js: collapses rapid resize events into a single
34
38
  // SIGWINCH so claude can redraw cleanly without mid-resize corruption.
@@ -139,6 +143,7 @@ export function openTuiModal(terminalId, sourceSlug, info) {
139
143
  modalTerminalId = terminalId;
140
144
  modalSourceSlug = sourceSlug;
141
145
  var infoObj = info || {};
146
+ modalCloseHook = typeof infoObj.onClose === "function" ? infoObj.onClose : null;
142
147
  setModalBreadcrumb({
143
148
  projectIcon: infoObj.projectIcon || null,
144
149
  projectName: infoObj.projectName || sourceSlug,
@@ -232,6 +237,8 @@ export function openTuiModal(terminalId, sourceSlug, info) {
232
237
 
233
238
  export function closeTuiModal() {
234
239
  if (!modalEl) return;
240
+ var closeHook = modalCloseHook;
241
+ modalCloseHook = null;
235
242
  if (modalTerminalId != null && modalWs && modalWs.readyState === 1) {
236
243
  try { modalWs.send(JSON.stringify({ type: "term_detach", id: modalTerminalId })); } catch (e) {}
237
244
  }
@@ -247,6 +254,9 @@ export function closeTuiModal() {
247
254
  document.removeEventListener("keydown", modalKeyHandler);
248
255
  modalKeyHandler = null;
249
256
  }
257
+ if (closeHook) {
258
+ try { closeHook(); } catch (e) {}
259
+ }
250
260
  }
251
261
 
252
262
  export function isTuiModalOpen() {
@@ -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
  }