clay-server 5.2.0-beta.5 → 5.2.0-beta.7

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.
Files changed (246) hide show
  1. package/lib/claude-hook-installer.js +1 -267
  2. package/lib/claude-jsonl-watcher.js +1 -204
  3. package/lib/clay-control-presentation-binding.js +96 -0
  4. package/lib/clay-control-presentation.js +128 -0
  5. package/lib/clay-control-target-projection.js +26 -0
  6. package/lib/clay-mate-chat.js +1 -0
  7. package/lib/clay-mate-knowledge.js +1 -1
  8. package/lib/clay-product-control-tools.js +1 -1
  9. package/lib/clay-product-control.js +22 -12
  10. package/lib/clay-project-records.js +2 -1
  11. package/lib/clay-scheduling.js +1 -1
  12. package/lib/clay-screen-controls.js +13 -5
  13. package/lib/clay-skill-install.js +1 -33
  14. package/lib/clay-workspace-controls.js +2 -1
  15. package/lib/cli-sessions.js +1 -300
  16. package/lib/codex-defaults.js +1 -28
  17. package/lib/context-usage.js +3 -2
  18. package/lib/contextual-screen-snapshot.js +38 -0
  19. package/lib/daemon.js +5 -55
  20. package/lib/default-ai-runtime.js +4 -8
  21. package/lib/email-mcp-server.js +1 -1
  22. package/lib/kiro-defaults.js +1 -22
  23. package/lib/mate-context-service.js +138 -0
  24. package/lib/mate-context-tools.js +21 -0
  25. package/lib/mcp-proxy.js +2 -32
  26. package/lib/mcp-query-bridge.js +1 -1
  27. package/lib/mcp-source-adapters.js +1 -91
  28. package/lib/mcp-source-context.js +1 -42
  29. package/lib/mcp-source-discovery.js +1 -169
  30. package/lib/mcp-source-plugins.js +1 -68
  31. package/lib/mcp-source-restrictions.js +1 -47
  32. package/lib/mcp-tool-policy.js +1 -26
  33. package/lib/os-user-diagnostics.js +2 -4
  34. package/lib/os-users.js +4 -65
  35. package/lib/project-connection.js +14 -10
  36. package/lib/project-control-git-view.js +12 -0
  37. package/lib/project-filesystem.js +5 -1
  38. package/lib/project-http.js +7 -4
  39. package/lib/project-mcp.js +2 -2
  40. package/lib/project-notifications.js +5 -4
  41. package/lib/project-pair-result-delivery.js +2 -2
  42. package/lib/project-pair-usage.js +12 -13
  43. package/lib/project-pending-message-queue.js +3 -1
  44. package/lib/project-product-control.js +3 -1
  45. package/lib/project-scheduled-messages.js +4 -4
  46. package/lib/project-scheduled-task-control.js +1 -1
  47. package/lib/project-session-document.js +20 -5
  48. package/lib/project-session-history-actions.js +5 -3
  49. package/lib/project-session-pair.js +1 -1
  50. package/lib/project-session-runtime-actions.js +2 -1
  51. package/lib/project-session-spawn.js +2 -3
  52. package/lib/project-session-vendor.js +2 -1
  53. package/lib/project-sessions.js +18 -26
  54. package/lib/project-shared-browser.js +22 -5
  55. package/lib/project-studio-presentation.js +58 -0
  56. package/lib/project-user-message.js +37 -0
  57. package/lib/project-vendor-login.js +6 -36
  58. package/lib/project-workspace-query.js +11 -1
  59. package/lib/project.js +24 -7
  60. package/lib/public/app.js +25 -0
  61. package/lib/public/appearance-bootstrap.js +2 -0
  62. package/lib/public/css/contextual-clay.css +240 -0
  63. package/lib/public/css/messages.css +6 -2
  64. package/lib/public/modules/app-favicon.js +18 -9
  65. package/lib/public/modules/app-header.js +4 -2
  66. package/lib/public/modules/app-loop-ui.js +2 -0
  67. package/lib/public/modules/app-message-router.js +5 -1
  68. package/lib/public/modules/app-messages.js +50 -6
  69. package/lib/public/modules/app-notifications.js +11 -5
  70. package/lib/public/modules/app-panels.js +61 -123
  71. package/lib/public/modules/app-projects.js +3 -0
  72. package/lib/public/modules/app-rate-limit.js +7 -27
  73. package/lib/public/modules/chat-render-runtime.js +3 -0
  74. package/lib/public/modules/clay-control-activity.js +214 -0
  75. package/lib/public/modules/clay-control-copy.js +36 -0
  76. package/lib/public/modules/clay-control-detail-targets.js +44 -0
  77. package/lib/public/modules/clay-control-object-targets.js +52 -0
  78. package/lib/public/modules/clay-control-target-visibility.js +12 -0
  79. package/lib/public/modules/clay-control-targets.js +44 -0
  80. package/lib/public/modules/clay-navigation-render.js +52 -0
  81. package/lib/public/modules/clay-owner-navigation.js +11 -2
  82. package/lib/public/modules/clay-session-links.js +31 -0
  83. package/lib/public/modules/clay-studio-bridge.js +20 -0
  84. package/lib/public/modules/clay-studio-document.js +67 -0
  85. package/lib/public/modules/clay-studio-render.js +144 -0
  86. package/lib/public/modules/clay-tool-presentation.js +4 -0
  87. package/lib/public/modules/command-palette.js +26 -31
  88. package/lib/public/modules/contextual-ask-session.js +26 -0
  89. package/lib/public/modules/contextual-clay-geometry.js +155 -0
  90. package/lib/public/modules/contextual-clay-session.js +130 -0
  91. package/lib/public/modules/contextual-clay-workspace.js +157 -0
  92. package/lib/public/modules/contextual-clay.js +237 -0
  93. package/lib/public/modules/contextual-composer.js +91 -0
  94. package/lib/public/modules/contextual-mate-context.js +115 -0
  95. package/lib/public/modules/contextual-mate-hint.js +40 -0
  96. package/lib/public/modules/contextual-mate-identity.js +23 -0
  97. package/lib/public/modules/contextual-mate-sessions.js +134 -0
  98. package/lib/public/modules/contextual-search.js +137 -0
  99. package/lib/public/modules/contextual-session-citations.js +58 -0
  100. package/lib/public/modules/filebrowser.js +20 -1
  101. package/lib/public/modules/generation-stop.js +8 -5
  102. package/lib/public/modules/git-panel.js +3 -1
  103. package/lib/public/modules/icon-strip-tooltip.js +2 -1
  104. package/lib/public/modules/input.js +35 -1
  105. package/lib/public/modules/issues-render.js +3 -2
  106. package/lib/public/modules/knowledge-db-workbench.js +4 -4
  107. package/lib/public/modules/markdown.js +2 -34
  108. package/lib/public/modules/mate-knowledge-workbench.js +1 -1
  109. package/lib/public/modules/mcp-connection-view.js +1 -1
  110. package/lib/public/modules/message-delivery.js +10 -0
  111. package/lib/public/modules/pane-bridge.js +2 -2
  112. package/lib/public/modules/pending-message-queue.js +1 -0
  113. package/lib/public/modules/permission-control.js +5 -4
  114. package/lib/public/modules/product-review.js +26 -0
  115. package/lib/public/modules/project-chat-identity.js +13 -4
  116. package/lib/public/modules/project-logs-render.js +3 -0
  117. package/lib/public/modules/project-mate-navigation.js +58 -21
  118. package/lib/public/modules/scheduled-tasks.js +2 -0
  119. package/lib/public/modules/scheduler.js +2 -0
  120. package/lib/public/modules/search-clay-chat.js +5 -454
  121. package/lib/public/modules/session-create-form.js +10 -10
  122. package/lib/public/modules/settings-defaults.js +27 -28
  123. package/lib/public/modules/shared-browser.js +2 -0
  124. package/lib/public/modules/sidebar-mobile.js +4 -3
  125. package/lib/public/modules/sidebar-projects.js +1 -0
  126. package/lib/public/modules/sidebar-sessions.js +1 -0
  127. package/lib/public/modules/split-view.js +7 -16
  128. package/lib/public/modules/terminal.js +9 -0
  129. package/lib/public/modules/tools.js +6 -2
  130. package/lib/public/modules/tui-grab.js +1 -2
  131. package/lib/public/modules/vendor-login-browser.js +5 -3
  132. package/lib/public/modules/vendor-login.js +2 -2
  133. package/lib/public/modules/vendor-priority.js +15 -1
  134. package/lib/public/modules/wireframe-workbench.js +5 -2
  135. package/lib/public/muse-avatar.svg +4 -0
  136. package/lib/public/style.css +2 -0
  137. package/lib/safe-bash-commands.js +3 -45
  138. package/lib/sdk-bridge.js +131 -395
  139. package/lib/sdk-foreground-admission.js +16 -0
  140. package/lib/sdk-mcp-descriptors.js +2 -37
  141. package/lib/sdk-message-processor.js +50 -17
  142. package/lib/sdk-quota-snapshot.js +1 -1
  143. package/lib/sdk-response-recovery.js +1 -155
  144. package/lib/sdk-worker.js +2 -677
  145. package/lib/server-contextual-clay-settings.js +40 -0
  146. package/lib/server-contextual-mate-sessions.js +70 -0
  147. package/lib/server-home-chat.js +7 -1
  148. package/lib/server-home-clay-entry.js +23 -4
  149. package/lib/server-home-clay-session-links.js +12 -0
  150. package/lib/server-owner-ui.js +49 -8
  151. package/lib/server-settings.js +2 -0
  152. package/lib/server.js +18 -1
  153. package/lib/session-fork-history.js +21 -0
  154. package/lib/session-hygiene.js +3 -35
  155. package/lib/session-permission-mode.js +4 -3
  156. package/lib/session-skill-scope.js +11 -0
  157. package/lib/sessions.js +24 -89
  158. package/lib/terminal-manager.js +18 -11
  159. package/lib/tool-llm.js +1 -1
  160. package/lib/tui-transcript-index.js +1 -132
  161. package/lib/users-contextual-clay-preferences.js +31 -0
  162. package/lib/users.js +3 -0
  163. package/lib/vendor-login-browser.js +1 -11
  164. package/lib/workspace-assignment-service.js +25 -0
  165. package/lib/workspace-query-mcp-server.js +1 -1
  166. package/lib/ws-request.js +1 -0
  167. package/lib/ws-schema.js +10 -0
  168. package/lib/yoke/acp-agent-profiles.js +5 -3
  169. package/lib/yoke/adapters/antigravity.js +3 -14
  170. package/lib/yoke/adapters/claude-query-contract.js +9 -0
  171. package/lib/yoke/adapters/claude.js +7 -42
  172. package/lib/yoke/adapters/codex-query-contract.js +24 -0
  173. package/lib/yoke/adapters/codex.js +1 -0
  174. package/lib/yoke/adapters/kiro.js +3 -1
  175. package/lib/yoke/adapters/muse-query-contract.js +7 -0
  176. package/lib/yoke/adapters/muse.js +122 -0
  177. package/lib/yoke/auth-events.js +17 -0
  178. package/lib/yoke/claude-hook-installer.js +271 -0
  179. package/lib/yoke/claude-jsonl-watcher.js +204 -0
  180. package/lib/yoke/claude-maintenance.js +55 -0
  181. package/lib/yoke/claude-permission-patterns.js +53 -0
  182. package/lib/yoke/claude-response-recovery.js +155 -0
  183. package/lib/yoke/claude-transcript-hygiene.js +34 -0
  184. package/lib/yoke/clay-skill-install.js +33 -0
  185. package/lib/yoke/cli-sessions.js +300 -0
  186. package/lib/yoke/codex-app-server.js +7 -12
  187. package/lib/yoke/configuration.js +13 -0
  188. package/lib/yoke/discovery.js +70 -0
  189. package/lib/yoke/errors.js +8 -0
  190. package/lib/yoke/host-environment.js +7 -0
  191. package/lib/yoke/index.js +9 -51
  192. package/lib/yoke/interface.js +116 -0
  193. package/lib/yoke/kiro-acp-server.js +5 -5
  194. package/lib/yoke/legacy-sessions.js +89 -0
  195. package/lib/yoke/login-protocol.js +25 -0
  196. package/lib/yoke/maintenance.js +12 -0
  197. package/lib/yoke/mcp-proxy.js +32 -0
  198. package/lib/yoke/mcp-source-adapters.js +91 -0
  199. package/lib/yoke/mcp-source-context.js +42 -0
  200. package/lib/yoke/mcp-source-discovery.js +169 -0
  201. package/lib/yoke/mcp-source-plugins.js +68 -0
  202. package/lib/yoke/mcp-source-restrictions.js +47 -0
  203. package/lib/yoke/mcp-tool-policy-normalize.js +28 -0
  204. package/lib/yoke/model-preferences.js +6 -0
  205. package/lib/yoke/muse-artifacts.js +36 -0
  206. package/lib/yoke/muse-controls.js +50 -0
  207. package/lib/yoke/muse-input.js +64 -0
  208. package/lib/yoke/muse-items.js +157 -0
  209. package/lib/yoke/muse-policy.js +52 -0
  210. package/lib/yoke/muse-query.js +428 -0
  211. package/lib/yoke/muse-runtime.js +211 -0
  212. package/lib/yoke/muse-services.js +79 -0
  213. package/lib/yoke/muse-skills.js +79 -0
  214. package/lib/yoke/muse-turns.js +71 -0
  215. package/lib/yoke/options/acp.js +2 -0
  216. package/lib/yoke/options/antigravity.js +2 -0
  217. package/lib/yoke/options/claude.js +21 -0
  218. package/lib/yoke/options/codex-defaults.js +29 -0
  219. package/lib/yoke/options/codex-settings.js +21 -0
  220. package/lib/yoke/options/codex.js +6 -0
  221. package/lib/yoke/options/kiro-defaults.js +22 -0
  222. package/lib/yoke/options/kiro.js +5 -0
  223. package/lib/yoke/options/muse.js +5 -0
  224. package/lib/yoke/provider-contract.js +5 -0
  225. package/lib/yoke/providers/antigravity-executable.js +8 -0
  226. package/lib/yoke/providers/claude-executable.js +40 -0
  227. package/lib/yoke/providers/claude-model-descriptions.js +14 -0
  228. package/lib/yoke/providers/claude-process.js +97 -0
  229. package/lib/yoke/providers/claude-provision.js +75 -0
  230. package/lib/yoke/providers/codex-errors.js +8 -0
  231. package/lib/yoke/providers/muse-executable.js +17 -0
  232. package/lib/yoke/provisioning.js +6 -0
  233. package/lib/yoke/query-options.js +10 -0
  234. package/lib/yoke/response-recovery.js +41 -0
  235. package/lib/yoke/sdk-mcp-descriptors.js +37 -0
  236. package/lib/yoke/sdk-server-factory.js +7 -0
  237. package/lib/yoke/session-history.js +8 -0
  238. package/lib/yoke/session-tool-transport.js +31 -13
  239. package/lib/yoke/settings.js +23 -0
  240. package/lib/yoke/source-locations.js +4 -0
  241. package/lib/yoke/title-query.js +18 -7
  242. package/lib/yoke/tui-transcript-index.js +132 -0
  243. package/lib/yoke/usage.js +48 -0
  244. package/lib/yoke/user-input.js +13 -0
  245. package/lib/yoke/vendor-registry.js +81 -1
  246. package/package.json +3 -2
package/lib/sdk-bridge.js CHANGED
@@ -1,12 +1,9 @@
1
- const crypto = require("crypto");
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 getCodexSessionMcpServer(session, queryGeneration) {
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
- function isAuthErrorMessage(errDetail) {
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) || "claude login";
169
+ return (info && info.loginCommand) || "";
182
170
  }
183
171
 
184
172
  function supportsOsUserIsolation(vendor) {
185
173
  var info = yoke.getVendorInfo(vendor);
186
- return !info || info.osUserIsolation !== false;
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("./sdk-response-recovery").attachResponseRecovery(Object.assign({
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
- function ensureLinuxUserProjectDir(linuxUser, session) {
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
 
@@ -759,6 +711,10 @@ function createSDKBridge(opts) {
759
711
  // Workspace inspection and proposal posting are session-bound. Keep this
760
712
  // list exact so a future execution/mutation tool cannot inherit approval.
761
713
  var safeWorkspaceTools = {
714
+ "mcp__clay-workspace__read_current_context": true,
715
+ "mcp__clay-workspace__read_selected_context": true,
716
+ "mcp__clay-workspace__open_context_conversation": true,
717
+ "mcp__clay-workspace__read_context_navigation_receipt": true,
762
718
  "mcp__clay-workspace__read_owner_screen": true,
763
719
  "mcp__clay-workspace__inspect_owner_screen_change": true,
764
720
  "mcp__clay-workspace__control_owner_screen": true,
@@ -986,58 +942,13 @@ function createSDKBridge(opts) {
986
942
  * Only returns processes whose cwd matches our project directory.
987
943
  * Returns an array of { pid, command } for each conflicting process found.
988
944
  */
989
- function findConflictingClaude() {
990
- try {
991
- var output = execFileSync("ps", ["ax", "-o", "pid,command"], { encoding: "utf8", timeout: 5000, stdio: ["pipe", "pipe", "pipe"] });
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
- }
945
+ function findConflictingProcesses(session, error) {
946
+ var service = require("./yoke/provider-contract").resolve(getAdapterForSession(session)).processControl;
947
+ return service && typeof service.shouldInspect === "function" && service.shouldInspect(error) && typeof service.findConflicts === "function" ? service.findConflicts(cwd) : [];
1029
948
  }
1030
-
1031
- /**
1032
- * Verify that a PID is actually a claude binary process (not arbitrary).
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
- }
949
+ function isProviderProcess(pid, session) {
950
+ var service = require("./yoke/provider-contract").resolve(session ? getAdapterForSession(session) : adapter).processControl;
951
+ return !!(service && typeof service.verifyProcess === "function" && service.verifyProcess(pid));
1041
952
  }
1042
953
 
1043
954
  function ownsQueryRuntime(session, queryInstance, queryGeneration) {
@@ -1080,7 +991,8 @@ function createSDKBridge(opts) {
1080
991
  return true;
1081
992
  }
1082
993
 
1083
- async function processQueryStream(session, codexCatalogCandidate) {
994
+ async function processQueryStream(session, catalogCandidate, queryContract) {
995
+ queryContract = queryContract || yoke.sessionTools.contract(getAdapterForSession(session));
1084
996
  // Capture references at start so we only clean up OUR resources in finally,
1085
997
  // not resources from a newer query that may have been created after an abort.
1086
998
  var myQueryInstance = session.queryInstance;
@@ -1146,19 +1058,14 @@ function createSDKBridge(opts) {
1146
1058
  // permission state. Setter acknowledgement deliberately does not
1147
1059
  // change this field.
1148
1060
  recordRuntimeEffectivePermissionMode(session, myQueryInstance, myQueryGeneration, msg);
1149
- if (codexCatalogCandidate && msg && msg.yokeType === "session_started" && msg.sessionId
1061
+ if (catalogCandidate && msg && msg.yokeType === "session_started" && msg.sessionId
1150
1062
  && session.queryInstance === myQueryInstance
1151
- && Number(session._sdkQueryGeneration || 0) === codexCatalogCandidate.generation
1063
+ && Number(session._sdkQueryGeneration || 0) === catalogCandidate.generation
1152
1064
  && session.cliSessionId === msg.sessionId
1153
1065
  && (!sm.sessions || typeof sm.sessions.get !== "function" || sm.sessions.get(session.localId) === session)) {
1154
- if (codexCatalogCandidate.version) session.codexDynamicToolCatalogVersion = codexCatalogCandidate.version;
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;
1066
+ queryContract.recordCatalog(session, catalogCandidate.versions);
1160
1067
  sm.saveSessionFile(session);
1161
- codexCatalogCandidate = null;
1068
+ catalogCandidate = null;
1162
1069
  }
1163
1070
  }
1164
1071
  // (getContextUsage moved to processSDKMessage result handler -- fire-and-forget)
@@ -1176,9 +1083,7 @@ function createSDKBridge(opts) {
1176
1083
  onProcessingChanged();
1177
1084
  send({ type: "status", processing: false });
1178
1085
  sendAndRecord(session, { type: "thinking_stop" });
1179
- var interruptMsg = (session.vendor && session.vendor !== "claude")
1180
- ? "\u25a0 Conversation interrupted - tell the model what to do differently."
1181
- : "Interrupted \u00b7 What should Claude do instead?";
1086
+ var interruptMsg = (queryContract && queryContract.interruptionMessage) || "■ Conversation interrupted - tell the model what to do differently.";
1182
1087
  sendAndRecord(session, { type: "info", text: interruptMsg });
1183
1088
  sendAndRecord(session, { type: "done", code: 0 });
1184
1089
  sm.broadcastSessionList();
@@ -1213,9 +1118,7 @@ function createSDKBridge(opts) {
1213
1118
  session._lastTurnInterrupted = true;
1214
1119
  if (!session.destroying) {
1215
1120
  sendAndRecord(session, { type: "thinking_stop" });
1216
- var interruptMsg2 = (session.vendor && session.vendor !== "claude")
1217
- ? "\u25a0 Conversation interrupted - tell the model what to do differently."
1218
- : "Interrupted \u00b7 What should Claude do instead?";
1121
+ var interruptMsg2 = (queryContract && queryContract.interruptionMessage) || "■ Conversation interrupted - tell the model what to do differently.";
1219
1122
  sendAndRecord(session, { type: "info", text: interruptMsg2 });
1220
1123
  sendAndRecord(session, { type: "done", code: 0 });
1221
1124
  }
@@ -1231,21 +1134,17 @@ function createSDKBridge(opts) {
1231
1134
  console.error("[sdk-bridge] Query stream error for session " + session.localId + ":", errDetail);
1232
1135
  console.error("[sdk-bridge] Stack:", err.stack || "(no stack)");
1233
1136
 
1234
- // Check for conflicting Claude processes only on exit code 1
1235
- var isExitCode1 = err.exitCode === 1 || (err.message && err.message.indexOf("exited with code 1") !== -1);
1236
- var conflicts = isExitCode1 ? findConflictingClaude() : [];
1137
+ var conflicts = findConflictingProcesses(session, err);
1237
1138
  if (conflicts.length > 0) {
1238
- console.error("[sdk-bridge] Found " + conflicts.length + " conflicting Claude process(es):", conflicts.map(function(c) { return "PID " + c.pid; }).join(", "));
1139
+ console.error("[sdk-bridge] Found " + conflicts.length + " conflicting provider process(es):", conflicts.map(function(c) { return "PID " + c.pid; }).join(", "));
1239
1140
  sendAndRecord(session, {
1240
1141
  type: "process_conflict",
1241
- text: "Another Claude Code process is already running in this project.",
1142
+ text: "Another provider process is already running in this project.",
1242
1143
  processes: conflicts,
1243
1144
  });
1244
1145
  } else {
1245
1146
  var errLower = errDetail.toLowerCase();
1246
- var isContextOverflow = errLower.indexOf("prompt is too long") !== -1
1247
- || errLower.indexOf("context_length") !== -1
1248
- || errLower.indexOf("maximum context length") !== -1;
1147
+ var isContextOverflow = require("./yoke/errors").contextOverflow(errDetail);
1249
1148
  var isAuthError = isAuthErrorMessage(errDetail);
1250
1149
  if (isContextOverflow) {
1251
1150
  sendAndRecord(session, {
@@ -1292,7 +1191,7 @@ function createSDKBridge(opts) {
1292
1191
  // chat message already sent above
1293
1192
  }
1294
1193
  } else {
1295
- sendAndRecord(session, { type: "error", text: "Claude process error: " + err.message });
1194
+ sendAndRecord(session, { type: "error", text: "Provider process error: " + err.message });
1296
1195
  }
1297
1196
  }
1298
1197
  sendAndRecord(session, { type: "done", code: 1 });
@@ -1375,20 +1274,16 @@ function createSDKBridge(opts) {
1375
1274
  if (session.queryInstance) return { query: session.queryInstance, isTemp: false, cleanup: function() {} };
1376
1275
 
1377
1276
  var handle;
1277
+ var selectedAdapter = getAdapterForSession(session);
1378
1278
  try {
1379
- handle = await adapter.createQuery({
1279
+ handle = await selectedAdapter.createQuery({
1380
1280
  cwd: cwd,
1381
1281
  resumeSessionId: session.cliSessionId,
1382
1282
  env: getRuntimeEnv(session),
1383
- adapterOptions: {
1384
- CLAUDE: {
1385
- settingSources: ["user", "project", "local"],
1386
- enableFileCheckpointing: true,
1387
- },
1388
- },
1283
+ adapterOptions: require("./yoke/query-options").map(getAdapterForSession(session), { nativePermissionMode: "default" }),
1389
1284
  });
1390
1285
  } catch (e) {
1391
- sendAndRecord(session, { type: "error", text: "Failed to load Claude SDK: " + (e.message || e) });
1286
+ sendAndRecord(session, { type: "error", text: "Failed to load provider session: " + (e.message || e) });
1392
1287
  throw e;
1393
1288
  }
1394
1289
 
@@ -1455,7 +1350,7 @@ function createSDKBridge(opts) {
1455
1350
  // abort the whole rewind instead of leaving local history trimmed
1456
1351
  // ahead of the real, un-rolled-back provider thread.
1457
1352
  if (!turnId) {
1458
- throw new Error("No turn id recorded for this turn; cannot roll back the Codex thread.");
1353
+ throw new Error("No turn id recorded for this turn; cannot roll back the provider thread.");
1459
1354
  }
1460
1355
  await sessionAdapter.rollbackThread(session.cliSessionId, turnId, {
1461
1356
  linuxUser: getSessionLinuxUser(session) || undefined,
@@ -1471,6 +1366,8 @@ function createSDKBridge(opts) {
1471
1366
 
1472
1367
  async function forkSessionUnified(session, uuid) {
1473
1368
  var sessionAdapter = getAdapterForSession(session);
1369
+ var localHistoryEndIndex = require("./session-fork-history").endIndex(session, uuid,
1370
+ require("./yoke/provider-contract").resolve(sessionAdapter).forkHistoryBoundary);
1474
1371
  var result = await sessionAdapter.forkSession(session.cliSessionId, {
1475
1372
  upToMessageId: uuid,
1476
1373
  dir: cwd,
@@ -1479,8 +1376,10 @@ function createSDKBridge(opts) {
1479
1376
  if (!result || !result.sessionId) throw new Error("Fork returned no session id");
1480
1377
 
1481
1378
  // Adapters with rollbackThread (e.g. Codex) use local history copy
1482
- if (typeof sessionAdapter.rollbackThread === "function") {
1483
- return { sessionId: result.sessionId, useLocalHistory: true };
1379
+ if (result.useLocalHistory === true || typeof sessionAdapter.rollbackThread === "function") {
1380
+ var localFork = { sessionId: result.sessionId, useLocalHistory: true };
1381
+ if (localHistoryEndIndex !== undefined) localFork.localHistoryEndIndex = localHistoryEndIndex;
1382
+ return localFork;
1484
1383
  }
1485
1384
  // Claude: read history from CLI session files
1486
1385
  return { sessionId: result.sessionId, useLocalHistory: false };
@@ -1502,9 +1401,8 @@ function createSDKBridge(opts) {
1502
1401
  }
1503
1402
  if (responseRecovery.stopping(session) && !retryState) return false;
1504
1403
  delete session._lastTurnInterrupted;
1505
- var claude = (session.vendor || adapter.vendor) === "claude";
1506
1404
  var payload = { text: text, images: images, linuxUser: linuxUser, beforePush: beforePush };
1507
- var recoveryState = claude ? responseRecovery.begin(session, payload, retryState) : null;
1405
+ var recoveryState = responseRecovery.begin(session, payload, retryState);
1508
1406
  if (recoveryState) recoveryState.handle = null;
1509
1407
  var guardedPush = function() {
1510
1408
  return !session.destroying && !session.taskStopRequested
@@ -1548,7 +1446,7 @@ function createSDKBridge(opts) {
1548
1446
  return false;
1549
1447
  } finally {
1550
1448
  session._queryStarting = false;
1551
- delete session._codexRecoveryWaiting;
1449
+ delete session._providerRecoveryWaiting;
1552
1450
  if (!session.queryInstance && session.pendingPush && session.pendingPush.length) session.pendingPush = [];
1553
1451
  if (!session.queryInstance && !session.isProcessing && !session._pendingMessageDrainPaused && !responseRecovery.stopping(session)) session.taskStopRequested = false;
1554
1452
  if (!responseRecovery.stopping(session) && !session.isProcessing && typeof opts.onSessionAvailable === "function") opts.onSessionAvailable(session);
@@ -1747,7 +1645,8 @@ function createSDKBridge(opts) {
1747
1645
 
1748
1646
  // Ensure Linux user project directory exists (runs in parallel with worker boot)
1749
1647
  if (linuxUser) {
1750
- ensureLinuxUserProjectDir(linuxUser, session);
1648
+ var preparation = require("./yoke/provider-contract").resolve(sessionAdapter).prepareIsolation;
1649
+ if (typeof preparation === "function") preparation(linuxUser, session, cwd);
1751
1650
  }
1752
1651
 
1753
1652
  session.blocks = {};
@@ -1764,100 +1663,27 @@ function createSDKBridge(opts) {
1764
1663
  session.abortController = new AbortController();
1765
1664
  }
1766
1665
 
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
1666
  var ls = session.loopSettings || {};
1779
-
1780
- if (sm.currentBetas && sm.currentBetas.length > 0) {
1781
- claudeOpts.betas = sm.currentBetas;
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
1667
+ if (ls.permissionMode) session._loopPermissionMode = ls.permissionMode;
1668
+ var permissionMode = session.permissionMode || sm.currentPermissionMode || "default";
1669
+ var nativeMode = permissionMode === "bypassPermissions" ? "default" : session.acceptEditsAfterStart ? "acceptEdits" : permissionMode;
1670
+ var isolationHome = null;
1845
1671
  if (linuxUser) {
1846
- claudeOpts.linuxUser = linuxUser;
1847
- claudeOpts.singleTurn = !!session.singleTurn;
1848
- try {
1849
- claudeOpts.originalHome = require("./os-users").resolveOsUserInfo(linuxUser).home;
1850
- } catch (e) {
1851
- claudeOpts.originalHome = null;
1852
- }
1853
- claudeOpts.projectPath = session.cwd || null;
1854
- claudeOpts._perfT0 = t0;
1855
- // Pass previous worker state for reuse
1856
- if (session._adapterWorkerState) {
1857
- claudeOpts._workerState = session._adapterWorkerState;
1858
- session._adapterWorkerState = null;
1859
- }
1860
- }
1672
+ try { isolationHome = require("./os-users").resolveOsUserInfo(linuxUser).home; } catch (error) {}
1673
+ }
1674
+ var executionIntent = {
1675
+ settings: sm, permissionMode: permissionMode, nativePermissionMode: session._loopPermissionMode || nativeMode,
1676
+ nativeBypass: dangerouslySkipPermissions || session.mode === "tui" && session.dangerouslySkipPermissions,
1677
+ readOnly: !!session.youCurator, betas: sm.currentBetas,
1678
+ thinking: { mode: ls.thinking || sm.currentThinking, budget: ls.thinkingBudget || sm.currentThinkingBudget },
1679
+ disableHooks: ls.disableAllHooks, resumeAt: session.cliSessionId && session.lastRewindUuid || undefined,
1680
+ singleTurn: !!session.singleTurn, startedAt: t0,
1681
+ isolation: { user: linuxUser, home: isolationHome, projectPath: session.cwd || null, workerState: session._adapterWorkerState },
1682
+ sessionBridge: null,
1683
+ };
1684
+ if (session.acceptEditsAfterStart) delete session.acceptEditsAfterStart;
1685
+ if (executionIntent.resumeAt) { delete session.lastRewindUuid; sm.saveSessionFile(session); }
1686
+ if (linuxUser && session._adapterWorkerState) session._adapterWorkerState = null;
1861
1687
 
1862
1688
  // Pick a model from the session or its vendor-specific default, then
1863
1689
  // validate it against the session vendor's known model list.
@@ -1883,20 +1709,21 @@ function createSDKBridge(opts) {
1883
1709
  sm.saveSessionFile(session);
1884
1710
  }
1885
1711
 
1886
- var codexConfig = getCodexConfig(sm);
1887
- var kiroConfig = getKiroConfig(sm);
1888
1712
  session._sdkQueryGeneration = Number(session._sdkQueryGeneration || 0) + 1;
1889
1713
  // Individual feature modules own their transport capability checks. Do not
1890
1714
  // suppress unrelated session tools merely because one feature cannot be
1891
1715
  // delivered by this provider.
1892
1716
  var sessionToolDefs = getSessionToolDefs(session) || [];
1893
1717
  var queryGeneration = Number(session._sdkQueryGeneration || 0);
1718
+ executionIntent.sessionBridge = getAcpMcpServers(session);
1719
+ var queryContract = yoke.sessionTools.contract(sessionAdapter);
1720
+ function needsCatalogRecovery(family, version) {
1721
+ return yoke.sessionTools.needsRecovery(sessionAdapter, session, family, version);
1722
+ }
1894
1723
  var hasYouCatalog = sessionToolDefs.some(function (tool) { return tool.name === "search_user_knowledge"; });
1895
- var recoverCodexYouTools = sessionAdapter.vendor === "codex" && !!session.cliSessionId
1896
- && hasYouCatalog && Number(session.codexYouToolCatalogVersion || 0) < 1;
1724
+ var recoverYouTools = needsCatalogRecovery("knowledge", 1) && hasYouCatalog;
1897
1725
  var hasLinearCatalog = sessionToolDefs.some(function (tool) { return tool.name === "link_session_linear"; });
1898
- var recoverCodexLinearTools = sessionAdapter.vendor === "codex" && !!session.cliSessionId
1899
- && hasLinearCatalog && Number(session.codexLinearToolCatalogVersion || 0) < 1 && canUseSessionTools(session) === true;
1726
+ var recoverLinearTools = needsCatalogRecovery("linear", 1) && hasLinearCatalog && canUseSessionTools(session) === true;
1900
1727
  var hasIssueCatalog = false;
1901
1728
  for (var issueToolIndex = 0; issueToolIndex < sessionToolDefs.length; issueToolIndex++) {
1902
1729
  if (sessionToolDefs[issueToolIndex].name === "create_issue") {
@@ -1905,19 +1732,16 @@ function createSDKBridge(opts) {
1905
1732
  }
1906
1733
  }
1907
1734
  var hasSharedBrowserCatalog = sessionToolDefs.some(function (definition) { return definition.name === "shared_browser"; });
1908
- var recoverCodexBrowserTools = sessionAdapter.vendor === "codex" && !!session.cliSessionId
1909
- && hasSharedBrowserCatalog && Number(session.codexSharedBrowserToolCatalogVersion || 0) < 6;
1910
- var recoverCodexIssueTools = sessionAdapter.vendor === "codex"
1911
- && !!session.cliSessionId
1912
- && hasIssueCatalog
1913
- && Number(session.codexIssuesToolCatalogVersion || 0) < 1;
1735
+ var recoverBrowserTools = needsCatalogRecovery("browser", 6) && hasSharedBrowserCatalog;
1736
+ var recoverIssueTools = needsCatalogRecovery("issues", 1) && hasIssueCatalog;
1914
1737
  function queryContextAvailable() {
1738
+ if (queryRetired()) return false;
1915
1739
  if (Number(session._sdkQueryGeneration || 0) !== queryGeneration) return false;
1916
1740
  if (sm && sm.sessions && typeof sm.sessions.get === "function" && sm.sessions.get(session.localId) !== session) return false;
1917
1741
  return canUseSessionTools(session) === true;
1918
1742
  }
1919
1743
  var hasMateScheduleTool = sessionToolDefs.some(function (tool) { return yoke.sessionTools.MATE_SCHEDULE_TOOLS.indexOf(tool.name) !== -1; });
1920
- var recoverCodexMateSchedules = sessionAdapter.vendor === "codex" && !!session.cliSessionId && hasMateScheduleTool && queryContextAvailable();
1744
+ var recoverMateSchedules = needsCatalogRecovery() && hasMateScheduleTool && queryContextAvailable();
1921
1745
  var hasScheduleTool = false;
1922
1746
  var hasScheduleInterviewTool = false;
1923
1747
  var scheduleInterviewActive = null;
@@ -1934,10 +1758,10 @@ function createSDKBridge(opts) {
1934
1758
  var activeLoopInterview = !isDriverOperatedSession(session) && !session.loopInterviewHandoff && !activeRun && !!loopInterview.activeInterviewId(session);
1935
1759
  var structuredInputSession = hasScheduleInterviewTool || activeLoopInterview || (!isDriverOperatedSession(session) && session.ralphCraftingMode === true && session.loop && session.loop.role === "crafting");
1936
1760
  var requestedUserInputMode = session.debateSetupMode || session.mateCreationMode ? "fallback" : "auto";
1937
- if (structuredInputSession || sessionAdapter.vendor === "codex") requestedUserInputMode = "fallback";
1761
+ if (structuredInputSession) requestedUserInputMode = "fallback";
1762
+ if (requestedUserInputMode === "auto" && queryContract.defaultUserInputMode) requestedUserInputMode = queryContract.defaultUserInputMode;
1938
1763
  var selectedUserInputMode = yoke.userInput.selectMode(sessionAdapter, requestedUserInputMode);
1939
- var recoverCodexQuestionTools = sessionAdapter.vendor === "codex" && !!session.cliSessionId
1940
- && Number(session.codexUserInputToolCatalogVersion || 0) < 1 && queryContextAvailable();
1764
+ var recoverQuestionTools = needsCatalogRecovery("questions", 1) && queryContextAvailable();
1941
1765
  function structuredInputContextAvailable() {
1942
1766
  if (!queryContextAvailable()) return false;
1943
1767
  // A schedule-triggered fallback shares the schedule feature's live
@@ -1979,26 +1803,33 @@ function createSDKBridge(opts) {
1979
1803
  return opts.onUserInputRequest(session, request, respond);
1980
1804
  };
1981
1805
  var localMcpServers = getMcpServers(session);
1982
- if (sessionAdapter.vendor === "claude" && hasScheduleTool && typeof sessionAdapter.createToolServer === "function") {
1806
+ if (queryContract.toolTransport === "sdk-mcp" && (hasScheduleTool || hasMateScheduleTool)
1807
+ && typeof sessionAdapter.createToolServer !== "function") {
1808
+ throw new Error("The adapter cannot mount native schedule tools without createToolServer.");
1809
+ }
1810
+ if (queryContract.toolTransport === "sdk-mcp" && hasScheduleTool) {
1983
1811
  var scheduledTaskToolDefs = [];
1984
1812
  for (var scheduledToolIndex = 0; scheduledToolIndex < sessionToolDefs.length; scheduledToolIndex++) {
1985
1813
  if (yoke.sessionTools.SCHEDULE_TOOLS.indexOf(sessionToolDefs[scheduledToolIndex].name) !== -1) scheduledTaskToolDefs.push(sessionToolDefs[scheduledToolIndex]);
1986
1814
  }
1987
1815
  if (scheduledTaskToolDefs.length) {
1988
1816
  var scheduledTaskServer = sessionAdapter.createToolServer({ name: "clay-scheduled-tasks", version: "1.0.0", tools: scheduledTaskToolDefs });
1989
- if (scheduledTaskServer) {
1990
- localMcpServers = Object.assign({}, localMcpServers || {});
1991
- localMcpServers["clay-scheduled-tasks"] = scheduledTaskServer;
1992
- }
1817
+ if (!scheduledTaskServer) throw new Error("The adapter could not mount scheduled-task tools.");
1818
+ localMcpServers = Object.assign({}, localMcpServers || {});
1819
+ localMcpServers["clay-scheduled-tasks"] = scheduledTaskServer;
1993
1820
  }
1994
1821
  }
1995
- if (sessionAdapter.vendor === "claude" && hasMateScheduleTool && typeof sessionAdapter.createToolServer === "function") {
1822
+ if (queryContract.toolTransport === "sdk-mcp" && hasMateScheduleTool) {
1996
1823
  localMcpServers = Object.assign({}, localMcpServers || {});
1997
1824
  localMcpServers["clay-mate-schedules"] = sessionAdapter.createToolServer({ name: "clay-mate-schedules", version: "1.0.0",
1998
1825
  tools: sessionToolDefs.filter(function (tool) { return yoke.sessionTools.MATE_SCHEDULE_TOOLS.indexOf(tool.name) !== -1; }) });
1826
+ if (!localMcpServers["clay-mate-schedules"]) throw new Error("The adapter could not mount Mate schedule tools.");
1999
1827
  }
2000
1828
  if (selectedUserInputMode === "fallback") {
2001
1829
  var userInputServer = yoke.userInput.createFallbackServer(sessionAdapter, sessionUserInputHandler, { force: true, maxQuestions: scheduleQuestionLimit });
1830
+ if (queryContract.toolTransport === "sdk-mcp" && !userInputServer) {
1831
+ throw new Error("The adapter could not mount structured-input tools.");
1832
+ }
2002
1833
  if (userInputServer) {
2003
1834
  localMcpServers = Object.assign({}, localMcpServers || {});
2004
1835
  localMcpServers[userInputServer.name || "yoke-user-input"] = userInputServer;
@@ -2035,9 +1866,9 @@ function createSDKBridge(opts) {
2035
1866
 
2036
1867
  var externalQueryBridge = require('./mcp-query-bridge').createQueryBridge({ valid: queryContextAvailable,
2037
1868
  getServers: function () { return getRemoteMcpServers ? getRemoteMcpServers(session) || {} : {}; } });
2038
- var externalToolDefs = sessionAdapter.vendor === 'codex' ? externalQueryBridge.dynamicDefs() : [];
1869
+ var externalToolDefs = queryContract.externalToolTransport === "dynamic" ? externalQueryBridge.dynamicDefs() : [];
2039
1870
  if (externalToolDefs.length) { sessionToolDefs = sessionToolDefs.concat(externalToolDefs); queryToolSnapshots.set(session, { generation: queryGeneration, defs: sessionToolDefs.slice() }); }
2040
- var recoverCodexExternalTools = sessionAdapter.vendor === 'codex' && !!session.cliSessionId && (externalToolDefs.length > 0 || isMate);
1871
+ var recoverExternalTools = needsCatalogRecovery() && (externalToolDefs.length > 0 || isMate);
2041
1872
 
2042
1873
  // Give fresh queries an immediate provisional label. The visible user
2043
1874
  // message in history wins over `text`, which may contain an internal
@@ -2054,7 +1885,6 @@ function createSDKBridge(opts) {
2054
1885
 
2055
1886
  var modelSwitchGeneration = session._queryGeneration;
2056
1887
  function queryRetired() {
2057
- if (sessionAdapter.vendor !== "claude") return false;
2058
1888
  return Number(session._sdkQueryGeneration || 0) !== queryGeneration || session.taskStopRequested
2059
1889
  || session.destroying || responseRecovery.stopping(session) || (handle && session.queryInstance !== handle);
2060
1890
  }
@@ -2072,7 +1902,7 @@ function createSDKBridge(opts) {
2072
1902
  clayBridgeToken: clayBridgeToken,
2073
1903
  linuxUser: linuxUser || undefined,
2074
1904
  env: sessionRuntimeEnv,
2075
- skillOptions: getSessionSkillOptions(session),
1905
+ skillOptions: require("./session-skill-scope").bind(session, sm.sessions, getSessionSkillOptions(session), queryContextAvailable, usersModule.isMultiUser()),
2076
1906
  // appendSystemPrompt, NOT systemPrompt: a plain-string systemPrompt
2077
1907
  // REPLACES the entire Claude Code system prompt (sdk.d.ts:2096), which
2078
1908
  // would strip the driver session down to the two-sentence pair preset.
@@ -2080,7 +1910,7 @@ function createSDKBridge(opts) {
2080
1910
  // codex/kiro: prepend to the first message).
2081
1911
  appendSystemPrompt: [
2082
1912
  typeof opts.getSessionSystemPrompt === "function" ? opts.getSessionSystemPrompt(session) : "",
2083
- sessionAdapter.vendor === "codex" && queryContextAvailable() ? codexQuestionGuidance() : "",
1913
+ typeof queryContract.questionGuidance === "function" && queryContextAvailable() ? queryContract.questionGuidance() : "",
2084
1914
  ].filter(Boolean).join("\n\n") || undefined,
2085
1915
  model: queryModel,
2086
1916
  // Effort is remembered per vendor and clamped to the session vendor's
@@ -2097,21 +1927,22 @@ function createSDKBridge(opts) {
2097
1927
  toolServers: mergedMcpServers,
2098
1928
  toolServerDescriptors: extractMcpDescriptors(mergedMcpServers) || undefined,
2099
1929
  dynamicTools: sessionToolDefs,
2100
- // Codex persists dynamic tools with a thread. A stale authorized Driver
1930
+ // Conversation-scoped catalogs persist definitions. A stale authorized Driver
2101
1931
  // gets this exact-query bridge; its handlers still reauthorize live.
2102
- sessionMcpServer: (recoverCodexMateSchedules || recoverCodexIssueTools || recoverCodexQuestionTools || recoverCodexBrowserTools || recoverCodexLinearTools || recoverCodexYouTools || recoverCodexExternalTools) ? getCodexSessionMcpServer(session, queryGeneration) : undefined,
1932
+ sessionMcpServer: (recoverMateSchedules || recoverIssueTools || recoverQuestionTools || recoverBrowserTools || recoverLinearTools || recoverYouTools || recoverExternalTools) ? getSessionMcpServer(session, queryGeneration) : undefined,
2103
1933
  onRecoveryAdmission: function(waiting) {
2104
1934
  if (!queryContextAvailable()) return;
2105
- if (waiting) session._codexRecoveryWaiting = queryGeneration;
2106
- else delete session._codexRecoveryWaiting;
1935
+ if (waiting) session._providerRecoveryWaiting = queryGeneration;
1936
+ else delete session._providerRecoveryWaiting;
2107
1937
  },
2108
1938
  onRecoveryWaiting: function(waiting) {
2109
1939
  if (!queryContextAvailable()) return;
2110
1940
  sendAndRecord(session, { type: 'informational', level: 'info', content: waiting
2111
- ? 'Codex recovery is waiting for this conversation’s runtime to become safely available. Your message is retained; Stop cancels the wait.'
2112
- : 'Codex recovery is ready. Continuing your retained message.' });
1941
+ ? 'Tool recovery is waiting for this conversation’s runtime to become safely available. Your message is retained; Stop cancels the wait.'
1942
+ : 'Tool recovery is ready. Continuing your retained message.' });
2113
1943
  },
2114
1944
  callDynamicTool: function (toolName, args) {
1945
+ if (queryRetired() || !queryContextAvailable()) return Promise.reject(new Error("This session tool query is no longer available."));
2115
1946
  var toolDef = findSessionToolDef(toolName);
2116
1947
  if (toolDef && typeof toolDef.handler === "function") {
2117
1948
  return toolDef.handler(args || {});
@@ -2123,7 +1954,8 @@ function createSDKBridge(opts) {
2123
1954
  canUseTool: function(toolName, input, toolOpts) {
2124
1955
  if (queryRetired()) return { behavior: "deny", message: "This attempt has stopped." };
2125
1956
  responseRecovery.progress(session);
2126
- var nativeQuestionError = toolName === "AskUserQuestion" ? scheduleQuestionError(input) : null;
1957
+ var questionInput = yoke.userInput.toolQuestions(sessionAdapter, toolName, input);
1958
+ var nativeQuestionError = questionInput ? scheduleQuestionError(questionInput) : null;
2127
1959
  if (nativeQuestionError) return { behavior: "deny", message: nativeQuestionError };
2128
1960
  var permissionName = toolName;
2129
1961
  var permissionToolDef = findSessionToolDef(toolName);
@@ -2133,7 +1965,8 @@ function createSDKBridge(opts) {
2133
1965
  preToolUsePolicy: function(toolName, input) {
2134
1966
  if (queryRetired()) return Promise.resolve({ behavior: "deny", message: "This attempt has stopped." });
2135
1967
  responseRecovery.progress(session);
2136
- var nativeQuestionError = toolName === "AskUserQuestion" ? scheduleQuestionError(input) : null;
1968
+ var questionInput = yoke.userInput.toolQuestions(sessionAdapter, toolName, input);
1969
+ var nativeQuestionError = questionInput ? scheduleQuestionError(questionInput) : null;
2137
1970
  if (nativeQuestionError) return Promise.resolve({ behavior: "deny", message: nativeQuestionError });
2138
1971
  var permissionName = toolName;
2139
1972
  var permissionToolDef = findSessionToolDef(toolName);
@@ -2160,33 +1993,7 @@ function createSDKBridge(opts) {
2160
1993
  responseRecovery.progress(session);
2161
1994
  return callMcpToolHandler(mergedMcpServers, serverName, toolName, args);
2162
1995
  },
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
- },
1996
+ adapterOptions: require("./yoke/query-options").map(sessionAdapter, executionIntent),
2190
1997
  };
2191
1998
 
2192
1999
  if (recoveryState) {
@@ -2265,11 +2072,9 @@ function createSDKBridge(opts) {
2265
2072
 
2266
2073
  // Store QueryHandle on session for iteration and control.
2267
2074
  session.queryInstance = handle;
2268
- // Capabilities come from the installed query handle, never a model-name
2269
- // table. `acceptEdits` and MCP overrides are Claude SDK semantics; a
2270
- // vendor may expose the generic setter without implementing either.
2271
- session.permissionCapabilities = require("./session-permission-mode").capabilities(session, sm.defaultVendor);
2272
- session.permissionCapabilities.mcpOverride = sessionAdapter.vendor === "claude" && typeof handle.setMcpPermissionModeOverride === "function";
2075
+ // An installed setter and the adapter's explicit protocol contract are
2076
+ // both required; generic handle stubs do not establish runtime support.
2077
+ session.permissionCapabilities = require("./session-permission-mode").capabilities(session, sm.defaultVendor, sessionAdapter);
2273
2078
  session.effectivePermissionMode = null;
2274
2079
  sm.saveSessionFile(session);
2275
2080
  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 +2158,27 @@ function createSDKBridge(opts) {
2353
2158
  }
2354
2159
 
2355
2160
  session.lastActivityAt = Date.now();
2356
- var codexCatalogCandidate = null;
2357
- if (sessionAdapter.vendor === "codex" && !resumedAtQueryStart) {
2161
+ var catalogCandidate = null;
2162
+ if (queryContract.catalogLifetime === "conversation" && typeof queryContract.recordCatalog === "function" && !resumedAtQueryStart) {
2358
2163
  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
2164
  for (var catalogToolIndex = 0; catalogToolIndex < sessionToolDefs.length; catalogToolIndex++) {
2360
2165
  if (Object.prototype.hasOwnProperty.call(requiredCatalogTools, sessionToolDefs[catalogToolIndex].name)) requiredCatalogTools[sessionToolDefs[catalogToolIndex].name] = true;
2361
2166
  }
2362
2167
  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
2168
  if (completeInterviewCatalog || requiredCatalogTools.report_scheduled_task_outcome) {
2364
- codexCatalogCandidate = { generation: queryGeneration, version: yoke.sessionTools.CODEX_CATALOG_VERSION };
2365
- }
2366
- if (requiredCatalogTools.ask_user_questions) {
2367
- codexCatalogCandidate = Object.assign({}, codexCatalogCandidate || {}, { generation: queryGeneration, userInputVersion: 1 });
2169
+ catalogCandidate = { generation: queryGeneration, versions: { schedules: yoke.sessionTools.SCHEDULE_CATALOG_VERSION } };
2368
2170
  }
2369
- if (hasYouCatalog) codexCatalogCandidate = Object.assign({}, codexCatalogCandidate || {}, { generation: queryGeneration, youVersion: 1 });
2370
- if (hasLinearCatalog) codexCatalogCandidate = Object.assign({}, codexCatalogCandidate || {}, { generation: queryGeneration, linearVersion: 1 });
2371
- if (hasSharedBrowserCatalog) {
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
- });
2171
+ function markCatalog(family, version) {
2172
+ if (!catalogCandidate) catalogCandidate = { generation: queryGeneration, versions: {} };
2173
+ catalogCandidate.versions[family] = version;
2379
2174
  }
2175
+ if (requiredCatalogTools.ask_user_questions) markCatalog("questions", 1);
2176
+ if (hasYouCatalog) markCatalog("knowledge", 1);
2177
+ if (hasLinearCatalog) markCatalog("linear", 1);
2178
+ if (hasSharedBrowserCatalog) markCatalog("browser", 6);
2179
+ if (hasIssueCatalog) markCatalog("issues", 1);
2380
2180
  }
2381
- session.streamPromise = processQueryStream(session, codexCatalogCandidate).catch(function(err) {
2181
+ session.streamPromise = processQueryStream(session, catalogCandidate, queryContract).catch(function(err) {
2382
2182
  });
2383
2183
  return true;
2384
2184
  }
@@ -2392,7 +2192,7 @@ function createSDKBridge(opts) {
2392
2192
  function pushMessage(session, text, images, deliveryMeta) {
2393
2193
  if (session._responseRecoveryStopPending && !responseRecovery.stopping(session)) return false;
2394
2194
  function trackDelivery() {
2395
- if ((session.vendor || adapter.vendor) === "claude") responseRecovery.enqueue(session, {
2195
+ responseRecovery.enqueue(session, {
2396
2196
  text: text, images: images, linuxUser: session.lastLinuxUser || null,
2397
2197
  });
2398
2198
  }
@@ -2429,6 +2229,7 @@ function createSDKBridge(opts) {
2429
2229
  console.error("[sdk-bridge] QueryHandle rejected message for session " + session.localId + ":", e.message || e);
2430
2230
  }
2431
2231
  if (delivered) {
2232
+ require('./sdk-foreground-admission').take(session);
2432
2233
  trackDelivery();
2433
2234
  delete session._lastTurnInterrupted;
2434
2235
  if (session._awaitingTurnResult) {
@@ -2535,65 +2336,8 @@ function createSDKBridge(opts) {
2535
2336
  // Detect which vendor binaries are installed for this user.
2536
2337
  // In multi-user mode, runs checks as the specific Linux user.
2537
2338
  function detectInstalledVendors(linuxUser) {
2538
- var execFileSync = require("child_process").execFileSync;
2539
- var fs = require("fs");
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;
2339
+ var installed = require("./yoke/discovery").installed({ user: linuxUser });
2340
+ return Object.keys(installed).filter(function(vendor) { return installed[vendor]; });
2597
2341
  }
2598
2342
 
2599
2343
  function getAvailableVendors(linuxUser) {
@@ -2850,16 +2594,7 @@ function createSDKBridge(opts) {
2850
2594
  message: "Read-only access. You cannot make changes via @mention.",
2851
2595
  });
2852
2596
  },
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
- },
2597
+ adapterOptions: require("./yoke/query-options").map(mentionAdapter, { instructionSources: ["user"], readOnly: true }),
2863
2598
  });
2864
2599
  } catch (e) {
2865
2600
  opts.onError("Failed to create mention query: " + (e.message || e));
@@ -3004,7 +2739,8 @@ function createSDKBridge(opts) {
3004
2739
  setModel: setModel,
3005
2740
  setEffort: setEffort,
3006
2741
  setPermissionMode: setPermissionMode,
3007
- isClaudeProcess: isClaudeProcess,
2742
+ isProviderProcess: isProviderProcess,
2743
+ isClaudeProcess: isProviderProcess, // Compatibility export; production uses the provider contract.
3008
2744
  permissionPushTitle: permissionPushTitle,
3009
2745
  permissionPushBody: permissionPushBody,
3010
2746
  warmup: warmup,