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.
- package/lib/ask-user-mcp-server.js +120 -0
- package/lib/daemon.js +97 -38
- package/lib/mate-datastore.js +27 -5
- package/lib/mates.js +2 -2
- package/lib/os-users.js +13 -0
- package/lib/project-connection.js +16 -9
- package/lib/project-mate-datastore.js +16 -2
- package/lib/project-sessions.js +110 -7
- package/lib/project-user-message.js +4 -3
- package/lib/project.js +97 -10
- package/lib/public/css/mates.css +94 -52
- package/lib/public/css/mobile-nav.css +0 -14
- package/lib/public/css/notifications-center.css +23 -19
- package/lib/public/css/sidebar.css +326 -101
- package/lib/public/index.html +24 -57
- package/lib/public/modules/app-dm.js +0 -2
- package/lib/public/modules/app-messages.js +3 -5
- package/lib/public/modules/app-rendering.js +0 -2
- package/lib/public/modules/diff.js +21 -7
- package/lib/public/modules/mate-datastore-ui.js +108 -98
- package/lib/public/modules/mate-sidebar.js +0 -9
- package/lib/public/modules/mate-wizard.js +15 -15
- package/lib/public/modules/sidebar-mobile.js +10 -20
- package/lib/public/modules/sidebar-sessions.js +490 -113
- package/lib/public/modules/sidebar.js +8 -6
- package/lib/public/modules/tools.js +58 -13
- package/lib/public/sw.js +1 -1
- package/lib/sdk-bridge.js +36 -28
- package/lib/sdk-message-processor.js +14 -3
- package/lib/server.js +28 -72
- package/lib/sessions.js +157 -20
- package/lib/ws-schema.js +2 -0
- package/lib/yoke/adapters/claude-worker.js +114 -2
- package/lib/yoke/adapters/claude.js +56 -5
- package/lib/yoke/adapters/codex.js +349 -58
- package/lib/yoke/index.js +73 -35
- package/lib/yoke/instructions.js +0 -1
- package/lib/yoke/mcp-bridge-server.js +14 -6
- package/package.json +1 -2
- 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
|
-
|
|
686
|
-
|
|
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
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
if (
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
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 =
|
|
251
|
-
var extraOpts = "
|
|
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.
|
|
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";
|