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.
Files changed (74) hide show
  1. package/lib/browser-mcp-server.js +32 -44
  2. package/lib/debate-mcp-server.js +14 -31
  3. package/lib/mcp-local.js +31 -1
  4. package/lib/project-connection.js +4 -2
  5. package/lib/project-filesystem.js +47 -1
  6. package/lib/project-http.js +75 -8
  7. package/lib/project-mcp.js +4 -0
  8. package/lib/project-sessions.js +88 -51
  9. package/lib/project-user-message.js +12 -7
  10. package/lib/project.js +204 -90
  11. package/lib/public/app.js +123 -448
  12. package/lib/public/codex-avatar.png +0 -0
  13. package/lib/public/css/debate.css +3 -2
  14. package/lib/public/css/filebrowser.css +91 -1
  15. package/lib/public/css/icon-strip.css +21 -5
  16. package/lib/public/css/input.css +181 -100
  17. package/lib/public/css/mates.css +43 -0
  18. package/lib/public/css/mention.css +48 -4
  19. package/lib/public/css/menus.css +1 -1
  20. package/lib/public/css/messages.css +2 -0
  21. package/lib/public/css/notifications-center.css +19 -0
  22. package/lib/public/index.html +46 -24
  23. package/lib/public/modules/app-connection.js +138 -37
  24. package/lib/public/modules/app-cursors.js +18 -17
  25. package/lib/public/modules/app-debate-ui.js +9 -9
  26. package/lib/public/modules/app-dm.js +170 -131
  27. package/lib/public/modules/app-favicon.js +28 -26
  28. package/lib/public/modules/app-header.js +79 -68
  29. package/lib/public/modules/app-home-hub.js +55 -47
  30. package/lib/public/modules/app-loop-ui.js +34 -18
  31. package/lib/public/modules/app-loop-wizard.js +6 -6
  32. package/lib/public/modules/app-messages.js +195 -152
  33. package/lib/public/modules/app-misc.js +23 -12
  34. package/lib/public/modules/app-notifications.js +97 -3
  35. package/lib/public/modules/app-panels.js +203 -49
  36. package/lib/public/modules/app-projects.js +159 -150
  37. package/lib/public/modules/app-rate-limit.js +5 -4
  38. package/lib/public/modules/app-rendering.js +149 -101
  39. package/lib/public/modules/app-skills-install.js +4 -4
  40. package/lib/public/modules/context-sources.js +12 -41
  41. package/lib/public/modules/dom-refs.js +21 -0
  42. package/lib/public/modules/filebrowser.js +173 -2
  43. package/lib/public/modules/input.js +86 -0
  44. package/lib/public/modules/mate-sidebar.js +38 -0
  45. package/lib/public/modules/mention.js +24 -6
  46. package/lib/public/modules/scheduler.js +1 -1
  47. package/lib/public/modules/sidebar-mates.js +66 -34
  48. package/lib/public/modules/sidebar-mobile.js +34 -30
  49. package/lib/public/modules/sidebar-projects.js +60 -57
  50. package/lib/public/modules/sidebar-sessions.js +75 -69
  51. package/lib/public/modules/sidebar.js +12 -20
  52. package/lib/public/modules/skills.js +8 -9
  53. package/lib/public/modules/sticky-notes.js +1 -2
  54. package/lib/public/modules/store.js +9 -2
  55. package/lib/public/modules/stt.js +4 -1
  56. package/lib/public/modules/tools.js +14 -9
  57. package/lib/sdk-bridge.js +511 -1113
  58. package/lib/sdk-message-processor.js +123 -134
  59. package/lib/sdk-worker.js +4 -0
  60. package/lib/server-dm.js +1 -0
  61. package/lib/server.js +86 -1
  62. package/lib/sessions.js +47 -36
  63. package/lib/ws-schema.js +2 -0
  64. package/lib/yoke/adapters/claude-worker.js +559 -0
  65. package/lib/yoke/adapters/claude.js +1418 -0
  66. package/lib/yoke/adapters/codex.js +968 -0
  67. package/lib/yoke/adapters/gemini.js +668 -0
  68. package/lib/yoke/codex-app-server.js +307 -0
  69. package/lib/yoke/index.js +199 -0
  70. package/lib/yoke/instructions.js +62 -0
  71. package/lib/yoke/interface.js +92 -0
  72. package/lib/yoke/mcp-bridge-server.js +294 -0
  73. package/lib/yoke/package.json +7 -0
  74. 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.parent_tool_use_id;
41
- var content = parsed.message.content;
41
+ var parentId = parsed.parentToolUseId;
42
+ var content = parsed.content;
42
43
  if (!Array.isArray(content)) return;
43
44
 
44
- if (parsed.type === "assistant") {
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 parent_tool_use_id contain tool_results -- skip silently
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.type === "system" && parsed.subtype === "init") {
80
+ if (parsed.yokeType === "init") {
80
81
  console.log("[PERF] processSDKMessage: system/init +" + _elapsed + "ms");
81
82
  }
82
- if (parsed.type === "stream_event" && parsed.event) {
83
- if (parsed.event.type === "message_start") {
84
- console.log("[PERF] processSDKMessage: message_start (API response begun) +" + _elapsed + "ms");
85
- }
86
- if (parsed.event.type === "content_block_delta" && !session._firstTextLogged) {
87
- session._firstTextLogged = true;
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.type === "result") {
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.session_id && !session.cliSessionId) {
98
- session.cliSessionId = parsed.session_id;
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.session_id) {
102
- session.cliSessionId = parsed.session_id;
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.type === "user" && !parsed.parent_tool_use_id) {
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.type === "assistant") {
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.type === "system" && parsed.subtype === "init") {
116
+ if (parsed.yokeType === "init") {
118
117
  var fsSkills = discoverSkillDirs();
119
118
  sm.skillNames = mergeSkills(parsed.skills, fsSkills);
120
- if (parsed.slash_commands) {
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.slash_commands.concat(Array.from(sm.skillNames));
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.fast_mode_state) {
139
- sendAndRecord(session, { type: "fast_mode_state", state: parsed.fast_mode_state });
137
+ if (parsed.fastModeState) {
138
+ sendAndRecord(session, { type: "fast_mode_state", state: parsed.fastModeState });
140
139
  }
141
140
  }
142
141
 
143
- if (parsed.type === "stream_event" && parsed.event) {
144
- var evt = parsed.event;
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
- if (evt.type === "content_block_start") {
152
- var block = evt.content_block;
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
- if (block.type === "tool_use") {
156
- session.blocks[idx] = { type: "tool_use", id: block.id, name: block.name, inputJson: "" };
157
- sendAndRecord(session, { type: "tool_start", id: block.id, name: block.name });
158
- } else if (block.type === "thinking") {
159
- session.blocks[idx] = { type: "thinking", thinkingText: "", startTime: Date.now() };
160
- sendAndRecord(session, { type: "thinking_start" });
161
- } else if (block.type === "text") {
162
- session.blocks[idx] = { type: "text" };
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
- if (evt.type === "content_block_delta" && evt.delta) {
167
- var idx = evt.index;
160
+ } else if (parsed.yokeType === "text_delta" || parsed.yokeType === "tool_input_delta" || parsed.yokeType === "thinking_delta") {
161
+ var idx = parsed.blockId;
168
162
 
169
- if (evt.delta.type === "text_delta" && typeof evt.delta.text === "string") {
170
- session.streamedText = true;
171
- if (session.responsePreview.length < 200) {
172
- session.responsePreview += evt.delta.text;
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
- if (evt.type === "content_block_stop") {
188
- var idx = evt.index;
189
- var block = session.blocks[idx];
190
-
191
- if (block && block.type === "tool_use") {
192
- var input = {};
193
- try { input = JSON.parse(block.inputJson); } catch (e) {}
194
- sendAndRecord(session, { type: "tool_executing", id: block.id, name: block.name, input: input });
195
-
196
- // Track active Task tools for sub-agent done detection
197
- if (block.name === "Task") {
198
- if (!session.activeTaskToolIds) session.activeTaskToolIds = {};
199
- session.activeTaskToolIds[block.id] = true;
200
- }
201
-
202
- if (pushModule && block.name === "AskUserQuestion" && input.questions) {
203
- var q = input.questions[0];
204
- pushModule.sendPush({
205
- type: "ask_user",
206
- slug: slug,
207
- title: (mateDisplayName || "Claude") + " has a question",
208
- body: q ? q.question : "Waiting for your response",
209
- tag: "claude-ask",
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
- delete session.blocks[idx];
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
- } else if ((parsed.type === "assistant" || parsed.type === "user") && parsed.message && parsed.message.content) {
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
- if (parsed.parent_tool_use_id) {
223
- processSubagentMessage(session, parsed);
224
- return;
225
- }
214
+ processSubagentMessage(session, parsed);
226
215
 
227
- var content = parsed.message.content;
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.type === "assistant" && !session.streamedText && Array.isArray(content)) {
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.type === "user") {
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.type === "result") {
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.total_cost_usd && parsed.total_cost_usd > 0) {
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.total_cost_usd + " rateLimitResetsAt=" + session.rateLimitResetsAt);
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.terminal_reason) execError += " (reason: " + parsed.terminal_reason + ")";
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.total_cost_usd || parsed.total_cost_usd === 0;
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.total_cost_usd,
365
- duration: parsed.duration_ms,
354
+ cost: parsed.cost,
355
+ duration: parsed.duration,
366
356
  usage: parsed.usage || null,
367
357
  modelUsage: parsed.modelUsage || null,
368
- sessionId: parsed.session_id,
358
+ sessionId: parsed.sessionId,
369
359
  lastStreamInputTokens: lastStreamInput,
370
360
  });
371
- if (parsed.fast_mode_state) {
372
- sendAndRecord(session, { type: "fast_mode_state", state: parsed.fast_mode_state });
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.type === "system" && parsed.subtype === "status") {
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.type === "system" && parsed.subtype === "task_started") {
435
- var parentId = parsed.tool_use_id;
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.task_id;
428
+ session.taskIdMap[parentId] = parsed.taskId;
439
429
  sendAndRecord(session, {
440
430
  type: "task_started",
441
431
  parentToolId: parentId,
442
- taskId: parsed.task_id,
432
+ taskId: parsed.taskId,
443
433
  description: parsed.description || "",
444
434
  });
445
435
  }
446
436
 
447
- } else if (parsed.type === "system" && parsed.subtype === "task_progress") {
448
- var parentId = parsed.tool_use_id;
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.task_id,
443
+ taskId: parsed.taskId,
454
444
  usage: parsed.usage || null,
455
- lastToolName: parsed.last_tool_name || null,
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.type === "system" && parsed.subtype === "task_updated") {
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.type === "tool_progress") {
470
+ } else if (parsed.yokeType === "tool_progress") {
481
471
  // Sub-agent tool_progress: forward as activity update
482
- var parentId = parsed.parent_tool_use_id;
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.content || "",
477
+ text: parsed.text || "",
488
478
  });
489
479
  }
490
480
 
491
- } else if (parsed.type === "task_notification") {
492
- var parentId = parsed.parent_tool_use_id;
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.task_id) {
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.type === "rate_limit_event" && parsed.rate_limit_info) {
512
- var info = parsed.rate_limit_info;
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.type === "prompt_suggestion") {
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.type === "system" && parsed.subtype === "notification") {
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.type === "system" && parsed.subtype === "api_retry") {
589
- // Transient retry notification show in UI but don't persist in history
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.type === "system") {
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
@@ -77,6 +77,7 @@ function attachDm(ctx) {
77
77
  primary: !!mate.primary,
78
78
  mateStatus: mate.status,
79
79
  seedData: mate.seedData || {},
80
+ vendor: mate.vendor || null,
80
81
  },
81
82
  }));
82
83
  return true;
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
- if (!isRequestAuthed(req)) {
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 = [];