clay-server 2.31.0 → 2.32.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/browser-mcp-server.js +32 -44
- package/lib/debate-mcp-server.js +14 -31
- package/lib/mcp-local.js +31 -1
- package/lib/project-connection.js +4 -2
- package/lib/project-filesystem.js +47 -1
- package/lib/project-http.js +75 -8
- package/lib/project-mcp.js +4 -0
- package/lib/project-sessions.js +88 -51
- package/lib/project-user-message.js +12 -7
- package/lib/project.js +204 -90
- package/lib/public/app.js +123 -448
- package/lib/public/codex-avatar.png +0 -0
- package/lib/public/css/debate.css +3 -2
- package/lib/public/css/filebrowser.css +91 -1
- package/lib/public/css/icon-strip.css +21 -5
- package/lib/public/css/input.css +181 -100
- package/lib/public/css/mates.css +43 -0
- package/lib/public/css/mention.css +48 -4
- package/lib/public/css/menus.css +1 -1
- package/lib/public/css/messages.css +2 -0
- package/lib/public/css/notifications-center.css +19 -0
- package/lib/public/index.html +46 -24
- package/lib/public/modules/app-connection.js +138 -37
- package/lib/public/modules/app-cursors.js +18 -17
- package/lib/public/modules/app-debate-ui.js +9 -9
- package/lib/public/modules/app-dm.js +170 -131
- package/lib/public/modules/app-favicon.js +28 -26
- package/lib/public/modules/app-header.js +79 -68
- package/lib/public/modules/app-home-hub.js +55 -47
- package/lib/public/modules/app-loop-ui.js +34 -18
- package/lib/public/modules/app-loop-wizard.js +6 -6
- package/lib/public/modules/app-messages.js +195 -152
- package/lib/public/modules/app-misc.js +23 -12
- package/lib/public/modules/app-notifications.js +97 -3
- package/lib/public/modules/app-panels.js +203 -49
- package/lib/public/modules/app-projects.js +159 -150
- package/lib/public/modules/app-rate-limit.js +5 -4
- package/lib/public/modules/app-rendering.js +149 -101
- package/lib/public/modules/app-skills-install.js +4 -4
- package/lib/public/modules/context-sources.js +12 -41
- package/lib/public/modules/dom-refs.js +21 -0
- package/lib/public/modules/filebrowser.js +173 -2
- package/lib/public/modules/input.js +86 -0
- package/lib/public/modules/mate-sidebar.js +38 -0
- package/lib/public/modules/mention.js +24 -6
- package/lib/public/modules/scheduler.js +1 -1
- package/lib/public/modules/sidebar-mates.js +66 -34
- package/lib/public/modules/sidebar-mobile.js +34 -30
- package/lib/public/modules/sidebar-projects.js +60 -57
- package/lib/public/modules/sidebar-sessions.js +75 -69
- package/lib/public/modules/sidebar.js +12 -20
- package/lib/public/modules/skills.js +8 -9
- package/lib/public/modules/sticky-notes.js +1 -2
- package/lib/public/modules/store.js +9 -2
- package/lib/public/modules/stt.js +4 -1
- package/lib/public/modules/tools.js +14 -9
- package/lib/sdk-bridge.js +511 -1113
- package/lib/sdk-message-processor.js +123 -134
- package/lib/sdk-worker.js +4 -0
- package/lib/server-dm.js +1 -0
- package/lib/server.js +86 -1
- package/lib/sessions.js +47 -36
- package/lib/ws-schema.js +2 -0
- package/lib/yoke/adapters/claude-worker.js +559 -0
- package/lib/yoke/adapters/claude.js +1418 -0
- package/lib/yoke/adapters/codex.js +968 -0
- package/lib/yoke/adapters/gemini.js +668 -0
- package/lib/yoke/codex-app-server.js +307 -0
- package/lib/yoke/index.js +199 -0
- package/lib/yoke/instructions.js +62 -0
- package/lib/yoke/interface.js +92 -0
- package/lib/yoke/mcp-bridge-server.js +294 -0
- package/lib/yoke/package.json +7 -0
- package/package.json +3 -1
|
@@ -9,6 +9,7 @@ function attachMessageProcessor(ctx) {
|
|
|
9
9
|
var pushModule = ctx.pushModule;
|
|
10
10
|
var getNotificationsModule = ctx.getNotificationsModule || function () { return null; };
|
|
11
11
|
var getSDK = ctx.getSDK;
|
|
12
|
+
var adapter = ctx.adapter; // YOKE adapter (unused currently, available for future use)
|
|
12
13
|
var onProcessingChanged = ctx.onProcessingChanged;
|
|
13
14
|
var onTurnDone = ctx.onTurnDone;
|
|
14
15
|
var opts = ctx.opts;
|
|
@@ -37,11 +38,11 @@ function attachMessageProcessor(ctx) {
|
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
function processSubagentMessage(session, parsed) {
|
|
40
|
-
var parentId = parsed.
|
|
41
|
-
var content = parsed.
|
|
41
|
+
var parentId = parsed.parentToolUseId;
|
|
42
|
+
var content = parsed.content;
|
|
42
43
|
if (!Array.isArray(content)) return;
|
|
43
44
|
|
|
44
|
-
if (parsed.
|
|
45
|
+
if (parsed.messageRole === "assistant") {
|
|
45
46
|
// Extract tool_use blocks from sub-agent assistant messages
|
|
46
47
|
for (var i = 0; i < content.length; i++) {
|
|
47
48
|
var block = content[i];
|
|
@@ -69,59 +70,57 @@ function attachMessageProcessor(ctx) {
|
|
|
69
70
|
}
|
|
70
71
|
}
|
|
71
72
|
}
|
|
72
|
-
// user messages with
|
|
73
|
+
// user messages with parentToolUseId contain tool_results -- skip silently
|
|
73
74
|
}
|
|
74
75
|
|
|
75
76
|
function processSDKMessage(session, parsed) {
|
|
76
77
|
// Timing: log key SDK milestones relative to query start
|
|
77
78
|
if (session._queryStartTs) {
|
|
78
79
|
var _elapsed = Date.now() - session._queryStartTs;
|
|
79
|
-
if (parsed.
|
|
80
|
+
if (parsed.yokeType === "init") {
|
|
80
81
|
console.log("[PERF] processSDKMessage: system/init +" + _elapsed + "ms");
|
|
81
82
|
}
|
|
82
|
-
if (parsed.
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
console.log("[PERF] processSDKMessage: FIRST content_block_delta (visible text) +" + _elapsed + "ms");
|
|
89
|
-
}
|
|
83
|
+
if (parsed.yokeType === "turn_start") {
|
|
84
|
+
console.log("[PERF] processSDKMessage: message_start (API response begun) +" + _elapsed + "ms");
|
|
85
|
+
}
|
|
86
|
+
if ((parsed.yokeType === "text_delta" || parsed.yokeType === "tool_input_delta" || parsed.yokeType === "thinking_delta") && !session._firstTextLogged) {
|
|
87
|
+
session._firstTextLogged = true;
|
|
88
|
+
console.log("[PERF] processSDKMessage: FIRST content_block_delta (visible text) +" + _elapsed + "ms");
|
|
90
89
|
}
|
|
91
|
-
if (parsed.
|
|
90
|
+
if (parsed.yokeType === "result") {
|
|
92
91
|
console.log("[PERF] processSDKMessage: result +" + _elapsed + "ms");
|
|
93
92
|
}
|
|
94
93
|
}
|
|
95
94
|
|
|
96
95
|
// Extract session_id from any message that carries it
|
|
97
|
-
if (parsed.
|
|
98
|
-
session.cliSessionId = parsed.
|
|
96
|
+
if (parsed.sessionId && !session.cliSessionId) {
|
|
97
|
+
session.cliSessionId = parsed.sessionId;
|
|
99
98
|
sm.saveSessionFile(session);
|
|
100
99
|
sendAndRecord(session, { type: "session_id", cliSessionId: session.cliSessionId });
|
|
101
|
-
} else if (parsed.
|
|
102
|
-
session.cliSessionId = parsed.
|
|
100
|
+
} else if (parsed.sessionId) {
|
|
101
|
+
session.cliSessionId = parsed.sessionId;
|
|
103
102
|
}
|
|
104
103
|
|
|
105
104
|
// Capture message UUIDs for rewind support
|
|
106
105
|
if (parsed.uuid) {
|
|
107
|
-
if (parsed.
|
|
106
|
+
if (parsed.messageType === "user" && !parsed.parentToolUseId) {
|
|
108
107
|
session.messageUUIDs.push({ uuid: parsed.uuid, type: "user", historyIndex: session.history.length });
|
|
109
108
|
sendAndRecord(session, { type: "message_uuid", uuid: parsed.uuid, messageType: "user" });
|
|
110
|
-
} else if (parsed.
|
|
109
|
+
} else if (parsed.messageType === "assistant") {
|
|
111
110
|
session.messageUUIDs.push({ uuid: parsed.uuid, type: "assistant", historyIndex: session.history.length });
|
|
112
111
|
sendAndRecord(session, { type: "message_uuid", uuid: parsed.uuid, messageType: "assistant" });
|
|
113
112
|
}
|
|
114
113
|
}
|
|
115
114
|
|
|
116
115
|
// Cache slash_commands and model from CLI init message
|
|
117
|
-
if (parsed.
|
|
116
|
+
if (parsed.yokeType === "init") {
|
|
118
117
|
var fsSkills = discoverSkillDirs();
|
|
119
118
|
sm.skillNames = mergeSkills(parsed.skills, fsSkills);
|
|
120
|
-
if (parsed.
|
|
119
|
+
if (parsed.slashCommands) {
|
|
121
120
|
// Union: SDK slash_commands + merged skills (deduplicated)
|
|
122
121
|
var seen = new Set();
|
|
123
122
|
var combined = [];
|
|
124
|
-
var all = parsed.
|
|
123
|
+
var all = parsed.slashCommands.concat(Array.from(sm.skillNames));
|
|
125
124
|
for (var k = 0; k < all.length; k++) {
|
|
126
125
|
if (!seen.has(all[k])) {
|
|
127
126
|
seen.add(all[k]);
|
|
@@ -135,99 +134,90 @@ function attachMessageProcessor(ctx) {
|
|
|
135
134
|
sm.currentModel = sm._savedDefaultModel || parsed.model;
|
|
136
135
|
send({ type: "model_info", model: sm.currentModel, models: sm.availableModels || [] });
|
|
137
136
|
}
|
|
138
|
-
if (parsed.
|
|
139
|
-
sendAndRecord(session, { type: "fast_mode_state", state: parsed.
|
|
137
|
+
if (parsed.fastModeState) {
|
|
138
|
+
sendAndRecord(session, { type: "fast_mode_state", state: parsed.fastModeState });
|
|
140
139
|
}
|
|
141
140
|
}
|
|
142
141
|
|
|
143
|
-
if (parsed.
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
if (evt.type === "message_start" && evt.message && evt.message.usage) {
|
|
147
|
-
var u = evt.message.usage;
|
|
148
|
-
session.lastStreamInputTokens = (u.input_tokens || 0) + (u.cache_read_input_tokens || 0);
|
|
142
|
+
if (parsed.yokeType === "turn_start") {
|
|
143
|
+
if (parsed.inputTokens) {
|
|
144
|
+
session.lastStreamInputTokens = parsed.inputTokens;
|
|
149
145
|
}
|
|
150
146
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
var idx = evt.index;
|
|
147
|
+
} else if (parsed.yokeType === "tool_start" || parsed.yokeType === "thinking_start" || parsed.yokeType === "text_start") {
|
|
148
|
+
var idx = parsed.blockId;
|
|
154
149
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
}
|
|
150
|
+
if (parsed.yokeType === "tool_start") {
|
|
151
|
+
session.blocks[idx] = { type: "tool_use", id: parsed.toolId, name: parsed.toolName, inputJson: "" };
|
|
152
|
+
sendAndRecord(session, { type: "tool_start", id: parsed.toolId, name: parsed.toolName });
|
|
153
|
+
} else if (parsed.yokeType === "thinking_start") {
|
|
154
|
+
session.blocks[idx] = { type: "thinking", thinkingText: "", startTime: Date.now() };
|
|
155
|
+
sendAndRecord(session, { type: "thinking_start" });
|
|
156
|
+
} else if (parsed.yokeType === "text_start") {
|
|
157
|
+
session.blocks[idx] = { type: "text" };
|
|
164
158
|
}
|
|
165
159
|
|
|
166
|
-
|
|
167
|
-
|
|
160
|
+
} else if (parsed.yokeType === "text_delta" || parsed.yokeType === "tool_input_delta" || parsed.yokeType === "thinking_delta") {
|
|
161
|
+
var idx = parsed.blockId;
|
|
168
162
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
}
|
|
174
|
-
// Accumulate text for mate DM response
|
|
175
|
-
if (typeof session._mateDmResponseText === "string") {
|
|
176
|
-
session._mateDmResponseText += evt.delta.text;
|
|
177
|
-
}
|
|
178
|
-
sendAndRecord(session, { type: "delta", text: evt.delta.text });
|
|
179
|
-
} else if (evt.delta.type === "input_json_delta" && session.blocks[idx]) {
|
|
180
|
-
session.blocks[idx].inputJson += evt.delta.partial_json;
|
|
181
|
-
} else if (evt.delta.type === "thinking_delta" && session.blocks[idx]) {
|
|
182
|
-
session.blocks[idx].thinkingText += evt.delta.thinking;
|
|
183
|
-
sendAndRecord(session, { type: "thinking_delta", text: evt.delta.thinking });
|
|
163
|
+
if (parsed.yokeType === "text_delta" && typeof parsed.text === "string") {
|
|
164
|
+
session.streamedText = true;
|
|
165
|
+
if (session.responsePreview.length < 200) {
|
|
166
|
+
session.responsePreview += parsed.text;
|
|
184
167
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
});
|
|
211
|
-
}
|
|
212
|
-
} else if (block && block.type === "thinking") {
|
|
213
|
-
var duration = block.startTime ? (Date.now() - block.startTime) / 1000 : 0;
|
|
214
|
-
sendAndRecord(session, { type: "thinking_stop", duration: duration });
|
|
168
|
+
// Accumulate text for mate DM response
|
|
169
|
+
if (typeof session._mateDmResponseText === "string") {
|
|
170
|
+
session._mateDmResponseText += parsed.text;
|
|
171
|
+
}
|
|
172
|
+
sendAndRecord(session, { type: "delta", text: parsed.text });
|
|
173
|
+
} else if (parsed.yokeType === "tool_input_delta" && session.blocks[idx]) {
|
|
174
|
+
session.blocks[idx].inputJson += parsed.partialJson;
|
|
175
|
+
} else if (parsed.yokeType === "thinking_delta" && session.blocks[idx]) {
|
|
176
|
+
session.blocks[idx].thinkingText += parsed.text;
|
|
177
|
+
sendAndRecord(session, { type: "thinking_delta", text: parsed.text });
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
} else if (parsed.yokeType === "block_stop") {
|
|
181
|
+
var idx = parsed.blockId;
|
|
182
|
+
var block = session.blocks[idx];
|
|
183
|
+
|
|
184
|
+
if (block && block.type === "tool_use") {
|
|
185
|
+
var input = {};
|
|
186
|
+
try { input = JSON.parse(block.inputJson); } catch (e) {}
|
|
187
|
+
sendAndRecord(session, { type: "tool_executing", id: block.id, name: block.name, input: input });
|
|
188
|
+
|
|
189
|
+
// Track active Task tools for sub-agent done detection
|
|
190
|
+
if (block.name === "Task") {
|
|
191
|
+
if (!session.activeTaskToolIds) session.activeTaskToolIds = {};
|
|
192
|
+
session.activeTaskToolIds[block.id] = true;
|
|
215
193
|
}
|
|
216
194
|
|
|
217
|
-
|
|
195
|
+
if (pushModule && block.name === "AskUserQuestion" && input.questions) {
|
|
196
|
+
var q = input.questions[0];
|
|
197
|
+
pushModule.sendPush({
|
|
198
|
+
type: "ask_user",
|
|
199
|
+
slug: slug,
|
|
200
|
+
title: (mateDisplayName || "Claude") + " has a question",
|
|
201
|
+
body: q ? q.question : "Waiting for your response",
|
|
202
|
+
tag: "claude-ask",
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
} else if (block && block.type === "thinking") {
|
|
206
|
+
var duration = block.startTime ? (Date.now() - block.startTime) / 1000 : 0;
|
|
207
|
+
sendAndRecord(session, { type: "thinking_stop", duration: duration });
|
|
218
208
|
}
|
|
219
209
|
|
|
220
|
-
|
|
210
|
+
delete session.blocks[idx];
|
|
211
|
+
|
|
212
|
+
} else if (parsed.yokeType === "subagent_message") {
|
|
221
213
|
// Sub-agent messages: extract tool_use blocks for activity display
|
|
222
|
-
|
|
223
|
-
processSubagentMessage(session, parsed);
|
|
224
|
-
return;
|
|
225
|
-
}
|
|
214
|
+
processSubagentMessage(session, parsed);
|
|
226
215
|
|
|
227
|
-
|
|
216
|
+
} else if (parsed.yokeType === "message") {
|
|
217
|
+
var content = parsed.content;
|
|
228
218
|
|
|
229
219
|
// Fallback: if assistant text wasn't streamed via deltas, send it now
|
|
230
|
-
if (parsed.
|
|
220
|
+
if (parsed.messageRole === "assistant" && !session.streamedText && Array.isArray(content)) {
|
|
231
221
|
var assistantText = content
|
|
232
222
|
.filter(function(c) { return c.type === "text"; })
|
|
233
223
|
.map(function(c) { return c.text; })
|
|
@@ -241,7 +231,7 @@ function attachMessageProcessor(ctx) {
|
|
|
241
231
|
}
|
|
242
232
|
|
|
243
233
|
// Check for local slash command output in user messages
|
|
244
|
-
if (parsed.
|
|
234
|
+
if (parsed.messageRole === "user") {
|
|
245
235
|
var fullText = "";
|
|
246
236
|
if (typeof content === "string") {
|
|
247
237
|
fullText = content;
|
|
@@ -303,7 +293,7 @@ function attachMessageProcessor(ctx) {
|
|
|
303
293
|
}
|
|
304
294
|
}
|
|
305
295
|
|
|
306
|
-
} else if (parsed.
|
|
296
|
+
} else if (parsed.yokeType === "result") {
|
|
307
297
|
session.blocks = {};
|
|
308
298
|
session.sentToolResults = {};
|
|
309
299
|
session.pendingPermissions = {};
|
|
@@ -319,10 +309,10 @@ function attachMessageProcessor(ctx) {
|
|
|
319
309
|
// Only clear rateLimitResetsAt on genuine success (non-zero cost).
|
|
320
310
|
// When rate-limited, the SDK sends result with zero cost right after
|
|
321
311
|
// rate_limit_event; clearing here would prevent auto-continue scheduling.
|
|
322
|
-
if (parsed.
|
|
312
|
+
if (parsed.cost && parsed.cost > 0) {
|
|
323
313
|
session.rateLimitResetsAt = null;
|
|
324
314
|
}
|
|
325
|
-
console.log("[sdk-bridge] result handler: session " + session.localId + " cost=" + parsed.
|
|
315
|
+
console.log("[sdk-bridge] result handler: session " + session.localId + " cost=" + parsed.cost + " rateLimitResetsAt=" + session.rateLimitResetsAt);
|
|
326
316
|
|
|
327
317
|
// Handle SDK execution errors: show the error to the user instead of
|
|
328
318
|
// silently swallowing it. These have subtype "error_during_execution".
|
|
@@ -331,7 +321,7 @@ function attachMessageProcessor(ctx) {
|
|
|
331
321
|
var execError = execErrors.length > 0
|
|
332
322
|
? execErrors.join("; ")
|
|
333
323
|
: "Unknown SDK error";
|
|
334
|
-
if (parsed.
|
|
324
|
+
if (parsed.terminalReason) execError += " (reason: " + parsed.terminalReason + ")";
|
|
335
325
|
console.error("[sdk-bridge] Execution error for session " + session.localId + ": " + execError);
|
|
336
326
|
session.isProcessing = false;
|
|
337
327
|
onProcessingChanged();
|
|
@@ -345,7 +335,7 @@ function attachMessageProcessor(ctx) {
|
|
|
345
335
|
onProcessingChanged();
|
|
346
336
|
// Detect "Not logged in" scenario early for the check below
|
|
347
337
|
var previewTrimmed = (session.responsePreview || "").trim();
|
|
348
|
-
var isZeroCost = !parsed.
|
|
338
|
+
var isZeroCost = !parsed.cost || parsed.cost === 0;
|
|
349
339
|
var isLoginPrompt = isZeroCost && previewTrimmed.length < 100
|
|
350
340
|
&& /not logged in/i.test(previewTrimmed) && /\/login/i.test(previewTrimmed);
|
|
351
341
|
// Fetch rich context usage breakdown (fire-and-forget, non-blocking)
|
|
@@ -357,19 +347,19 @@ function attachMessageProcessor(ctx) {
|
|
|
357
347
|
console.error("[sdk-bridge] getContextUsage failed (non-fatal):", e.message || e);
|
|
358
348
|
});
|
|
359
349
|
}
|
|
360
|
-
var lastStreamInput = session.lastStreamInputTokens || null;
|
|
350
|
+
var lastStreamInput = session.lastStreamInputTokens || parsed.lastStreamInputTokens || null;
|
|
361
351
|
session.lastStreamInputTokens = null;
|
|
362
352
|
sendAndRecord(session, {
|
|
363
353
|
type: "result",
|
|
364
|
-
cost: parsed.
|
|
365
|
-
duration: parsed.
|
|
354
|
+
cost: parsed.cost,
|
|
355
|
+
duration: parsed.duration,
|
|
366
356
|
usage: parsed.usage || null,
|
|
367
357
|
modelUsage: parsed.modelUsage || null,
|
|
368
|
-
sessionId: parsed.
|
|
358
|
+
sessionId: parsed.sessionId,
|
|
369
359
|
lastStreamInputTokens: lastStreamInput,
|
|
370
360
|
});
|
|
371
|
-
if (parsed.
|
|
372
|
-
sendAndRecord(session, { type: "fast_mode_state", state: parsed.
|
|
361
|
+
if (parsed.fastModeState) {
|
|
362
|
+
sendAndRecord(session, { type: "fast_mode_state", state: parsed.fastModeState });
|
|
373
363
|
}
|
|
374
364
|
// Detect "Not logged in / Please run /login" from SDK.
|
|
375
365
|
// This is a short canned response with zero cost, not actual AI output.
|
|
@@ -423,7 +413,7 @@ function attachMessageProcessor(ctx) {
|
|
|
423
413
|
try { onTurnDone(session, donePreview); } catch (e) {}
|
|
424
414
|
}
|
|
425
415
|
|
|
426
|
-
} else if (parsed.
|
|
416
|
+
} else if (parsed.yokeType === "status") {
|
|
427
417
|
if (parsed.status === "compacting") {
|
|
428
418
|
sendAndRecord(session, { type: "compacting", active: true });
|
|
429
419
|
} else if (session.compacting) {
|
|
@@ -431,34 +421,34 @@ function attachMessageProcessor(ctx) {
|
|
|
431
421
|
}
|
|
432
422
|
session.compacting = parsed.status === "compacting";
|
|
433
423
|
|
|
434
|
-
} else if (parsed.
|
|
435
|
-
var parentId = parsed.
|
|
424
|
+
} else if (parsed.yokeType === "task_started") {
|
|
425
|
+
var parentId = parsed.parentToolId;
|
|
436
426
|
if (parentId) {
|
|
437
427
|
if (!session.taskIdMap) session.taskIdMap = {};
|
|
438
|
-
session.taskIdMap[parentId] = parsed.
|
|
428
|
+
session.taskIdMap[parentId] = parsed.taskId;
|
|
439
429
|
sendAndRecord(session, {
|
|
440
430
|
type: "task_started",
|
|
441
431
|
parentToolId: parentId,
|
|
442
|
-
taskId: parsed.
|
|
432
|
+
taskId: parsed.taskId,
|
|
443
433
|
description: parsed.description || "",
|
|
444
434
|
});
|
|
445
435
|
}
|
|
446
436
|
|
|
447
|
-
} else if (parsed.
|
|
448
|
-
var parentId = parsed.
|
|
437
|
+
} else if (parsed.yokeType === "task_progress") {
|
|
438
|
+
var parentId = parsed.parentToolId;
|
|
449
439
|
if (parentId) {
|
|
450
440
|
sendAndRecord(session, {
|
|
451
441
|
type: "task_progress",
|
|
452
442
|
parentToolId: parentId,
|
|
453
|
-
taskId: parsed.
|
|
443
|
+
taskId: parsed.taskId,
|
|
454
444
|
usage: parsed.usage || null,
|
|
455
|
-
lastToolName: parsed.
|
|
445
|
+
lastToolName: parsed.lastToolName || null,
|
|
456
446
|
description: parsed.description || "",
|
|
457
447
|
summary: parsed.summary || null,
|
|
458
448
|
});
|
|
459
449
|
}
|
|
460
450
|
|
|
461
|
-
} else if (parsed.
|
|
451
|
+
} else if (parsed.yokeType === "task_updated") {
|
|
462
452
|
// Live task state patches (status, description, error, backgrounded)
|
|
463
453
|
var taskId = parsed.task_id;
|
|
464
454
|
var patch = parsed.patch || {};
|
|
@@ -477,19 +467,19 @@ function attachMessageProcessor(ctx) {
|
|
|
477
467
|
});
|
|
478
468
|
}
|
|
479
469
|
|
|
480
|
-
} else if (parsed.
|
|
470
|
+
} else if (parsed.yokeType === "tool_progress") {
|
|
481
471
|
// Sub-agent tool_progress: forward as activity update
|
|
482
|
-
var parentId = parsed.
|
|
472
|
+
var parentId = parsed.parentToolId;
|
|
483
473
|
if (parentId) {
|
|
484
474
|
sendAndRecord(session, {
|
|
485
475
|
type: "subagent_activity",
|
|
486
476
|
parentToolId: parentId,
|
|
487
|
-
text: parsed.
|
|
477
|
+
text: parsed.text || "",
|
|
488
478
|
});
|
|
489
479
|
}
|
|
490
480
|
|
|
491
|
-
} else if (parsed.
|
|
492
|
-
var parentId = parsed.
|
|
481
|
+
} else if (parsed.yokeType === "task_notification") {
|
|
482
|
+
var parentId = parsed.parentToolId;
|
|
493
483
|
if (parentId) {
|
|
494
484
|
sendAndRecord(session, {
|
|
495
485
|
type: "subagent_done",
|
|
@@ -501,15 +491,15 @@ function attachMessageProcessor(ctx) {
|
|
|
501
491
|
}
|
|
502
492
|
if (session.taskIdMap) {
|
|
503
493
|
for (var k in session.taskIdMap) {
|
|
504
|
-
if (session.taskIdMap[k] === parsed.
|
|
494
|
+
if (session.taskIdMap[k] === parsed.taskId) {
|
|
505
495
|
delete session.taskIdMap[k];
|
|
506
496
|
break;
|
|
507
497
|
}
|
|
508
498
|
}
|
|
509
499
|
}
|
|
510
500
|
|
|
511
|
-
} else if (parsed.
|
|
512
|
-
var info = parsed.
|
|
501
|
+
} else if (parsed.yokeType === "rate_limit") {
|
|
502
|
+
var info = parsed.rateLimitInfo;
|
|
513
503
|
console.log("[sdk-bridge] rate_limit_event for session " + session.localId + ": status=" + info.status + " resetsAt=" + info.resetsAt + " isUsingOverage=" + info.isUsingOverage + " isProcessing=" + session.isProcessing);
|
|
514
504
|
|
|
515
505
|
// Broadcast reset time for top-bar usage link
|
|
@@ -565,13 +555,13 @@ function attachMessageProcessor(ctx) {
|
|
|
565
555
|
}
|
|
566
556
|
}
|
|
567
557
|
|
|
568
|
-
} else if (parsed.
|
|
558
|
+
} else if (parsed.yokeType === "prompt_suggestion") {
|
|
569
559
|
sendAndRecord(session, {
|
|
570
560
|
type: "prompt_suggestion",
|
|
571
561
|
suggestion: parsed.suggestion || "",
|
|
572
562
|
});
|
|
573
563
|
|
|
574
|
-
} else if (parsed.
|
|
564
|
+
} else if (parsed.yokeType === "notification") {
|
|
575
565
|
var notifText = parsed.text || "";
|
|
576
566
|
var notifPriority = parsed.priority || "low";
|
|
577
567
|
if (notifText) {
|
|
@@ -585,12 +575,12 @@ function attachMessageProcessor(ctx) {
|
|
|
585
575
|
});
|
|
586
576
|
}
|
|
587
577
|
|
|
588
|
-
} else if (parsed.
|
|
589
|
-
// Transient retry notification
|
|
578
|
+
} else if (parsed.yokeType === "api_retry") {
|
|
579
|
+
// Transient retry notification, show in UI but don't persist in history
|
|
590
580
|
var retryText = parsed.message || parsed.error || "Retrying API request...";
|
|
591
581
|
sendToSession(session, { type: "system_info", text: retryText });
|
|
592
582
|
|
|
593
|
-
} else if (parsed.
|
|
583
|
+
} else if (parsed.yokeType === "system") {
|
|
594
584
|
// Catch-all for unhandled system subtypes (e.g. hook-block errors).
|
|
595
585
|
// Extract any error text and surface it in the UI.
|
|
596
586
|
var sysText = parsed.error || parsed.message || parsed.text || "";
|
|
@@ -604,7 +594,6 @@ function attachMessageProcessor(ctx) {
|
|
|
604
594
|
console.log("[sdk-bridge] Unhandled system message (subtype=" + (parsed.subtype || "none") + "): " + sysText.substring(0, 200));
|
|
605
595
|
sendAndRecord(session, { type: "error", text: sysText });
|
|
606
596
|
}
|
|
607
|
-
} else if (parsed.type && parsed.type !== "user") {
|
|
608
597
|
}
|
|
609
598
|
}
|
|
610
599
|
|
package/lib/sdk-worker.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
// DEPRECATED: This file has been moved to lib/yoke/adapters/claude-worker.js
|
|
2
|
+
// It is kept here temporarily for backward compatibility with running worker processes.
|
|
3
|
+
// New worker spawns use the path from adapter.workerScriptPath.
|
|
4
|
+
//
|
|
1
5
|
// sdk-worker.js — Standalone worker process for OS-level user isolation.
|
|
2
6
|
// Runs as a target Linux user, loads the Claude Agent SDK, and communicates
|
|
3
7
|
// with the main Clay daemon over a Unix domain socket using JSON lines.
|
package/lib/server-dm.js
CHANGED
package/lib/server.js
CHANGED
|
@@ -459,6 +459,75 @@ function createServer(opts) {
|
|
|
459
459
|
return;
|
|
460
460
|
}
|
|
461
461
|
|
|
462
|
+
// --- Global MCP bridge endpoint (localhost only) ---
|
|
463
|
+
// Used by Codex mcp-bridge-server.js which can't know the active project slug.
|
|
464
|
+
// Aggregates MCP tools from all project contexts.
|
|
465
|
+
if (req.method === "POST" && fullUrl === "/api/mcp-bridge") {
|
|
466
|
+
var mcpRemoteAddr = req.socket.remoteAddress || "";
|
|
467
|
+
var mcpIsLocal = mcpRemoteAddr === "127.0.0.1" || mcpRemoteAddr === "::1" || mcpRemoteAddr === "::ffff:127.0.0.1";
|
|
468
|
+
if (!mcpIsLocal) {
|
|
469
|
+
res.writeHead(403, { "Content-Type": "application/json" });
|
|
470
|
+
res.end('{"error":"Forbidden"}');
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
var parseJsonBody = function(r) {
|
|
474
|
+
return new Promise(function(resolve, reject) {
|
|
475
|
+
var chunks = [];
|
|
476
|
+
r.on("data", function(c) { chunks.push(c); });
|
|
477
|
+
r.on("end", function() {
|
|
478
|
+
try { resolve(JSON.parse(Buffer.concat(chunks).toString("utf8"))); }
|
|
479
|
+
catch(e) { reject(e); }
|
|
480
|
+
});
|
|
481
|
+
});
|
|
482
|
+
};
|
|
483
|
+
parseJsonBody(req).then(function(body) {
|
|
484
|
+
// Find the first project context that has a MCP bridge handler
|
|
485
|
+
var handler = null;
|
|
486
|
+
projects.forEach(function(ctx) {
|
|
487
|
+
if (handler) return;
|
|
488
|
+
if (ctx.getMcpBridgeHandler) {
|
|
489
|
+
var h = ctx.getMcpBridgeHandler();
|
|
490
|
+
if (h) handler = h;
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
if (!handler) {
|
|
494
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
495
|
+
res.end('{"error":"No MCP bridge handler available"}');
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
if (body.action === "list_tools") {
|
|
499
|
+
handler.listTools().then(function(tools) {
|
|
500
|
+
var serverCounts = {};
|
|
501
|
+
for (var ti = 0; ti < tools.length; ti++) {
|
|
502
|
+
serverCounts[tools[ti].server] = (serverCounts[tools[ti].server] || 0) + 1;
|
|
503
|
+
}
|
|
504
|
+
console.log("[mcp-bridge-http] global list_tools:", tools.length, "tools -", Object.keys(serverCounts).map(function(s) { return s + "(" + serverCounts[s] + ")"; }).join(", ") || "(none)");
|
|
505
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
506
|
+
res.end(JSON.stringify({ tools: tools }));
|
|
507
|
+
}).catch(function(err) {
|
|
508
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
509
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
510
|
+
});
|
|
511
|
+
} else if (body.action === "call_tool") {
|
|
512
|
+
console.log("[mcp-bridge-http] global call_tool:", body.server + "/" + body.tool);
|
|
513
|
+
handler.callTool(body.server, body.tool, body.args || {}).then(function(result) {
|
|
514
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
515
|
+
res.end(JSON.stringify({ result: result }));
|
|
516
|
+
}).catch(function(err) {
|
|
517
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
518
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
519
|
+
});
|
|
520
|
+
} else {
|
|
521
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
522
|
+
res.end('{"error":"Unknown action"}');
|
|
523
|
+
}
|
|
524
|
+
}).catch(function() {
|
|
525
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
526
|
+
res.end('{"error":"Invalid JSON"}');
|
|
527
|
+
});
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
|
|
462
531
|
// --- Skills routes (delegated to server-skills) ---
|
|
463
532
|
if (skills.handleRequest(req, res, fullUrl)) return;
|
|
464
533
|
|
|
@@ -563,7 +632,22 @@ function createServer(opts) {
|
|
|
563
632
|
}
|
|
564
633
|
|
|
565
634
|
// Auth check for project routes
|
|
566
|
-
|
|
635
|
+
// Bypass auth for MCP bridge endpoint (localhost only).
|
|
636
|
+
// The mcp-bridge-server.js runs as a local child process and cannot carry cookies.
|
|
637
|
+
var projectUrlForAuth = stripPrefix(req.url.split("?")[0], slug);
|
|
638
|
+
// req.socket.remoteAddress may differ between HTTP/HTTPS (TLSSocket wraps net.Socket).
|
|
639
|
+
// Also check req.connection.remoteAddress for compatibility.
|
|
640
|
+
var remoteAddr = req.socket.remoteAddress
|
|
641
|
+
|| (req.connection && req.connection.remoteAddress)
|
|
642
|
+
|| "";
|
|
643
|
+
var isLocalhost = remoteAddr === "127.0.0.1" || remoteAddr === "::1" || remoteAddr === "::ffff:127.0.0.1";
|
|
644
|
+
var isMcpBridgeLocal = projectUrlForAuth === "/api/mcp-bridge"
|
|
645
|
+
&& req.method === "POST"
|
|
646
|
+
&& isLocalhost;
|
|
647
|
+
if (projectUrlForAuth === "/api/mcp-bridge") {
|
|
648
|
+
console.log("[server] MCP bridge auth: method=" + req.method + " addr=" + remoteAddr + " bypass=" + isMcpBridgeLocal);
|
|
649
|
+
}
|
|
650
|
+
if (!isMcpBridgeLocal && !isRequestAuthed(req)) {
|
|
567
651
|
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
568
652
|
res.end(auth.getAuthPage());
|
|
569
653
|
return;
|
|
@@ -872,6 +956,7 @@ function createServer(opts) {
|
|
|
872
956
|
lanHost: lanHost,
|
|
873
957
|
port: portNum,
|
|
874
958
|
tls: !!tlsOptions,
|
|
959
|
+
authToken: pinHash || null,
|
|
875
960
|
getProjectCount: function () { return projects.size; },
|
|
876
961
|
getProjectList: function (userId) {
|
|
877
962
|
var list = [];
|