clay-server 4.1.0-beta.9 → 4.1.0
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/context-usage.js +47 -0
- package/lib/issues-mcp-server.js +1 -1
- package/lib/issues-service.js +3 -1
- package/lib/loop-registry-occurrence.js +1 -0
- package/lib/notes.js +20 -3
- package/lib/os-users.js +6 -0
- package/lib/project-connection.js +14 -3
- package/lib/project-file-path.js +13 -0
- package/lib/project-file-watch.js +19 -8
- package/lib/project-filesystem.js +10 -16
- package/lib/project-http.js +53 -48
- package/lib/project-logs-mcp-server.js +8 -8
- package/lib/project-logs-schema.js +1 -1
- package/lib/project-logs-service.js +46 -13
- package/lib/project-logs-store.js +17 -15
- package/lib/project-logs.js +70 -0
- package/lib/project-loop.js +1 -0
- package/lib/project-notifications.js +1 -1
- package/lib/project-scheduled-result-store.js +131 -0
- package/lib/project-scheduled-task-execution.js +35 -2
- package/lib/project-session-notes.js +11 -4
- package/lib/project-sessions.js +7 -0
- package/lib/project-user-message.js +47 -0
- package/lib/project.js +53 -3
- package/lib/public/app.js +26 -9
- package/lib/public/css/avatar-imprints.css +1 -1
- package/lib/public/css/command-palette.css +20 -0
- package/lib/public/css/git-panel.css +13 -12
- package/lib/public/css/git-placard.css +4 -4
- package/lib/public/css/issues.css +78 -31
- package/lib/public/css/mobile-nav.css +7 -0
- package/lib/public/css/notifications-center.css +3 -3
- package/lib/public/css/project-logs.css +15 -0
- package/lib/public/css/sidebar.css +7 -0
- package/lib/public/css/sticky-notes.css +29 -0
- package/lib/public/modules/app-connection.js +9 -1
- package/lib/public/modules/app-favicon.js +10 -0
- package/lib/public/modules/app-messages.js +43 -12
- package/lib/public/modules/app-notifications.js +2 -0
- package/lib/public/modules/app-panels.js +77 -23
- package/lib/public/modules/app-projects.js +2 -0
- package/lib/public/modules/app-rate-limit.js +148 -61
- package/lib/public/modules/avatar-imprint.js +25 -70
- package/lib/public/modules/avatar.js +12 -4
- package/lib/public/modules/chat-render-runtime.js +20 -0
- package/lib/public/modules/clay-issue-links.js +23 -2
- package/lib/public/modules/clay-log-links.js +11 -1
- package/lib/public/modules/context-view-preference.js +143 -0
- package/lib/public/modules/default-vendor.js +116 -0
- package/lib/public/modules/filebrowser-tabs.js +1 -0
- package/lib/public/modules/filebrowser.js +45 -18
- package/lib/public/modules/issues-render.js +36 -10
- package/lib/public/modules/issues.js +95 -13
- package/lib/public/modules/markdown.js +3 -1
- package/lib/public/modules/pane-bridge.js +14 -8
- package/lib/public/modules/pane-issue-bridge.js +27 -0
- package/lib/public/modules/permission-hints.js +13 -0
- package/lib/public/modules/profile.js +9 -9
- package/lib/public/modules/project-activation.js +49 -0
- package/lib/public/modules/project-logs-render.js +13 -3
- package/lib/public/modules/project-logs.js +45 -15
- package/lib/public/modules/right-workbench.js +44 -0
- package/lib/public/modules/scheduled-result-client.js +33 -0
- package/lib/public/modules/scheduled-result-display.js +50 -0
- package/lib/public/modules/scheduled-result-notification.js +31 -0
- package/lib/public/modules/scheduled-task-presentation.js +15 -0
- package/lib/public/modules/scheduled-tasks.js +9 -21
- package/lib/public/modules/sidebar-mobile.js +51 -3
- package/lib/public/modules/sidebar-sessions.js +65 -5
- package/lib/public/modules/split-session-boundary.js +28 -0
- package/lib/public/modules/split-view.js +42 -7
- package/lib/public/modules/sticky-notes-browser.js +130 -18
- package/lib/public/modules/sticky-notes.js +2 -3
- package/lib/public/modules/terminal.js +4 -4
- package/lib/public/modules/thinking-lifecycle.js +4 -1
- package/lib/public/modules/tools.js +5 -1
- package/lib/scheduled-run-state.js +10 -2
- package/lib/scheduler.js +1 -0
- package/lib/sdk-bridge.js +97 -61
- package/lib/sdk-message-processor.js +44 -13
- package/lib/sdk-skill-discovery.js +6 -28
- package/lib/sdk-turn-queue.js +14 -0
- package/lib/server-context-view.js +53 -0
- package/lib/server-default-vendor.js +60 -0
- package/lib/server.js +23 -6
- package/lib/session-notes-mcp-server.js +3 -5
- package/lib/session-pair-factory.js +2 -2
- package/lib/session-title-generator.js +89 -0
- package/lib/session-title-policy.js +8 -2
- package/lib/sessions.js +26 -1
- package/lib/user-presence.js +6 -0
- package/lib/users-context-view-preferences.js +65 -0
- package/lib/users-default-vendor-preferences.js +80 -0
- package/lib/users.js +12 -0
- package/lib/ws-schema.js +11 -0
- package/lib/yoke/acp-agent-profiles.js +15 -2
- package/lib/yoke/adapters/acp.js +3 -2
- package/lib/yoke/adapters/antigravity.js +2 -2
- package/lib/yoke/adapters/claude-worker.js +11 -12
- package/lib/yoke/adapters/claude.js +75 -22
- package/lib/yoke/adapters/codex.js +216 -29
- package/lib/yoke/adapters/kiro.js +6 -5
- package/lib/yoke/claude-context-usage.js +57 -0
- package/lib/yoke/claude-message-correlation.js +31 -0
- package/lib/yoke/claude-model-switch.js +15 -0
- package/lib/yoke/index.js +5 -1
- package/lib/yoke/mcp-bridge-server.js +18 -5
- package/lib/yoke/session-skill-catalog.js +45 -0
- package/lib/yoke/skill-discovery.js +60 -20
- package/lib/yoke/vendor-registry.js +1 -1
- package/package.json +5 -7
|
@@ -29,7 +29,7 @@ var CODEX_REASONING_DEFAULTS = {
|
|
|
29
29
|
model_supports_reasoning_summaries: true,
|
|
30
30
|
};
|
|
31
31
|
var DEFAULT_CODEX_MODEL = "gpt-6-astra";
|
|
32
|
-
var CODEX_QUERY_BOUND_TOOL_CAPABILITY = { mode: "dynamic", resume: "
|
|
32
|
+
var CODEX_QUERY_BOUND_TOOL_CAPABILITY = { mode: "dynamic", resume: "session-mcp-recovery" };
|
|
33
33
|
var CODEX_NOTE_TOOLS = {
|
|
34
34
|
list_notes: true,
|
|
35
35
|
write_note: true,
|
|
@@ -181,6 +181,11 @@ function flattenEvent(notification, state) {
|
|
|
181
181
|
|
|
182
182
|
if (method === "turn/started") {
|
|
183
183
|
state.turnStarted = true;
|
|
184
|
+
state.currentTurnId = params.turnId || params.turn_id || (params.turn && params.turn.id) || null;
|
|
185
|
+
state.lastInputTokens = null;
|
|
186
|
+
state.lastCachedTokens = null;
|
|
187
|
+
state.lastOutputTokens = null;
|
|
188
|
+
state.currentContextUsage = null;
|
|
184
189
|
var userUuid = generateUuid();
|
|
185
190
|
events.push({ yokeType: "turn_start", uuid: userUuid, messageType: "user" });
|
|
186
191
|
return events;
|
|
@@ -194,16 +199,38 @@ function flattenEvent(notification, state) {
|
|
|
194
199
|
if (turnStatus === "interrupted" || state.aborted) {
|
|
195
200
|
events.push({ yokeType: "interrupted" });
|
|
196
201
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
202
|
+
// Generic usage reporting is independent from current context occupancy.
|
|
203
|
+
// The latter is supplied only by a valid thread/tokenUsage snapshot.
|
|
204
|
+
// Some normal protocol completions omit usage even though the preceding
|
|
205
|
+
// token snapshot carried useful per-turn accounting. Keep that fallback
|
|
206
|
+
// for the generic usage panel, but never use it as context occupancy.
|
|
207
|
+
var inputTokens = usage && typeof usage.input_tokens === "number"
|
|
208
|
+
? usage.input_tokens : (typeof state.lastInputTokens === "number" ? state.lastInputTokens : 0);
|
|
209
|
+
var outputTokens = usage && typeof usage.output_tokens === "number"
|
|
210
|
+
? usage.output_tokens : (typeof state.lastOutputTokens === "number" ? state.lastOutputTokens : 0);
|
|
211
|
+
var cachedTokens = usage && typeof usage.cached_input_tokens === "number"
|
|
212
|
+
? usage.cached_input_tokens : (typeof state.lastCachedTokens === "number" ? state.lastCachedTokens : 0);
|
|
201
213
|
var hasTokenData = inputTokens > 0 || outputTokens > 0;
|
|
202
214
|
var resultModelUsage = {};
|
|
203
215
|
// Codex reports the real window in thread/tokenUsage/updated. Dropping it
|
|
204
216
|
// made the client fall back to its model-name table, which has no entry
|
|
205
217
|
// for codex model ids and defaulted to Claude's 200k denominator.
|
|
206
|
-
|
|
218
|
+
var contextSnapshot = { valid: false };
|
|
219
|
+
if (state.currentContextUsage
|
|
220
|
+
&& typeof state.currentContextUsage.input_tokens === "number"
|
|
221
|
+
&& typeof state.currentContextUsage.contextWindow === "number"
|
|
222
|
+
&& state.currentContextUsage.contextWindow > 0) {
|
|
223
|
+
contextSnapshot = {
|
|
224
|
+
valid: true,
|
|
225
|
+
inputTokens: state.currentContextUsage.input_tokens,
|
|
226
|
+
contextWindow: state.currentContextUsage.contextWindow,
|
|
227
|
+
};
|
|
228
|
+
if (state.currentContextUsage.turnId) contextSnapshot.turnId = state.currentContextUsage.turnId;
|
|
229
|
+
}
|
|
230
|
+
resultModelUsage[state.model] = {
|
|
231
|
+
contextWindow: state.modelContextWindow || null,
|
|
232
|
+
contextSnapshot: contextSnapshot,
|
|
233
|
+
};
|
|
207
234
|
var assistantUuid = generateUuid();
|
|
208
235
|
events.push({
|
|
209
236
|
yokeType: "result",
|
|
@@ -683,24 +710,36 @@ function flattenEvent(notification, state) {
|
|
|
683
710
|
if (method === "thread/tokenUsage/updated") {
|
|
684
711
|
var tu = params.tokenUsage;
|
|
685
712
|
if (tu) {
|
|
713
|
+
var tokenTurnId = params.turnId || params.turn_id || tu.turnId || null;
|
|
714
|
+
if (tokenTurnId && state.currentTurnId && tokenTurnId !== state.currentTurnId) {
|
|
715
|
+
state.lastInputTokens = null;
|
|
716
|
+
state.lastCachedTokens = null;
|
|
717
|
+
state.lastOutputTokens = null;
|
|
718
|
+
state.currentContextUsage = null;
|
|
719
|
+
return events;
|
|
720
|
+
}
|
|
721
|
+
var currentWindow = null;
|
|
686
722
|
var turnUsage = tu.lastTurn;
|
|
687
723
|
if (turnUsage) {
|
|
688
724
|
var candidateInput = typeof turnUsage.inputTokens === "number" ? turnUsage.inputTokens : null;
|
|
689
|
-
|
|
690
|
-
|
|
725
|
+
currentWindow = typeof tu.modelContextWindow === "number" && isFinite(tu.modelContextWindow) && tu.modelContextWindow > 0
|
|
726
|
+
? tu.modelContextWindow : null;
|
|
727
|
+
state.lastInputTokens = candidateInput !== null && isFinite(candidateInput) && candidateInput >= 0 && currentWindow !== null && candidateInput <= currentWindow ? candidateInput : null;
|
|
691
728
|
state.lastCachedTokens = turnUsage.cachedInputTokens || 0;
|
|
692
729
|
state.lastOutputTokens = turnUsage.outputTokens || 0;
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
730
|
+
if (state.lastInputTokens !== null && currentWindow !== null) {
|
|
731
|
+
state.currentContextUsage = { input_tokens: state.lastInputTokens, contextWindow: currentWindow };
|
|
732
|
+
if (tokenTurnId || state.currentTurnId) state.currentContextUsage.turnId = tokenTurnId || state.currentTurnId;
|
|
733
|
+
} else {
|
|
734
|
+
state.currentContextUsage = null;
|
|
735
|
+
}
|
|
697
736
|
} else {
|
|
698
737
|
state.lastInputTokens = null;
|
|
699
738
|
state.lastCachedTokens = null;
|
|
700
739
|
state.lastOutputTokens = null;
|
|
701
740
|
state.currentContextUsage = null;
|
|
702
741
|
}
|
|
703
|
-
if (
|
|
742
|
+
if (currentWindow !== null) state.modelContextWindow = currentWindow;
|
|
704
743
|
}
|
|
705
744
|
return events;
|
|
706
745
|
}
|
|
@@ -750,6 +789,7 @@ function createEventState(model) {
|
|
|
750
789
|
lastCachedTokens: null,
|
|
751
790
|
lastOutputTokens: null,
|
|
752
791
|
currentContextUsage: null,
|
|
792
|
+
currentTurnId: null,
|
|
753
793
|
modelContextWindow: null,
|
|
754
794
|
done: false,
|
|
755
795
|
aborted: false,
|
|
@@ -1123,16 +1163,25 @@ function createCodexQueryHandle(appServer, queryOpts) {
|
|
|
1123
1163
|
|
|
1124
1164
|
var threadResult;
|
|
1125
1165
|
if (queryOpts.resumeSessionId) {
|
|
1126
|
-
// ThreadResumeParams
|
|
1127
|
-
//
|
|
1128
|
-
//
|
|
1129
|
-
|
|
1166
|
+
// ThreadResumeParams has no dynamicTools field, but its supported
|
|
1167
|
+
// config override can start an exact-query MCP bridge without making
|
|
1168
|
+
// a replacement thread or changing the saved conversation history.
|
|
1169
|
+
var resumeParams = {
|
|
1130
1170
|
threadId: queryOpts.resumeSessionId,
|
|
1131
1171
|
model: threadParams.model,
|
|
1132
1172
|
sandbox: threadParams.sandbox,
|
|
1133
1173
|
approvalPolicy: threadParams.approvalPolicy,
|
|
1134
1174
|
cwd: threadParams.cwd,
|
|
1135
|
-
}
|
|
1175
|
+
};
|
|
1176
|
+
if (queryOpts.sessionMcpServer && !queryOpts.isolatedRecovery) {
|
|
1177
|
+
resumeParams.config = { mcp_servers: {} };
|
|
1178
|
+
resumeParams.config.mcp_servers[queryOpts.sessionMcpServer.name] = {
|
|
1179
|
+
command: queryOpts.sessionMcpServer.command,
|
|
1180
|
+
args: queryOpts.sessionMcpServer.args || [],
|
|
1181
|
+
env: queryOpts.sessionMcpServer.env || {},
|
|
1182
|
+
};
|
|
1183
|
+
}
|
|
1184
|
+
threadResult = await appServer.send("thread/resume", resumeParams, 60000);
|
|
1136
1185
|
} else {
|
|
1137
1186
|
threadResult = await appServer.send("thread/start", threadParams, 60000);
|
|
1138
1187
|
}
|
|
@@ -1173,7 +1222,7 @@ function createCodexQueryHandle(appServer, queryOpts) {
|
|
|
1173
1222
|
|
|
1174
1223
|
// Explicit references use Codex's native skill input item regardless of
|
|
1175
1224
|
// which vendor owns the SKILL.md file.
|
|
1176
|
-
var availableSkills = queryOpts.skipSkills ? [] : skillDiscovery.discoverSkills(queryOpts.cwd);
|
|
1225
|
+
var availableSkills = queryOpts.skipSkills ? [] : skillDiscovery.discoverSkills(queryOpts.cwd, queryOpts.skillOptions || { env: queryOpts.env });
|
|
1177
1226
|
var skillItemsToInject = [];
|
|
1178
1227
|
var injected = {};
|
|
1179
1228
|
for (var ii = 0; ii < input.length; ii++) {
|
|
@@ -1288,7 +1337,10 @@ function createCodexQueryHandle(appServer, queryOpts) {
|
|
|
1288
1337
|
},
|
|
1289
1338
|
|
|
1290
1339
|
getContextUsage: function() {
|
|
1291
|
-
return Promise.resolve(state.currentContextUsage
|
|
1340
|
+
return Promise.resolve(state.currentContextUsage || {
|
|
1341
|
+
unavailable: true,
|
|
1342
|
+
reason: "current_context_unavailable",
|
|
1343
|
+
});
|
|
1292
1344
|
},
|
|
1293
1345
|
|
|
1294
1346
|
abort: function() {
|
|
@@ -1384,11 +1436,25 @@ function createCodexAdapter(opts) {
|
|
|
1384
1436
|
for (var i = 0; i < listeners.length; i++) listeners[i]();
|
|
1385
1437
|
}
|
|
1386
1438
|
var _initPromise = null;
|
|
1439
|
+
var _initializing = false;
|
|
1387
1440
|
var _shutdownPromise = null;
|
|
1388
1441
|
var _refCount = 0;
|
|
1389
1442
|
var _lastActiveAt = Date.now();
|
|
1390
1443
|
var _shuttingDown = false;
|
|
1391
1444
|
var _activeQueries = [];
|
|
1445
|
+
var _recoveryHandoff = null;
|
|
1446
|
+
var _recoveryRuntimes = [];
|
|
1447
|
+
var _recoveryCleanup = Promise.resolve();
|
|
1448
|
+
var _recoveryCleanupTasks = [];
|
|
1449
|
+
|
|
1450
|
+
function scopedSkillOptions(env) {
|
|
1451
|
+
if (!_runtimeLinuxUser) return { env: env };
|
|
1452
|
+
try {
|
|
1453
|
+
return Object.assign(skillDiscovery.optionsForOsUser(_resolveOsUserInfo(_runtimeLinuxUser)), { env: env });
|
|
1454
|
+
} catch (e) {
|
|
1455
|
+
return { disable: true };
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1392
1458
|
|
|
1393
1459
|
function withoutLinuxUser(callOpts) {
|
|
1394
1460
|
var cleaned = Object.assign({}, callOpts || {});
|
|
@@ -1455,6 +1521,95 @@ function createCodexAdapter(opts) {
|
|
|
1455
1521
|
updateLastActiveAt();
|
|
1456
1522
|
}
|
|
1457
1523
|
|
|
1524
|
+
function waitForRecoveryHandoff() {
|
|
1525
|
+
return _recoveryHandoff || Promise.resolve();
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1528
|
+
function retainRecoveryRuntime(runtime) {
|
|
1529
|
+
_recoveryRuntimes.push(runtime);
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
function removeRecoveryRuntime(runtime) {
|
|
1533
|
+
var next = [];
|
|
1534
|
+
for (var i = 0; i < _recoveryRuntimes.length; i++) {
|
|
1535
|
+
if (_recoveryRuntimes[i] !== runtime) next.push(_recoveryRuntimes[i]);
|
|
1536
|
+
}
|
|
1537
|
+
_recoveryRuntimes = next;
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
function cleanupRecoveryRuntime(runtime) {
|
|
1541
|
+
for (var i = 0; i < _recoveryCleanupTasks.length; i++) {
|
|
1542
|
+
if (_recoveryCleanupTasks[i].runtime === runtime) return _recoveryCleanupTasks[i].promise;
|
|
1543
|
+
}
|
|
1544
|
+
var task = { runtime: runtime, promise: null };
|
|
1545
|
+
_recoveryCleanupTasks.push(task);
|
|
1546
|
+
var priorCleanup = _recoveryCleanup;
|
|
1547
|
+
task.promise = Promise.resolve(priorCleanup).catch(function() {
|
|
1548
|
+
return null;
|
|
1549
|
+
}).then(function() {
|
|
1550
|
+
return runtime.shutdown();
|
|
1551
|
+
}).catch(function(error) {
|
|
1552
|
+
console.error("[yoke/codex] Isolated recovery runtime shutdown failed:", error.message || error);
|
|
1553
|
+
}).then(function() {
|
|
1554
|
+
removeRecoveryRuntime(runtime);
|
|
1555
|
+
var remainingTasks = [];
|
|
1556
|
+
for (var ti = 0; ti < _recoveryCleanupTasks.length; ti++) {
|
|
1557
|
+
if (_recoveryCleanupTasks[ti] !== task) remainingTasks.push(_recoveryCleanupTasks[ti]);
|
|
1558
|
+
}
|
|
1559
|
+
_recoveryCleanupTasks = remainingTasks;
|
|
1560
|
+
});
|
|
1561
|
+
_recoveryCleanup = task.promise;
|
|
1562
|
+
return task.promise;
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1565
|
+
function shutdownRecoveryRuntimes() {
|
|
1566
|
+
var runtimes = _recoveryRuntimes.slice();
|
|
1567
|
+
if (!runtimes.length) return _recoveryCleanup;
|
|
1568
|
+
return Promise.all(runtimes.map(function(runtime) {
|
|
1569
|
+
return cleanupRecoveryRuntime(runtime);
|
|
1570
|
+
})).then(function() {
|
|
1571
|
+
_recoveryRuntimes = [];
|
|
1572
|
+
});
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
async function createIsolatedRecoveryQuery(queryOpts) {
|
|
1576
|
+
var releaseHandoff;
|
|
1577
|
+
var priorHandoff = _recoveryHandoff;
|
|
1578
|
+
var myHandoff = new Promise(function(resolve) { releaseHandoff = resolve; });
|
|
1579
|
+
_recoveryHandoff = myHandoff;
|
|
1580
|
+
await (priorHandoff || Promise.resolve());
|
|
1581
|
+
try {
|
|
1582
|
+
await _recoveryCleanup;
|
|
1583
|
+
if (_refCount > 0 || _initializing) {
|
|
1584
|
+
throw new Error("Codex resumed-tool recovery is waiting for an active shared runtime; retry after its current queries finish.");
|
|
1585
|
+
}
|
|
1586
|
+
if (_appServer) {
|
|
1587
|
+
var stopped = await adapter.shutdownIfIdle(0);
|
|
1588
|
+
if (!stopped) throw new Error("Codex resumed-tool recovery could not safely release the shared runtime; retry after its current queries finish.");
|
|
1589
|
+
}
|
|
1590
|
+
var recoveryRuntime = createCodexAdapter(Object.assign({}, _defaultInitOpts, {
|
|
1591
|
+
recoveryRuntime: true,
|
|
1592
|
+
recoverySessionMcpServer: queryOpts.sessionMcpServer,
|
|
1593
|
+
}));
|
|
1594
|
+
retainRecoveryRuntime(recoveryRuntime);
|
|
1595
|
+
var recoveryOpts = Object.assign({}, queryOpts, {
|
|
1596
|
+
isolatedRecovery: true,
|
|
1597
|
+
onRuntimeFinished: function() {
|
|
1598
|
+
return cleanupRecoveryRuntime(recoveryRuntime);
|
|
1599
|
+
},
|
|
1600
|
+
});
|
|
1601
|
+
try {
|
|
1602
|
+
return await recoveryRuntime.createQuery(recoveryOpts);
|
|
1603
|
+
} catch (error) {
|
|
1604
|
+
await cleanupRecoveryRuntime(recoveryRuntime);
|
|
1605
|
+
throw error;
|
|
1606
|
+
}
|
|
1607
|
+
} finally {
|
|
1608
|
+
releaseHandoff();
|
|
1609
|
+
if (_recoveryHandoff === myHandoff) _recoveryHandoff = null;
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1458
1613
|
function buildReadyResponse(skillNames) {
|
|
1459
1614
|
return {
|
|
1460
1615
|
models: _cachedModels,
|
|
@@ -1487,6 +1642,7 @@ function createCodexAdapter(opts) {
|
|
|
1487
1642
|
function clearRuntimeState() {
|
|
1488
1643
|
_appServer = null;
|
|
1489
1644
|
_initPromise = null;
|
|
1645
|
+
_initializing = false;
|
|
1490
1646
|
_cachedModels = [];
|
|
1491
1647
|
_refCount = 0;
|
|
1492
1648
|
_activeQueries = [];
|
|
@@ -1541,7 +1697,7 @@ function createCodexAdapter(opts) {
|
|
|
1541
1697
|
var deadline = Date.now() + 5000;
|
|
1542
1698
|
var shouldAbort = !!force;
|
|
1543
1699
|
|
|
1544
|
-
if (_initPromise) {
|
|
1700
|
+
if (_initializing && _initPromise) {
|
|
1545
1701
|
try {
|
|
1546
1702
|
await Promise.race([
|
|
1547
1703
|
_initPromise.catch(function() { return null; }),
|
|
@@ -1606,9 +1762,10 @@ function createCodexAdapter(opts) {
|
|
|
1606
1762
|
}
|
|
1607
1763
|
|
|
1608
1764
|
// Deduplicate concurrent init calls
|
|
1609
|
-
if (_initPromise) return _initPromise;
|
|
1765
|
+
if (_initializing && _initPromise) return _initPromise;
|
|
1610
1766
|
|
|
1611
|
-
|
|
1767
|
+
_initializing = true;
|
|
1768
|
+
var initPromise = (async function() {
|
|
1612
1769
|
var serverOpts = { cwd: _cwd, env: effectiveInitOpts.env || null };
|
|
1613
1770
|
if (_runtimeLinuxUser) {
|
|
1614
1771
|
serverOpts.osUserInfo = _resolveOsUserInfo(_runtimeLinuxUser);
|
|
@@ -1628,7 +1785,7 @@ function createCodexAdapter(opts) {
|
|
|
1628
1785
|
|
|
1629
1786
|
// Track 1: Read local MCP server definitions from ~/.clay/mcp.json
|
|
1630
1787
|
// and inject into Codex config so Codex manages them natively.
|
|
1631
|
-
var mcpServerConfig = {};
|
|
1788
|
+
var mcpServerConfig = Object.assign({}, serverOpts.config.mcp_servers || {});
|
|
1632
1789
|
try {
|
|
1633
1790
|
var mcpLocal = require("../../mcp-local");
|
|
1634
1791
|
var localMcpServers = mcpLocal.readMergedServers();
|
|
@@ -1651,6 +1808,7 @@ function createCodexAdapter(opts) {
|
|
|
1651
1808
|
var clayPort = effectiveInitOpts.clayPort || process.env.CLAY_PORT || 2633;
|
|
1652
1809
|
var clayTls = effectiveInitOpts.clayTls || false;
|
|
1653
1810
|
var clayAuthToken = effectiveInitOpts.clayAuthToken || "";
|
|
1811
|
+
var clayBridgeToken = effectiveInitOpts.clayBridgeToken || "";
|
|
1654
1812
|
var claySlug = effectiveInitOpts.slug || _slug || "";
|
|
1655
1813
|
try {
|
|
1656
1814
|
if (require("fs").existsSync(bridgePath)) {
|
|
@@ -1658,6 +1816,7 @@ function createCodexAdapter(opts) {
|
|
|
1658
1816
|
if (clayTls) bridgeArgs.push("--tls");
|
|
1659
1817
|
var bridgeEnv = {};
|
|
1660
1818
|
if (clayAuthToken) bridgeEnv.CLAY_AUTH_TOKEN = clayAuthToken;
|
|
1819
|
+
if (clayBridgeToken) bridgeEnv.CLAY_BRIDGE_TOKEN = clayBridgeToken;
|
|
1661
1820
|
mcpServerConfig["clay-tools"] = {
|
|
1662
1821
|
command: process.execPath,
|
|
1663
1822
|
args: bridgeArgs,
|
|
@@ -1668,6 +1827,17 @@ function createCodexAdapter(opts) {
|
|
|
1668
1827
|
console.error("[codex] Failed to configure clay-tools bridge:", e.message);
|
|
1669
1828
|
}
|
|
1670
1829
|
|
|
1830
|
+
// Recovery uses a dedicated app-server so the query-local bridge can
|
|
1831
|
+
// coexist with every server already configured for this runtime.
|
|
1832
|
+
var recoveryMcpServer = effectiveInitOpts.recoverySessionMcpServer;
|
|
1833
|
+
if (recoveryMcpServer && recoveryMcpServer.name && recoveryMcpServer.command) {
|
|
1834
|
+
mcpServerConfig[recoveryMcpServer.name] = {
|
|
1835
|
+
command: recoveryMcpServer.command,
|
|
1836
|
+
args: recoveryMcpServer.args || [],
|
|
1837
|
+
env: recoveryMcpServer.env || {},
|
|
1838
|
+
};
|
|
1839
|
+
}
|
|
1840
|
+
|
|
1671
1841
|
if (Object.keys(mcpServerConfig).length > 0) {
|
|
1672
1842
|
serverOpts.config = Object.assign({}, serverOpts.config || {}, {
|
|
1673
1843
|
mcp_servers: mcpServerConfig,
|
|
@@ -1706,7 +1876,8 @@ function createCodexAdapter(opts) {
|
|
|
1706
1876
|
// triggering and skills/list use the same merged inventory.
|
|
1707
1877
|
var skillNames = [];
|
|
1708
1878
|
try {
|
|
1709
|
-
var
|
|
1879
|
+
var skillOptions = scopedSkillOptions(effectiveInitOpts.env);
|
|
1880
|
+
var roots = skillOptions.disable ? [] : skillDiscovery.getSkillRoots(_cwd, skillOptions);
|
|
1710
1881
|
var extraRootPaths = [];
|
|
1711
1882
|
for (var ri = 0; ri < roots.length; ri++) {
|
|
1712
1883
|
if (roots[ri].source !== "codex-user" && path.isAbsolute(roots[ri].path)) extraRootPaths.push(roots[ri].path);
|
|
@@ -1735,7 +1906,7 @@ function createCodexAdapter(opts) {
|
|
|
1735
1906
|
}
|
|
1736
1907
|
// Direct discovery remains the fallback for older Codex app-server
|
|
1737
1908
|
// versions and keeps the GUI inventory vendor-neutral.
|
|
1738
|
-
var sharedSkills = skillDiscovery.discoverSkills(_cwd);
|
|
1909
|
+
var sharedSkills = skillDiscovery.discoverSkills(_cwd, skillOptions);
|
|
1739
1910
|
for (var csn = 0; csn < sharedSkills.length; csn++) {
|
|
1740
1911
|
if (skillNames.indexOf(sharedSkills[csn].name) === -1) skillNames.push(sharedSkills[csn].name);
|
|
1741
1912
|
}
|
|
@@ -1749,13 +1920,20 @@ function createCodexAdapter(opts) {
|
|
|
1749
1920
|
throw createShutdownError();
|
|
1750
1921
|
}
|
|
1751
1922
|
|
|
1752
|
-
_initPromise = null;
|
|
1753
1923
|
updateLastActiveAt();
|
|
1754
1924
|
|
|
1755
1925
|
return buildReadyResponse(skillNames);
|
|
1756
1926
|
})();
|
|
1757
1927
|
|
|
1758
|
-
|
|
1928
|
+
_initPromise = initPromise;
|
|
1929
|
+
initPromise.then(function() {
|
|
1930
|
+
_initializing = false;
|
|
1931
|
+
if (_initPromise === initPromise) _initPromise = null;
|
|
1932
|
+
}, function() {
|
|
1933
|
+
_initializing = false;
|
|
1934
|
+
if (_initPromise === initPromise) _initPromise = null;
|
|
1935
|
+
});
|
|
1936
|
+
return initPromise;
|
|
1759
1937
|
},
|
|
1760
1938
|
|
|
1761
1939
|
supportedModels: function() {
|
|
@@ -1784,6 +1962,10 @@ function createCodexAdapter(opts) {
|
|
|
1784
1962
|
if (_shuttingDown) {
|
|
1785
1963
|
throw createShutdownError();
|
|
1786
1964
|
}
|
|
1965
|
+
if (queryOpts && queryOpts.sessionMcpServer && queryOpts.resumeSessionId && !queryOpts.isolatedRecovery) {
|
|
1966
|
+
return createIsolatedRecoveryQuery(queryOpts);
|
|
1967
|
+
}
|
|
1968
|
+
await waitForRecoveryHandoff();
|
|
1787
1969
|
|
|
1788
1970
|
if (!_appServer || !_appServer.started) {
|
|
1789
1971
|
await adapter.init(queryOpts || {});
|
|
@@ -1822,8 +2004,11 @@ function createCodexAdapter(opts) {
|
|
|
1822
2004
|
onElicitation: queryOpts.userInputMode === "fallback" ? null : (queryOpts.onElicitation || null),
|
|
1823
2005
|
onUserInputRequest: queryOpts.userInputMode === "fallback" ? null : (queryOpts.onUserInputRequest || null),
|
|
1824
2006
|
resumeSessionId: queryOpts.resumeSessionId || null,
|
|
2007
|
+
sessionMcpServer: queryOpts.sessionMcpServer || null,
|
|
2008
|
+
isolatedRecovery: queryOpts.isolatedRecovery === true,
|
|
1825
2009
|
registerBackgroundTaskReset: registerBackgroundTaskReset,
|
|
1826
2010
|
ephemeral: queryOpts.ephemeral === true,
|
|
2011
|
+
skillOptions: scopedSkillOptions(queryOpts.env),
|
|
1827
2012
|
};
|
|
1828
2013
|
|
|
1829
2014
|
// Reasoning effort
|
|
@@ -1856,6 +2041,7 @@ function createCodexAdapter(opts) {
|
|
|
1856
2041
|
handleOpts.onFinished = function() {
|
|
1857
2042
|
removeActiveQuery(activeEntry);
|
|
1858
2043
|
decrementRefCount();
|
|
2044
|
+
if (typeof queryOpts.onRuntimeFinished === "function") queryOpts.onRuntimeFinished();
|
|
1859
2045
|
};
|
|
1860
2046
|
handle = createCodexQueryHandle(_appServer, handleOpts);
|
|
1861
2047
|
} catch (e) {
|
|
@@ -1970,6 +2156,7 @@ function createCodexAdapter(opts) {
|
|
|
1970
2156
|
return Promise.all([
|
|
1971
2157
|
beginShutdown(true),
|
|
1972
2158
|
shutdownUserRuntimes("shutdown"),
|
|
2159
|
+
shutdownRecoveryRuntimes(),
|
|
1973
2160
|
]).then(function() { return true; });
|
|
1974
2161
|
},
|
|
1975
2162
|
|
|
@@ -1997,7 +2184,7 @@ function createCodexAdapter(opts) {
|
|
|
1997
2184
|
});
|
|
1998
2185
|
}
|
|
1999
2186
|
if (_shuttingDown || _shutdownPromise) return Promise.resolve(false);
|
|
2000
|
-
if (
|
|
2187
|
+
if (_initializing) return Promise.resolve(false);
|
|
2001
2188
|
if (!_appServer) return Promise.resolve(false);
|
|
2002
2189
|
if (_refCount > 0) return Promise.resolve(false);
|
|
2003
2190
|
if (Date.now() - _lastActiveAt < (idleMs || 0)) return Promise.resolve(false);
|
|
@@ -18,7 +18,7 @@ var INITIALIZE_TIMEOUT_MS = require("../interface").INITIALIZE_TIMEOUT_MS;
|
|
|
18
18
|
var skillDiscovery = require("../skill-discovery");
|
|
19
19
|
var { KIRO_DEFAULTS } = require("../../kiro-defaults");
|
|
20
20
|
|
|
21
|
-
function injectSharedSkillContent(message, skills) {
|
|
21
|
+
function injectSharedSkillContent(message, skills, readOpts) {
|
|
22
22
|
var text = "";
|
|
23
23
|
if (typeof message === "string") text = message;
|
|
24
24
|
else if (Array.isArray(message)) {
|
|
@@ -26,7 +26,7 @@ function injectSharedSkillContent(message, skills) {
|
|
|
26
26
|
if (message[i] && message[i].type === "text") text += "\n" + (message[i].text || "");
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
|
-
var content = skillDiscovery.readReferencedSkills(text, skills, 32768);
|
|
29
|
+
var content = skillDiscovery.readReferencedSkills(text, skills, 32768, readOpts);
|
|
30
30
|
if (!content) return message;
|
|
31
31
|
var appendix = "\n\nUse the following shared skill instructions for this turn:\n" + content;
|
|
32
32
|
if (typeof message === "string") return message + appendix;
|
|
@@ -682,7 +682,7 @@ function createKiroQueryHandle(acp, queryOpts) {
|
|
|
682
682
|
while (!isCancelled()) {
|
|
683
683
|
resetTurnState();
|
|
684
684
|
|
|
685
|
-
var messageWithSkills = injectSharedSkillContent(currentMessage, queryOpts.skills || []);
|
|
685
|
+
var messageWithSkills = injectSharedSkillContent(currentMessage, queryOpts.skills || [], queryOpts.skillOptions);
|
|
686
686
|
var input = typeof messageWithSkills === "string"
|
|
687
687
|
? [{ type: "text", text: messageWithSkills }]
|
|
688
688
|
: messageWithSkills;
|
|
@@ -1012,7 +1012,7 @@ function createKiroAdapter(opts) {
|
|
|
1012
1012
|
_defaultModel = "auto";
|
|
1013
1013
|
}
|
|
1014
1014
|
|
|
1015
|
-
var skillNames = skillDiscovery.discoverSkills(_cwd).map(function(skill) { return skill.name; });
|
|
1015
|
+
var skillNames = skillDiscovery.discoverSkills(_cwd, { env: (initOpts && initOpts.env) || null }).map(function(skill) { return skill.name; });
|
|
1016
1016
|
console.log("[yoke/kiro] ACP initialized, models: " + _cachedModels.length + ", skills: " + skillNames.length);
|
|
1017
1017
|
|
|
1018
1018
|
updateLastActiveAt();
|
|
@@ -1065,7 +1065,7 @@ function createKiroAdapter(opts) {
|
|
|
1065
1065
|
var model = queryOpts.model || _defaultModel || "auto";
|
|
1066
1066
|
var ac = queryOpts.abortController || new AbortController();
|
|
1067
1067
|
var kiroOpts = (queryOpts.adapterOptions && queryOpts.adapterOptions.KIRO) || {};
|
|
1068
|
-
var sharedSkills = queryOpts.skipSkills ? [] : skillDiscovery.discoverSkills(queryOpts.cwd || _cwd);
|
|
1068
|
+
var sharedSkills = queryOpts.skipSkills ? [] : skillDiscovery.discoverSkills(queryOpts.cwd || _cwd, queryOpts.skillOptions || { env: queryOpts.env || null });
|
|
1069
1069
|
var skillIndex = skillDiscovery.buildSkillIndex(sharedSkills);
|
|
1070
1070
|
|
|
1071
1071
|
var activeEntry = { abort: function() { try { ac.abort(); } catch (e) {} } };
|
|
@@ -1079,6 +1079,7 @@ function createKiroAdapter(opts) {
|
|
|
1079
1079
|
systemPrompt: queryOpts.systemPrompt || "",
|
|
1080
1080
|
appendSystemPrompt: [queryOpts.appendSystemPrompt, skillIndex].filter(function (part) { return !!part; }).join("\n\n"),
|
|
1081
1081
|
skills: sharedSkills,
|
|
1082
|
+
skillOptions: queryOpts.skillOptions || { env: queryOpts.env || null },
|
|
1082
1083
|
abortController: ac,
|
|
1083
1084
|
canUseTool: queryOpts.canUseTool || null,
|
|
1084
1085
|
resumeSessionId: queryOpts.resumeSessionId || null,
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
var crypto = require("crypto");
|
|
2
|
+
|
|
3
|
+
function options(value) {
|
|
4
|
+
return { detail: value && value.detail === "full" ? "full" : "summary" };
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function createRequests(worker) {
|
|
8
|
+
var pending = new Map();
|
|
9
|
+
return {
|
|
10
|
+
request: function(value) {
|
|
11
|
+
return new Promise(function(resolve, reject) {
|
|
12
|
+
var id = crypto.randomUUID();
|
|
13
|
+
var timer = setTimeout(function() {
|
|
14
|
+
pending.delete(id);
|
|
15
|
+
reject(new Error("Context usage request timed out"));
|
|
16
|
+
}, 30000);
|
|
17
|
+
pending.set(id, { resolve: resolve, reject: reject, timer: timer });
|
|
18
|
+
var accepted = false;
|
|
19
|
+
try { accepted = worker.send({ type: "get_context_usage", requestId: id, options: options(value) }); }
|
|
20
|
+
catch (error) {
|
|
21
|
+
clearTimeout(timer);
|
|
22
|
+
pending.delete(id);
|
|
23
|
+
reject(error);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (!accepted) {
|
|
27
|
+
clearTimeout(timer);
|
|
28
|
+
pending.delete(id);
|
|
29
|
+
resolve(null);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
},
|
|
33
|
+
receive: function(message) {
|
|
34
|
+
var entry = pending.get(message.requestId);
|
|
35
|
+
if (!entry) return;
|
|
36
|
+
pending.delete(message.requestId);
|
|
37
|
+
clearTimeout(entry.timer);
|
|
38
|
+
if (message.error) entry.reject(new Error(message.error));
|
|
39
|
+
else entry.resolve(message.data || null);
|
|
40
|
+
},
|
|
41
|
+
close: function() {
|
|
42
|
+
pending.forEach(function(entry) { clearTimeout(entry.timer); entry.resolve(null); });
|
|
43
|
+
pending.clear();
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function respond(query, message, send) {
|
|
49
|
+
try {
|
|
50
|
+
var data = query && typeof query.getContextUsage === "function" ? await query.getContextUsage(options(message.options)) : null;
|
|
51
|
+
send({ type: "context_usage_response", requestId: message.requestId, data: data });
|
|
52
|
+
} catch (error) {
|
|
53
|
+
send({ type: "context_usage_response", requestId: message.requestId, error: error.message });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
module.exports = { options: options, createRequests: createRequests, respond: respond };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Match merged Claude replies to the messages admitted by this query.
|
|
2
|
+
var crypto = require("crypto");
|
|
3
|
+
|
|
4
|
+
function createCorrelation() {
|
|
5
|
+
var pending = new Set();
|
|
6
|
+
var submitted = 0;
|
|
7
|
+
return {
|
|
8
|
+
message: function(content) {
|
|
9
|
+
return { type: "user", uuid: crypto.randomUUID(), message: { role: "user", content: content } };
|
|
10
|
+
},
|
|
11
|
+
accepted: function(message) { pending.add(message.uuid); submitted++; },
|
|
12
|
+
submitted: function() { return submitted; },
|
|
13
|
+
result: function(raw, event) {
|
|
14
|
+
if (raw.type !== "result") return event;
|
|
15
|
+
event.submittedMessageCount = submitted;
|
|
16
|
+
var ids = Array.isArray(raw.user_message_uuids) ? raw.user_message_uuids : [];
|
|
17
|
+
if (raw.user_message_uuid) ids = ids.concat(raw.user_message_uuid);
|
|
18
|
+
var matched = 0;
|
|
19
|
+
ids.forEach(function(id) {
|
|
20
|
+
if (pending.delete(id)) matched++;
|
|
21
|
+
});
|
|
22
|
+
// Only count UUIDs admitted by this query, once each. Older providers
|
|
23
|
+
// without correlation fields retain the bridge's legacy accounting.
|
|
24
|
+
if (matched) event.answeredUserMessageCount = matched;
|
|
25
|
+
else if (!ids.length && pending.size) pending.delete(pending.values().next().value);
|
|
26
|
+
return event;
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
module.exports = { createCorrelation: createCorrelation };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
function attach(options, notify) {
|
|
2
|
+
if (typeof notify !== "function") return;
|
|
3
|
+
var hooks = Object.assign({}, options.hooks || {});
|
|
4
|
+
hooks.PostModelSwitch = (hooks.PostModelSwitch || []).concat([{
|
|
5
|
+
hooks: [async function(input) {
|
|
6
|
+
if (input.from_model && input.to_model && input.from_model !== input.to_model) {
|
|
7
|
+
notify({ fromModel: input.from_model, toModel: input.to_model, source: input.source || "unknown" });
|
|
8
|
+
}
|
|
9
|
+
return {};
|
|
10
|
+
}],
|
|
11
|
+
}]);
|
|
12
|
+
options.hooks = hooks;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
module.exports = { attach: attach };
|
package/lib/yoke/index.js
CHANGED
|
@@ -379,7 +379,11 @@ function createAdapters(opts) {
|
|
|
379
379
|
|
|
380
380
|
if (installed.codex && supportsConfiguredIsolation("codex")) {
|
|
381
381
|
try {
|
|
382
|
-
adapters.codex = createAdapter({
|
|
382
|
+
adapters.codex = createAdapter({
|
|
383
|
+
vendor: "codex", cwd: opts.cwd, slug: opts.slug, osUsers: !!opts.osUsers,
|
|
384
|
+
clayPort: opts.clayPort, clayTls: opts.clayTls,
|
|
385
|
+
clayAuthToken: opts.clayAuthToken, clayBridgeToken: opts.clayBridgeToken,
|
|
386
|
+
});
|
|
383
387
|
auth.codex = true;
|
|
384
388
|
} catch (e) {
|
|
385
389
|
console.error("[yoke] Failed to create adapter for codex:", e.message);
|