clay-server 2.34.0-beta.1 → 2.34.0-beta.10

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 (40) hide show
  1. package/lib/ask-user-mcp-server.js +120 -0
  2. package/lib/daemon.js +97 -38
  3. package/lib/mate-datastore.js +27 -5
  4. package/lib/mates.js +2 -2
  5. package/lib/os-users.js +13 -0
  6. package/lib/project-connection.js +16 -9
  7. package/lib/project-mate-datastore.js +16 -2
  8. package/lib/project-sessions.js +110 -7
  9. package/lib/project-user-message.js +4 -3
  10. package/lib/project.js +97 -10
  11. package/lib/public/css/mates.css +94 -52
  12. package/lib/public/css/mobile-nav.css +0 -14
  13. package/lib/public/css/notifications-center.css +23 -19
  14. package/lib/public/css/sidebar.css +326 -101
  15. package/lib/public/index.html +24 -57
  16. package/lib/public/modules/app-dm.js +0 -2
  17. package/lib/public/modules/app-messages.js +3 -5
  18. package/lib/public/modules/app-rendering.js +0 -2
  19. package/lib/public/modules/diff.js +21 -7
  20. package/lib/public/modules/mate-datastore-ui.js +108 -98
  21. package/lib/public/modules/mate-sidebar.js +0 -9
  22. package/lib/public/modules/mate-wizard.js +15 -15
  23. package/lib/public/modules/sidebar-mobile.js +10 -20
  24. package/lib/public/modules/sidebar-sessions.js +490 -113
  25. package/lib/public/modules/sidebar.js +8 -6
  26. package/lib/public/modules/tools.js +58 -13
  27. package/lib/public/sw.js +1 -1
  28. package/lib/sdk-bridge.js +36 -28
  29. package/lib/sdk-message-processor.js +14 -3
  30. package/lib/server.js +28 -72
  31. package/lib/sessions.js +157 -20
  32. package/lib/ws-schema.js +2 -0
  33. package/lib/yoke/adapters/claude-worker.js +114 -2
  34. package/lib/yoke/adapters/claude.js +56 -5
  35. package/lib/yoke/adapters/codex.js +349 -58
  36. package/lib/yoke/index.js +73 -35
  37. package/lib/yoke/instructions.js +0 -1
  38. package/lib/yoke/mcp-bridge-server.js +14 -6
  39. package/package.json +1 -2
  40. package/lib/yoke/adapters/gemini.js +0 -709
package/lib/sessions.js CHANGED
@@ -96,6 +96,7 @@ function createSessionManager(opts) {
96
96
  if (session.vendor) metaObj.vendor = session.vendor;
97
97
  if (session.sessionVisibility) metaObj.sessionVisibility = session.sessionVisibility;
98
98
  if (session.bookmarked) metaObj.bookmarked = true;
99
+ if (typeof session.favoriteOrder === "number") metaObj.favoriteOrder = session.favoriteOrder;
99
100
  if (session.lastRewindUuid) metaObj.lastRewindUuid = session.lastRewindUuid;
100
101
  if (session.loop) metaObj.loop = session.loop;
101
102
  if (session.debateState) metaObj.debateState = session.debateState;
@@ -202,6 +203,7 @@ function createSessionManager(opts) {
202
203
  if (m.ownerId) session.ownerId = m.ownerId;
203
204
  session.sessionVisibility = m.sessionVisibility || "shared";
204
205
  session.bookmarked = !!m.bookmarked;
206
+ session.favoriteOrder = typeof m.favoriteOrder === "number" ? m.favoriteOrder : null;
205
207
  sessions.set(localId, session);
206
208
  }
207
209
  }
@@ -241,6 +243,7 @@ function createSessionManager(opts) {
241
243
  ownerId: s.ownerId || null,
242
244
  sessionVisibility: s.sessionVisibility || "shared",
243
245
  bookmarked: !!s.bookmarked,
246
+ favoriteOrder: typeof s.favoriteOrder === "number" ? s.favoriteOrder : null,
244
247
  unread: unreadMap[s.localId] || 0,
245
248
  vendor: s.vendor || null,
246
249
  };
@@ -303,6 +306,7 @@ function createSessionManager(opts) {
303
306
  ownerId: (sessionOpts && sessionOpts.ownerId) || null,
304
307
  sessionVisibility: (sessionOpts && sessionOpts.sessionVisibility) || "shared",
305
308
  bookmarked: false,
309
+ favoriteOrder: null,
306
310
  vendor: (sessionOpts && sessionOpts.vendor) || null,
307
311
  };
308
312
  sessions.set(localId, session);
@@ -334,6 +338,7 @@ function createSessionManager(opts) {
334
338
  ownerId: (sessionOpts && sessionOpts.ownerId) || null,
335
339
  sessionVisibility: (sessionOpts && sessionOpts.sessionVisibility) || "shared",
336
340
  bookmarked: false,
341
+ favoriteOrder: null,
337
342
  vendor: (sessionOpts && sessionOpts.vendor) || null,
338
343
  };
339
344
  sessions.set(localId, session);
@@ -525,6 +530,38 @@ function createSessionManager(opts) {
525
530
  sessions.delete(localId);
526
531
  }
527
532
 
533
+ function deleteSessionsBulk(localIds, targetWs) {
534
+ if (!Array.isArray(localIds) || localIds.length === 0) return;
535
+
536
+ var seen = {};
537
+ var ids = [];
538
+ for (var i = 0; i < localIds.length; i++) {
539
+ var id = localIds[i];
540
+ if (typeof id !== "number" || seen[id] || !sessions.has(id)) continue;
541
+ seen[id] = true;
542
+ ids.push(id);
543
+ }
544
+ if (ids.length === 0) return;
545
+
546
+ var deletedActive = false;
547
+ for (var j = 0; j < ids.length; j++) {
548
+ if (ids[j] === activeSessionId) deletedActive = true;
549
+ deleteSessionQuiet(ids[j]);
550
+ }
551
+
552
+ if (sessions.size === 0) {
553
+ createSession(null, targetWs);
554
+ return;
555
+ }
556
+
557
+ if (deletedActive) {
558
+ var remaining = [...sessions.keys()];
559
+ switchSession(remaining[remaining.length - 1], targetWs);
560
+ } else {
561
+ broadcastSessionList();
562
+ }
563
+ }
564
+
528
565
  function doSendToSession(session, obj) {
529
566
  // Send to active clients without recording to history/disk (ephemeral data)
530
567
  if (sendEach) {
@@ -622,6 +659,7 @@ function createSessionManager(opts) {
622
659
  history: cliHistory,
623
660
  messageUUIDs: [],
624
661
  bookmarked: false,
662
+ favoriteOrder: null,
625
663
  };
626
664
  sessions.set(localId, session);
627
665
  saveSessionFile(session);
@@ -680,35 +718,82 @@ function createSessionManager(opts) {
680
718
  var session = sessions.get(localId);
681
719
  if (!session) return { hits: [], total: 0 };
682
720
  var q = query.toLowerCase();
721
+ var qLen = query.length;
683
722
  var history = session.history;
684
723
  var hits = [];
685
- var lastAssistantHitTurn = -1; // track current assistant turn to deduplicate delta hits
686
- var currentTurnStart = -1;
724
+
725
+ // Assistant turns can consist of many streaming deltas (especially Codex,
726
+ // where agentMessage/delta fragments arrive in small chunks). We accumulate
727
+ // delta text per turn, scan for ALL occurrences of the query across the
728
+ // accumulated buffer, then map each occurrence back to the historyIndex of
729
+ // the delta that contains its starting offset. This catches multiple
730
+ // matches within a single turn and also matches that straddle delta
731
+ // boundaries.
732
+ var turnBuffer = "";
733
+ var turnSegments = []; // [{ start, end, historyIndex, ts }]
734
+
735
+ function pushScalarHits(text, historyIndex, role, ts) {
736
+ if (!text) return;
737
+ var lower = text.toLowerCase();
738
+ var from = 0;
739
+ while (true) {
740
+ var idx = lower.indexOf(q, from);
741
+ if (idx === -1) break;
742
+ var s = Math.max(0, idx - 15);
743
+ var e = Math.min(text.length, idx + qLen + 15);
744
+ var snippet = (s > 0 ? "\u2026" : "") + text.substring(s, e) + (e < text.length ? "\u2026" : "");
745
+ hits.push({ historyIndex: historyIndex, snippet: snippet, role: role, ts: ts });
746
+ from = idx + qLen;
747
+ }
748
+ }
749
+
750
+ function flushTurn() {
751
+ if (!turnBuffer || turnSegments.length === 0) {
752
+ turnBuffer = "";
753
+ turnSegments = [];
754
+ return;
755
+ }
756
+ var lowerBuf = turnBuffer.toLowerCase();
757
+ var from = 0;
758
+ var segCursor = 0;
759
+ while (true) {
760
+ var idx = lowerBuf.indexOf(q, from);
761
+ if (idx === -1) break;
762
+ // Advance segCursor to the segment containing idx.
763
+ while (segCursor < turnSegments.length - 1 && turnSegments[segCursor].end <= idx) {
764
+ segCursor++;
765
+ }
766
+ var seg = turnSegments[segCursor];
767
+ var s = Math.max(0, idx - 15);
768
+ var e = Math.min(turnBuffer.length, idx + qLen + 15);
769
+ var snippet = (s > 0 ? "\u2026" : "") + turnBuffer.substring(s, e) + (e < turnBuffer.length ? "\u2026" : "");
770
+ hits.push({ historyIndex: seg.historyIndex, snippet: snippet, role: "assistant", ts: seg.ts });
771
+ from = idx + qLen;
772
+ }
773
+ turnBuffer = "";
774
+ turnSegments = [];
775
+ }
776
+
687
777
  for (var i = 0; i < history.length; i++) {
688
778
  var entry = history[i];
689
- if (entry.type === "user_message" || entry.type === "mention_user") {
690
- currentTurnStart = i;
691
- lastAssistantHitTurn = -1;
692
- }
693
- if ((entry.type === "delta" || entry.type === "user_message" || entry.type === "mention_user" || entry.type === "mention_response" || entry.type === "debate_turn_done" || entry.type === "debate_comment_injected") && entry.text) {
694
- // Skip duplicate delta hits within the same assistant turn
695
- if (entry.type === "delta" && currentTurnStart === lastAssistantHitTurn) continue;
696
- var text = entry.text;
697
- var lowerText = text.toLowerCase();
698
- var idx = lowerText.indexOf(q);
699
- if (idx === -1) continue;
700
- var start = Math.max(0, idx - 15);
701
- var end = Math.min(text.length, idx + query.length + 15);
702
- var snippet = (start > 0 ? "\u2026" : "") + text.substring(start, end) + (end < text.length ? "\u2026" : "");
703
- if (entry.type === "delta") lastAssistantHitTurn = currentTurnStart;
704
- hits.push({
779
+ var t = entry.type;
780
+ if (t === "user_message" || t === "mention_user") {
781
+ flushTurn();
782
+ pushScalarHits(entry.text, i, t === "user_message" ? "user" : "assistant", entry._ts || null);
783
+ } else if (t === "delta" && entry.text) {
784
+ turnSegments.push({
785
+ start: turnBuffer.length,
786
+ end: turnBuffer.length + entry.text.length,
705
787
  historyIndex: i,
706
- snippet: snippet,
707
- role: entry.type === "user_message" ? "user" : "assistant",
708
788
  ts: entry._ts || null,
709
789
  });
790
+ turnBuffer += entry.text;
791
+ } else if ((t === "mention_response" || t === "debate_turn_done" || t === "debate_comment_injected") && entry.text) {
792
+ flushTurn();
793
+ pushScalarHits(entry.text, i, "assistant", entry._ts || null);
710
794
  }
711
795
  }
796
+ flushTurn();
712
797
  return { hits: hits, total: history.length };
713
798
  }
714
799
 
@@ -796,6 +881,7 @@ function createSessionManager(opts) {
796
881
  switchSession: switchSession,
797
882
  deleteSession: deleteSession,
798
883
  deleteSessionQuiet: deleteSessionQuiet,
884
+ deleteSessionsBulk: deleteSessionsBulk,
799
885
  resumeSession: resumeSession,
800
886
  broadcastSessionList: broadcastSessionList,
801
887
  getTotalUnread: function (ws) {
@@ -829,10 +915,61 @@ function createSessionManager(opts) {
829
915
  var session = sessions.get(localId);
830
916
  if (!session) return { error: "Session not found" };
831
917
  session.bookmarked = !!bookmarked;
918
+ if (session.bookmarked) {
919
+ var maxOrder = -1;
920
+ sessions.forEach(function (s) {
921
+ if (s.bookmarked && typeof s.favoriteOrder === "number" && s.favoriteOrder > maxOrder) {
922
+ maxOrder = s.favoriteOrder;
923
+ }
924
+ });
925
+ session.favoriteOrder = maxOrder + 1;
926
+ } else {
927
+ session.favoriteOrder = null;
928
+ }
832
929
  saveSessionFile(session);
833
930
  broadcastSessionList();
834
931
  return { ok: true };
835
932
  },
933
+ reorderBookmarkedSessions: function (sourceId, targetId, insertBefore) {
934
+ var source = sessions.get(sourceId);
935
+ var target = sessions.get(targetId);
936
+ if (!source || !target) return { error: "Session not found" };
937
+ if (!source.bookmarked || !target.bookmarked) return { error: "Only favorites can be reordered" };
938
+
939
+ var favorites = [];
940
+ sessions.forEach(function (s) {
941
+ if (s.bookmarked) favorites.push(s);
942
+ });
943
+ favorites.sort(function (a, b) {
944
+ var ao = typeof a.favoriteOrder === "number" ? a.favoriteOrder : Number.MAX_SAFE_INTEGER;
945
+ var bo = typeof b.favoriteOrder === "number" ? b.favoriteOrder : Number.MAX_SAFE_INTEGER;
946
+ if (ao !== bo) return ao - bo;
947
+ return (b.lastActivity || 0) - (a.lastActivity || 0);
948
+ });
949
+
950
+ var reordered = [];
951
+ for (var i = 0; i < favorites.length; i++) {
952
+ if (favorites[i].localId !== sourceId) reordered.push(favorites[i]);
953
+ }
954
+
955
+ var targetIdx = -1;
956
+ for (var j = 0; j < reordered.length; j++) {
957
+ if (reordered[j].localId === targetId) {
958
+ targetIdx = j;
959
+ break;
960
+ }
961
+ }
962
+ if (targetIdx === -1) return { error: "Target favorite not found" };
963
+ if (!insertBefore) targetIdx++;
964
+ reordered.splice(targetIdx, 0, source);
965
+
966
+ for (var k = 0; k < reordered.length; k++) {
967
+ reordered[k].favoriteOrder = k;
968
+ saveSessionFile(reordered[k]);
969
+ }
970
+ broadcastSessionList();
971
+ return { ok: true };
972
+ },
836
973
  setSessionOwner: function (localId, ownerId) {
837
974
  var session = sessions.get(localId);
838
975
  if (!session) return { error: "Session not found" };
package/lib/ws-schema.js CHANGED
@@ -21,6 +21,8 @@ var schema = {
21
21
  "delete_session": { direction: "c2s", handler: "lib/project-sessions.js", description: "Delete a session by ID" },
22
22
  "rename_session": { direction: "c2s", handler: "lib/project-sessions.js", description: "Rename a session" },
23
23
  "set_session_bookmark": { direction: "c2s", handler: "lib/project-sessions.js", description: "Bookmark or unbookmark a session in the sidebar" },
24
+ "reorder_session_bookmarks": { direction: "c2s", handler: "lib/project-sessions.js", description: "Reorder favorited sessions within the favorites area" },
25
+ "bulk_delete_sessions": { direction: "c2s", handler: "lib/project-sessions.js", description: "Delete a group of sessions at once" },
24
26
  "resume_session": { direction: "c2s", handler: "lib/project-sessions.js", description: "Resume a CLI session by its CLI session ID" },
25
27
  "set_session_visibility": { direction: "c2s", handler: "lib/project-sessions.js", description: "Show or hide a session in the sidebar" },
26
28
  "search_sessions": { direction: "c2s", handler: "lib/project-sessions.js", description: "Search session titles" },
@@ -16,6 +16,7 @@ try { require("fs").writeSync(2, "[sdk-worker] BOOT pid=" + process.pid + " uid=
16
16
  var net = require("net");
17
17
  var crypto = require("crypto");
18
18
  var path = require("path");
19
+ var fs = require("fs");
19
20
 
20
21
  var socketPath = process.argv[2];
21
22
  if (!socketPath) {
@@ -31,6 +32,7 @@ var abortController = null;
31
32
  var pendingPermissions = {}; // requestId -> resolve
32
33
  var pendingAskUser = {}; // toolUseId -> resolve
33
34
  var pendingElicitations = {}; // requestId -> resolve
35
+ var pendingMcpToolCalls = {}; // requestId -> { resolve, reject }
34
36
  var conn = null;
35
37
  var buffer = "";
36
38
 
@@ -81,6 +83,90 @@ function getSDK() {
81
83
  return sdkModule;
82
84
  }
83
85
 
86
+ function buildZodShape(z, inputSchema) {
87
+ if (!inputSchema || !inputSchema.properties) return {};
88
+ var shape = {};
89
+ var props = inputSchema.properties;
90
+ var required = inputSchema.required || [];
91
+ var keys = Object.keys(props);
92
+
93
+ for (var i = 0; i < keys.length; i++) {
94
+ var key = keys[i];
95
+ var prop = props[key];
96
+ var field;
97
+
98
+ if (prop.type === "number" || prop.type === "integer") {
99
+ field = z.number();
100
+ } else if (prop.type === "boolean") {
101
+ field = z.boolean();
102
+ } else if (prop.type === "array") {
103
+ field = z.array(z.any());
104
+ } else if (prop.type === "object") {
105
+ field = z.record(z.any());
106
+ } else if (prop.enum) {
107
+ field = z.enum(prop.enum);
108
+ } else {
109
+ field = z.string();
110
+ }
111
+
112
+ if (prop.description) field = field.describe(prop.description);
113
+ if (required.indexOf(key) === -1) field = field.optional();
114
+ shape[key] = field;
115
+ }
116
+
117
+ return shape;
118
+ }
119
+
120
+ function createWorkerMcpToolHandler(serverName, toolName) {
121
+ return function(args) {
122
+ var requestId = crypto.randomUUID();
123
+ sendToDaemon({
124
+ type: "mcp_tool_call",
125
+ requestId: requestId,
126
+ serverName: serverName,
127
+ toolName: toolName,
128
+ args: args || {},
129
+ });
130
+ return new Promise(function(resolve, reject) {
131
+ pendingMcpToolCalls[requestId] = { resolve: resolve, reject: reject };
132
+ });
133
+ };
134
+ }
135
+
136
+ function buildMcpServersFromDescriptors(descriptors, sdk) {
137
+ if (!descriptors || !descriptors.length) return null;
138
+ var z;
139
+ try { z = require("zod").z; } catch (e) {
140
+ try { z = require("zod"); } catch (e2) { return null; }
141
+ }
142
+
143
+ var servers = {};
144
+ for (var i = 0; i < descriptors.length; i++) {
145
+ var descriptor = descriptors[i];
146
+ if (!descriptor || !descriptor.serverName || !descriptor.tools || !descriptor.tools.length) continue;
147
+ var tools = [];
148
+ for (var j = 0; j < descriptor.tools.length; j++) {
149
+ var toolDescriptor = descriptor.tools[j];
150
+ if (!toolDescriptor || !toolDescriptor.name) continue;
151
+ tools.push(sdk.tool(
152
+ toolDescriptor.name,
153
+ toolDescriptor.description || toolDescriptor.name,
154
+ buildZodShape(z, toolDescriptor.inputSchema),
155
+ createWorkerMcpToolHandler(descriptor.serverName, toolDescriptor.name)
156
+ ));
157
+ }
158
+ if (tools.length > 0) {
159
+ servers[descriptor.serverName] = sdk.createSdkMcpServer({
160
+ name: descriptor.serverName,
161
+ version: "1.0.0",
162
+ tools: tools,
163
+ });
164
+ }
165
+ }
166
+
167
+ return Object.keys(servers).length > 0 ? servers : null;
168
+ }
169
+
84
170
  // --- IPC helpers ---
85
171
  function sendToDaemon(msg) {
86
172
  if (!conn || conn.destroyed) return;
@@ -127,6 +213,9 @@ function handleMessage(msg) {
127
213
  case "elicitation_response":
128
214
  handleElicitationResponse(msg);
129
215
  break;
216
+ case "mcp_tool_result":
217
+ handleMcpToolResult(msg);
218
+ break;
130
219
  case "warmup":
131
220
  handleWarmup(msg);
132
221
  break;
@@ -208,6 +297,17 @@ function handleElicitationResponse(msg) {
208
297
  }
209
298
  }
210
299
 
300
+ function handleMcpToolResult(msg) {
301
+ var pending = pendingMcpToolCalls[msg.requestId];
302
+ if (!pending) return;
303
+ delete pendingMcpToolCalls[msg.requestId];
304
+ if (msg.error) {
305
+ pending.reject(new Error(msg.error));
306
+ return;
307
+ }
308
+ pending.resolve(msg.result);
309
+ }
310
+
211
311
  // --- Query handling ---
212
312
  async function handleQueryStart(msg) {
213
313
  var t0 = msg._perfT0 || Date.now();
@@ -238,6 +338,15 @@ async function handleQueryStart(msg) {
238
338
  options.abortController = abortController;
239
339
  options.debug = true;
240
340
  options.debugFile = "/tmp/clay-cli-debug-" + process.pid + ".log";
341
+ if (options.mcpServerDescriptors && options.mcpServerDescriptors.length) {
342
+ try {
343
+ var mcpServers = buildMcpServersFromDescriptors(options.mcpServerDescriptors, sdk);
344
+ if (mcpServers) options.mcpServers = mcpServers;
345
+ } catch (e) {
346
+ console.error("[sdk-worker] Failed to build MCP servers:", e.message || e);
347
+ }
348
+ delete options.mcpServerDescriptors;
349
+ }
241
350
  // Override CLI subprocess spawn to inject NODE_OPTIONS for IPv4-first DNS.
242
351
  // The SDK constructs its own env for the CLI process, so worker env vars
243
352
  // like NODE_OPTIONS are not inherited. We intercept the spawn to fix this.
@@ -247,8 +356,11 @@ async function handleQueryStart(msg) {
247
356
  // This is needed because the CLI's Axios-based HTTP client ignores
248
357
  // NODE_OPTIONS dns flags and still attempts IPv6 connections via its
249
358
  // custom TLS agent, causing 5-10s timeouts on IPv6-less servers.
250
- var preloadScript = require("path").join(__dirname, "ipv4-only.js");
251
- var extraOpts = " --require " + JSON.stringify(preloadScript);
359
+ var preloadScript = path.join(__dirname, "..", "..", "ipv4-only.js");
360
+ var extraOpts = "";
361
+ if (fs.existsSync(preloadScript)) {
362
+ extraOpts += " --require " + JSON.stringify(preloadScript);
363
+ }
252
364
  extraOpts += " --dns-result-order=ipv4first --no-network-family-autoselection";
253
365
  spawnOpts.env.NODE_OPTIONS = (spawnOpts.env.NODE_OPTIONS || "") + extraOpts;
254
366
  console.log("[sdk-worker] spawnClaudeCodeProcess called, command=" + spawnOpts.command);
@@ -600,7 +600,7 @@ function spawnWorker(linuxUser, workerScriptPath, cwd) {
600
600
  worker.send = function(msg) {
601
601
  if (!worker.connection || worker.connection.destroyed) return;
602
602
  try {
603
- worker.connection.write(JSON.stringify(msg) + "\n");
603
+ worker.connection.write(JSON.stringify(serializeWorkerValue(msg)) + "\n");
604
604
  } catch (e) {
605
605
  console.error("[yoke/claude] Failed to send to worker:", e.message);
606
606
  }
@@ -643,12 +643,45 @@ function cleanupWorker(worker) {
643
643
  worker.ready = false;
644
644
  }
645
645
 
646
+ function serializeWorkerValue(value, seen) {
647
+ if (value == null) return value;
648
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") return value;
649
+ if (typeof value === "bigint") return String(value);
650
+ if (typeof value === "function" || typeof value === "symbol" || typeof value === "undefined") return undefined;
651
+ if (value instanceof Date) return value.toISOString();
652
+ if (Buffer.isBuffer(value)) return value.toString("base64");
653
+
654
+ if (!seen) seen = new WeakSet();
655
+ if (typeof value === "object") {
656
+ if (seen.has(value)) return undefined;
657
+ seen.add(value);
658
+ }
659
+
660
+ if (Array.isArray(value)) {
661
+ var arr = [];
662
+ for (var i = 0; i < value.length; i++) {
663
+ var item = serializeWorkerValue(value[i], seen);
664
+ if (item !== undefined) arr.push(item);
665
+ }
666
+ return arr;
667
+ }
668
+
669
+ var out = {};
670
+ var keys = Object.keys(value);
671
+ for (var j = 0; j < keys.length; j++) {
672
+ var key = keys[j];
673
+ var child = serializeWorkerValue(value[key], seen);
674
+ if (child !== undefined) out[key] = child;
675
+ }
676
+ return out;
677
+ }
678
+
646
679
  // --- Worker QueryHandle ---
647
680
  // Wraps worker IPC into the same async iterable + control interface as the
648
681
  // in-process QueryHandle. This allows processQueryStream to iterate a worker
649
682
  // query identically to an in-process query.
650
683
 
651
- function createWorkerQueryHandle(worker, canUseTool, onElicitation) {
684
+ function createWorkerQueryHandle(worker, canUseTool, onElicitation, callMcpTool) {
652
685
  // Async iterable state
653
686
  var iterQueue = [];
654
687
  var iterWaiting = null;
@@ -741,6 +774,20 @@ function createWorkerQueryHandle(worker, canUseTool, onElicitation) {
741
774
  }
742
775
  break;
743
776
 
777
+ case "mcp_tool_call":
778
+ if (callMcpTool) {
779
+ callMcpTool(msg.serverName, msg.toolName, msg.args || {}).then(function(result) {
780
+ worker.send({ type: "mcp_tool_result", requestId: msg.requestId, result: result });
781
+ }).catch(function(e) {
782
+ worker.send({
783
+ type: "mcp_tool_result",
784
+ requestId: msg.requestId,
785
+ error: (e && e.message) ? e.message : String(e),
786
+ });
787
+ });
788
+ }
789
+ break;
790
+
744
791
  case "context_usage":
745
792
  case "model_changed":
746
793
  case "effort_changed":
@@ -943,6 +990,7 @@ function createClaudeAdapter(opts) {
943
990
  cwd: (initOpts && initOpts.cwd) || _cwd,
944
991
  settingSources: ["user", "project", "local"],
945
992
  abortController: ac,
993
+ settings: { disableAllHooks: true },
946
994
  };
947
995
  if (_claudeBinaryPath) warmupOptions.pathToClaudeCodeExecutable = _claudeBinaryPath;
948
996
 
@@ -1102,6 +1150,7 @@ function createClaudeAdapter(opts) {
1102
1150
  if (co.permissionMode) sdkOptions.permissionMode = co.permissionMode;
1103
1151
  if (co.allowDangerouslySkipPermissions) sdkOptions.allowDangerouslySkipPermissions = true;
1104
1152
  if (co.resumeSessionAt) sdkOptions.resumeSessionAt = co.resumeSessionAt;
1153
+ if (co.settings) sdkOptions.settings = co.settings;
1105
1154
 
1106
1155
  var rawQuery = sdk.query({ prompt: mq, options: sdkOptions });
1107
1156
  return createQueryHandle(rawQuery, mq, ac);
@@ -1229,7 +1278,7 @@ function createClaudeAdapter(opts) {
1229
1278
  }
1230
1279
 
1231
1280
  // Create the worker query handle (sets up message handler on worker)
1232
- var handle = createWorkerQueryHandle(worker, queryOpts.canUseTool, queryOpts.onElicitation);
1281
+ var handle = createWorkerQueryHandle(worker, queryOpts.canUseTool, queryOpts.onElicitation, queryOpts.callMcpTool);
1233
1282
 
1234
1283
  // Wait for worker to be ready before sending query_start
1235
1284
  if (!reusingWorker) {
@@ -1250,8 +1299,9 @@ function createClaudeAdapter(opts) {
1250
1299
  if (claudeOpts.betas && claudeOpts.betas.length > 0) queryOptions.betas = claudeOpts.betas;
1251
1300
  if (claudeOpts.permissionMode) queryOptions.permissionMode = claudeOpts.permissionMode;
1252
1301
  if (claudeOpts.allowDangerouslySkipPermissions) queryOptions.allowDangerouslySkipPermissions = true;
1302
+ if (claudeOpts.settings) queryOptions.settings = claudeOpts.settings;
1253
1303
 
1254
- if (queryOpts.toolServers) queryOptions.mcpServers = queryOpts.toolServers;
1304
+ if (queryOpts.toolServerDescriptors) queryOptions.mcpServerDescriptors = queryOpts.toolServerDescriptors;
1255
1305
  if (queryOpts.model) queryOptions.model = queryOpts.model;
1256
1306
  if (queryOpts.effort) queryOptions.effort = queryOpts.effort;
1257
1307
  if (queryOpts.resumeSessionId) queryOptions.resume = queryOpts.resumeSessionId;
@@ -1356,6 +1406,7 @@ function createClaudeAdapter(opts) {
1356
1406
  cwd: (initOpts && initOpts.cwd) || _cwd,
1357
1407
  settingSources: ["user", "project", "local"],
1358
1408
  abortController: ac,
1409
+ settings: { disableAllHooks: true },
1359
1410
  };
1360
1411
  if (_claudeBinaryPath) warmupOptions.pathToClaudeCodeExecutable = _claudeBinaryPath;
1361
1412
 
@@ -1430,7 +1481,7 @@ function createClaudeAdapter(opts) {
1430
1481
  throw new Error("Warmup worker failed to connect: " + (e.message || e));
1431
1482
  }
1432
1483
 
1433
- var warmupOptions = { cwd: workerCwd, settingSources: ["user", "project", "local"] };
1484
+ var warmupOptions = { cwd: workerCwd, settingSources: ["user", "project", "local"], settings: { disableAllHooks: true } };
1434
1485
  if (_claudeBinaryPath) warmupOptions.pathToClaudeCodeExecutable = _claudeBinaryPath;
1435
1486
  if (initOpts && initOpts.dangerouslySkipPermissions) {
1436
1487
  warmupOptions.permissionMode = "bypassPermissions";