clay-server 5.2.0-beta.4 → 5.2.0-beta.5
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/account-auth-socket.js +1 -0
- package/lib/builtin-mates.js +17 -14
- package/lib/canonical-clay-record-authority.js +15 -0
- package/lib/canonical-clay-scheduling-authority.js +19 -0
- package/lib/claude-hook-installer.js +28 -0
- package/lib/clay-mate-chat-receipts.js +26 -0
- package/lib/clay-mate-chat.js +87 -0
- package/lib/clay-mate-knowledge-input.js +26 -0
- package/lib/clay-mate-knowledge.js +95 -0
- package/lib/clay-mate-lifecycle.js +95 -0
- package/lib/clay-product-control-input.js +33 -0
- package/lib/clay-product-control-tools.js +51 -0
- package/lib/clay-product-control.js +237 -0
- package/lib/clay-project-lifecycle.js +154 -0
- package/lib/clay-project-record-input.js +44 -0
- package/lib/clay-project-records.js +83 -0
- package/lib/clay-scheduling-input.js +21 -0
- package/lib/clay-scheduling-receipts.js +26 -0
- package/lib/clay-scheduling.js +119 -0
- package/lib/clay-screen-controls.js +96 -0
- package/lib/clay-workspace-controls.js +83 -0
- package/lib/clay-workspace-input.js +17 -0
- package/lib/clay-workspace-receipts.js +26 -0
- package/lib/daemon-project-access.js +11 -0
- package/lib/daemon-project-lifecycle-callbacks.js +15 -0
- package/lib/daemon-project-lifecycle.js +262 -0
- package/lib/daemon-projects.js +1 -1
- package/lib/daemon.js +73 -221
- package/lib/issues-service.js +29 -15
- package/lib/knowledge-db-import.js +5 -4
- package/lib/knowledge-db-service.js +9 -6
- package/lib/knowledge-document-index.js +2 -1
- package/lib/knowledge-product-operations.js +46 -0
- package/lib/mate-creation-mcp-server.js +2 -0
- package/lib/mate-knowledge-canonical-access.js +10 -0
- package/lib/mate-knowledge-service.js +7 -4
- package/lib/mate-lifecycle-service.js +94 -0
- package/lib/mate-message-delivery.js +43 -0
- package/lib/mate-ready-creation.js +9 -1
- package/lib/mate-schedule-input.js +29 -0
- package/lib/mate-schedule-lifecycle.js +151 -0
- package/lib/mate-scheduling.js +103 -0
- package/lib/mates.js +4 -3
- package/lib/notes-checklist.js +50 -0
- package/lib/notes.js +49 -3
- package/lib/os-users.js +20 -5
- package/lib/project-admin-overview.js +51 -0
- package/lib/project-ask-user.js +4 -2
- package/lib/project-delegated-follow-up.js +2 -1
- package/lib/project-directory-claim.js +65 -0
- package/lib/project-file-operations.js +61 -0
- package/lib/project-filesystem.js +13 -87
- package/lib/project-full-access.js +23 -2
- package/lib/project-knowledge.js +32 -18
- package/lib/project-lifecycle-operations.js +98 -0
- package/lib/project-lifecycle-process-witness.js +32 -0
- package/lib/project-lifecycle-runtime.js +50 -0
- package/lib/project-linux-access.js +22 -0
- package/lib/project-logs-canonical-clay.js +27 -0
- package/lib/project-logs-clay-query.js +16 -0
- package/lib/project-logs-schema.js +4 -2
- package/lib/project-logs-service.js +8 -7
- package/lib/project-logs.js +1 -0
- package/lib/project-loop.js +1 -0
- package/lib/project-mate-chat-control.js +55 -0
- package/lib/project-mate-creation-proposal.js +18 -3
- package/lib/project-models.js +14 -31
- package/lib/project-notifications.js +74 -16
- package/lib/project-ownership.js +77 -0
- package/lib/project-pair-result-delivery.js +14 -3
- package/lib/project-pending-message-queue.js +1 -0
- package/lib/project-product-control.js +87 -0
- package/lib/project-product-session-queue.js +46 -0
- package/lib/project-push-access.js +28 -0
- package/lib/project-scheduled-message-queue.js +30 -0
- package/lib/project-scheduled-messages.js +63 -64
- package/lib/project-scheduled-task-control.js +83 -0
- package/lib/project-scheduled-tasks.js +13 -2
- package/lib/project-scheduling-access.js +10 -0
- package/lib/project-session-actions.js +65 -0
- package/lib/project-session-delete.js +8 -1
- package/lib/project-session-github.js +47 -12
- package/lib/project-session-history-actions.js +165 -0
- package/lib/project-session-runtime-actions.js +63 -0
- package/lib/project-session-vendor.js +54 -0
- package/lib/project-sessions.js +64 -241
- package/lib/project-user-message.js +30 -1
- package/lib/project-workspace-operations.js +126 -0
- package/lib/project-workspace-query.js +9 -2
- package/lib/project.js +95 -3
- package/lib/public/css/clay-tool-activity.css +51 -0
- package/lib/public/css/home-mate-creation.css +5 -0
- package/lib/public/css/overlays.css +4 -2
- package/lib/public/css/project-admin-overview.css +13 -0
- package/lib/public/css/session-folders.css +11 -0
- package/lib/public/css/session-github.css +3 -0
- package/lib/public/css/sticky-notes.css +61 -1
- package/lib/public/index.html +1 -1
- package/lib/public/modules/admin-settings-projects.js +6 -2
- package/lib/public/modules/app-connection.js +8 -1
- package/lib/public/modules/app-message-router.js +1 -1
- package/lib/public/modules/app-messages.js +18 -3
- package/lib/public/modules/app-projects.js +5 -0
- package/lib/public/modules/clay-owner-navigation.js +27 -0
- package/lib/public/modules/clay-tool-activity.js +140 -0
- package/lib/public/modules/clay-tool-presentation.js +338 -0
- package/lib/public/modules/command-palette.js +1 -1
- package/lib/public/modules/default-ai.js +11 -3
- package/lib/public/modules/home-debate-planning.js +5 -5
- package/lib/public/modules/home-mate-chat.js +8 -6
- package/lib/public/modules/home-mate-creation.js +73 -1
- package/lib/public/modules/personal-settings-dialog.js +7 -1
- package/lib/public/modules/project-access-recovery.js +17 -0
- package/lib/public/modules/project-admin-overview.js +21 -0
- package/lib/public/modules/session-folder-layout.js +1 -1
- package/lib/public/modules/session-folder-view-menu.js +9 -0
- package/lib/public/modules/session-github.js +65 -8
- package/lib/public/modules/sidebar-session-folders-ui.js +1 -1
- package/lib/public/modules/sticky-note-markdown.js +72 -6
- package/lib/public/modules/sticky-notes-actions.js +30 -0
- package/lib/public/modules/sticky-notes-browser.js +9 -2
- package/lib/public/modules/sticky-notes-card.js +23 -7
- package/lib/public/modules/sticky-notes-checklist.js +71 -0
- package/lib/public/modules/sticky-notes-editor.js +108 -44
- package/lib/public/modules/sticky-notes-shared.js +50 -2
- package/lib/public/modules/sticky-notes-state.js +66 -0
- package/lib/public/modules/sticky-notes.js +34 -51
- package/lib/public/modules/thinking-lifecycle.js +33 -3
- package/lib/public/modules/thinking-view.js +23 -1
- package/lib/public/modules/tools.js +34 -6
- package/lib/public/modules/user-settings-appearance.js +10 -3
- package/lib/public/modules/user-settings-editors.js +1 -1
- package/lib/public/modules/user-settings-layout.js +11 -2
- package/lib/public/modules/user-settings-preferences.js +6 -2
- package/lib/public/modules/user-settings-profile.js +12 -2
- package/lib/public/modules/user-settings.js +3 -2
- package/lib/public/style.css +2 -0
- package/lib/scheduled-task-records.js +3 -1
- package/lib/scheduler.js +2 -0
- package/lib/sdk-bridge.js +220 -21
- package/lib/sdk-message-processor.js +2 -0
- package/lib/sdk-response-recovery.js +155 -0
- package/lib/server-admin.js +63 -16
- package/lib/server-dm.js +1 -1
- package/lib/server-global-ws.js +10 -20
- package/lib/server-home-chat-events.js +7 -3
- package/lib/server-home-chat.js +4 -16
- package/lib/server-home-mate-creation.js +1 -0
- package/lib/server-home-mcp-consent.js +3 -3
- package/lib/server-mates.js +25 -67
- package/lib/server-owner-ui.js +77 -0
- package/lib/server-path-access.js +38 -0
- package/lib/server-project-removal.js +22 -0
- package/lib/server.js +99 -29
- package/lib/session-config-transition.js +45 -0
- package/lib/session-folders.js +7 -2
- package/lib/session-github.js +9 -2
- package/lib/session-visibility.js +5 -2
- package/lib/sessions.js +10 -0
- package/lib/terminal-manager.js +52 -0
- package/lib/users-permissions.js +2 -8
- package/lib/users.js +4 -0
- package/lib/workspace-query-access.js +4 -1
- package/lib/ws-schema.js +8 -2
- package/lib/yoke/adapters/claude.js +47 -8
- package/lib/yoke/adapters/codex.js +52 -111
- package/lib/yoke/claude-worker-startup.js +28 -0
- package/lib/yoke/codex-idle-recovery.js +81 -1
- package/lib/yoke/codex-recovery-admission.js +164 -0
- package/lib/yoke/session-tool-transport.js +3 -0
- package/package.json +1 -1
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
// This is the ONLY file (besides claude-worker.js) that imports the SDK.
|
|
5
5
|
// Also manages worker processes for OS-level user isolation.
|
|
6
6
|
|
|
7
|
+
var attachWorkerStartup = require("../claude-worker-startup").attachWorkerStartup;
|
|
7
8
|
var bindQueryAbort = require("../query-abort").bindAbort;
|
|
8
9
|
var path = require("path");
|
|
9
10
|
var fs = require("fs");
|
|
@@ -600,6 +601,8 @@ function createQueryHandle(rawQuery, messageQueue, abortController) {
|
|
|
600
601
|
var correlation = claudeCorrelation.createCorrelation();
|
|
601
602
|
var closed = false;
|
|
602
603
|
var removeAbort = function () {};
|
|
604
|
+
var resolveStopped;
|
|
605
|
+
var stopped = new Promise(function(resolve) { resolveStopped = resolve; });
|
|
603
606
|
var handle = {
|
|
604
607
|
// Opaque adapter state (null for in-process queries)
|
|
605
608
|
_adapterState: null,
|
|
@@ -610,12 +613,13 @@ function createQueryHandle(rawQuery, messageQueue, abortController) {
|
|
|
610
613
|
return {
|
|
611
614
|
next: function() {
|
|
612
615
|
return rawIter.next().then(function(result) {
|
|
613
|
-
if (result.done) { closed = true; removeAbort(); }
|
|
616
|
+
if (result.done) { closed = true; removeAbort(); resolveStopped(); }
|
|
614
617
|
if (result.done) return result;
|
|
615
618
|
return { value: correlation.result(result.value, flattenEvent(result.value)), done: false };
|
|
616
619
|
}, function(err) {
|
|
617
620
|
closed = true;
|
|
618
621
|
removeAbort();
|
|
622
|
+
resolveStopped();
|
|
619
623
|
throw err;
|
|
620
624
|
});
|
|
621
625
|
},
|
|
@@ -715,6 +719,11 @@ function createQueryHandle(rawQuery, messageQueue, abortController) {
|
|
|
715
719
|
}
|
|
716
720
|
},
|
|
717
721
|
|
|
722
|
+
stopAndWait: function() {
|
|
723
|
+
handle.abort();
|
|
724
|
+
return stopped;
|
|
725
|
+
},
|
|
726
|
+
|
|
718
727
|
close: function() {
|
|
719
728
|
closed = true;
|
|
720
729
|
removeAbort();
|
|
@@ -818,9 +827,7 @@ function spawnWorker(linuxUser, workerScriptPath, cwd) {
|
|
|
818
827
|
buffer: "",
|
|
819
828
|
};
|
|
820
829
|
|
|
821
|
-
worker
|
|
822
|
-
worker._readyResolve = resolve;
|
|
823
|
-
});
|
|
830
|
+
attachWorkerStartup(worker);
|
|
824
831
|
|
|
825
832
|
// Resolves when the worker process actually exits.
|
|
826
833
|
// Used to prevent spawning a new worker before the old one finishes
|
|
@@ -860,7 +867,15 @@ function spawnWorker(linuxUser, workerScriptPath, cwd) {
|
|
|
860
867
|
});
|
|
861
868
|
connection.on("error", function(err) {
|
|
862
869
|
console.error("[yoke/claude] Worker connection error:", err.message);
|
|
870
|
+
if (worker._readyReject) worker._readyReject(err);
|
|
863
871
|
});
|
|
872
|
+
connection.on("close", function() {
|
|
873
|
+
if (worker._readyReject) worker._readyReject(new Error("Claude worker connection closed before ready."));
|
|
874
|
+
});
|
|
875
|
+
});
|
|
876
|
+
worker.server.on("error", function(error) {
|
|
877
|
+
if (worker._readyReject) worker._readyReject(error);
|
|
878
|
+
cleanupWorker(worker);
|
|
864
879
|
});
|
|
865
880
|
|
|
866
881
|
worker.server.listen(socketPath, function() {
|
|
@@ -892,6 +907,14 @@ function spawnWorker(linuxUser, workerScriptPath, cwd) {
|
|
|
892
907
|
});
|
|
893
908
|
worker.process = spawn(workerSpawn.command, workerSpawn.args, workerSpawn.options);
|
|
894
909
|
|
|
910
|
+
worker.process.on("error", function(error) {
|
|
911
|
+
if (worker._readyReject) worker._readyReject(error);
|
|
912
|
+
worker.messageHandlers.forEach(function(handler) { handler({ type: "query_error", error: error.message, _fallback: true }); });
|
|
913
|
+
cleanupWorker(worker);
|
|
914
|
+
// Only a spawn failure proves that no child execution exists. Other
|
|
915
|
+
// ChildProcess errors (for example a failed signal) are not exit evidence.
|
|
916
|
+
if (!worker.process.pid && worker._exitResolve) { worker._exitResolve(); worker._exitResolve = null; }
|
|
917
|
+
});
|
|
895
918
|
worker.process.stdout.on("data", function(data) {
|
|
896
919
|
console.log("[sdk-worker:" + linuxUser + "] " + data.toString().trim());
|
|
897
920
|
});
|
|
@@ -905,9 +928,8 @@ function spawnWorker(linuxUser, workerScriptPath, cwd) {
|
|
|
905
928
|
worker.process.on("exit", function(code, signal) {
|
|
906
929
|
console.log("[yoke/claude] Worker for " + linuxUser + " exited (code=" + code + ", signal=" + signal + ")" + (worker._stderrBuf ? " stderr: " + worker._stderrBuf.trim() : ""));
|
|
907
930
|
// Reject readyPromise if worker dies before becoming ready
|
|
908
|
-
if (!worker.ready && worker.
|
|
909
|
-
worker.
|
|
910
|
-
// Let the readyPromise hang; the query_error handler will clean up
|
|
931
|
+
if (!worker.ready && worker._readyReject) {
|
|
932
|
+
worker._readyReject(new Error("Claude worker exited before ready (code=" + code + ")."));
|
|
911
933
|
}
|
|
912
934
|
// Notify message handlers about unexpected exit so sessions don't hang.
|
|
913
935
|
// Always dispatch a fallback query_error. The handler is idempotent:
|
|
@@ -1082,6 +1104,9 @@ function createWorkerQueryHandle(worker, canUseTool, onElicitation, callMcpTool,
|
|
|
1082
1104
|
var iterWaiting = null;
|
|
1083
1105
|
var iterEnded = false;
|
|
1084
1106
|
var iterError = null;
|
|
1107
|
+
var resolveStopped;
|
|
1108
|
+
var stopped = new Promise(function(resolve) { resolveStopped = resolve; });
|
|
1109
|
+
if (worker.exitPromise) worker.exitPromise.then(resolveStopped);
|
|
1085
1110
|
|
|
1086
1111
|
// Pending request/response correlation for handle methods that need a
|
|
1087
1112
|
// result from the worker (e.g. rewindFiles). Each entry is keyed by a
|
|
@@ -1299,6 +1324,8 @@ function createWorkerQueryHandle(worker, canUseTool, onElicitation, callMcpTool,
|
|
|
1299
1324
|
}
|
|
1300
1325
|
|
|
1301
1326
|
case "query_done":
|
|
1327
|
+
if (worker._abortTimeout) { clearTimeout(worker._abortTimeout); worker._abortTimeout = null; }
|
|
1328
|
+
resolveStopped();
|
|
1302
1329
|
rejectPendingPermissionModes(new Error("Worker query ended while changing permission mode"));
|
|
1303
1330
|
console.log("[yoke/claude] IPC query_done received, pid=" + (worker.process ? worker.process.pid : "?"));
|
|
1304
1331
|
worker._queryEnded = true;
|
|
@@ -1306,6 +1333,10 @@ function createWorkerQueryHandle(worker, canUseTool, onElicitation, callMcpTool,
|
|
|
1306
1333
|
break;
|
|
1307
1334
|
|
|
1308
1335
|
case "query_error": {
|
|
1336
|
+
if (!msg._fallback) {
|
|
1337
|
+
if (worker._abortTimeout) { clearTimeout(worker._abortTimeout); worker._abortTimeout = null; }
|
|
1338
|
+
resolveStopped();
|
|
1339
|
+
}
|
|
1309
1340
|
rejectPendingPermissionModes(new Error(msg.error || "Worker exited while changing permission mode"));
|
|
1310
1341
|
console.log("[yoke/claude] IPC query_error received, pid=" + (worker.process ? worker.process.pid : "?") + " _fallback=" + !!msg._fallback + " _queryEnded=" + worker._queryEnded + " error=" + (msg.error || "").substring(0, 100));
|
|
1311
1342
|
// Skip fallback errors from exit handler if we already handled the real one
|
|
@@ -1472,6 +1503,11 @@ function createWorkerQueryHandle(worker, canUseTool, onElicitation, callMcpTool,
|
|
|
1472
1503
|
}, 5000);
|
|
1473
1504
|
},
|
|
1474
1505
|
|
|
1506
|
+
stopAndWait: function() {
|
|
1507
|
+
handle.abort();
|
|
1508
|
+
return stopped;
|
|
1509
|
+
},
|
|
1510
|
+
|
|
1475
1511
|
close: function() {
|
|
1476
1512
|
rejectPendingPermissionModes(new Error("Worker query closed while changing permission mode"));
|
|
1477
1513
|
// End the iterator
|
|
@@ -1909,7 +1945,10 @@ function createClaudeAdapter(opts) {
|
|
|
1909
1945
|
|
|
1910
1946
|
// Wait for worker to be ready before sending query_start
|
|
1911
1947
|
if (!reusingWorker) {
|
|
1912
|
-
await worker.readyPromise;
|
|
1948
|
+
try { await worker.readyPromise; } catch (error) {
|
|
1949
|
+
worker.kill();
|
|
1950
|
+
throw error;
|
|
1951
|
+
}
|
|
1913
1952
|
}
|
|
1914
1953
|
|
|
1915
1954
|
// Build serializable query options (no callbacks, no AbortController).
|
|
@@ -1343,6 +1343,8 @@ function createCodexQueryHandle(appServer, queryOpts) {
|
|
|
1343
1343
|
return { threadId: state.threadId, revision: inputRevision };
|
|
1344
1344
|
},
|
|
1345
1345
|
|
|
1346
|
+
getRecoveryThreadId: function() { return state.threadId || queryOpts.resumeSessionId || null; },
|
|
1347
|
+
|
|
1346
1348
|
retireIdleForRecovery: function() {
|
|
1347
1349
|
handle.endInput();
|
|
1348
1350
|
return loopFinished;
|
|
@@ -1461,11 +1463,23 @@ function createCodexAdapter(opts) {
|
|
|
1461
1463
|
var _refCount = 0;
|
|
1462
1464
|
var _lastActiveAt = Date.now();
|
|
1463
1465
|
var _shuttingDown = false;
|
|
1466
|
+
var _recoveryIdleShutdown = null;
|
|
1464
1467
|
var _activeQueries = [];
|
|
1465
|
-
var
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1468
|
+
var recoveryAdmission = require("../codex-recovery-admission").attachRecoveryAdmission({
|
|
1469
|
+
state: function() { return { epoch: _shutdownEpoch, shuttingDown: _shuttingDown, retiring: _recoveryIdleShutdown === _shutdownEpoch,
|
|
1470
|
+
initializing: _initializing, refCount: _refCount, server: _appServer, entries: _activeQueries.slice() }; },
|
|
1471
|
+
shutdownError: createShutdownError,
|
|
1472
|
+
stopIdle: async function() {
|
|
1473
|
+
_recoveryIdleShutdown = _shutdownEpoch;
|
|
1474
|
+
try { return await adapter.shutdownIfIdle(0); }
|
|
1475
|
+
finally { _recoveryIdleShutdown = null; }
|
|
1476
|
+
},
|
|
1477
|
+
createRuntime: function(queryOpts) {
|
|
1478
|
+
return createCodexAdapter(Object.assign({}, _defaultInitOpts, {
|
|
1479
|
+
recoveryRuntime: true, recoverySessionMcpServer: queryOpts.sessionMcpServer,
|
|
1480
|
+
}));
|
|
1481
|
+
},
|
|
1482
|
+
});
|
|
1469
1483
|
|
|
1470
1484
|
function scopedSkillOptions(env) {
|
|
1471
1485
|
if (!_runtimeLinuxUser) return { env: env };
|
|
@@ -1541,102 +1555,6 @@ function createCodexAdapter(opts) {
|
|
|
1541
1555
|
updateLastActiveAt();
|
|
1542
1556
|
}
|
|
1543
1557
|
|
|
1544
|
-
function retainRecoveryRuntime(runtime) {
|
|
1545
|
-
_recoveryRuntimes.push(runtime);
|
|
1546
|
-
}
|
|
1547
|
-
|
|
1548
|
-
function removeRecoveryRuntime(runtime) {
|
|
1549
|
-
var next = [];
|
|
1550
|
-
for (var i = 0; i < _recoveryRuntimes.length; i++) {
|
|
1551
|
-
if (_recoveryRuntimes[i] !== runtime) next.push(_recoveryRuntimes[i]);
|
|
1552
|
-
}
|
|
1553
|
-
_recoveryRuntimes = next;
|
|
1554
|
-
}
|
|
1555
|
-
|
|
1556
|
-
function cleanupRecoveryRuntime(runtime) {
|
|
1557
|
-
for (var i = 0; i < _recoveryCleanupTasks.length; i++) {
|
|
1558
|
-
if (_recoveryCleanupTasks[i].runtime === runtime) return _recoveryCleanupTasks[i].promise;
|
|
1559
|
-
}
|
|
1560
|
-
var task = { runtime: runtime, promise: null };
|
|
1561
|
-
_recoveryCleanupTasks.push(task);
|
|
1562
|
-
var priorCleanup = _recoveryCleanup;
|
|
1563
|
-
task.promise = Promise.resolve(priorCleanup).catch(function() {
|
|
1564
|
-
return null;
|
|
1565
|
-
}).then(function() {
|
|
1566
|
-
return runtime.shutdown();
|
|
1567
|
-
}).catch(function(error) {
|
|
1568
|
-
console.error("[yoke/codex] Isolated recovery runtime shutdown failed:", error.message || error);
|
|
1569
|
-
}).then(function() {
|
|
1570
|
-
removeRecoveryRuntime(runtime);
|
|
1571
|
-
var remainingTasks = [];
|
|
1572
|
-
for (var ti = 0; ti < _recoveryCleanupTasks.length; ti++) {
|
|
1573
|
-
if (_recoveryCleanupTasks[ti] !== task) remainingTasks.push(_recoveryCleanupTasks[ti]);
|
|
1574
|
-
}
|
|
1575
|
-
_recoveryCleanupTasks = remainingTasks;
|
|
1576
|
-
});
|
|
1577
|
-
_recoveryCleanup = task.promise;
|
|
1578
|
-
return task.promise;
|
|
1579
|
-
}
|
|
1580
|
-
|
|
1581
|
-
function shutdownRecoveryRuntimes() {
|
|
1582
|
-
var runtimes = _recoveryRuntimes.slice();
|
|
1583
|
-
if (!runtimes.length) return _recoveryCleanup;
|
|
1584
|
-
return Promise.all(runtimes.map(function(runtime) {
|
|
1585
|
-
return cleanupRecoveryRuntime(runtime);
|
|
1586
|
-
})).then(function() {
|
|
1587
|
-
_recoveryRuntimes = [];
|
|
1588
|
-
});
|
|
1589
|
-
}
|
|
1590
|
-
|
|
1591
|
-
async function createIsolatedRecoveryQuery(queryOpts) {
|
|
1592
|
-
var shutdownEpoch = _shutdownEpoch;
|
|
1593
|
-
function checkCurrent() {
|
|
1594
|
-
idleRecovery.checkAbort(queryOpts);
|
|
1595
|
-
if (_shuttingDown || shutdownEpoch !== _shutdownEpoch) throw createShutdownError();
|
|
1596
|
-
}
|
|
1597
|
-
var releaseHandoff;
|
|
1598
|
-
var priorHandoff = _recoveryHandoff;
|
|
1599
|
-
var myHandoff = new Promise(function(resolve) { releaseHandoff = resolve; });
|
|
1600
|
-
_recoveryHandoff = myHandoff;
|
|
1601
|
-
await (priorHandoff || Promise.resolve());
|
|
1602
|
-
try {
|
|
1603
|
-
await _recoveryCleanup;
|
|
1604
|
-
checkCurrent();
|
|
1605
|
-
if (!_initializing && _refCount > 0) {
|
|
1606
|
-
await idleRecovery.retireIdleQueries(_activeQueries.slice(), _appServer, checkCurrent);
|
|
1607
|
-
}
|
|
1608
|
-
checkCurrent();
|
|
1609
|
-
if (_refCount > 0 || _initializing) {
|
|
1610
|
-
throw new Error("Codex resumed-tool recovery is waiting for an active shared runtime; retry after its current queries finish.");
|
|
1611
|
-
}
|
|
1612
|
-
if (_appServer) {
|
|
1613
|
-
var stopped = await adapter.shutdownIfIdle(0);
|
|
1614
|
-
if (!stopped) throw new Error("Codex resumed-tool recovery could not safely release the shared runtime; retry after its current queries finish.");
|
|
1615
|
-
}
|
|
1616
|
-
checkCurrent();
|
|
1617
|
-
var recoveryRuntime = createCodexAdapter(Object.assign({}, _defaultInitOpts, {
|
|
1618
|
-
recoveryRuntime: true,
|
|
1619
|
-
recoverySessionMcpServer: queryOpts.sessionMcpServer,
|
|
1620
|
-
}));
|
|
1621
|
-
retainRecoveryRuntime(recoveryRuntime);
|
|
1622
|
-
var recoveryOpts = Object.assign({}, queryOpts, {
|
|
1623
|
-
isolatedRecovery: true,
|
|
1624
|
-
onRuntimeFinished: function() {
|
|
1625
|
-
return cleanupRecoveryRuntime(recoveryRuntime);
|
|
1626
|
-
},
|
|
1627
|
-
});
|
|
1628
|
-
try {
|
|
1629
|
-
return await recoveryRuntime.createQuery(recoveryOpts);
|
|
1630
|
-
} catch (error) {
|
|
1631
|
-
await cleanupRecoveryRuntime(recoveryRuntime);
|
|
1632
|
-
throw error;
|
|
1633
|
-
}
|
|
1634
|
-
} finally {
|
|
1635
|
-
releaseHandoff();
|
|
1636
|
-
if (_recoveryHandoff === myHandoff) _recoveryHandoff = null;
|
|
1637
|
-
}
|
|
1638
|
-
}
|
|
1639
|
-
|
|
1640
1558
|
function buildReadyResponse(skillNames) {
|
|
1641
1559
|
return {
|
|
1642
1560
|
models: _cachedModels,
|
|
@@ -1777,12 +1695,14 @@ function createCodexAdapter(opts) {
|
|
|
1777
1695
|
if (_runtimeLinuxUser && requestedLinuxUser && requestedLinuxUser !== _runtimeLinuxUser) {
|
|
1778
1696
|
return Promise.reject(new Error("Codex runtime user mismatch"));
|
|
1779
1697
|
}
|
|
1780
|
-
if (_shuttingDown) {
|
|
1698
|
+
if (_shuttingDown && _recoveryIdleShutdown !== _shutdownEpoch) {
|
|
1781
1699
|
return Promise.reject(createShutdownError());
|
|
1782
1700
|
}
|
|
1783
|
-
|
|
1701
|
+
try { recoveryAdmission.checkSharedExit(); }
|
|
1702
|
+
catch (error) { return Promise.reject(error); }
|
|
1703
|
+
if (recoveryAdmission.pending()) {
|
|
1784
1704
|
var initEpoch = _shutdownEpoch;
|
|
1785
|
-
return
|
|
1705
|
+
return recoveryAdmission.pending().then(function() {
|
|
1786
1706
|
if (initEpoch !== _shutdownEpoch) throw createShutdownError();
|
|
1787
1707
|
return adapter.init(initOpts);
|
|
1788
1708
|
});
|
|
@@ -1963,6 +1883,10 @@ function createCodexAdapter(opts) {
|
|
|
1963
1883
|
return null;
|
|
1964
1884
|
},
|
|
1965
1885
|
|
|
1886
|
+
retireIdleForRecovery: function(checkCurrent) {
|
|
1887
|
+
return idleRecovery.retireIdleQueries(_activeQueries.slice(), _appServer, checkCurrent);
|
|
1888
|
+
},
|
|
1889
|
+
|
|
1966
1890
|
createQuery: async function(queryOpts) {
|
|
1967
1891
|
var requestedLinuxUser = queryOpts && queryOpts.linuxUser;
|
|
1968
1892
|
if (!_runtimeLinuxUser && requestedLinuxUser) {
|
|
@@ -1974,28 +1898,37 @@ function createCodexAdapter(opts) {
|
|
|
1974
1898
|
if (_runtimeLinuxUser && requestedLinuxUser && requestedLinuxUser !== _runtimeLinuxUser) {
|
|
1975
1899
|
throw new Error("Codex runtime user mismatch");
|
|
1976
1900
|
}
|
|
1977
|
-
if (_shuttingDown) {
|
|
1901
|
+
if (_shuttingDown && _recoveryIdleShutdown !== _shutdownEpoch) {
|
|
1978
1902
|
throw createShutdownError();
|
|
1979
1903
|
}
|
|
1980
1904
|
if (queryOpts && queryOpts.sessionMcpServer && queryOpts.resumeSessionId && !queryOpts.isolatedRecovery) {
|
|
1981
|
-
return
|
|
1905
|
+
return recoveryAdmission.createQuery(queryOpts);
|
|
1982
1906
|
}
|
|
1983
1907
|
var queryEpoch = _shutdownEpoch;
|
|
1908
|
+
function checkAdmission() {
|
|
1909
|
+
idleRecovery.checkAbort(queryOpts || {});
|
|
1910
|
+
if ((_shuttingDown && _recoveryIdleShutdown !== _shutdownEpoch) || queryEpoch !== _shutdownEpoch) throw createShutdownError();
|
|
1911
|
+
}
|
|
1984
1912
|
// Recheck after every await: recovery may replace the runtime while an
|
|
1985
1913
|
// ordinary start is waiting for initialization or an earlier handoff.
|
|
1986
1914
|
while (true) {
|
|
1987
1915
|
idleRecovery.checkAbort(queryOpts || {});
|
|
1988
|
-
if (_shuttingDown || queryEpoch !== _shutdownEpoch) throw createShutdownError();
|
|
1989
|
-
|
|
1990
|
-
|
|
1916
|
+
if ((_shuttingDown && _recoveryIdleShutdown !== _shutdownEpoch) || queryEpoch !== _shutdownEpoch) throw createShutdownError();
|
|
1917
|
+
recoveryAdmission.checkSharedExit();
|
|
1918
|
+
if (recoveryAdmission.pending()) {
|
|
1919
|
+
await idleRecovery.wait(recoveryAdmission.pending(), checkAdmission);
|
|
1920
|
+
continue;
|
|
1921
|
+
}
|
|
1922
|
+
if (queryOpts.resumeSessionId && recoveryAdmission.hasTarget(queryOpts.resumeSessionId)) {
|
|
1923
|
+
await recoveryAdmission.waitForTarget(queryOpts);
|
|
1991
1924
|
continue;
|
|
1992
1925
|
}
|
|
1993
1926
|
if (_initializing && _initPromise) {
|
|
1994
|
-
await _initPromise;
|
|
1927
|
+
await idleRecovery.wait(_initPromise, checkAdmission);
|
|
1995
1928
|
continue;
|
|
1996
1929
|
}
|
|
1997
1930
|
if (!_appServer || !_appServer.started) {
|
|
1998
|
-
await adapter.init(queryOpts || {});
|
|
1931
|
+
await idleRecovery.wait(adapter.init(queryOpts || {}), checkAdmission);
|
|
1999
1932
|
continue;
|
|
2000
1933
|
}
|
|
2001
1934
|
break;
|
|
@@ -2174,12 +2107,20 @@ function createCodexAdapter(opts) {
|
|
|
2174
2107
|
},
|
|
2175
2108
|
|
|
2176
2109
|
// Shutdown the app-server process
|
|
2110
|
+
shutdownForRecovery: async function() {
|
|
2111
|
+
var proc = _appServer && _appServer.proc;
|
|
2112
|
+
await adapter.shutdown();
|
|
2113
|
+
if (proc && proc.exitCode === null && proc.signalCode === null) {
|
|
2114
|
+
throw new Error('Codex recovery runtime exit could not be confirmed; its thread remains reserved.');
|
|
2115
|
+
}
|
|
2116
|
+
},
|
|
2117
|
+
|
|
2177
2118
|
shutdown: function() {
|
|
2178
2119
|
_shutdownEpoch++;
|
|
2179
2120
|
return Promise.all([
|
|
2180
2121
|
beginShutdown(true),
|
|
2181
2122
|
shutdownUserRuntimes("shutdown"),
|
|
2182
|
-
|
|
2123
|
+
recoveryAdmission.shutdown(),
|
|
2183
2124
|
]).then(function() { return true; });
|
|
2184
2125
|
},
|
|
2185
2126
|
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// A transport error must settle startup even before a query iterator exists.
|
|
2
|
+
function attachWorkerStartup(worker, options) {
|
|
3
|
+
options = options || {};
|
|
4
|
+
var schedule = options.setTimeout || setTimeout;
|
|
5
|
+
var cancel = options.clearTimeout || clearTimeout;
|
|
6
|
+
var timer;
|
|
7
|
+
var settled = false;
|
|
8
|
+
worker.readyPromise = new Promise(function(resolve, reject) {
|
|
9
|
+
worker._readyResolve = function() { settle(null, resolve, reject); };
|
|
10
|
+
worker._readyReject = function(error) { settle(error, resolve, reject); };
|
|
11
|
+
});
|
|
12
|
+
// Warmup and cancellation may abandon startup before their await is installed.
|
|
13
|
+
worker.readyPromise.catch(function() {});
|
|
14
|
+
function settle(error, resolve, reject) {
|
|
15
|
+
if (settled) return;
|
|
16
|
+
settled = true;
|
|
17
|
+
if (timer) cancel(timer);
|
|
18
|
+
worker._readyResolve = null;
|
|
19
|
+
worker._readyReject = null;
|
|
20
|
+
if (error) reject(error); else resolve();
|
|
21
|
+
}
|
|
22
|
+
timer = schedule(function() {
|
|
23
|
+
if (worker._readyReject) worker._readyReject(new Error("Claude worker did not become ready before the startup deadline."));
|
|
24
|
+
if (typeof worker.kill === "function") worker.kill();
|
|
25
|
+
}, options.timeoutMs || 180000);
|
|
26
|
+
if (timer && typeof timer.unref === "function") timer.unref();
|
|
27
|
+
}
|
|
28
|
+
module.exports = { attachWorkerStartup: attachWorkerStartup };
|
|
@@ -49,4 +49,84 @@ async function retireIdleQueries(entries, server, checkCurrent) {
|
|
|
49
49
|
return true;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
// A separate process may resume a different thread only after the shared
|
|
53
|
+
// process proves it has no writer for that thread. Never infer this from the
|
|
54
|
+
// number of query handles: Codex can retain a loaded thread after input ends.
|
|
55
|
+
async function loadedThreads(server, checkCurrent) {
|
|
56
|
+
var cursor = null;
|
|
57
|
+
var threads = [];
|
|
58
|
+
var deadline = Date.now() + 5000;
|
|
59
|
+
for (var page = 0; page < 20; page++) {
|
|
60
|
+
var remaining = deadline - Date.now();
|
|
61
|
+
if (remaining <= 0) return null;
|
|
62
|
+
var result;
|
|
63
|
+
try {
|
|
64
|
+
result = await server.send('thread/loaded/list', { cursor: cursor, limit: 100 }, remaining);
|
|
65
|
+
} catch (error) {
|
|
66
|
+
checkCurrent();
|
|
67
|
+
if (!server.started) throw error;
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
checkCurrent();
|
|
71
|
+
if (!result || !Array.isArray(result.data) || !result.data.every(function(id) { return typeof id === 'string'; })) return null;
|
|
72
|
+
threads = threads.concat(result.data);
|
|
73
|
+
if (!result.nextCursor) return threads;
|
|
74
|
+
if (result.nextCursor === cursor) return null;
|
|
75
|
+
cursor = result.nextCursor;
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async function threadIsAbsent(server, threadId, checkCurrent) {
|
|
81
|
+
var threads = await loadedThreads(server, checkCurrent);
|
|
82
|
+
return !!threads && threads.indexOf(threadId) === -1;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async function runtimeCanStop(server, retiredThreads, checkCurrent) {
|
|
86
|
+
var threads = await loadedThreads(server, checkCurrent);
|
|
87
|
+
return !!threads && threads.every(function(threadId) { return retiredThreads.indexOf(threadId) !== -1; });
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async function releaseThread(entries, server, threadId, checkCurrent) {
|
|
91
|
+
if (!server || !server.started) throw new Error('Codex disconnected while checking recovery admission');
|
|
92
|
+
var matching = entries.filter(function(entry) {
|
|
93
|
+
return entry.handle && entry.handle.getRecoveryThreadId() === threadId;
|
|
94
|
+
});
|
|
95
|
+
if (!matching.length && await threadIsAbsent(server, threadId, checkCurrent)) return true;
|
|
96
|
+
if (matching.length) {
|
|
97
|
+
if (!await retireIdleQueries(matching, server, checkCurrent)) return false;
|
|
98
|
+
checkCurrent();
|
|
99
|
+
} else {
|
|
100
|
+
// A closed SDK handle can leave a loaded provider thread behind. Only an
|
|
101
|
+
// explicitly idle provider thread with verified terminal state may unload.
|
|
102
|
+
try {
|
|
103
|
+
var status = await server.send('thread/read', { threadId: threadId, includeTurns: false }, 5000);
|
|
104
|
+
checkCurrent();
|
|
105
|
+
if (!status || !status.thread || status.thread.id !== threadId
|
|
106
|
+
|| !status.thread.status || status.thread.status.type !== 'idle') return false;
|
|
107
|
+
var terminals = await server.send('thread/backgroundTerminals/list', { threadId: threadId }, 5000);
|
|
108
|
+
checkCurrent();
|
|
109
|
+
if (!terminalsAreIdle(terminals)) return false;
|
|
110
|
+
} catch (error) { checkCurrent(); if (!server.started) throw error; return false; }
|
|
111
|
+
}
|
|
112
|
+
try { await server.send('thread/unsubscribe', { threadId: threadId }, 5000); }
|
|
113
|
+
catch (error) { checkCurrent(); if (!server.started) throw error; return false; }
|
|
114
|
+
return threadIsAbsent(server, threadId, checkCurrent);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Poll the lifecycle without holding the admission lock. Also check Stop and
|
|
118
|
+
// shutdown while a provider probe, initialization, or process exit is pending.
|
|
119
|
+
function wait(promise, checkCurrent) {
|
|
120
|
+
return new Promise(function(resolve, reject) {
|
|
121
|
+
var timer = setInterval(function() {
|
|
122
|
+
try { checkCurrent(); } catch (error) { clearInterval(timer); reject(error); }
|
|
123
|
+
}, 25);
|
|
124
|
+
Promise.resolve(promise).then(function(value) {
|
|
125
|
+
clearInterval(timer);
|
|
126
|
+
try { checkCurrent(); resolve(value); } catch (error) { reject(error); }
|
|
127
|
+
}, function(error) { clearInterval(timer); reject(error); });
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
module.exports = { retireIdleQueries: retireIdleQueries, checkAbort: checkAbort,
|
|
132
|
+
releaseThread: releaseThread, runtimeCanStop: runtimeCanStop, wait: wait };
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
var idleRecovery = require('./codex-idle-recovery');
|
|
2
|
+
|
|
3
|
+
// Serialize decisions, not whole model turns. Waiting for a target must release
|
|
4
|
+
// the gate so an unrelated Driver can still answer its Worker's permissions.
|
|
5
|
+
function attachRecoveryAdmission(ctx) {
|
|
6
|
+
var handoff = null;
|
|
7
|
+
var runtimes = [];
|
|
8
|
+
var targets = new Map();
|
|
9
|
+
var cleanupTasks = new Map();
|
|
10
|
+
var cleanupFailures = new Map();
|
|
11
|
+
var cleanup = Promise.resolve();
|
|
12
|
+
var unconfirmedSharedProcess = null;
|
|
13
|
+
|
|
14
|
+
function checkSharedExit() {
|
|
15
|
+
if (!unconfirmedSharedProcess) return;
|
|
16
|
+
if (unconfirmedSharedProcess.exitCode === null && unconfirmedSharedProcess.signalCode === null) {
|
|
17
|
+
throw new Error('Codex shared runtime exit could not be confirmed; recovery remains reserved.');
|
|
18
|
+
}
|
|
19
|
+
unconfirmedSharedProcess = null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function checker(queryOpts) {
|
|
23
|
+
var epoch = ctx.state().epoch;
|
|
24
|
+
return function() {
|
|
25
|
+
idleRecovery.checkAbort(queryOpts);
|
|
26
|
+
var state = ctx.state();
|
|
27
|
+
if ((state.shuttingDown && !state.retiring) || state.epoch !== epoch) throw ctx.shutdownError();
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function cleanupRuntime(runtime) {
|
|
32
|
+
if (cleanupTasks.has(runtime)) return cleanupTasks.get(runtime);
|
|
33
|
+
var task = cleanup.catch(function() { return null; }).then(function() {
|
|
34
|
+
return runtime.shutdownForRecovery();
|
|
35
|
+
}).then(function() {
|
|
36
|
+
runtimes = runtimes.filter(function(current) { return current !== runtime; });
|
|
37
|
+
targets.forEach(function(current, threadId) { if (current === runtime) targets.delete(threadId); });
|
|
38
|
+
cleanupTasks.delete(runtime);
|
|
39
|
+
return true;
|
|
40
|
+
}, function(error) {
|
|
41
|
+
console.error('[yoke/codex] Isolated recovery runtime shutdown failed:', error.message || error);
|
|
42
|
+
// Retain ownership after an unconfirmed exit. In particular, never admit
|
|
43
|
+
// a second writer just because bounded process cleanup reached its limit.
|
|
44
|
+
cleanupFailures.set(runtime, error);
|
|
45
|
+
return false;
|
|
46
|
+
});
|
|
47
|
+
cleanupTasks.set(runtime, task);
|
|
48
|
+
cleanup = task;
|
|
49
|
+
return task;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function withAdmission(checkCurrent, action) {
|
|
53
|
+
var release;
|
|
54
|
+
var prior = handoff;
|
|
55
|
+
var own = new Promise(function(resolve) { release = resolve; });
|
|
56
|
+
var tail = Promise.resolve(prior).then(function() { return own; });
|
|
57
|
+
handoff = tail;
|
|
58
|
+
tail.then(function() { if (handoff === tail) handoff = null; });
|
|
59
|
+
try {
|
|
60
|
+
await idleRecovery.wait(prior || Promise.resolve(), checkCurrent);
|
|
61
|
+
await idleRecovery.wait(cleanup, checkCurrent);
|
|
62
|
+
checkSharedExit();
|
|
63
|
+
return await action();
|
|
64
|
+
} finally {
|
|
65
|
+
// An aborted waiter still links to its predecessor. Releasing its own
|
|
66
|
+
// slot cannot let a later caller overtake a probe that is still running.
|
|
67
|
+
release();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function releaseTarget(threadId, checkCurrent) {
|
|
72
|
+
var prior = targets.get(threadId);
|
|
73
|
+
if (!prior) return true;
|
|
74
|
+
if (cleanupFailures.has(prior)) throw cleanupFailures.get(prior);
|
|
75
|
+
await prior.retireIdleForRecovery(checkCurrent);
|
|
76
|
+
await cleanup;
|
|
77
|
+
checkCurrent();
|
|
78
|
+
if (cleanupFailures.has(prior)) throw cleanupFailures.get(prior);
|
|
79
|
+
return !targets.has(threadId);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async function prepareShared(threadId, checkCurrent) {
|
|
83
|
+
var state = ctx.state();
|
|
84
|
+
if (state.initializing) return false;
|
|
85
|
+
var retiredThreads = [];
|
|
86
|
+
if (state.refCount > 0) {
|
|
87
|
+
var snapshots = state.entries.map(function(entry) { return entry.handle && entry.handle.getIdleRecoverySnapshot(); });
|
|
88
|
+
if (await idleRecovery.retireIdleQueries(state.entries, state.server, checkCurrent)) {
|
|
89
|
+
retiredThreads = snapshots.filter(Boolean).map(function(snapshot) { return snapshot.threadId; });
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
state = ctx.state();
|
|
93
|
+
if (!state.server) return true;
|
|
94
|
+
if (!state.refCount && await idleRecovery.runtimeCanStop(state.server, retiredThreads, checkCurrent)) {
|
|
95
|
+
var proc = state.server.proc;
|
|
96
|
+
var stopped = await ctx.stopIdle();
|
|
97
|
+
if (stopped) { unconfirmedSharedProcess = proc || null; checkSharedExit(); }
|
|
98
|
+
return stopped;
|
|
99
|
+
}
|
|
100
|
+
return idleRecovery.releaseThread(state.entries, state.server, threadId, checkCurrent);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async function waitUntilAvailable(queryOpts, action) {
|
|
104
|
+
var checkCurrent = checker(queryOpts);
|
|
105
|
+
var waiting = false;
|
|
106
|
+
if (typeof queryOpts.onRecoveryAdmission === 'function') queryOpts.onRecoveryAdmission(true);
|
|
107
|
+
try {
|
|
108
|
+
while (true) {
|
|
109
|
+
checkCurrent();
|
|
110
|
+
// Cancellation returns to the caller immediately, but an in-flight
|
|
111
|
+
// unsubscribe or shutdown retains the gate until its bounded RPC settles.
|
|
112
|
+
var result = await idleRecovery.wait(withAdmission(checkCurrent, action.bind(null, checkCurrent)), checkCurrent);
|
|
113
|
+
if (result) {
|
|
114
|
+
if (waiting && typeof queryOpts.onRecoveryWaiting === 'function') queryOpts.onRecoveryWaiting(false);
|
|
115
|
+
return result;
|
|
116
|
+
}
|
|
117
|
+
if (!waiting && typeof queryOpts.onRecoveryWaiting === 'function') queryOpts.onRecoveryWaiting(true);
|
|
118
|
+
waiting = true;
|
|
119
|
+
await idleRecovery.wait(new Promise(function(resolve) { setTimeout(resolve, 50); }), checkCurrent);
|
|
120
|
+
}
|
|
121
|
+
} finally {
|
|
122
|
+
if (typeof queryOpts.onRecoveryAdmission === 'function') queryOpts.onRecoveryAdmission(false);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function createQuery(queryOpts) {
|
|
127
|
+
return waitUntilAvailable(queryOpts, async function(checkCurrent) {
|
|
128
|
+
if (!await releaseTarget(queryOpts.resumeSessionId, checkCurrent)) return false;
|
|
129
|
+
if (!await prepareShared(queryOpts.resumeSessionId, checkCurrent)) return false;
|
|
130
|
+
checkCurrent();
|
|
131
|
+
var runtime = ctx.createRuntime(queryOpts);
|
|
132
|
+
runtimes.push(runtime);
|
|
133
|
+
targets.set(queryOpts.resumeSessionId, runtime);
|
|
134
|
+
try {
|
|
135
|
+
return await runtime.createQuery(Object.assign({}, queryOpts, {
|
|
136
|
+
isolatedRecovery: true,
|
|
137
|
+
onRuntimeFinished: function() { return cleanupRuntime(runtime); },
|
|
138
|
+
}));
|
|
139
|
+
} catch (error) {
|
|
140
|
+
await cleanupRuntime(runtime);
|
|
141
|
+
throw error;
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
createQuery: createQuery,
|
|
148
|
+
pending: function() { return handoff; },
|
|
149
|
+
checkSharedExit: checkSharedExit,
|
|
150
|
+
hasTarget: function(threadId) { return targets.has(threadId); },
|
|
151
|
+
waitForTarget: function(queryOpts) {
|
|
152
|
+
return waitUntilAvailable(queryOpts, function(checkCurrent) { return releaseTarget(queryOpts.resumeSessionId, checkCurrent); });
|
|
153
|
+
},
|
|
154
|
+
shutdown: function() {
|
|
155
|
+
return Promise.all(runtimes.slice().map(cleanupRuntime)).then(function(results) {
|
|
156
|
+
checkSharedExit();
|
|
157
|
+
if (results.indexOf(false) !== -1) throw new Error('Codex recovery runtime cleanup could not be confirmed.');
|
|
158
|
+
return cleanup;
|
|
159
|
+
});
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
module.exports = { attachRecoveryAdmission: attachRecoveryAdmission };
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
var vendorRegistry = require("./vendor-registry");
|
|
4
4
|
var CODEX_CATALOG_VERSION = 5;
|
|
5
|
+
var MATE_SCHEDULE_TOOLS = ["begin_mate_schedule_interview", "propose_mate_schedule", "list_mate_schedules", "read_mate_schedule", "update_mate_schedule", "cancel_mate_schedule"];
|
|
5
6
|
var SCHEDULE_TOOLS = ["begin_scheduled_task_interview", "propose_scheduled_task", "list_scheduled_tasks", "read_scheduled_task", "update_scheduled_task", "report_scheduled_task_outcome"];
|
|
6
7
|
|
|
7
8
|
function capability(vendor) {
|
|
@@ -38,6 +39,7 @@ function questionToolName(vendor) {
|
|
|
38
39
|
|
|
39
40
|
function bridgeServerForTool(name) {
|
|
40
41
|
if (name === "ask_user_questions") return "yoke-user-input";
|
|
42
|
+
if (MATE_SCHEDULE_TOOLS.indexOf(name) !== -1) return "clay-mate-schedules";
|
|
41
43
|
if (SCHEDULE_TOOLS.indexOf(name) !== -1) return "clay-scheduled-tasks";
|
|
42
44
|
return null;
|
|
43
45
|
}
|
|
@@ -50,4 +52,5 @@ module.exports = {
|
|
|
50
52
|
questionToolName: questionToolName,
|
|
51
53
|
bridgeServerForTool: bridgeServerForTool,
|
|
52
54
|
SCHEDULE_TOOLS: SCHEDULE_TOOLS,
|
|
55
|
+
MATE_SCHEDULE_TOOLS: MATE_SCHEDULE_TOOLS,
|
|
53
56
|
};
|