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.
- package/lib/ask-user-mcp-server.js +5 -119
- package/lib/builtin-mates.js +6 -6
- package/lib/claude-hook-installer.js +15 -0
- package/lib/clay-history-mcp-server.js +62 -297
- package/lib/daemon.js +1 -1
- package/lib/debate-model-selection.js +119 -0
- package/lib/home-debate-tool-policy.js +164 -0
- package/lib/home-response-notifications.js +26 -0
- package/lib/mate-creation-mcp-server.js +55 -0
- package/lib/mate-ready-creation.js +95 -0
- package/lib/mates.js +2 -0
- package/lib/notes.js +2 -1
- package/lib/project-ask-user.js +64 -0
- package/lib/project-capsule-catalog.js +101 -0
- package/lib/project-connection.js +4 -0
- package/lib/project-debate-proposal.js +133 -17
- package/lib/project-debate.js +214 -110
- package/lib/project-delegated-follow-up.js +84 -0
- package/lib/project-delegated-session.js +53 -0
- package/lib/project-filesystem.js +7 -9
- package/lib/project-knowledge.js +1 -0
- package/lib/project-mate-creation-proposal.js +120 -0
- package/lib/project-memory.js +14 -7
- package/lib/project-models.js +157 -32
- package/lib/project-session-notes.js +2 -2
- package/lib/project-session-pair.js +8 -8
- package/lib/project-sessions.js +66 -51
- package/lib/project-vendor-login.js +397 -0
- package/lib/project-worker-proposal.js +25 -25
- package/lib/project-workspace-query.js +144 -0
- package/lib/project.js +349 -102
- package/lib/public/app.js +85 -42
- package/lib/public/css/avatar-imprints.css +0 -1
- package/lib/public/css/base.css +4 -0
- package/lib/public/css/capsule-ui.css +389 -0
- package/lib/public/css/command-palette.css +306 -6
- package/lib/public/css/home-capsule-library.css +243 -0
- package/lib/public/css/home-capsule-source.css +192 -0
- package/lib/public/css/home-debate-live.css +146 -0
- package/lib/public/css/home-debate-planning.css +125 -0
- package/lib/public/css/home-debates-archive.css +281 -0
- package/lib/public/css/home-hub.css +896 -405
- package/lib/public/css/home-mate-creation.css +34 -0
- package/lib/public/css/home-mate-model-picker.css +131 -0
- package/lib/public/css/home-mate-settings.css +344 -0
- package/lib/public/css/home-session-actions.css +265 -0
- package/lib/public/css/home-sidebar.css +667 -0
- package/lib/public/css/icon-strip.css +1 -2
- package/lib/public/css/mates.css +7 -34
- package/lib/public/css/pane.css +1 -1
- package/lib/public/css/playbook.css +0 -80
- package/lib/public/css/sticky-notes.css +53 -117
- package/lib/public/css/tui-attention.css +0 -44
- package/lib/public/css/workspace-assignment.css +45 -0
- package/lib/public/fonts/source-serif-4/LICENSE.md +93 -0
- package/lib/public/fonts/source-serif-4/SourceSerif4Caption-Semibold.ttf.woff2 +0 -0
- package/lib/public/index.html +93 -74
- package/lib/public/modules/app-connection.js +10 -1
- package/lib/public/modules/app-dm.js +43 -160
- package/lib/public/modules/app-home-hub.js +328 -585
- package/lib/public/modules/app-message-cards.js +242 -0
- package/lib/public/modules/app-message-router.js +84 -0
- package/lib/public/modules/app-messages.js +102 -37
- package/lib/public/modules/app-notifications.js +8 -119
- package/lib/public/modules/app-projects.js +9 -1
- package/lib/public/modules/app-rendering.js +21 -602
- package/lib/public/modules/avatar-imprint.js +11 -8
- package/lib/public/modules/chat-bubble-renderer.js +152 -0
- package/lib/public/modules/chat-render-runtime.js +198 -0
- package/lib/public/modules/command-palette.js +225 -436
- package/lib/public/modules/dm-render.js +82 -0
- package/lib/public/modules/home-capsule-creation-intent.js +31 -0
- package/lib/public/modules/home-capsule-library.js +146 -0
- package/lib/public/modules/home-capsule-source.js +383 -0
- package/lib/public/modules/home-chat-identity.js +35 -0
- package/lib/public/modules/home-chat-scroll.js +77 -0
- package/lib/public/modules/home-chat-stream-state.js +34 -0
- package/lib/public/modules/home-composer-focus.js +25 -0
- package/lib/public/modules/home-conversations-sheet.js +179 -0
- package/lib/public/modules/home-debate-activity.js +48 -0
- package/lib/public/modules/home-debate-controls.js +295 -0
- package/lib/public/modules/home-debate-launch.js +41 -0
- package/lib/public/modules/home-debate-live.js +284 -0
- package/lib/public/modules/home-debate-models.js +73 -0
- package/lib/public/modules/home-debate-planning.js +335 -0
- package/lib/public/modules/home-debates-archive.js +279 -0
- package/lib/public/modules/home-dock-resize.js +74 -0
- package/lib/public/modules/home-dock.js +446 -0
- package/lib/public/modules/home-mate-chat.js +496 -0
- package/lib/public/modules/home-mate-creation.js +109 -0
- package/lib/public/modules/home-mate-model-picker.js +281 -0
- package/lib/public/modules/home-mate-selection.js +38 -0
- package/lib/public/modules/home-mate-settings-menu.js +131 -0
- package/lib/public/modules/home-mate-settings.js +383 -0
- package/lib/public/modules/home-session-actions.js +373 -0
- package/lib/public/modules/home-session-model-confirmation.js +13 -0
- package/lib/public/modules/home-shell.js +9 -0
- package/lib/public/modules/home-sidebar-chat-list.js +63 -0
- package/lib/public/modules/home-sidebar.js +158 -0
- package/lib/public/modules/home-sub-surface.js +19 -0
- package/lib/public/modules/home-surface-boot.js +68 -0
- package/lib/public/modules/home-surface.js +141 -0
- package/lib/public/modules/home-tools.js +271 -0
- package/lib/public/modules/markdown.js +50 -2
- package/lib/public/modules/mate-management.js +86 -0
- package/lib/public/modules/mate-wizard.js +0 -7
- package/lib/public/modules/mention.js +17 -19
- package/lib/public/modules/pane-bridge.js +9 -0
- package/lib/public/modules/profile.js +4 -5
- package/lib/public/modules/project-settings.js +34 -28
- package/lib/public/modules/project-switcher.js +11 -5
- package/lib/public/modules/project-workspace-assignment.js +23 -0
- package/lib/public/modules/search-clay-chat.js +402 -0
- package/lib/public/modules/server-settings.js +14 -12
- package/lib/public/modules/session-actions.js +1 -1
- package/lib/public/modules/sidebar-mates.js +56 -552
- package/lib/public/modules/sidebar-mobile.js +7 -64
- package/lib/public/modules/sidebar-presence.js +37 -0
- package/lib/public/modules/split-pair-ui.js +13 -13
- package/lib/public/modules/split-view.js +7 -0
- package/lib/public/modules/sticky-notes.js +7 -6
- package/lib/public/modules/tool-input-composition.js +48 -0
- package/lib/public/modules/tool-llm-status.js +167 -0
- package/lib/public/modules/tool-renderer-advanced.js +285 -0
- package/lib/public/modules/tool-renderer-chart.js +158 -0
- package/lib/public/modules/tool-renderer-semantics.js +35 -0
- package/lib/public/modules/tool-renderer.js +464 -0
- package/lib/public/modules/tool-runtime.js +309 -0
- package/lib/public/modules/tool-ui-evaluator.js +97 -0
- package/lib/public/modules/tool-ui-tree.js +21 -0
- package/lib/public/modules/tui-attention.js +10 -0
- package/lib/public/modules/user-settings.js +16 -23
- package/lib/public/modules/vendor-login.js +287 -0
- package/lib/public/modules/worker-proposal.js +2 -2
- package/lib/public/modules/workspace-assignment-card.js +149 -0
- package/lib/public/modules/workspace-assignment-routing.js +4 -0
- package/lib/public/style.css +13 -2
- package/lib/runtime-env.js +65 -0
- package/lib/sdk-bridge.js +120 -32
- package/lib/sdk-message-processor.js +10 -6
- package/lib/server-home-capsule-creation.js +36 -0
- package/lib/server-home-chat-events.js +287 -0
- package/lib/server-home-chat.js +518 -0
- package/lib/server-home-clay-entry.js +65 -0
- package/lib/server-home-clay-session-links.js +26 -0
- package/lib/server-home-debate-planning.js +214 -0
- package/lib/server-home-debates.js +137 -0
- package/lib/server-home-mate-creation.js +261 -0
- package/lib/server-home-models.js +241 -0
- package/lib/server-home-preferences.js +61 -0
- package/lib/server-mates.js +37 -0
- package/lib/server-tools.js +377 -0
- package/lib/server.js +111 -59
- package/lib/session-pair-mcp-server.js +6 -6
- package/lib/sessions.js +18 -3
- package/lib/tool-capsule-source.js +142 -0
- package/lib/tool-control-mcp-server.js +126 -0
- package/lib/tool-llm.js +48 -0
- package/lib/tool-storage.js +77 -0
- package/lib/tool-ui-spec-advanced.js +82 -0
- package/lib/tool-ui-spec.js +261 -0
- package/lib/tools-registry.js +286 -0
- package/lib/users-home-dock-preferences.js +77 -0
- package/lib/users-home-surface-preferences.js +99 -0
- package/lib/users-preferences.js +4 -7
- package/lib/users.js +12 -0
- package/lib/whats-new-content.js +3 -3
- package/lib/workspace-assignment-service.js +420 -0
- package/lib/workspace-query-mcp-server.js +154 -0
- package/lib/workspace-query-service.js +492 -0
- package/lib/ws-schema.js +93 -12
- package/lib/yoke/adapters/acp.js +2 -1
- package/lib/yoke/adapters/antigravity.js +2 -1
- package/lib/yoke/adapters/claude-worker.js +11 -4
- package/lib/yoke/adapters/claude.js +73 -43
- package/lib/yoke/adapters/codex.js +40 -10
- package/lib/yoke/adapters/kiro.js +2 -1
- package/lib/yoke/claude-user-input.js +38 -0
- package/lib/yoke/codex-app-server.js +20 -2
- package/lib/yoke/codex-user-input.js +64 -0
- package/lib/yoke/complete-once.js +164 -0
- package/lib/yoke/index.js +5 -1
- package/lib/yoke/interface.js +19 -0
- package/lib/yoke/mcp-bridge-server.js +2 -6
- package/lib/yoke/user-input.js +254 -0
- package/package.json +3 -2
- package/lib/public/css/home-chat.css +0 -380
- package/lib/public/modules/home-chat.js +0 -438
- 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
|
-
//
|
|
215
|
-
//
|
|
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
|
-
|
|
223
|
-
store.set({
|
|
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 ? '
|
|
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
|
-
//
|
|
321
|
-
//
|
|
322
|
-
|
|
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('
|
|
322
|
+
document.body.classList.toggle('project-mate-dms-disabled', !enabled);
|
|
329
323
|
}
|
|
330
324
|
|
|
331
|
-
export function
|
|
332
|
-
// Default to true
|
|
333
|
-
//
|
|
334
|
-
|
|
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
|
-
//
|
|
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({
|
|
475
|
-
|
|
467
|
+
store.set({ projectMateDmsEnabled: matesOn });
|
|
468
|
+
applyProjectMateDmsClass(matesOn);
|
|
476
469
|
if (data.chatLayout) {
|
|
477
470
|
setChatLayout(data.chatLayout); // update local cache + CSS
|
|
478
471
|
}
|