clay-server 5.2.0-beta.5 → 5.2.0-beta.6
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/claude-hook-installer.js +1 -267
- package/lib/claude-jsonl-watcher.js +1 -204
- package/lib/clay-skill-install.js +1 -33
- package/lib/cli-sessions.js +1 -300
- package/lib/codex-defaults.js +1 -28
- package/lib/context-usage.js +3 -2
- package/lib/daemon.js +5 -55
- package/lib/default-ai-runtime.js +4 -8
- package/lib/email-mcp-server.js +1 -1
- package/lib/kiro-defaults.js +1 -22
- package/lib/mcp-proxy.js +2 -32
- package/lib/mcp-query-bridge.js +1 -1
- package/lib/mcp-source-adapters.js +1 -91
- package/lib/mcp-source-context.js +1 -42
- package/lib/mcp-source-discovery.js +1 -169
- package/lib/mcp-source-plugins.js +1 -68
- package/lib/mcp-source-restrictions.js +1 -47
- package/lib/mcp-tool-policy.js +1 -26
- package/lib/os-user-diagnostics.js +2 -4
- package/lib/os-users.js +4 -65
- package/lib/project-connection.js +8 -7
- package/lib/project-http.js +4 -4
- package/lib/project-mcp.js +2 -2
- package/lib/project-pair-result-delivery.js +2 -2
- package/lib/project-pair-usage.js +12 -13
- package/lib/project-pending-message-queue.js +3 -1
- package/lib/project-session-history-actions.js +5 -3
- package/lib/project-session-pair.js +1 -1
- package/lib/project-session-runtime-actions.js +2 -1
- package/lib/project-session-spawn.js +2 -3
- package/lib/project-session-vendor.js +2 -1
- package/lib/project-sessions.js +18 -26
- package/lib/project-user-message.js +1 -0
- package/lib/project-vendor-login.js +6 -36
- package/lib/public/app.js +1 -0
- package/lib/public/css/messages.css +6 -2
- package/lib/public/modules/app-favicon.js +11 -2
- package/lib/public/modules/app-header.js +4 -2
- package/lib/public/modules/app-loop-ui.js +2 -0
- package/lib/public/modules/app-messages.js +36 -5
- package/lib/public/modules/app-notifications.js +1 -2
- package/lib/public/modules/app-panels.js +61 -123
- package/lib/public/modules/app-rate-limit.js +7 -27
- package/lib/public/modules/chat-render-runtime.js +3 -0
- package/lib/public/modules/mcp-connection-view.js +1 -1
- package/lib/public/modules/permission-control.js +5 -4
- package/lib/public/modules/scheduler.js +2 -0
- package/lib/public/modules/session-create-form.js +10 -10
- package/lib/public/modules/settings-defaults.js +27 -28
- package/lib/public/modules/tools.js +2 -1
- package/lib/public/modules/tui-grab.js +1 -2
- package/lib/public/modules/vendor-login-browser.js +5 -3
- package/lib/public/modules/vendor-login.js +2 -2
- package/lib/public/modules/vendor-priority.js +15 -1
- package/lib/public/muse-avatar.svg +4 -0
- package/lib/safe-bash-commands.js +3 -45
- package/lib/sdk-bridge.js +127 -395
- package/lib/sdk-foreground-admission.js +16 -0
- package/lib/sdk-mcp-descriptors.js +2 -37
- package/lib/sdk-message-processor.js +50 -17
- package/lib/sdk-quota-snapshot.js +1 -1
- package/lib/sdk-response-recovery.js +1 -155
- package/lib/sdk-worker.js +2 -677
- package/lib/server.js +1 -1
- package/lib/session-fork-history.js +21 -0
- package/lib/session-hygiene.js +3 -35
- package/lib/session-permission-mode.js +4 -3
- package/lib/session-skill-scope.js +11 -0
- package/lib/sessions.js +17 -89
- package/lib/tool-llm.js +1 -1
- package/lib/tui-transcript-index.js +1 -132
- package/lib/vendor-login-browser.js +1 -11
- package/lib/yoke/acp-agent-profiles.js +5 -3
- package/lib/yoke/adapters/antigravity.js +3 -14
- package/lib/yoke/adapters/claude-query-contract.js +9 -0
- package/lib/yoke/adapters/claude.js +7 -42
- package/lib/yoke/adapters/codex-query-contract.js +24 -0
- package/lib/yoke/adapters/codex.js +1 -0
- package/lib/yoke/adapters/kiro.js +3 -1
- package/lib/yoke/adapters/muse-query-contract.js +7 -0
- package/lib/yoke/adapters/muse.js +122 -0
- package/lib/yoke/auth-events.js +17 -0
- package/lib/yoke/claude-hook-installer.js +267 -0
- package/lib/yoke/claude-jsonl-watcher.js +204 -0
- package/lib/yoke/claude-maintenance.js +55 -0
- package/lib/yoke/claude-permission-patterns.js +53 -0
- package/lib/yoke/claude-response-recovery.js +155 -0
- package/lib/yoke/claude-transcript-hygiene.js +34 -0
- package/lib/yoke/clay-skill-install.js +33 -0
- package/lib/yoke/cli-sessions.js +300 -0
- package/lib/yoke/codex-app-server.js +7 -12
- package/lib/yoke/configuration.js +13 -0
- package/lib/yoke/discovery.js +70 -0
- package/lib/yoke/errors.js +8 -0
- package/lib/yoke/host-environment.js +7 -0
- package/lib/yoke/index.js +9 -51
- package/lib/yoke/interface.js +116 -0
- package/lib/yoke/kiro-acp-server.js +5 -5
- package/lib/yoke/legacy-sessions.js +89 -0
- package/lib/yoke/login-protocol.js +25 -0
- package/lib/yoke/maintenance.js +12 -0
- package/lib/yoke/mcp-proxy.js +32 -0
- package/lib/yoke/mcp-source-adapters.js +91 -0
- package/lib/yoke/mcp-source-context.js +42 -0
- package/lib/yoke/mcp-source-discovery.js +169 -0
- package/lib/yoke/mcp-source-plugins.js +68 -0
- package/lib/yoke/mcp-source-restrictions.js +47 -0
- package/lib/yoke/mcp-tool-policy-normalize.js +28 -0
- package/lib/yoke/model-preferences.js +6 -0
- package/lib/yoke/muse-artifacts.js +36 -0
- package/lib/yoke/muse-controls.js +50 -0
- package/lib/yoke/muse-input.js +64 -0
- package/lib/yoke/muse-items.js +157 -0
- package/lib/yoke/muse-policy.js +52 -0
- package/lib/yoke/muse-query.js +428 -0
- package/lib/yoke/muse-runtime.js +211 -0
- package/lib/yoke/muse-services.js +79 -0
- package/lib/yoke/muse-skills.js +79 -0
- package/lib/yoke/muse-turns.js +71 -0
- package/lib/yoke/options/acp.js +2 -0
- package/lib/yoke/options/antigravity.js +2 -0
- package/lib/yoke/options/claude.js +21 -0
- package/lib/yoke/options/codex-defaults.js +29 -0
- package/lib/yoke/options/codex-settings.js +21 -0
- package/lib/yoke/options/codex.js +6 -0
- package/lib/yoke/options/kiro-defaults.js +22 -0
- package/lib/yoke/options/kiro.js +5 -0
- package/lib/yoke/options/muse.js +5 -0
- package/lib/yoke/provider-contract.js +5 -0
- package/lib/yoke/providers/antigravity-executable.js +8 -0
- package/lib/yoke/providers/claude-executable.js +40 -0
- package/lib/yoke/providers/claude-model-descriptions.js +14 -0
- package/lib/yoke/providers/claude-process.js +97 -0
- package/lib/yoke/providers/claude-provision.js +75 -0
- package/lib/yoke/providers/codex-errors.js +8 -0
- package/lib/yoke/providers/muse-executable.js +17 -0
- package/lib/yoke/provisioning.js +6 -0
- package/lib/yoke/query-options.js +10 -0
- package/lib/yoke/response-recovery.js +41 -0
- package/lib/yoke/sdk-mcp-descriptors.js +37 -0
- package/lib/yoke/sdk-server-factory.js +7 -0
- package/lib/yoke/session-history.js +8 -0
- package/lib/yoke/session-tool-transport.js +31 -13
- package/lib/yoke/settings.js +23 -0
- package/lib/yoke/source-locations.js +4 -0
- package/lib/yoke/title-query.js +18 -7
- package/lib/yoke/tui-transcript-index.js +132 -0
- package/lib/yoke/usage.js +48 -0
- package/lib/yoke/user-input.js +13 -0
- package/lib/yoke/vendor-registry.js +81 -1
- package/package.json +3 -2
package/lib/sdk-bridge.js
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
var codexQuestionGuidance = require("./yoke/user-input-guidance").codexQuestionGuidance;
|
|
1
|
+
var crypto = require("crypto");
|
|
3
2
|
var fs = require("fs");
|
|
4
3
|
var path = require("path");
|
|
5
4
|
var execSync = require("child_process").execSync;
|
|
6
5
|
var execFileSync = require("child_process").execFileSync;
|
|
7
6
|
var usersModule = require("./users");
|
|
8
|
-
var { getCodexConfig } = require("./codex-defaults");
|
|
9
|
-
var { getKiroConfig } = require("./kiro-defaults");
|
|
10
7
|
var { splitShellSegments, attachSkillDiscovery } = require("./sdk-skill-discovery");
|
|
11
8
|
var skillDiscovery = require("./yoke/skill-discovery");
|
|
12
9
|
var { isSafeBashSegment } = require("./safe-bash-commands");
|
|
@@ -19,7 +16,7 @@ var sessionTitlePolicy = require("./session-title-policy");
|
|
|
19
16
|
var sessionTitleGenerator = require("./session-title-generator");
|
|
20
17
|
var loopInterview = require("./project-loop-interview");
|
|
21
18
|
|
|
22
|
-
var extractMcpDescriptors = require("./sdk-mcp-descriptors").extractMcpDescriptors;
|
|
19
|
+
var extractMcpDescriptors = require("./yoke/sdk-mcp-descriptors").extractMcpDescriptors;
|
|
23
20
|
|
|
24
21
|
// Call an MCP tool handler by server name and tool name.
|
|
25
22
|
// Returns a promise that resolves with the tool result.
|
|
@@ -137,7 +134,7 @@ function createSDKBridge(opts) {
|
|
|
137
134
|
}];
|
|
138
135
|
}
|
|
139
136
|
|
|
140
|
-
function
|
|
137
|
+
function getSessionMcpServer(session, queryGeneration) {
|
|
141
138
|
if (!session || !Number.isInteger(queryGeneration)) return null;
|
|
142
139
|
var bridgeArgs = [
|
|
143
140
|
path.join(__dirname, "yoke", "mcp-bridge-server.js"),
|
|
@@ -165,25 +162,16 @@ function createSDKBridge(opts) {
|
|
|
165
162
|
return _cachedFreshAuthState;
|
|
166
163
|
}
|
|
167
164
|
|
|
168
|
-
|
|
169
|
-
if (!errDetail) return false;
|
|
170
|
-
var errLower = String(errDetail).toLowerCase();
|
|
171
|
-
return errLower.indexOf("not logged in") !== -1
|
|
172
|
-
|| errLower.indexOf("unauthenticated") !== -1
|
|
173
|
-
|| errLower.indexOf("authentication") !== -1
|
|
174
|
-
|| errLower.indexOf("sign in") !== -1
|
|
175
|
-
|| errLower.indexOf("log in") !== -1
|
|
176
|
-
|| errLower.indexOf("please login") !== -1;
|
|
177
|
-
}
|
|
165
|
+
var isAuthErrorMessage = require("./yoke/auth-events").isAuthenticationError;
|
|
178
166
|
|
|
179
167
|
function getLoginCommand(vendor) {
|
|
180
168
|
var info = yoke.getVendorInfo(vendor);
|
|
181
|
-
return (info && info.loginCommand) || "
|
|
169
|
+
return (info && info.loginCommand) || "";
|
|
182
170
|
}
|
|
183
171
|
|
|
184
172
|
function supportsOsUserIsolation(vendor) {
|
|
185
173
|
var info = yoke.getVendorInfo(vendor);
|
|
186
|
-
return
|
|
174
|
+
return !!info && info.osUserIsolation === true;
|
|
187
175
|
}
|
|
188
176
|
|
|
189
177
|
function notifyAuthRequired(session, title, body, authLinuxUser, canAutoLogin, loginCommand) {
|
|
@@ -390,7 +378,8 @@ function createSDKBridge(opts) {
|
|
|
390
378
|
var sendAndRecord = msgProcessor.sendAndRecord;
|
|
391
379
|
var sendToSession = msgProcessor.sendToSession;
|
|
392
380
|
|
|
393
|
-
var responseRecovery = require("./
|
|
381
|
+
var responseRecovery = require("./yoke/response-recovery").attachResponseRecovery(Object.assign({
|
|
382
|
+
getProvider: function(session) { var vendor = session.vendor || sm.defaultVendor || adapter && adapter.vendor; return adapters[vendor] || vendor || adapter; },
|
|
394
383
|
sessions: sm.sessions,
|
|
395
384
|
changed: onProcessingChanged,
|
|
396
385
|
record: sendAndRecord,
|
|
@@ -503,44 +492,7 @@ function createSDKBridge(opts) {
|
|
|
503
492
|
// Ensures the linux user's .claude project directory exists and is writable,
|
|
504
493
|
// then pre-copies CLI session file if needed. Called before starting a query
|
|
505
494
|
// so the worker can resume from the correct session file.
|
|
506
|
-
|
|
507
|
-
try {
|
|
508
|
-
var configMod = require("./config");
|
|
509
|
-
var osUsersMod = require("./os-users");
|
|
510
|
-
var originalHome = configMod.REAL_HOME || require("os").homedir();
|
|
511
|
-
var linuxUserHome = osUsersMod.getLinuxUserHome(linuxUser);
|
|
512
|
-
var uid = osUsersMod.getLinuxUserUid(linuxUser);
|
|
513
|
-
if (originalHome !== linuxUserHome && uid != null) {
|
|
514
|
-
var projectSlug = (cwd || "").replace(/\//g, "-");
|
|
515
|
-
var dstDir = path.join(linuxUserHome, ".claude", "projects", projectSlug);
|
|
516
|
-
// Create and chown the project directory once
|
|
517
|
-
if (!fs.existsSync(dstDir)) {
|
|
518
|
-
fs.mkdirSync(dstDir, { recursive: true });
|
|
519
|
-
try { execFileSync("chown", ["-R", String(uid), path.join(linuxUserHome, ".claude")]); } catch (e2) {}
|
|
520
|
-
} else {
|
|
521
|
-
try {
|
|
522
|
-
var dirStat = fs.statSync(dstDir);
|
|
523
|
-
if (dirStat.uid !== uid) {
|
|
524
|
-
execFileSync("chown", [String(uid), dstDir]);
|
|
525
|
-
}
|
|
526
|
-
} catch (e2) {}
|
|
527
|
-
}
|
|
528
|
-
// Pre-copy CLI session file so the worker can resume the conversation
|
|
529
|
-
if (session.cliSessionId) {
|
|
530
|
-
var sessionFileName = session.cliSessionId + ".jsonl";
|
|
531
|
-
var srcFile = path.join(originalHome, ".claude", "projects", projectSlug, sessionFileName);
|
|
532
|
-
var dstFile = path.join(dstDir, sessionFileName);
|
|
533
|
-
if (fs.existsSync(srcFile) && !fs.existsSync(dstFile)) {
|
|
534
|
-
fs.copyFileSync(srcFile, dstFile);
|
|
535
|
-
try { execFileSync("chown", [String(uid), dstFile]); } catch (e2) {}
|
|
536
|
-
console.log("[sdk-bridge] Pre-copied CLI session " + session.cliSessionId + " to " + linuxUser);
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
} catch (copyErr) {
|
|
541
|
-
console.log("[sdk-bridge] Dir setup / session pre-copy skipped:", copyErr.message);
|
|
542
|
-
}
|
|
543
|
-
}
|
|
495
|
+
|
|
544
496
|
|
|
545
497
|
// --- SDK query lifecycle ---
|
|
546
498
|
|
|
@@ -986,58 +938,13 @@ function createSDKBridge(opts) {
|
|
|
986
938
|
* Only returns processes whose cwd matches our project directory.
|
|
987
939
|
* Returns an array of { pid, command } for each conflicting process found.
|
|
988
940
|
*/
|
|
989
|
-
function
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
var lines = output.trim().split("\n");
|
|
993
|
-
var candidates = [];
|
|
994
|
-
for (var i = 1; i < lines.length; i++) { // skip header
|
|
995
|
-
var line = lines[i].trim();
|
|
996
|
-
var m = line.match(/^(\d+)\s+(.+)/);
|
|
997
|
-
if (!m) continue;
|
|
998
|
-
var pid = parseInt(m[1], 10);
|
|
999
|
-
var cmd = m[2];
|
|
1000
|
-
// Skip our own process
|
|
1001
|
-
if (pid === process.pid) continue;
|
|
1002
|
-
// Skip node processes (our daemon, dev watchers, etc.)
|
|
1003
|
-
if (/\bnode\b/.test(cmd.split(/\s/)[0])) continue;
|
|
1004
|
-
// Match actual claude binary (e.g. /Users/x/.claude/local/claude, /usr/local/bin/claude)
|
|
1005
|
-
if (/\/claude(\s|$)/.test(cmd) || /^claude(\s|$)/.test(cmd)) {
|
|
1006
|
-
candidates.push({ pid: pid, command: cmd.substring(0, 200) });
|
|
1007
|
-
}
|
|
1008
|
-
}
|
|
1009
|
-
|
|
1010
|
-
// Filter to only processes whose cwd matches our project
|
|
1011
|
-
var results = [];
|
|
1012
|
-
for (var j = 0; j < candidates.length; j++) {
|
|
1013
|
-
var c = candidates[j];
|
|
1014
|
-
try {
|
|
1015
|
-
// Use /proc/<pid>/cwd symlink (always available on Linux, no lsof dependency)
|
|
1016
|
-
var procCwd = fs.readlinkSync("/proc/" + c.pid + "/cwd");
|
|
1017
|
-
if (procCwd === cwd) {
|
|
1018
|
-
results.push(c);
|
|
1019
|
-
}
|
|
1020
|
-
} catch (e) {
|
|
1021
|
-
// /proc read failed — include as candidate anyway (conservative)
|
|
1022
|
-
results.push(c);
|
|
1023
|
-
}
|
|
1024
|
-
}
|
|
1025
|
-
return results;
|
|
1026
|
-
} catch (e) {
|
|
1027
|
-
return [];
|
|
1028
|
-
}
|
|
941
|
+
function findConflictingProcesses(session, error) {
|
|
942
|
+
var service = require("./yoke/provider-contract").resolve(getAdapterForSession(session)).processControl;
|
|
943
|
+
return service && typeof service.shouldInspect === "function" && service.shouldInspect(error) && typeof service.findConflicts === "function" ? service.findConflicts(cwd) : [];
|
|
1029
944
|
}
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
*/
|
|
1034
|
-
function isClaudeProcess(pid) {
|
|
1035
|
-
try {
|
|
1036
|
-
var output = execFileSync("ps", ["-p", String(pid), "-o", "command="], { encoding: "utf8", timeout: 3000, stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
1037
|
-
return /\/claude(\s|$)/.test(output) || /^claude(\s|$)/.test(output);
|
|
1038
|
-
} catch (e) {
|
|
1039
|
-
return false;
|
|
1040
|
-
}
|
|
945
|
+
function isProviderProcess(pid, session) {
|
|
946
|
+
var service = require("./yoke/provider-contract").resolve(session ? getAdapterForSession(session) : adapter).processControl;
|
|
947
|
+
return !!(service && typeof service.verifyProcess === "function" && service.verifyProcess(pid));
|
|
1041
948
|
}
|
|
1042
949
|
|
|
1043
950
|
function ownsQueryRuntime(session, queryInstance, queryGeneration) {
|
|
@@ -1080,7 +987,8 @@ function createSDKBridge(opts) {
|
|
|
1080
987
|
return true;
|
|
1081
988
|
}
|
|
1082
989
|
|
|
1083
|
-
async function processQueryStream(session,
|
|
990
|
+
async function processQueryStream(session, catalogCandidate, queryContract) {
|
|
991
|
+
queryContract = queryContract || yoke.sessionTools.contract(getAdapterForSession(session));
|
|
1084
992
|
// Capture references at start so we only clean up OUR resources in finally,
|
|
1085
993
|
// not resources from a newer query that may have been created after an abort.
|
|
1086
994
|
var myQueryInstance = session.queryInstance;
|
|
@@ -1146,19 +1054,14 @@ function createSDKBridge(opts) {
|
|
|
1146
1054
|
// permission state. Setter acknowledgement deliberately does not
|
|
1147
1055
|
// change this field.
|
|
1148
1056
|
recordRuntimeEffectivePermissionMode(session, myQueryInstance, myQueryGeneration, msg);
|
|
1149
|
-
if (
|
|
1057
|
+
if (catalogCandidate && msg && msg.yokeType === "session_started" && msg.sessionId
|
|
1150
1058
|
&& session.queryInstance === myQueryInstance
|
|
1151
|
-
&& Number(session._sdkQueryGeneration || 0) ===
|
|
1059
|
+
&& Number(session._sdkQueryGeneration || 0) === catalogCandidate.generation
|
|
1152
1060
|
&& session.cliSessionId === msg.sessionId
|
|
1153
1061
|
&& (!sm.sessions || typeof sm.sessions.get !== "function" || sm.sessions.get(session.localId) === session)) {
|
|
1154
|
-
|
|
1155
|
-
if (codexCatalogCandidate.userInputVersion) session.codexUserInputToolCatalogVersion = codexCatalogCandidate.userInputVersion;
|
|
1156
|
-
if (codexCatalogCandidate.sharedBrowserVersion) session.codexSharedBrowserToolCatalogVersion = codexCatalogCandidate.sharedBrowserVersion;
|
|
1157
|
-
if (codexCatalogCandidate.issuesVersion) session.codexIssuesToolCatalogVersion = codexCatalogCandidate.issuesVersion;
|
|
1158
|
-
if (codexCatalogCandidate.youVersion) session.codexYouToolCatalogVersion = codexCatalogCandidate.youVersion;
|
|
1159
|
-
if (codexCatalogCandidate.linearVersion) session.codexLinearToolCatalogVersion = codexCatalogCandidate.linearVersion;
|
|
1062
|
+
queryContract.recordCatalog(session, catalogCandidate.versions);
|
|
1160
1063
|
sm.saveSessionFile(session);
|
|
1161
|
-
|
|
1064
|
+
catalogCandidate = null;
|
|
1162
1065
|
}
|
|
1163
1066
|
}
|
|
1164
1067
|
// (getContextUsage moved to processSDKMessage result handler -- fire-and-forget)
|
|
@@ -1176,9 +1079,7 @@ function createSDKBridge(opts) {
|
|
|
1176
1079
|
onProcessingChanged();
|
|
1177
1080
|
send({ type: "status", processing: false });
|
|
1178
1081
|
sendAndRecord(session, { type: "thinking_stop" });
|
|
1179
|
-
var interruptMsg = (
|
|
1180
|
-
? "\u25a0 Conversation interrupted - tell the model what to do differently."
|
|
1181
|
-
: "Interrupted \u00b7 What should Claude do instead?";
|
|
1082
|
+
var interruptMsg = (queryContract && queryContract.interruptionMessage) || "■ Conversation interrupted - tell the model what to do differently.";
|
|
1182
1083
|
sendAndRecord(session, { type: "info", text: interruptMsg });
|
|
1183
1084
|
sendAndRecord(session, { type: "done", code: 0 });
|
|
1184
1085
|
sm.broadcastSessionList();
|
|
@@ -1213,9 +1114,7 @@ function createSDKBridge(opts) {
|
|
|
1213
1114
|
session._lastTurnInterrupted = true;
|
|
1214
1115
|
if (!session.destroying) {
|
|
1215
1116
|
sendAndRecord(session, { type: "thinking_stop" });
|
|
1216
|
-
var interruptMsg2 = (
|
|
1217
|
-
? "\u25a0 Conversation interrupted - tell the model what to do differently."
|
|
1218
|
-
: "Interrupted \u00b7 What should Claude do instead?";
|
|
1117
|
+
var interruptMsg2 = (queryContract && queryContract.interruptionMessage) || "■ Conversation interrupted - tell the model what to do differently.";
|
|
1219
1118
|
sendAndRecord(session, { type: "info", text: interruptMsg2 });
|
|
1220
1119
|
sendAndRecord(session, { type: "done", code: 0 });
|
|
1221
1120
|
}
|
|
@@ -1231,21 +1130,17 @@ function createSDKBridge(opts) {
|
|
|
1231
1130
|
console.error("[sdk-bridge] Query stream error for session " + session.localId + ":", errDetail);
|
|
1232
1131
|
console.error("[sdk-bridge] Stack:", err.stack || "(no stack)");
|
|
1233
1132
|
|
|
1234
|
-
|
|
1235
|
-
var isExitCode1 = err.exitCode === 1 || (err.message && err.message.indexOf("exited with code 1") !== -1);
|
|
1236
|
-
var conflicts = isExitCode1 ? findConflictingClaude() : [];
|
|
1133
|
+
var conflicts = findConflictingProcesses(session, err);
|
|
1237
1134
|
if (conflicts.length > 0) {
|
|
1238
|
-
console.error("[sdk-bridge] Found " + conflicts.length + " conflicting
|
|
1135
|
+
console.error("[sdk-bridge] Found " + conflicts.length + " conflicting provider process(es):", conflicts.map(function(c) { return "PID " + c.pid; }).join(", "));
|
|
1239
1136
|
sendAndRecord(session, {
|
|
1240
1137
|
type: "process_conflict",
|
|
1241
|
-
text: "Another
|
|
1138
|
+
text: "Another provider process is already running in this project.",
|
|
1242
1139
|
processes: conflicts,
|
|
1243
1140
|
});
|
|
1244
1141
|
} else {
|
|
1245
1142
|
var errLower = errDetail.toLowerCase();
|
|
1246
|
-
var isContextOverflow =
|
|
1247
|
-
|| errLower.indexOf("context_length") !== -1
|
|
1248
|
-
|| errLower.indexOf("maximum context length") !== -1;
|
|
1143
|
+
var isContextOverflow = require("./yoke/errors").contextOverflow(errDetail);
|
|
1249
1144
|
var isAuthError = isAuthErrorMessage(errDetail);
|
|
1250
1145
|
if (isContextOverflow) {
|
|
1251
1146
|
sendAndRecord(session, {
|
|
@@ -1292,7 +1187,7 @@ function createSDKBridge(opts) {
|
|
|
1292
1187
|
// chat message already sent above
|
|
1293
1188
|
}
|
|
1294
1189
|
} else {
|
|
1295
|
-
sendAndRecord(session, { type: "error", text: "
|
|
1190
|
+
sendAndRecord(session, { type: "error", text: "Provider process error: " + err.message });
|
|
1296
1191
|
}
|
|
1297
1192
|
}
|
|
1298
1193
|
sendAndRecord(session, { type: "done", code: 1 });
|
|
@@ -1375,20 +1270,16 @@ function createSDKBridge(opts) {
|
|
|
1375
1270
|
if (session.queryInstance) return { query: session.queryInstance, isTemp: false, cleanup: function() {} };
|
|
1376
1271
|
|
|
1377
1272
|
var handle;
|
|
1273
|
+
var selectedAdapter = getAdapterForSession(session);
|
|
1378
1274
|
try {
|
|
1379
|
-
handle = await
|
|
1275
|
+
handle = await selectedAdapter.createQuery({
|
|
1380
1276
|
cwd: cwd,
|
|
1381
1277
|
resumeSessionId: session.cliSessionId,
|
|
1382
1278
|
env: getRuntimeEnv(session),
|
|
1383
|
-
adapterOptions: {
|
|
1384
|
-
CLAUDE: {
|
|
1385
|
-
settingSources: ["user", "project", "local"],
|
|
1386
|
-
enableFileCheckpointing: true,
|
|
1387
|
-
},
|
|
1388
|
-
},
|
|
1279
|
+
adapterOptions: require("./yoke/query-options").map(getAdapterForSession(session), { nativePermissionMode: "default" }),
|
|
1389
1280
|
});
|
|
1390
1281
|
} catch (e) {
|
|
1391
|
-
sendAndRecord(session, { type: "error", text: "Failed to load
|
|
1282
|
+
sendAndRecord(session, { type: "error", text: "Failed to load provider session: " + (e.message || e) });
|
|
1392
1283
|
throw e;
|
|
1393
1284
|
}
|
|
1394
1285
|
|
|
@@ -1455,7 +1346,7 @@ function createSDKBridge(opts) {
|
|
|
1455
1346
|
// abort the whole rewind instead of leaving local history trimmed
|
|
1456
1347
|
// ahead of the real, un-rolled-back provider thread.
|
|
1457
1348
|
if (!turnId) {
|
|
1458
|
-
throw new Error("No turn id recorded for this turn; cannot roll back the
|
|
1349
|
+
throw new Error("No turn id recorded for this turn; cannot roll back the provider thread.");
|
|
1459
1350
|
}
|
|
1460
1351
|
await sessionAdapter.rollbackThread(session.cliSessionId, turnId, {
|
|
1461
1352
|
linuxUser: getSessionLinuxUser(session) || undefined,
|
|
@@ -1471,6 +1362,8 @@ function createSDKBridge(opts) {
|
|
|
1471
1362
|
|
|
1472
1363
|
async function forkSessionUnified(session, uuid) {
|
|
1473
1364
|
var sessionAdapter = getAdapterForSession(session);
|
|
1365
|
+
var localHistoryEndIndex = require("./session-fork-history").endIndex(session, uuid,
|
|
1366
|
+
require("./yoke/provider-contract").resolve(sessionAdapter).forkHistoryBoundary);
|
|
1474
1367
|
var result = await sessionAdapter.forkSession(session.cliSessionId, {
|
|
1475
1368
|
upToMessageId: uuid,
|
|
1476
1369
|
dir: cwd,
|
|
@@ -1479,8 +1372,10 @@ function createSDKBridge(opts) {
|
|
|
1479
1372
|
if (!result || !result.sessionId) throw new Error("Fork returned no session id");
|
|
1480
1373
|
|
|
1481
1374
|
// Adapters with rollbackThread (e.g. Codex) use local history copy
|
|
1482
|
-
if (typeof sessionAdapter.rollbackThread === "function") {
|
|
1483
|
-
|
|
1375
|
+
if (result.useLocalHistory === true || typeof sessionAdapter.rollbackThread === "function") {
|
|
1376
|
+
var localFork = { sessionId: result.sessionId, useLocalHistory: true };
|
|
1377
|
+
if (localHistoryEndIndex !== undefined) localFork.localHistoryEndIndex = localHistoryEndIndex;
|
|
1378
|
+
return localFork;
|
|
1484
1379
|
}
|
|
1485
1380
|
// Claude: read history from CLI session files
|
|
1486
1381
|
return { sessionId: result.sessionId, useLocalHistory: false };
|
|
@@ -1502,9 +1397,8 @@ function createSDKBridge(opts) {
|
|
|
1502
1397
|
}
|
|
1503
1398
|
if (responseRecovery.stopping(session) && !retryState) return false;
|
|
1504
1399
|
delete session._lastTurnInterrupted;
|
|
1505
|
-
var claude = (session.vendor || adapter.vendor) === "claude";
|
|
1506
1400
|
var payload = { text: text, images: images, linuxUser: linuxUser, beforePush: beforePush };
|
|
1507
|
-
var recoveryState =
|
|
1401
|
+
var recoveryState = responseRecovery.begin(session, payload, retryState);
|
|
1508
1402
|
if (recoveryState) recoveryState.handle = null;
|
|
1509
1403
|
var guardedPush = function() {
|
|
1510
1404
|
return !session.destroying && !session.taskStopRequested
|
|
@@ -1548,7 +1442,7 @@ function createSDKBridge(opts) {
|
|
|
1548
1442
|
return false;
|
|
1549
1443
|
} finally {
|
|
1550
1444
|
session._queryStarting = false;
|
|
1551
|
-
delete session.
|
|
1445
|
+
delete session._providerRecoveryWaiting;
|
|
1552
1446
|
if (!session.queryInstance && session.pendingPush && session.pendingPush.length) session.pendingPush = [];
|
|
1553
1447
|
if (!session.queryInstance && !session.isProcessing && !session._pendingMessageDrainPaused && !responseRecovery.stopping(session)) session.taskStopRequested = false;
|
|
1554
1448
|
if (!responseRecovery.stopping(session) && !session.isProcessing && typeof opts.onSessionAvailable === "function") opts.onSessionAvailable(session);
|
|
@@ -1747,7 +1641,8 @@ function createSDKBridge(opts) {
|
|
|
1747
1641
|
|
|
1748
1642
|
// Ensure Linux user project directory exists (runs in parallel with worker boot)
|
|
1749
1643
|
if (linuxUser) {
|
|
1750
|
-
|
|
1644
|
+
var preparation = require("./yoke/provider-contract").resolve(sessionAdapter).prepareIsolation;
|
|
1645
|
+
if (typeof preparation === "function") preparation(linuxUser, session, cwd);
|
|
1751
1646
|
}
|
|
1752
1647
|
|
|
1753
1648
|
session.blocks = {};
|
|
@@ -1764,100 +1659,27 @@ function createSDKBridge(opts) {
|
|
|
1764
1659
|
session.abortController = new AbortController();
|
|
1765
1660
|
}
|
|
1766
1661
|
|
|
1767
|
-
// Build Claude-specific adapter options
|
|
1768
|
-
var claudeOpts = {
|
|
1769
|
-
settingSources: ["user", "project", "local"],
|
|
1770
|
-
includePartialMessages: true,
|
|
1771
|
-
enableFileCheckpointing: true,
|
|
1772
|
-
extraArgs: { "replay-user-messages": null },
|
|
1773
|
-
promptSuggestions: true,
|
|
1774
|
-
agentProgressSummaries: true,
|
|
1775
|
-
};
|
|
1776
|
-
|
|
1777
|
-
// Per-loop settings override global defaults when present
|
|
1778
1662
|
var ls = session.loopSettings || {};
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
var thinkingMode = ls.thinking || sm.currentThinking;
|
|
1784
|
-
if (thinkingMode === "disabled") {
|
|
1785
|
-
claudeOpts.thinking = { type: "disabled" };
|
|
1786
|
-
} else if (thinkingMode === "budget") {
|
|
1787
|
-
var budgetTokens = ls.thinkingBudget || sm.currentThinkingBudget;
|
|
1788
|
-
if (budgetTokens) claudeOpts.thinking = { type: "enabled", budgetTokens: budgetTokens };
|
|
1789
|
-
} else {
|
|
1790
|
-
// Adaptive (the default). Request summarized display explicitly:
|
|
1791
|
-
// current models default thinking.display to "omitted", which streams
|
|
1792
|
-
// thinking blocks whose text is an EMPTY string -- the UI's thinking
|
|
1793
|
-
// section stays blank even though thinking happens and is billed.
|
|
1794
|
-
// (SDK ThinkingAdaptive: { type: "adaptive", display?: "summarized" | "omitted" })
|
|
1795
|
-
claudeOpts.thinking = { type: "adaptive", display: "summarized" };
|
|
1796
|
-
}
|
|
1797
|
-
|
|
1798
|
-
if (ls.permissionMode) {
|
|
1799
|
-
session._loopPermissionMode = ls.permissionMode;
|
|
1800
|
-
}
|
|
1801
|
-
|
|
1802
|
-
// Pass through any extra SDK settings from LOOP.json
|
|
1803
|
-
if (ls.disableAllHooks !== undefined) {
|
|
1804
|
-
claudeOpts.settings = Object.assign({}, claudeOpts.settings || {}, { disableAllHooks: ls.disableAllHooks });
|
|
1805
|
-
}
|
|
1806
|
-
|
|
1807
|
-
if (dangerouslySkipPermissions || (session.mode === "tui" && session.dangerouslySkipPermissions)) {
|
|
1808
|
-
claudeOpts.allowDangerouslySkipPermissions = true;
|
|
1809
|
-
claudeOpts.permissionMode = "bypassPermissions";
|
|
1810
|
-
} else {
|
|
1811
|
-
var globalMode = session.permissionMode || sm.currentPermissionMode || "default";
|
|
1812
|
-
var effectiveDefault;
|
|
1813
|
-
// GUI full access stays inside Clay's canUseTool callback. Passing the
|
|
1814
|
-
// SDK bypass mode would require its process-level dangerous escape hatch
|
|
1815
|
-
// and would skip Clay's permission policy entirely.
|
|
1816
|
-
if (globalMode === "bypassPermissions") effectiveDefault = "default";
|
|
1817
|
-
else if (session.acceptEditsAfterStart) effectiveDefault = "acceptEdits";
|
|
1818
|
-
else effectiveDefault = globalMode;
|
|
1819
|
-
var modeToApply = session._loopPermissionMode || effectiveDefault;
|
|
1820
|
-
// Always set permissionMode explicitly, including "default". SDK
|
|
1821
|
-
// 0.3.286+ leaves an omitted permissionMode to Claude Code itself,
|
|
1822
|
-
// which can apply a settings defaultMode or start in auto mode on
|
|
1823
|
-
// third-party providers / with telemetry off -- silently widening
|
|
1824
|
-
// past Clay's Ask policy. Sending "default" keeps the SDK's own
|
|
1825
|
-
// decision point at the same "default" behavior Clay has always run
|
|
1826
|
-
// under: a tool the SDK's own allowed/denied rules or allowedTools
|
|
1827
|
-
// list already settles is not re-asked, but anything left undecided
|
|
1828
|
-
// still reaches Clay's canUseTool callback, as before.
|
|
1829
|
-
claudeOpts.permissionMode = modeToApply || "default";
|
|
1830
|
-
}
|
|
1831
|
-
if (session.youCurator) {
|
|
1832
|
-
claudeOpts.permissionMode = "default";
|
|
1833
|
-
claudeOpts.allowDangerouslySkipPermissions = false;
|
|
1834
|
-
}
|
|
1835
|
-
// Clear one-shot acceptEditsAfterStart regardless of which branch ran above,
|
|
1836
|
-
// so the flag does not linger into subsequent turns.
|
|
1837
|
-
if (session.acceptEditsAfterStart) delete session.acceptEditsAfterStart;
|
|
1838
|
-
if (session.cliSessionId && session.lastRewindUuid) {
|
|
1839
|
-
claudeOpts.resumeSessionAt = session.lastRewindUuid;
|
|
1840
|
-
delete session.lastRewindUuid;
|
|
1841
|
-
sm.saveSessionFile(session);
|
|
1842
|
-
}
|
|
1843
|
-
|
|
1844
|
-
// Pass linuxUser to adapter for worker-based queries
|
|
1663
|
+
if (ls.permissionMode) session._loopPermissionMode = ls.permissionMode;
|
|
1664
|
+
var permissionMode = session.permissionMode || sm.currentPermissionMode || "default";
|
|
1665
|
+
var nativeMode = permissionMode === "bypassPermissions" ? "default" : session.acceptEditsAfterStart ? "acceptEdits" : permissionMode;
|
|
1666
|
+
var isolationHome = null;
|
|
1845
1667
|
if (linuxUser) {
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
}
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1668
|
+
try { isolationHome = require("./os-users").resolveOsUserInfo(linuxUser).home; } catch (error) {}
|
|
1669
|
+
}
|
|
1670
|
+
var executionIntent = {
|
|
1671
|
+
settings: sm, permissionMode: permissionMode, nativePermissionMode: session._loopPermissionMode || nativeMode,
|
|
1672
|
+
nativeBypass: dangerouslySkipPermissions || session.mode === "tui" && session.dangerouslySkipPermissions,
|
|
1673
|
+
readOnly: !!session.youCurator, betas: sm.currentBetas,
|
|
1674
|
+
thinking: { mode: ls.thinking || sm.currentThinking, budget: ls.thinkingBudget || sm.currentThinkingBudget },
|
|
1675
|
+
disableHooks: ls.disableAllHooks, resumeAt: session.cliSessionId && session.lastRewindUuid || undefined,
|
|
1676
|
+
singleTurn: !!session.singleTurn, startedAt: t0,
|
|
1677
|
+
isolation: { user: linuxUser, home: isolationHome, projectPath: session.cwd || null, workerState: session._adapterWorkerState },
|
|
1678
|
+
sessionBridge: null,
|
|
1679
|
+
};
|
|
1680
|
+
if (session.acceptEditsAfterStart) delete session.acceptEditsAfterStart;
|
|
1681
|
+
if (executionIntent.resumeAt) { delete session.lastRewindUuid; sm.saveSessionFile(session); }
|
|
1682
|
+
if (linuxUser && session._adapterWorkerState) session._adapterWorkerState = null;
|
|
1861
1683
|
|
|
1862
1684
|
// Pick a model from the session or its vendor-specific default, then
|
|
1863
1685
|
// validate it against the session vendor's known model list.
|
|
@@ -1883,20 +1705,21 @@ function createSDKBridge(opts) {
|
|
|
1883
1705
|
sm.saveSessionFile(session);
|
|
1884
1706
|
}
|
|
1885
1707
|
|
|
1886
|
-
var codexConfig = getCodexConfig(sm);
|
|
1887
|
-
var kiroConfig = getKiroConfig(sm);
|
|
1888
1708
|
session._sdkQueryGeneration = Number(session._sdkQueryGeneration || 0) + 1;
|
|
1889
1709
|
// Individual feature modules own their transport capability checks. Do not
|
|
1890
1710
|
// suppress unrelated session tools merely because one feature cannot be
|
|
1891
1711
|
// delivered by this provider.
|
|
1892
1712
|
var sessionToolDefs = getSessionToolDefs(session) || [];
|
|
1893
1713
|
var queryGeneration = Number(session._sdkQueryGeneration || 0);
|
|
1714
|
+
executionIntent.sessionBridge = getAcpMcpServers(session);
|
|
1715
|
+
var queryContract = yoke.sessionTools.contract(sessionAdapter);
|
|
1716
|
+
function needsCatalogRecovery(family, version) {
|
|
1717
|
+
return yoke.sessionTools.needsRecovery(sessionAdapter, session, family, version);
|
|
1718
|
+
}
|
|
1894
1719
|
var hasYouCatalog = sessionToolDefs.some(function (tool) { return tool.name === "search_user_knowledge"; });
|
|
1895
|
-
var
|
|
1896
|
-
&& hasYouCatalog && Number(session.codexYouToolCatalogVersion || 0) < 1;
|
|
1720
|
+
var recoverYouTools = needsCatalogRecovery("knowledge", 1) && hasYouCatalog;
|
|
1897
1721
|
var hasLinearCatalog = sessionToolDefs.some(function (tool) { return tool.name === "link_session_linear"; });
|
|
1898
|
-
var
|
|
1899
|
-
&& hasLinearCatalog && Number(session.codexLinearToolCatalogVersion || 0) < 1 && canUseSessionTools(session) === true;
|
|
1722
|
+
var recoverLinearTools = needsCatalogRecovery("linear", 1) && hasLinearCatalog && canUseSessionTools(session) === true;
|
|
1900
1723
|
var hasIssueCatalog = false;
|
|
1901
1724
|
for (var issueToolIndex = 0; issueToolIndex < sessionToolDefs.length; issueToolIndex++) {
|
|
1902
1725
|
if (sessionToolDefs[issueToolIndex].name === "create_issue") {
|
|
@@ -1905,19 +1728,16 @@ function createSDKBridge(opts) {
|
|
|
1905
1728
|
}
|
|
1906
1729
|
}
|
|
1907
1730
|
var hasSharedBrowserCatalog = sessionToolDefs.some(function (definition) { return definition.name === "shared_browser"; });
|
|
1908
|
-
var
|
|
1909
|
-
|
|
1910
|
-
var recoverCodexIssueTools = sessionAdapter.vendor === "codex"
|
|
1911
|
-
&& !!session.cliSessionId
|
|
1912
|
-
&& hasIssueCatalog
|
|
1913
|
-
&& Number(session.codexIssuesToolCatalogVersion || 0) < 1;
|
|
1731
|
+
var recoverBrowserTools = needsCatalogRecovery("browser", 6) && hasSharedBrowserCatalog;
|
|
1732
|
+
var recoverIssueTools = needsCatalogRecovery("issues", 1) && hasIssueCatalog;
|
|
1914
1733
|
function queryContextAvailable() {
|
|
1734
|
+
if (queryRetired()) return false;
|
|
1915
1735
|
if (Number(session._sdkQueryGeneration || 0) !== queryGeneration) return false;
|
|
1916
1736
|
if (sm && sm.sessions && typeof sm.sessions.get === "function" && sm.sessions.get(session.localId) !== session) return false;
|
|
1917
1737
|
return canUseSessionTools(session) === true;
|
|
1918
1738
|
}
|
|
1919
1739
|
var hasMateScheduleTool = sessionToolDefs.some(function (tool) { return yoke.sessionTools.MATE_SCHEDULE_TOOLS.indexOf(tool.name) !== -1; });
|
|
1920
|
-
var
|
|
1740
|
+
var recoverMateSchedules = needsCatalogRecovery() && hasMateScheduleTool && queryContextAvailable();
|
|
1921
1741
|
var hasScheduleTool = false;
|
|
1922
1742
|
var hasScheduleInterviewTool = false;
|
|
1923
1743
|
var scheduleInterviewActive = null;
|
|
@@ -1934,10 +1754,10 @@ function createSDKBridge(opts) {
|
|
|
1934
1754
|
var activeLoopInterview = !isDriverOperatedSession(session) && !session.loopInterviewHandoff && !activeRun && !!loopInterview.activeInterviewId(session);
|
|
1935
1755
|
var structuredInputSession = hasScheduleInterviewTool || activeLoopInterview || (!isDriverOperatedSession(session) && session.ralphCraftingMode === true && session.loop && session.loop.role === "crafting");
|
|
1936
1756
|
var requestedUserInputMode = session.debateSetupMode || session.mateCreationMode ? "fallback" : "auto";
|
|
1937
|
-
if (structuredInputSession
|
|
1757
|
+
if (structuredInputSession) requestedUserInputMode = "fallback";
|
|
1758
|
+
if (requestedUserInputMode === "auto" && queryContract.defaultUserInputMode) requestedUserInputMode = queryContract.defaultUserInputMode;
|
|
1938
1759
|
var selectedUserInputMode = yoke.userInput.selectMode(sessionAdapter, requestedUserInputMode);
|
|
1939
|
-
var
|
|
1940
|
-
&& Number(session.codexUserInputToolCatalogVersion || 0) < 1 && queryContextAvailable();
|
|
1760
|
+
var recoverQuestionTools = needsCatalogRecovery("questions", 1) && queryContextAvailable();
|
|
1941
1761
|
function structuredInputContextAvailable() {
|
|
1942
1762
|
if (!queryContextAvailable()) return false;
|
|
1943
1763
|
// A schedule-triggered fallback shares the schedule feature's live
|
|
@@ -1979,26 +1799,33 @@ function createSDKBridge(opts) {
|
|
|
1979
1799
|
return opts.onUserInputRequest(session, request, respond);
|
|
1980
1800
|
};
|
|
1981
1801
|
var localMcpServers = getMcpServers(session);
|
|
1982
|
-
if (
|
|
1802
|
+
if (queryContract.toolTransport === "sdk-mcp" && (hasScheduleTool || hasMateScheduleTool)
|
|
1803
|
+
&& typeof sessionAdapter.createToolServer !== "function") {
|
|
1804
|
+
throw new Error("The adapter cannot mount native schedule tools without createToolServer.");
|
|
1805
|
+
}
|
|
1806
|
+
if (queryContract.toolTransport === "sdk-mcp" && hasScheduleTool) {
|
|
1983
1807
|
var scheduledTaskToolDefs = [];
|
|
1984
1808
|
for (var scheduledToolIndex = 0; scheduledToolIndex < sessionToolDefs.length; scheduledToolIndex++) {
|
|
1985
1809
|
if (yoke.sessionTools.SCHEDULE_TOOLS.indexOf(sessionToolDefs[scheduledToolIndex].name) !== -1) scheduledTaskToolDefs.push(sessionToolDefs[scheduledToolIndex]);
|
|
1986
1810
|
}
|
|
1987
1811
|
if (scheduledTaskToolDefs.length) {
|
|
1988
1812
|
var scheduledTaskServer = sessionAdapter.createToolServer({ name: "clay-scheduled-tasks", version: "1.0.0", tools: scheduledTaskToolDefs });
|
|
1989
|
-
if (scheduledTaskServer)
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
}
|
|
1813
|
+
if (!scheduledTaskServer) throw new Error("The adapter could not mount scheduled-task tools.");
|
|
1814
|
+
localMcpServers = Object.assign({}, localMcpServers || {});
|
|
1815
|
+
localMcpServers["clay-scheduled-tasks"] = scheduledTaskServer;
|
|
1993
1816
|
}
|
|
1994
1817
|
}
|
|
1995
|
-
if (
|
|
1818
|
+
if (queryContract.toolTransport === "sdk-mcp" && hasMateScheduleTool) {
|
|
1996
1819
|
localMcpServers = Object.assign({}, localMcpServers || {});
|
|
1997
1820
|
localMcpServers["clay-mate-schedules"] = sessionAdapter.createToolServer({ name: "clay-mate-schedules", version: "1.0.0",
|
|
1998
1821
|
tools: sessionToolDefs.filter(function (tool) { return yoke.sessionTools.MATE_SCHEDULE_TOOLS.indexOf(tool.name) !== -1; }) });
|
|
1822
|
+
if (!localMcpServers["clay-mate-schedules"]) throw new Error("The adapter could not mount Mate schedule tools.");
|
|
1999
1823
|
}
|
|
2000
1824
|
if (selectedUserInputMode === "fallback") {
|
|
2001
1825
|
var userInputServer = yoke.userInput.createFallbackServer(sessionAdapter, sessionUserInputHandler, { force: true, maxQuestions: scheduleQuestionLimit });
|
|
1826
|
+
if (queryContract.toolTransport === "sdk-mcp" && !userInputServer) {
|
|
1827
|
+
throw new Error("The adapter could not mount structured-input tools.");
|
|
1828
|
+
}
|
|
2002
1829
|
if (userInputServer) {
|
|
2003
1830
|
localMcpServers = Object.assign({}, localMcpServers || {});
|
|
2004
1831
|
localMcpServers[userInputServer.name || "yoke-user-input"] = userInputServer;
|
|
@@ -2035,9 +1862,9 @@ function createSDKBridge(opts) {
|
|
|
2035
1862
|
|
|
2036
1863
|
var externalQueryBridge = require('./mcp-query-bridge').createQueryBridge({ valid: queryContextAvailable,
|
|
2037
1864
|
getServers: function () { return getRemoteMcpServers ? getRemoteMcpServers(session) || {} : {}; } });
|
|
2038
|
-
var externalToolDefs =
|
|
1865
|
+
var externalToolDefs = queryContract.externalToolTransport === "dynamic" ? externalQueryBridge.dynamicDefs() : [];
|
|
2039
1866
|
if (externalToolDefs.length) { sessionToolDefs = sessionToolDefs.concat(externalToolDefs); queryToolSnapshots.set(session, { generation: queryGeneration, defs: sessionToolDefs.slice() }); }
|
|
2040
|
-
var
|
|
1867
|
+
var recoverExternalTools = needsCatalogRecovery() && (externalToolDefs.length > 0 || isMate);
|
|
2041
1868
|
|
|
2042
1869
|
// Give fresh queries an immediate provisional label. The visible user
|
|
2043
1870
|
// message in history wins over `text`, which may contain an internal
|
|
@@ -2054,7 +1881,6 @@ function createSDKBridge(opts) {
|
|
|
2054
1881
|
|
|
2055
1882
|
var modelSwitchGeneration = session._queryGeneration;
|
|
2056
1883
|
function queryRetired() {
|
|
2057
|
-
if (sessionAdapter.vendor !== "claude") return false;
|
|
2058
1884
|
return Number(session._sdkQueryGeneration || 0) !== queryGeneration || session.taskStopRequested
|
|
2059
1885
|
|| session.destroying || responseRecovery.stopping(session) || (handle && session.queryInstance !== handle);
|
|
2060
1886
|
}
|
|
@@ -2072,7 +1898,7 @@ function createSDKBridge(opts) {
|
|
|
2072
1898
|
clayBridgeToken: clayBridgeToken,
|
|
2073
1899
|
linuxUser: linuxUser || undefined,
|
|
2074
1900
|
env: sessionRuntimeEnv,
|
|
2075
|
-
skillOptions: getSessionSkillOptions(session),
|
|
1901
|
+
skillOptions: require("./session-skill-scope").bind(session, sm.sessions, getSessionSkillOptions(session), queryContextAvailable, usersModule.isMultiUser()),
|
|
2076
1902
|
// appendSystemPrompt, NOT systemPrompt: a plain-string systemPrompt
|
|
2077
1903
|
// REPLACES the entire Claude Code system prompt (sdk.d.ts:2096), which
|
|
2078
1904
|
// would strip the driver session down to the two-sentence pair preset.
|
|
@@ -2080,7 +1906,7 @@ function createSDKBridge(opts) {
|
|
|
2080
1906
|
// codex/kiro: prepend to the first message).
|
|
2081
1907
|
appendSystemPrompt: [
|
|
2082
1908
|
typeof opts.getSessionSystemPrompt === "function" ? opts.getSessionSystemPrompt(session) : "",
|
|
2083
|
-
|
|
1909
|
+
typeof queryContract.questionGuidance === "function" && queryContextAvailable() ? queryContract.questionGuidance() : "",
|
|
2084
1910
|
].filter(Boolean).join("\n\n") || undefined,
|
|
2085
1911
|
model: queryModel,
|
|
2086
1912
|
// Effort is remembered per vendor and clamped to the session vendor's
|
|
@@ -2097,21 +1923,22 @@ function createSDKBridge(opts) {
|
|
|
2097
1923
|
toolServers: mergedMcpServers,
|
|
2098
1924
|
toolServerDescriptors: extractMcpDescriptors(mergedMcpServers) || undefined,
|
|
2099
1925
|
dynamicTools: sessionToolDefs,
|
|
2100
|
-
//
|
|
1926
|
+
// Conversation-scoped catalogs persist definitions. A stale authorized Driver
|
|
2101
1927
|
// gets this exact-query bridge; its handlers still reauthorize live.
|
|
2102
|
-
sessionMcpServer: (
|
|
1928
|
+
sessionMcpServer: (recoverMateSchedules || recoverIssueTools || recoverQuestionTools || recoverBrowserTools || recoverLinearTools || recoverYouTools || recoverExternalTools) ? getSessionMcpServer(session, queryGeneration) : undefined,
|
|
2103
1929
|
onRecoveryAdmission: function(waiting) {
|
|
2104
1930
|
if (!queryContextAvailable()) return;
|
|
2105
|
-
if (waiting) session.
|
|
2106
|
-
else delete session.
|
|
1931
|
+
if (waiting) session._providerRecoveryWaiting = queryGeneration;
|
|
1932
|
+
else delete session._providerRecoveryWaiting;
|
|
2107
1933
|
},
|
|
2108
1934
|
onRecoveryWaiting: function(waiting) {
|
|
2109
1935
|
if (!queryContextAvailable()) return;
|
|
2110
1936
|
sendAndRecord(session, { type: 'informational', level: 'info', content: waiting
|
|
2111
|
-
? '
|
|
2112
|
-
: '
|
|
1937
|
+
? 'Tool recovery is waiting for this conversation’s runtime to become safely available. Your message is retained; Stop cancels the wait.'
|
|
1938
|
+
: 'Tool recovery is ready. Continuing your retained message.' });
|
|
2113
1939
|
},
|
|
2114
1940
|
callDynamicTool: function (toolName, args) {
|
|
1941
|
+
if (queryRetired() || !queryContextAvailable()) return Promise.reject(new Error("This session tool query is no longer available."));
|
|
2115
1942
|
var toolDef = findSessionToolDef(toolName);
|
|
2116
1943
|
if (toolDef && typeof toolDef.handler === "function") {
|
|
2117
1944
|
return toolDef.handler(args || {});
|
|
@@ -2123,7 +1950,8 @@ function createSDKBridge(opts) {
|
|
|
2123
1950
|
canUseTool: function(toolName, input, toolOpts) {
|
|
2124
1951
|
if (queryRetired()) return { behavior: "deny", message: "This attempt has stopped." };
|
|
2125
1952
|
responseRecovery.progress(session);
|
|
2126
|
-
var
|
|
1953
|
+
var questionInput = yoke.userInput.toolQuestions(sessionAdapter, toolName, input);
|
|
1954
|
+
var nativeQuestionError = questionInput ? scheduleQuestionError(questionInput) : null;
|
|
2127
1955
|
if (nativeQuestionError) return { behavior: "deny", message: nativeQuestionError };
|
|
2128
1956
|
var permissionName = toolName;
|
|
2129
1957
|
var permissionToolDef = findSessionToolDef(toolName);
|
|
@@ -2133,7 +1961,8 @@ function createSDKBridge(opts) {
|
|
|
2133
1961
|
preToolUsePolicy: function(toolName, input) {
|
|
2134
1962
|
if (queryRetired()) return Promise.resolve({ behavior: "deny", message: "This attempt has stopped." });
|
|
2135
1963
|
responseRecovery.progress(session);
|
|
2136
|
-
var
|
|
1964
|
+
var questionInput = yoke.userInput.toolQuestions(sessionAdapter, toolName, input);
|
|
1965
|
+
var nativeQuestionError = questionInput ? scheduleQuestionError(questionInput) : null;
|
|
2137
1966
|
if (nativeQuestionError) return Promise.resolve({ behavior: "deny", message: nativeQuestionError });
|
|
2138
1967
|
var permissionName = toolName;
|
|
2139
1968
|
var permissionToolDef = findSessionToolDef(toolName);
|
|
@@ -2160,33 +1989,7 @@ function createSDKBridge(opts) {
|
|
|
2160
1989
|
responseRecovery.progress(session);
|
|
2161
1990
|
return callMcpToolHandler(mergedMcpServers, serverName, toolName, args);
|
|
2162
1991
|
},
|
|
2163
|
-
adapterOptions:
|
|
2164
|
-
CLAUDE: claudeOpts,
|
|
2165
|
-
CODEX: {
|
|
2166
|
-
// Always use "never" (auto-approve) because Clay handles tool
|
|
2167
|
-
// permissions via its own UI (checkToolWhitelist + handleCanUseTool).
|
|
2168
|
-
// Codex's native approval prompts are terminal-based and cannot be
|
|
2169
|
-
// relayed through Clay's web UI, causing MCP tool calls to hang.
|
|
2170
|
-
approvalPolicy: "never",
|
|
2171
|
-
sandboxMode: session.youCurator ? "read-only" : codexConfig.sandbox,
|
|
2172
|
-
webSearchMode: session.youCurator ? "disabled" : codexConfig.webSearch,
|
|
2173
|
-
},
|
|
2174
|
-
ACP: {
|
|
2175
|
-
mcpServers: getAcpMcpServers(session),
|
|
2176
|
-
},
|
|
2177
|
-
ANTIGRAVITY: {
|
|
2178
|
-
dangerouslySkipPermissions: dangerouslySkipPermissions
|
|
2179
|
-
|| session.permissionMode === "bypassPermissions",
|
|
2180
|
-
},
|
|
2181
|
-
KIRO: {
|
|
2182
|
-
engine: kiroConfig.engine,
|
|
2183
|
-
mode: kiroConfig.mode,
|
|
2184
|
-
// ACP accepts additional stdio MCP servers per session. The bridge is
|
|
2185
|
-
// bound to this Clay session so partner calls cannot leak across
|
|
2186
|
-
// concurrent Kiro sessions or users.
|
|
2187
|
-
mcpServers: getAcpMcpServers(session),
|
|
2188
|
-
},
|
|
2189
|
-
},
|
|
1992
|
+
adapterOptions: require("./yoke/query-options").map(sessionAdapter, executionIntent),
|
|
2190
1993
|
};
|
|
2191
1994
|
|
|
2192
1995
|
if (recoveryState) {
|
|
@@ -2265,11 +2068,9 @@ function createSDKBridge(opts) {
|
|
|
2265
2068
|
|
|
2266
2069
|
// Store QueryHandle on session for iteration and control.
|
|
2267
2070
|
session.queryInstance = handle;
|
|
2268
|
-
//
|
|
2269
|
-
//
|
|
2270
|
-
|
|
2271
|
-
session.permissionCapabilities = require("./session-permission-mode").capabilities(session, sm.defaultVendor);
|
|
2272
|
-
session.permissionCapabilities.mcpOverride = sessionAdapter.vendor === "claude" && typeof handle.setMcpPermissionModeOverride === "function";
|
|
2071
|
+
// An installed setter and the adapter's explicit protocol contract are
|
|
2072
|
+
// both required; generic handle stubs do not establish runtime support.
|
|
2073
|
+
session.permissionCapabilities = require("./session-permission-mode").capabilities(session, sm.defaultVendor, sessionAdapter);
|
|
2273
2074
|
session.effectivePermissionMode = null;
|
|
2274
2075
|
sm.saveSessionFile(session);
|
|
2275
2076
|
sendToSession(session, { type: "config_state", model: session.model || defaultModelForVendor(session.vendor || "claude"), vendor: session.vendor || "claude", sessionId: session.localId, mode: session.permissionMode || sm.currentPermissionMode || "default", effectivePermissionMode: null, permissionCapabilities: session.permissionCapabilities, mcpPermissionModeOverrides: session.mcpPermissionModeOverrides || {}, effort: session.effort || sm.currentEffort || "medium", betas: sm.currentBetas || [] });
|
|
@@ -2353,32 +2154,27 @@ function createSDKBridge(opts) {
|
|
|
2353
2154
|
}
|
|
2354
2155
|
|
|
2355
2156
|
session.lastActivityAt = Date.now();
|
|
2356
|
-
var
|
|
2357
|
-
if (
|
|
2157
|
+
var catalogCandidate = null;
|
|
2158
|
+
if (queryContract.catalogLifetime === "conversation" && typeof queryContract.recordCatalog === "function" && !resumedAtQueryStart) {
|
|
2358
2159
|
var requiredCatalogTools = { begin_scheduled_task_interview: false, propose_scheduled_task: false, list_scheduled_tasks: false, read_scheduled_task: false, update_scheduled_task: false, report_scheduled_task_outcome: false, ask_user_questions: false };
|
|
2359
2160
|
for (var catalogToolIndex = 0; catalogToolIndex < sessionToolDefs.length; catalogToolIndex++) {
|
|
2360
2161
|
if (Object.prototype.hasOwnProperty.call(requiredCatalogTools, sessionToolDefs[catalogToolIndex].name)) requiredCatalogTools[sessionToolDefs[catalogToolIndex].name] = true;
|
|
2361
2162
|
}
|
|
2362
2163
|
var completeInterviewCatalog = requiredCatalogTools.begin_scheduled_task_interview && requiredCatalogTools.propose_scheduled_task && requiredCatalogTools.list_scheduled_tasks && requiredCatalogTools.read_scheduled_task && requiredCatalogTools.update_scheduled_task && requiredCatalogTools.ask_user_questions;
|
|
2363
2164
|
if (completeInterviewCatalog || requiredCatalogTools.report_scheduled_task_outcome) {
|
|
2364
|
-
|
|
2365
|
-
}
|
|
2366
|
-
if (requiredCatalogTools.ask_user_questions) {
|
|
2367
|
-
codexCatalogCandidate = Object.assign({}, codexCatalogCandidate || {}, { generation: queryGeneration, userInputVersion: 1 });
|
|
2165
|
+
catalogCandidate = { generation: queryGeneration, versions: { schedules: yoke.sessionTools.SCHEDULE_CATALOG_VERSION } };
|
|
2368
2166
|
}
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
codexCatalogCandidate = Object.assign({}, codexCatalogCandidate || {}, { generation: queryGeneration, sharedBrowserVersion: 6 });
|
|
2373
|
-
}
|
|
2374
|
-
if (hasIssueCatalog) {
|
|
2375
|
-
codexCatalogCandidate = Object.assign({}, codexCatalogCandidate || {}, {
|
|
2376
|
-
generation: queryGeneration,
|
|
2377
|
-
issuesVersion: 1,
|
|
2378
|
-
});
|
|
2167
|
+
function markCatalog(family, version) {
|
|
2168
|
+
if (!catalogCandidate) catalogCandidate = { generation: queryGeneration, versions: {} };
|
|
2169
|
+
catalogCandidate.versions[family] = version;
|
|
2379
2170
|
}
|
|
2171
|
+
if (requiredCatalogTools.ask_user_questions) markCatalog("questions", 1);
|
|
2172
|
+
if (hasYouCatalog) markCatalog("knowledge", 1);
|
|
2173
|
+
if (hasLinearCatalog) markCatalog("linear", 1);
|
|
2174
|
+
if (hasSharedBrowserCatalog) markCatalog("browser", 6);
|
|
2175
|
+
if (hasIssueCatalog) markCatalog("issues", 1);
|
|
2380
2176
|
}
|
|
2381
|
-
session.streamPromise = processQueryStream(session,
|
|
2177
|
+
session.streamPromise = processQueryStream(session, catalogCandidate, queryContract).catch(function(err) {
|
|
2382
2178
|
});
|
|
2383
2179
|
return true;
|
|
2384
2180
|
}
|
|
@@ -2392,7 +2188,7 @@ function createSDKBridge(opts) {
|
|
|
2392
2188
|
function pushMessage(session, text, images, deliveryMeta) {
|
|
2393
2189
|
if (session._responseRecoveryStopPending && !responseRecovery.stopping(session)) return false;
|
|
2394
2190
|
function trackDelivery() {
|
|
2395
|
-
|
|
2191
|
+
responseRecovery.enqueue(session, {
|
|
2396
2192
|
text: text, images: images, linuxUser: session.lastLinuxUser || null,
|
|
2397
2193
|
});
|
|
2398
2194
|
}
|
|
@@ -2429,6 +2225,7 @@ function createSDKBridge(opts) {
|
|
|
2429
2225
|
console.error("[sdk-bridge] QueryHandle rejected message for session " + session.localId + ":", e.message || e);
|
|
2430
2226
|
}
|
|
2431
2227
|
if (delivered) {
|
|
2228
|
+
require('./sdk-foreground-admission').take(session);
|
|
2432
2229
|
trackDelivery();
|
|
2433
2230
|
delete session._lastTurnInterrupted;
|
|
2434
2231
|
if (session._awaitingTurnResult) {
|
|
@@ -2535,65 +2332,8 @@ function createSDKBridge(opts) {
|
|
|
2535
2332
|
// Detect which vendor binaries are installed for this user.
|
|
2536
2333
|
// In multi-user mode, runs checks as the specific Linux user.
|
|
2537
2334
|
function detectInstalledVendors(linuxUser) {
|
|
2538
|
-
var
|
|
2539
|
-
|
|
2540
|
-
var result = [];
|
|
2541
|
-
|
|
2542
|
-
function tryLookup(name) {
|
|
2543
|
-
try {
|
|
2544
|
-
if (linuxUser) {
|
|
2545
|
-
execFileSync("su", ["-", linuxUser, "-c", "which " + name], { timeout: 3000, stdio: ["pipe", "pipe", "pipe"] });
|
|
2546
|
-
} else {
|
|
2547
|
-
if (process.platform === "win32") execFileSync("where", [name], { timeout: 3000, stdio: ["pipe", "pipe", "pipe"] });
|
|
2548
|
-
else execFileSync("which", [name], { timeout: 3000, stdio: ["pipe", "pipe", "pipe"] });
|
|
2549
|
-
}
|
|
2550
|
-
return true;
|
|
2551
|
-
} catch (e) {
|
|
2552
|
-
return false;
|
|
2553
|
-
}
|
|
2554
|
-
}
|
|
2555
|
-
|
|
2556
|
-
// Claude: check if binary is in PATH
|
|
2557
|
-
if (tryLookup(yoke.getVendorInfo("claude").binaryName)) result.push("claude");
|
|
2558
|
-
|
|
2559
|
-
// Codex: check bundled binary or PATH
|
|
2560
|
-
var codexBin = null;
|
|
2561
|
-
try {
|
|
2562
|
-
codexBin = require("./yoke/codex-app-server").findCodexPath();
|
|
2563
|
-
} catch (e) {}
|
|
2564
|
-
if ((codexBin && fs.existsSync(codexBin)) || tryLookup(yoke.getVendorInfo("codex").binaryName)) result.push("codex");
|
|
2565
|
-
|
|
2566
|
-
var subprocessVendorKeys = ["antigravity", "opencode", "kimi", "grok", "copilot", "qwen", "junie"];
|
|
2567
|
-
var acpProfiles = require("./yoke/acp-agent-profiles");
|
|
2568
|
-
for (var subprocessVendorIndex = 0; subprocessVendorIndex < subprocessVendorKeys.length; subprocessVendorIndex++) {
|
|
2569
|
-
var subprocessVendor = subprocessVendorKeys[subprocessVendorIndex];
|
|
2570
|
-
var subprocessInfo = yoke.getVendorInfo(subprocessVendor);
|
|
2571
|
-
if (!linuxUser || subprocessInfo.osUserIsolation) {
|
|
2572
|
-
if (subprocessVendor === "antigravity") {
|
|
2573
|
-
if (tryLookup(subprocessInfo.binaryName)) result.push(subprocessVendor);
|
|
2574
|
-
} else {
|
|
2575
|
-
var acpProfile = acpProfiles.getAcpAgentProfile(subprocessVendor);
|
|
2576
|
-
if (acpProfiles.findAcpAgentPath(acpProfile)) result.push(subprocessVendor);
|
|
2577
|
-
}
|
|
2578
|
-
} else {
|
|
2579
|
-
console.log("[sdk-bridge] " + subprocessInfo.displayName + " hidden for OS-isolated user " + linuxUser + ": per-user spawning is not implemented");
|
|
2580
|
-
}
|
|
2581
|
-
}
|
|
2582
|
-
|
|
2583
|
-
// Kiro has no per-user ACP spawn path yet. Do not advertise the daemon's
|
|
2584
|
-
// binary to an OS-isolated user, even if that user also has Kiro installed.
|
|
2585
|
-
var kiroInfo = yoke.getVendorInfo("kiro");
|
|
2586
|
-
if (!linuxUser || kiroInfo.osUserIsolation) {
|
|
2587
|
-
var kiroBin = null;
|
|
2588
|
-
try {
|
|
2589
|
-
kiroBin = require("./yoke/kiro-acp-server").findKiroPath();
|
|
2590
|
-
} catch (e) {}
|
|
2591
|
-
if ((kiroBin && fs.existsSync(kiroBin)) || tryLookup(kiroInfo.binaryName)) result.push("kiro");
|
|
2592
|
-
} else {
|
|
2593
|
-
console.log("[sdk-bridge] " + kiroInfo.displayName + " hidden for OS-isolated user " + linuxUser + ": per-user spawning is not implemented");
|
|
2594
|
-
}
|
|
2595
|
-
|
|
2596
|
-
return result;
|
|
2335
|
+
var installed = require("./yoke/discovery").installed({ user: linuxUser });
|
|
2336
|
+
return Object.keys(installed).filter(function(vendor) { return installed[vendor]; });
|
|
2597
2337
|
}
|
|
2598
2338
|
|
|
2599
2339
|
function getAvailableVendors(linuxUser) {
|
|
@@ -2850,16 +2590,7 @@ function createSDKBridge(opts) {
|
|
|
2850
2590
|
message: "Read-only access. You cannot make changes via @mention.",
|
|
2851
2591
|
});
|
|
2852
2592
|
},
|
|
2853
|
-
adapterOptions: {
|
|
2854
|
-
CLAUDE: {
|
|
2855
|
-
settingSources: ["user"],
|
|
2856
|
-
includePartialMessages: true,
|
|
2857
|
-
// Explicit, not omitted: SDK 0.3.286+ leaves an omitted
|
|
2858
|
-
// permissionMode to Claude Code's own default/auto behavior,
|
|
2859
|
-
// which could bypass the deny-by-default canUseTool above.
|
|
2860
|
-
permissionMode: "default",
|
|
2861
|
-
},
|
|
2862
|
-
},
|
|
2593
|
+
adapterOptions: require("./yoke/query-options").map(mentionAdapter, { instructionSources: ["user"], readOnly: true }),
|
|
2863
2594
|
});
|
|
2864
2595
|
} catch (e) {
|
|
2865
2596
|
opts.onError("Failed to create mention query: " + (e.message || e));
|
|
@@ -3004,7 +2735,8 @@ function createSDKBridge(opts) {
|
|
|
3004
2735
|
setModel: setModel,
|
|
3005
2736
|
setEffort: setEffort,
|
|
3006
2737
|
setPermissionMode: setPermissionMode,
|
|
3007
|
-
|
|
2738
|
+
isProviderProcess: isProviderProcess,
|
|
2739
|
+
isClaudeProcess: isProviderProcess, // Compatibility export; production uses the provider contract.
|
|
3008
2740
|
permissionPushTitle: permissionPushTitle,
|
|
3009
2741
|
permissionPushBody: permissionPushBody,
|
|
3010
2742
|
warmup: warmup,
|