clay-server 2.33.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/config.js +9 -13
- package/lib/daemon.js +116 -55
- package/lib/mate-datastore.js +359 -0
- package/lib/mates.js +2 -2
- package/lib/os-users.js +70 -37
- package/lib/project-connection.js +16 -9
- package/lib/project-http.js +3 -4
- package/lib/project-image.js +3 -2
- package/lib/project-mate-datastore.js +232 -0
- package/lib/project-sessions.js +110 -7
- package/lib/project-user-message.js +4 -3
- package/lib/project.js +126 -10
- package/lib/public/app.js +2 -0
- package/lib/public/css/mates.css +228 -11
- package/lib/public/css/messages.css +23 -0
- package/lib/public/css/mobile-nav.css +0 -14
- package/lib/public/css/notifications-center.css +80 -0
- package/lib/public/css/sidebar.css +326 -101
- package/lib/public/index.html +24 -29
- package/lib/public/modules/app-dm.js +0 -2
- package/lib/public/modules/app-messages.js +23 -0
- 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 +280 -0
- package/lib/public/modules/mate-sidebar.js +3 -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 +115 -18
- package/lib/public/sw.js +1 -1
- package/lib/sdk-bridge.js +56 -41
- package/lib/sdk-message-processor.js +21 -4
- package/lib/server.js +28 -72
- package/lib/sessions.js +157 -20
- package/lib/updater.js +2 -2
- package/lib/users.js +2 -2
- package/lib/ws-schema.js +16 -0
- package/lib/yoke/adapters/claude-worker.js +114 -2
- package/lib/yoke/adapters/claude.js +56 -5
- package/lib/yoke/adapters/codex.js +350 -58
- package/lib/yoke/index.js +93 -48
- 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
|
@@ -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";
|
|
@@ -89,6 +89,56 @@ function generateUuid() {
|
|
|
89
89
|
return "codex-" + ts + "-" + cnt + "-" + rnd;
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
function waitMs(ms) {
|
|
93
|
+
return new Promise(function(resolve) {
|
|
94
|
+
setTimeout(resolve, ms);
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function waitForProcessExit(proc, timeoutMs) {
|
|
99
|
+
return new Promise(function(resolve) {
|
|
100
|
+
if (!proc) {
|
|
101
|
+
resolve(true);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (proc.exitCode !== null || proc.signalCode !== null) {
|
|
106
|
+
resolve(true);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
var done = false;
|
|
111
|
+
var timer = null;
|
|
112
|
+
|
|
113
|
+
function cleanup() {
|
|
114
|
+
if (done) return;
|
|
115
|
+
done = true;
|
|
116
|
+
if (timer) clearTimeout(timer);
|
|
117
|
+
proc.removeListener("exit", onDone);
|
|
118
|
+
proc.removeListener("close", onDone);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function onDone() {
|
|
122
|
+
cleanup();
|
|
123
|
+
resolve(true);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
proc.once("exit", onDone);
|
|
127
|
+
proc.once("close", onDone);
|
|
128
|
+
|
|
129
|
+
timer = setTimeout(function() {
|
|
130
|
+
cleanup();
|
|
131
|
+
resolve(false);
|
|
132
|
+
}, timeoutMs || 5000);
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function createShutdownError() {
|
|
137
|
+
var err = new Error("Codex adapter is shutting down, retry shortly");
|
|
138
|
+
err.code = "CODEX_ADAPTER_SHUTTING_DOWN";
|
|
139
|
+
return err;
|
|
140
|
+
}
|
|
141
|
+
|
|
92
142
|
function normalizePlanStatus(status) {
|
|
93
143
|
if (status === "inProgress") return "in_progress";
|
|
94
144
|
if (status === "completed") return "completed";
|
|
@@ -164,6 +214,7 @@ function flattenEvent(notification, state) {
|
|
|
164
214
|
yokeType: "plan_updated",
|
|
165
215
|
turnId: params.turnId || null,
|
|
166
216
|
explanation: params.explanation || "",
|
|
217
|
+
title: "Plan",
|
|
167
218
|
plan: Array.isArray(params.plan) ? params.plan.map(function(step) {
|
|
168
219
|
return {
|
|
169
220
|
step: step && step.step ? step.step : "",
|
|
@@ -309,16 +360,33 @@ function flattenEvent(notification, state) {
|
|
|
309
360
|
state.thinkingBlocks[item.id] = "blk_" + state.blockCounter;
|
|
310
361
|
events.push({ yokeType: "thinking_start", blockId: "blk_" + state.blockCounter });
|
|
311
362
|
}
|
|
312
|
-
|
|
363
|
+
// Codex reasoning items may expose plain text via `text`, a short
|
|
364
|
+
// `summary`, or nested `content` parts. Prefer whichever is present;
|
|
365
|
+
// many turns arrive with only encrypted reasoning and no readable
|
|
366
|
+
// text at all, in which case the UI will hide the expand affordance.
|
|
367
|
+
var reasoningText = "";
|
|
368
|
+
if (typeof item.text === "string" && item.text.length > 0) {
|
|
369
|
+
reasoningText = item.text;
|
|
370
|
+
} else if (typeof item.summary === "string" && item.summary.length > 0) {
|
|
371
|
+
reasoningText = item.summary;
|
|
372
|
+
} else if (Array.isArray(item.content)) {
|
|
373
|
+
var parts = [];
|
|
374
|
+
for (var rpi = 0; rpi < item.content.length; rpi++) {
|
|
375
|
+
var rp = item.content[rpi];
|
|
376
|
+
if (rp && typeof rp.text === "string") parts.push(rp.text);
|
|
377
|
+
}
|
|
378
|
+
reasoningText = parts.join("\n");
|
|
379
|
+
}
|
|
380
|
+
if (reasoningText) {
|
|
313
381
|
var thinkBlockId = state.thinkingBlocks[item.id];
|
|
314
382
|
var prevThinkLen = state.thinkingLengths[item.id] || 0;
|
|
315
|
-
if (
|
|
383
|
+
if (reasoningText.length > prevThinkLen) {
|
|
316
384
|
events.push({
|
|
317
385
|
yokeType: "thinking_delta",
|
|
318
386
|
blockId: thinkBlockId,
|
|
319
|
-
text:
|
|
387
|
+
text: reasoningText.substring(prevThinkLen),
|
|
320
388
|
});
|
|
321
|
-
state.thinkingLengths[item.id] =
|
|
389
|
+
state.thinkingLengths[item.id] = reasoningText.length;
|
|
322
390
|
}
|
|
323
391
|
}
|
|
324
392
|
if (evtPhase === "completed") {
|
|
@@ -502,6 +570,7 @@ function createCodexQueryHandle(appServer, queryOpts) {
|
|
|
502
570
|
var systemPrompt = queryOpts.systemPrompt || "";
|
|
503
571
|
var canUseTool = queryOpts.canUseTool || null;
|
|
504
572
|
var onElicitation = queryOpts.onElicitation || null;
|
|
573
|
+
var onFinished = queryOpts.onFinished || null;
|
|
505
574
|
|
|
506
575
|
// Check if the query was cancelled (either via handle.abort() or direct signal abort)
|
|
507
576
|
function isCancelled() {
|
|
@@ -532,6 +601,19 @@ function createCodexQueryHandle(appServer, queryOpts) {
|
|
|
532
601
|
var eventBuffer = [];
|
|
533
602
|
var eventWaiting = null;
|
|
534
603
|
var iteratorDone = false;
|
|
604
|
+
var finishedNotified = false;
|
|
605
|
+
|
|
606
|
+
function notifyFinished() {
|
|
607
|
+
if (finishedNotified) return;
|
|
608
|
+
finishedNotified = true;
|
|
609
|
+
if (typeof onFinished === "function") {
|
|
610
|
+
try {
|
|
611
|
+
onFinished();
|
|
612
|
+
} catch (e) {
|
|
613
|
+
console.error("[yoke/codex] onFinished error:", e.message || e);
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
}
|
|
535
617
|
|
|
536
618
|
function pushEvent(evt) {
|
|
537
619
|
if (iteratorDone) return;
|
|
@@ -551,6 +633,7 @@ function createCodexQueryHandle(appServer, queryOpts) {
|
|
|
551
633
|
eventWaiting = null;
|
|
552
634
|
resolve({ value: undefined, done: true });
|
|
553
635
|
}
|
|
636
|
+
notifyFinished();
|
|
554
637
|
}
|
|
555
638
|
|
|
556
639
|
// Message queue for multi-turn
|
|
@@ -710,10 +793,37 @@ function createCodexQueryHandle(appServer, queryOpts) {
|
|
|
710
793
|
|
|
711
794
|
// --- Main query loop ---
|
|
712
795
|
async function runQueryLoop(initialMessage) {
|
|
713
|
-
// Prepend system prompt (project instructions from YOKE layer) to first message
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
796
|
+
// Prepend system prompt (project instructions from YOKE layer) to first message.
|
|
797
|
+
// initialMessage may be a string (text-only) or an array of content items
|
|
798
|
+
// (e.g. [{ type: "text", text: "..." }, ...] when images/attachments are present).
|
|
799
|
+
// Naive string concatenation on an array coerces it via toString(), producing
|
|
800
|
+
// "[object Object]" inside the prompt, so we must branch on the shape.
|
|
801
|
+
var currentMessage;
|
|
802
|
+
if (!systemPrompt) {
|
|
803
|
+
currentMessage = initialMessage;
|
|
804
|
+
} else if (typeof initialMessage === "string") {
|
|
805
|
+
currentMessage = systemPrompt + "\n\n" + initialMessage;
|
|
806
|
+
} else if (Array.isArray(initialMessage)) {
|
|
807
|
+
// Prepend systemPrompt to the first text item; if none exists, insert one.
|
|
808
|
+
var cloned = initialMessage.slice();
|
|
809
|
+
var injected = false;
|
|
810
|
+
for (var i = 0; i < cloned.length; i++) {
|
|
811
|
+
if (cloned[i] && cloned[i].type === "text") {
|
|
812
|
+
cloned[i] = {
|
|
813
|
+
type: "text",
|
|
814
|
+
text: systemPrompt + "\n\n" + (cloned[i].text || ""),
|
|
815
|
+
};
|
|
816
|
+
injected = true;
|
|
817
|
+
break;
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
if (!injected) {
|
|
821
|
+
cloned.unshift({ type: "text", text: systemPrompt });
|
|
822
|
+
}
|
|
823
|
+
currentMessage = cloned;
|
|
824
|
+
} else {
|
|
825
|
+
currentMessage = initialMessage;
|
|
826
|
+
}
|
|
717
827
|
|
|
718
828
|
try {
|
|
719
829
|
// Set event handler on app-server
|
|
@@ -935,48 +1045,182 @@ function createCodexQueryHandle(appServer, queryOpts) {
|
|
|
935
1045
|
|
|
936
1046
|
function createCodexAdapter(opts) {
|
|
937
1047
|
var _cwd = (opts && opts.cwd) || process.cwd();
|
|
1048
|
+
var _slug = (opts && opts.slug) || "";
|
|
1049
|
+
var _defaultInitOpts = Object.assign({}, opts || {});
|
|
938
1050
|
var _cachedModels = [];
|
|
939
1051
|
var _appServer = null;
|
|
940
1052
|
var _initPromise = null;
|
|
941
|
-
var
|
|
1053
|
+
var _shutdownPromise = null;
|
|
1054
|
+
var _refCount = 0;
|
|
1055
|
+
var _lastActiveAt = Date.now();
|
|
1056
|
+
var _shuttingDown = false;
|
|
1057
|
+
var _activeQueries = [];
|
|
1058
|
+
|
|
1059
|
+
function updateLastActiveAt() {
|
|
1060
|
+
_lastActiveAt = Date.now();
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
function registerActiveQuery(entry) {
|
|
1064
|
+
_activeQueries.push(entry);
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
function removeActiveQuery(entry) {
|
|
1068
|
+
var next = [];
|
|
1069
|
+
for (var i = 0; i < _activeQueries.length; i++) {
|
|
1070
|
+
if (_activeQueries[i] !== entry) next.push(_activeQueries[i]);
|
|
1071
|
+
}
|
|
1072
|
+
_activeQueries = next;
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
function decrementRefCount() {
|
|
1076
|
+
if (_refCount > 0) {
|
|
1077
|
+
_refCount--;
|
|
1078
|
+
} else {
|
|
1079
|
+
console.error("[yoke/codex] refCount negative, bug!");
|
|
1080
|
+
_refCount = 0;
|
|
1081
|
+
}
|
|
1082
|
+
updateLastActiveAt();
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
function buildReadyResponse(skillNames) {
|
|
1086
|
+
return {
|
|
1087
|
+
models: _cachedModels,
|
|
1088
|
+
defaultModel: "gpt-5.4",
|
|
1089
|
+
skills: skillNames || [],
|
|
1090
|
+
slashCommands: skillNames || [],
|
|
1091
|
+
fastModeState: null,
|
|
1092
|
+
capabilities: {
|
|
1093
|
+
thinking: true,
|
|
1094
|
+
betas: false,
|
|
1095
|
+
rewind: false,
|
|
1096
|
+
sessionResume: true,
|
|
1097
|
+
promptSuggestions: true,
|
|
1098
|
+
elicitation: true,
|
|
1099
|
+
fileCheckpointing: false,
|
|
1100
|
+
contextCompacting: false,
|
|
1101
|
+
toolPolicy: ["ask", "allow-all"],
|
|
1102
|
+
},
|
|
1103
|
+
};
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
function clearRuntimeState() {
|
|
1107
|
+
_appServer = null;
|
|
1108
|
+
_initPromise = null;
|
|
1109
|
+
_cachedModels = [];
|
|
1110
|
+
_refCount = 0;
|
|
1111
|
+
_activeQueries = [];
|
|
1112
|
+
updateLastActiveAt();
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
function waitForRefCount(targetCount, timeoutMs) {
|
|
1116
|
+
var deadline = Date.now() + (timeoutMs || 5000);
|
|
1117
|
+
return new Promise(function(resolve) {
|
|
1118
|
+
function tick() {
|
|
1119
|
+
if (_refCount <= targetCount) {
|
|
1120
|
+
resolve(true);
|
|
1121
|
+
return;
|
|
1122
|
+
}
|
|
1123
|
+
if (Date.now() >= deadline) {
|
|
1124
|
+
resolve(false);
|
|
1125
|
+
return;
|
|
1126
|
+
}
|
|
1127
|
+
setTimeout(tick, 50);
|
|
1128
|
+
}
|
|
1129
|
+
tick();
|
|
1130
|
+
});
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
function stopAppServer(deadlineMs) {
|
|
1134
|
+
var proc = _appServer && _appServer.proc ? _appServer.proc : null;
|
|
1135
|
+
if (!_appServer) return Promise.resolve(true);
|
|
1136
|
+
try {
|
|
1137
|
+
_appServer.stop();
|
|
1138
|
+
} catch (e) {
|
|
1139
|
+
console.error("[yoke/codex] App-server stop error:", e.message || e);
|
|
1140
|
+
}
|
|
1141
|
+
if (!proc) return Promise.resolve(true);
|
|
1142
|
+
var remaining = (typeof deadlineMs === "number") ? Math.max(0, deadlineMs - Date.now()) : 5000;
|
|
1143
|
+
return waitForProcessExit(proc, remaining).then(function(exited) {
|
|
1144
|
+
if (!exited) {
|
|
1145
|
+
try {
|
|
1146
|
+
proc.kill("SIGKILL");
|
|
1147
|
+
} catch (e) {}
|
|
1148
|
+
}
|
|
1149
|
+
return exited;
|
|
1150
|
+
});
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
function beginShutdown(force, idleMs) {
|
|
1154
|
+
if (_shutdownPromise) return _shutdownPromise;
|
|
1155
|
+
if (_shuttingDown) return null;
|
|
1156
|
+
|
|
1157
|
+
_shuttingDown = true;
|
|
1158
|
+
|
|
1159
|
+
_shutdownPromise = (async function() {
|
|
1160
|
+
var deadline = Date.now() + 5000;
|
|
1161
|
+
var shouldAbort = !!force;
|
|
1162
|
+
|
|
1163
|
+
if (_initPromise) {
|
|
1164
|
+
try {
|
|
1165
|
+
await Promise.race([
|
|
1166
|
+
_initPromise.catch(function() { return null; }),
|
|
1167
|
+
waitMs(Math.max(0, deadline - Date.now())),
|
|
1168
|
+
]);
|
|
1169
|
+
} catch (e) {}
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
if (shouldAbort && _activeQueries.length > 0) {
|
|
1173
|
+
var active = _activeQueries.slice();
|
|
1174
|
+
for (var i = 0; i < active.length; i++) {
|
|
1175
|
+
try {
|
|
1176
|
+
if (active[i] && active[i].abort) active[i].abort();
|
|
1177
|
+
} catch (e) {}
|
|
1178
|
+
}
|
|
1179
|
+
await waitForRefCount(0, Math.max(0, deadline - Date.now()));
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
if (_appServer) {
|
|
1183
|
+
await stopAppServer(deadline);
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
clearRuntimeState();
|
|
1187
|
+
_shuttingDown = false;
|
|
1188
|
+
_shutdownPromise = null;
|
|
1189
|
+
return true;
|
|
1190
|
+
})().catch(function(err) {
|
|
1191
|
+
clearRuntimeState();
|
|
1192
|
+
_shuttingDown = false;
|
|
1193
|
+
_shutdownPromise = null;
|
|
1194
|
+
throw err;
|
|
1195
|
+
});
|
|
1196
|
+
|
|
1197
|
+
return _shutdownPromise;
|
|
1198
|
+
}
|
|
942
1199
|
|
|
943
1200
|
var adapter = {
|
|
944
1201
|
vendor: "codex",
|
|
945
1202
|
|
|
946
1203
|
init: function(initOpts) {
|
|
1204
|
+
if (_shuttingDown) {
|
|
1205
|
+
return Promise.reject(createShutdownError());
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
var effectiveInitOpts = Object.assign({}, _defaultInitOpts, initOpts || {});
|
|
1209
|
+
|
|
947
1210
|
// Already initialized - return cached result
|
|
948
1211
|
if (_appServer && _appServer.started && _cachedModels.length > 0) {
|
|
949
|
-
return Promise.resolve(
|
|
950
|
-
models: _cachedModels,
|
|
951
|
-
defaultModel: "gpt-5.4",
|
|
952
|
-
skills: [],
|
|
953
|
-
slashCommands: [],
|
|
954
|
-
fastModeState: null,
|
|
955
|
-
capabilities: {
|
|
956
|
-
thinking: true,
|
|
957
|
-
betas: false,
|
|
958
|
-
rewind: false,
|
|
959
|
-
sessionResume: true,
|
|
960
|
-
promptSuggestions: true,
|
|
961
|
-
elicitation: true,
|
|
962
|
-
fileCheckpointing: false,
|
|
963
|
-
contextCompacting: false,
|
|
964
|
-
toolPolicy: ["ask", "allow-all"],
|
|
965
|
-
},
|
|
966
|
-
});
|
|
1212
|
+
return Promise.resolve(buildReadyResponse([]));
|
|
967
1213
|
}
|
|
968
1214
|
|
|
969
1215
|
// Deduplicate concurrent init calls
|
|
970
1216
|
if (_initPromise) return _initPromise;
|
|
971
1217
|
|
|
972
1218
|
_initPromise = (async function() {
|
|
973
|
-
_initOpts = initOpts;
|
|
974
|
-
|
|
975
1219
|
var serverOpts = { cwd: _cwd };
|
|
976
1220
|
|
|
977
1221
|
// Extract adapter options
|
|
978
|
-
if (
|
|
979
|
-
var co =
|
|
1222
|
+
if (effectiveInitOpts && effectiveInitOpts.adapterOptions && effectiveInitOpts.adapterOptions.CODEX) {
|
|
1223
|
+
var co = effectiveInitOpts.adapterOptions.CODEX;
|
|
980
1224
|
if (co.apiKey) serverOpts.env = Object.assign({}, serverOpts.env || {}, { OPENAI_API_KEY: co.apiKey });
|
|
981
1225
|
if (co.baseUrl) serverOpts.env = Object.assign({}, serverOpts.env || {}, { OPENAI_BASE_URL: co.baseUrl });
|
|
982
1226
|
if (co.config) serverOpts.config = co.config;
|
|
@@ -1004,10 +1248,10 @@ function createCodexAdapter(opts) {
|
|
|
1004
1248
|
|
|
1005
1249
|
// Track 2: Add clay-tools bridge server for in-app + remote MCP tools.
|
|
1006
1250
|
var bridgePath = require("path").join(__dirname, "..", "mcp-bridge-server.js");
|
|
1007
|
-
var clayPort =
|
|
1008
|
-
var clayTls =
|
|
1009
|
-
var clayAuthToken =
|
|
1010
|
-
var claySlug =
|
|
1251
|
+
var clayPort = effectiveInitOpts.clayPort || process.env.CLAY_PORT || 2633;
|
|
1252
|
+
var clayTls = effectiveInitOpts.clayTls || false;
|
|
1253
|
+
var clayAuthToken = effectiveInitOpts.clayAuthToken || "";
|
|
1254
|
+
var claySlug = effectiveInitOpts.slug || _slug || "";
|
|
1011
1255
|
try {
|
|
1012
1256
|
if (require("fs").existsSync(bridgePath)) {
|
|
1013
1257
|
var bridgeArgs = [bridgePath, "--port", String(clayPort), "--slug", claySlug];
|
|
@@ -1048,6 +1292,11 @@ function createCodexAdapter(opts) {
|
|
|
1048
1292
|
});
|
|
1049
1293
|
_appServer.notify("initialized", {});
|
|
1050
1294
|
|
|
1295
|
+
if (_shuttingDown) {
|
|
1296
|
+
await stopAppServer(Date.now() + 1000);
|
|
1297
|
+
throw createShutdownError();
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1051
1300
|
console.log("[codex] App-server initialized, models: gpt-5.4, gpt-5.4-mini, gpt-5.3-codex, gpt-5.3-codex-spark, gpt-5.2");
|
|
1052
1301
|
|
|
1053
1302
|
_cachedModels = [
|
|
@@ -1096,26 +1345,15 @@ function createCodexAdapter(opts) {
|
|
|
1096
1345
|
console.error("[codex] Failed to discover skills:", e.message);
|
|
1097
1346
|
}
|
|
1098
1347
|
|
|
1348
|
+
if (_shuttingDown) {
|
|
1349
|
+
await stopAppServer(Date.now() + 1000);
|
|
1350
|
+
throw createShutdownError();
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1099
1353
|
_initPromise = null;
|
|
1354
|
+
updateLastActiveAt();
|
|
1100
1355
|
|
|
1101
|
-
return
|
|
1102
|
-
models: _cachedModels,
|
|
1103
|
-
defaultModel: "gpt-5.4",
|
|
1104
|
-
skills: skillNames,
|
|
1105
|
-
slashCommands: skillNames,
|
|
1106
|
-
fastModeState: null,
|
|
1107
|
-
capabilities: {
|
|
1108
|
-
thinking: true,
|
|
1109
|
-
betas: false,
|
|
1110
|
-
rewind: false,
|
|
1111
|
-
sessionResume: true,
|
|
1112
|
-
promptSuggestions: true,
|
|
1113
|
-
elicitation: true,
|
|
1114
|
-
fileCheckpointing: false,
|
|
1115
|
-
contextCompacting: false,
|
|
1116
|
-
toolPolicy: ["ask", "allow-all"],
|
|
1117
|
-
},
|
|
1118
|
-
};
|
|
1356
|
+
return buildReadyResponse(skillNames);
|
|
1119
1357
|
})();
|
|
1120
1358
|
|
|
1121
1359
|
return _initPromise;
|
|
@@ -1133,12 +1371,31 @@ function createCodexAdapter(opts) {
|
|
|
1133
1371
|
},
|
|
1134
1372
|
|
|
1135
1373
|
createQuery: async function(queryOpts) {
|
|
1374
|
+
if (_shuttingDown) {
|
|
1375
|
+
throw createShutdownError();
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
if (!_appServer || !_appServer.started) {
|
|
1379
|
+
await adapter.init(queryOpts || {});
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
if (_shuttingDown) {
|
|
1383
|
+
throw createShutdownError();
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1136
1386
|
if (!_appServer || !_appServer.started) {
|
|
1137
1387
|
throw new Error("[yoke/codex] Adapter not initialized. Call init() first.");
|
|
1138
1388
|
}
|
|
1139
1389
|
|
|
1140
1390
|
var model = queryOpts.model || "gpt-5.4";
|
|
1141
1391
|
var ac = queryOpts.abortController || new AbortController();
|
|
1392
|
+
var activeEntry = {
|
|
1393
|
+
abort: function() {
|
|
1394
|
+
try {
|
|
1395
|
+
ac.abort();
|
|
1396
|
+
} catch (e) {}
|
|
1397
|
+
},
|
|
1398
|
+
};
|
|
1142
1399
|
|
|
1143
1400
|
// Map YOKE options to Codex thread options
|
|
1144
1401
|
var codexOpts = (queryOpts.adapterOptions && queryOpts.adapterOptions.CODEX) || {};
|
|
@@ -1175,7 +1432,33 @@ function createCodexAdapter(opts) {
|
|
|
1175
1432
|
|
|
1176
1433
|
console.log("[yoke/codex] createQuery: model=" + model + " approval=" + handleOpts.approvalPolicy + " sandbox=" + handleOpts.sandboxMode);
|
|
1177
1434
|
|
|
1178
|
-
|
|
1435
|
+
_refCount++;
|
|
1436
|
+
registerActiveQuery(activeEntry);
|
|
1437
|
+
|
|
1438
|
+
var handle;
|
|
1439
|
+
try {
|
|
1440
|
+
handleOpts.onFinished = function() {
|
|
1441
|
+
removeActiveQuery(activeEntry);
|
|
1442
|
+
decrementRefCount();
|
|
1443
|
+
};
|
|
1444
|
+
handle = createCodexQueryHandle(_appServer, handleOpts);
|
|
1445
|
+
} catch (e) {
|
|
1446
|
+
removeActiveQuery(activeEntry);
|
|
1447
|
+
decrementRefCount();
|
|
1448
|
+
throw e;
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
activeEntry.handle = handle;
|
|
1452
|
+
activeEntry.abort = function() {
|
|
1453
|
+
try {
|
|
1454
|
+
if (handle && typeof handle.abort === "function") {
|
|
1455
|
+
handle.abort();
|
|
1456
|
+
} else {
|
|
1457
|
+
ac.abort();
|
|
1458
|
+
}
|
|
1459
|
+
} catch (e) {}
|
|
1460
|
+
};
|
|
1461
|
+
|
|
1179
1462
|
return handle;
|
|
1180
1463
|
},
|
|
1181
1464
|
|
|
@@ -1242,10 +1525,19 @@ function createCodexAdapter(opts) {
|
|
|
1242
1525
|
|
|
1243
1526
|
// Shutdown the app-server process
|
|
1244
1527
|
shutdown: function() {
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1528
|
+
return beginShutdown(true);
|
|
1529
|
+
},
|
|
1530
|
+
|
|
1531
|
+
shutdownIfIdle: function(idleMs) {
|
|
1532
|
+
if (_shuttingDown || _shutdownPromise) return Promise.resolve(false);
|
|
1533
|
+
if (_initPromise) return Promise.resolve(false);
|
|
1534
|
+
if (!_appServer) return Promise.resolve(false);
|
|
1535
|
+
if (_refCount > 0) return Promise.resolve(false);
|
|
1536
|
+
if (Date.now() - _lastActiveAt < (idleMs || 0)) return Promise.resolve(false);
|
|
1537
|
+
return beginShutdown(false).then(function() {
|
|
1538
|
+
console.log("[yoke/codex] Reclaimed idle adapter for project " + (_slug || _cwd));
|
|
1539
|
+
return true;
|
|
1540
|
+
});
|
|
1249
1541
|
},
|
|
1250
1542
|
};
|
|
1251
1543
|
|