clay-server 5.2.0-beta.1 → 5.2.0-beta.11

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 (608) hide show
  1. package/README.md +0 -4
  2. package/lib/account-auth-socket.js +1 -0
  3. package/lib/builtin-mates.js +19 -16
  4. package/lib/bundled-skills/clay-agent-browser/SKILL.md +6 -2
  5. package/lib/canonical-clay-record-authority.js +15 -0
  6. package/lib/canonical-clay-scheduling-authority.js +19 -0
  7. package/lib/claude-hook-installer.js +1 -239
  8. package/lib/claude-jsonl-watcher.js +1 -204
  9. package/lib/clay-control-presentation-binding.js +96 -0
  10. package/lib/clay-control-presentation.js +128 -0
  11. package/lib/clay-control-target-projection.js +26 -0
  12. package/lib/clay-mate-chat-receipts.js +26 -0
  13. package/lib/clay-mate-chat.js +88 -0
  14. package/lib/clay-mate-knowledge-input.js +26 -0
  15. package/lib/clay-mate-knowledge.js +95 -0
  16. package/lib/clay-mate-lifecycle.js +95 -0
  17. package/lib/clay-product-control-input.js +33 -0
  18. package/lib/clay-product-control-tools.js +52 -0
  19. package/lib/clay-product-control.js +250 -0
  20. package/lib/clay-project-lifecycle.js +154 -0
  21. package/lib/clay-project-record-input.js +44 -0
  22. package/lib/clay-project-records.js +84 -0
  23. package/lib/clay-scheduling-input.js +21 -0
  24. package/lib/clay-scheduling-receipts.js +26 -0
  25. package/lib/clay-scheduling.js +119 -0
  26. package/lib/clay-screen-controls.js +104 -0
  27. package/lib/clay-skill-install.js +1 -33
  28. package/lib/clay-vendor-usage.js +44 -0
  29. package/lib/clay-workspace-controls.js +84 -0
  30. package/lib/clay-workspace-input.js +17 -0
  31. package/lib/clay-workspace-receipts.js +26 -0
  32. package/lib/cli-sessions.js +1 -300
  33. package/lib/codex-defaults.js +1 -28
  34. package/lib/context-usage.js +3 -2
  35. package/lib/contextual-screen-snapshot.js +38 -0
  36. package/lib/daemon-project-access.js +11 -0
  37. package/lib/daemon-project-lifecycle-callbacks.js +15 -0
  38. package/lib/daemon-project-lifecycle.js +262 -0
  39. package/lib/daemon-projects.js +1 -1
  40. package/lib/daemon.js +78 -276
  41. package/lib/default-ai-runtime.js +4 -8
  42. package/lib/dm-digest-cursor.js +120 -0
  43. package/lib/email-mcp-server.js +1 -1
  44. package/lib/github-star-count.js +147 -0
  45. package/lib/issues-service.js +29 -15
  46. package/lib/kiro-defaults.js +1 -22
  47. package/lib/knowledge-db-import-validation.js +46 -0
  48. package/lib/knowledge-db-import.js +99 -0
  49. package/lib/knowledge-db-service.js +24 -33
  50. package/lib/knowledge-db-storage-nedb-maintenance.js +99 -0
  51. package/lib/knowledge-db-storage-nedb.js +66 -0
  52. package/lib/knowledge-db-storage.js +72 -0
  53. package/lib/knowledge-db-validation.js +0 -0
  54. package/lib/knowledge-document-index.js +2 -1
  55. package/lib/knowledge-file-store.js +7 -1
  56. package/lib/knowledge-product-operations.js +46 -0
  57. package/lib/mate-avatar-catalog.js +12 -0
  58. package/lib/mate-avatar-migration.js +41 -0
  59. package/lib/mate-context-service.js +138 -0
  60. package/lib/mate-context-tools.js +21 -0
  61. package/lib/mate-creation-mcp-server.js +2 -0
  62. package/lib/mate-knowledge-canonical-access.js +10 -0
  63. package/lib/mate-knowledge-mate-bound.js +53 -0
  64. package/lib/mate-knowledge-mcp-server.js +55 -8
  65. package/lib/mate-knowledge-service.js +16 -34
  66. package/lib/mate-knowledge-text-tools.js +106 -0
  67. package/lib/mate-lifecycle-service.js +94 -0
  68. package/lib/mate-message-delivery.js +43 -0
  69. package/lib/mate-ready-creation.js +9 -1
  70. package/lib/mate-schedule-input.js +29 -0
  71. package/lib/mate-schedule-lifecycle.js +151 -0
  72. package/lib/mate-scheduling.js +103 -0
  73. package/lib/mates.js +18 -6
  74. package/lib/mcp-agent-tools.js +117 -0
  75. package/lib/mcp-connection-store.js +119 -15
  76. package/lib/mcp-import-credentials.js +31 -0
  77. package/lib/mcp-local-transport.js +13 -0
  78. package/lib/mcp-oauth.js +8 -6
  79. package/lib/mcp-proxy.js +2 -32
  80. package/lib/mcp-query-bridge.js +30 -0
  81. package/lib/mcp-remote-connections.js +74 -20
  82. package/lib/mcp-source-adapters.js +1 -0
  83. package/lib/mcp-source-context.js +1 -0
  84. package/lib/mcp-source-discovery.js +1 -0
  85. package/lib/mcp-source-plugins.js +1 -0
  86. package/lib/mcp-source-restrictions.js +1 -0
  87. package/lib/mcp-tool-policy.js +9 -0
  88. package/lib/notes-checklist.js +92 -0
  89. package/lib/notes.js +51 -4
  90. package/lib/os-user-diagnostics.js +2 -4
  91. package/lib/os-users.js +24 -70
  92. package/lib/pane-pending-interactions.js +20 -0
  93. package/lib/pane-session-activation.js +114 -0
  94. package/lib/pane-session-transport.js +26 -0
  95. package/lib/project-admin-overview.js +51 -0
  96. package/lib/project-ask-user.js +8 -4
  97. package/lib/project-connection.js +14 -10
  98. package/lib/project-control-git-view.js +12 -0
  99. package/lib/project-debate.js +1 -0
  100. package/lib/project-delegated-follow-up.js +2 -1
  101. package/lib/project-directory-claim.js +65 -0
  102. package/lib/project-file-operations.js +61 -0
  103. package/lib/project-filesystem.js +15 -85
  104. package/lib/project-full-access.js +23 -2
  105. package/lib/project-http.js +7 -4
  106. package/lib/project-knowledge.js +32 -18
  107. package/lib/project-lifecycle-operations.js +98 -0
  108. package/lib/project-lifecycle-process-witness.js +32 -0
  109. package/lib/project-lifecycle-runtime.js +50 -0
  110. package/lib/project-linux-access.js +22 -0
  111. package/lib/project-logs-canonical-clay.js +27 -0
  112. package/lib/project-logs-clay-query.js +16 -0
  113. package/lib/project-logs-schema.js +4 -2
  114. package/lib/project-logs-service.js +8 -7
  115. package/lib/project-logs.js +1 -0
  116. package/lib/project-loop.js +1 -0
  117. package/lib/project-mate-chat-control.js +55 -0
  118. package/lib/project-mate-creation-proposal.js +18 -3
  119. package/lib/project-mate-interaction.js +40 -62
  120. package/lib/project-mcp.js +54 -16
  121. package/lib/project-models.js +14 -31
  122. package/lib/project-notifications.js +76 -17
  123. package/lib/project-ownership.js +77 -0
  124. package/lib/project-pair-result-delivery.js +14 -3
  125. package/lib/project-pair-usage.js +12 -13
  126. package/lib/project-pending-message-queue.js +4 -1
  127. package/lib/project-product-control.js +89 -0
  128. package/lib/project-product-session-queue.js +46 -0
  129. package/lib/project-push-access.js +28 -0
  130. package/lib/project-scheduled-message-queue.js +30 -0
  131. package/lib/project-scheduled-messages.js +67 -68
  132. package/lib/project-scheduled-task-control.js +83 -0
  133. package/lib/project-scheduled-tasks.js +13 -2
  134. package/lib/project-scheduling-access.js +10 -0
  135. package/lib/project-session-actions.js +65 -0
  136. package/lib/project-session-delete.js +8 -1
  137. package/lib/project-session-document.js +27 -7
  138. package/lib/project-session-github.js +63 -19
  139. package/lib/project-session-history-actions.js +167 -0
  140. package/lib/project-session-linear.js +14 -6
  141. package/lib/project-session-notes.js +40 -11
  142. package/lib/project-session-pair.js +1 -1
  143. package/lib/project-session-runtime-actions.js +64 -0
  144. package/lib/project-session-spawn.js +2 -3
  145. package/lib/project-session-vendor.js +55 -0
  146. package/lib/project-sessions.js +160 -290
  147. package/lib/project-shared-browser.js +78 -17
  148. package/lib/project-start-protocol.js +29 -0
  149. package/lib/project-studio-presentation.js +58 -0
  150. package/lib/project-user-message.js +67 -1
  151. package/lib/project-vendor-login.js +8 -36
  152. package/lib/project-workspace-operations.js +126 -0
  153. package/lib/project-workspace-query.js +19 -2
  154. package/lib/project.js +181 -45
  155. package/lib/public/app.js +71 -17
  156. package/lib/public/appearance-bootstrap.js +16 -0
  157. package/lib/public/css/clay-tool-activity.css +51 -0
  158. package/lib/public/css/clay-workspace.css +29 -0
  159. package/lib/public/css/command-palette.css +12 -33
  160. package/lib/public/css/contextual-clay.css +240 -0
  161. package/lib/public/css/filebrowser.css +1 -1
  162. package/lib/public/css/home-debate-planning.css +2 -0
  163. package/lib/public/css/home-mate-creation.css +5 -0
  164. package/lib/public/css/input.css +6 -0
  165. package/lib/public/css/mate-avatar-gallery.css +12 -0
  166. package/lib/public/css/mate-conversation.css +10 -5
  167. package/lib/public/css/mate-loading-scene.css +21 -0
  168. package/lib/public/css/messages.css +6 -2
  169. package/lib/public/css/overlays.css +68 -258
  170. package/lib/public/css/project-admin-overview.css +13 -0
  171. package/lib/public/css/project-mate-creation.css +5 -6
  172. package/lib/public/css/project-start.css +108 -0
  173. package/lib/public/css/projects-hub.css +53 -14
  174. package/lib/public/css/session-folders.css +11 -0
  175. package/lib/public/css/session-github.css +46 -1
  176. package/lib/public/css/settings-shell.css +103 -0
  177. package/lib/public/css/shared-browser.css +35 -1
  178. package/lib/public/css/sidebar.css +7 -0
  179. package/lib/public/css/sticky-notes.css +87 -1
  180. package/lib/public/css/title-bar.css +79 -0
  181. package/lib/public/index.html +61 -68
  182. package/lib/public/mate-avatars/B01.png +0 -0
  183. package/lib/public/mate-avatars/B02.png +0 -0
  184. package/lib/public/mate-avatars/B03.png +0 -0
  185. package/lib/public/mate-avatars/B04.png +0 -0
  186. package/lib/public/mate-avatars/B05.png +0 -0
  187. package/lib/public/mate-avatars/B06.png +0 -0
  188. package/lib/public/mate-avatars/B07.png +0 -0
  189. package/lib/public/mate-avatars/B08.png +0 -0
  190. package/lib/public/mate-avatars/B09.png +0 -0
  191. package/lib/public/mate-avatars/B10.png +0 -0
  192. package/lib/public/mate-avatars/B11.png +0 -0
  193. package/lib/public/mate-avatars/B12.png +0 -0
  194. package/lib/public/mate-avatars/B13.png +0 -0
  195. package/lib/public/mate-avatars/B14.png +0 -0
  196. package/lib/public/mate-avatars/B15.png +0 -0
  197. package/lib/public/mate-avatars/B16.png +0 -0
  198. package/lib/public/mate-avatars/B17.png +0 -0
  199. package/lib/public/mate-avatars/B18.png +0 -0
  200. package/lib/public/mate-avatars/B19.png +0 -0
  201. package/lib/public/mate-avatars/B20.png +0 -0
  202. package/lib/public/mate-avatars/B21.png +0 -0
  203. package/lib/public/mate-avatars/B22.png +0 -0
  204. package/lib/public/mate-avatars/B23.png +0 -0
  205. package/lib/public/mate-avatars/B24.png +0 -0
  206. package/lib/public/mate-avatars/B25.png +0 -0
  207. package/lib/public/mate-avatars/B26.png +0 -0
  208. package/lib/public/mate-avatars/B27.png +0 -0
  209. package/lib/public/mate-avatars/B28.png +0 -0
  210. package/lib/public/mate-avatars/B29.png +0 -0
  211. package/lib/public/mate-avatars/B30.png +0 -0
  212. package/lib/public/mate-avatars/B31.png +0 -0
  213. package/lib/public/mate-avatars/B32.png +0 -0
  214. package/lib/public/mate-avatars/B33.png +0 -0
  215. package/lib/public/mate-avatars/B34.png +0 -0
  216. package/lib/public/mate-avatars/B35.png +0 -0
  217. package/lib/public/mate-avatars/B36.png +0 -0
  218. package/lib/public/mate-avatars/B37.png +0 -0
  219. package/lib/public/mate-avatars/B38.png +0 -0
  220. package/lib/public/mate-avatars/B39.png +0 -0
  221. package/lib/public/mate-avatars/B40.png +0 -0
  222. package/lib/public/mate-avatars/B41.png +0 -0
  223. package/lib/public/mate-avatars/B42.png +0 -0
  224. package/lib/public/mate-avatars/B43.png +0 -0
  225. package/lib/public/mate-avatars/B44.png +0 -0
  226. package/lib/public/mate-avatars/B45.png +0 -0
  227. package/lib/public/mate-avatars/B46.png +0 -0
  228. package/lib/public/mate-avatars/B47.png +0 -0
  229. package/lib/public/mate-avatars/B48.png +0 -0
  230. package/lib/public/mate-avatars/C01.png +0 -0
  231. package/lib/public/mate-avatars/C02.png +0 -0
  232. package/lib/public/mate-avatars/C03.png +0 -0
  233. package/lib/public/mate-avatars/C04.png +0 -0
  234. package/lib/public/mate-avatars/C05.png +0 -0
  235. package/lib/public/mate-avatars/C06.png +0 -0
  236. package/lib/public/mate-avatars/C07.png +0 -0
  237. package/lib/public/mate-avatars/C08.png +0 -0
  238. package/lib/public/mate-avatars/C09.png +0 -0
  239. package/lib/public/mate-avatars/C10.png +0 -0
  240. package/lib/public/mate-avatars/C11.png +0 -0
  241. package/lib/public/mate-avatars/C12.png +0 -0
  242. package/lib/public/mate-avatars/C13.png +0 -0
  243. package/lib/public/mate-avatars/C14.png +0 -0
  244. package/lib/public/mate-avatars/C15.png +0 -0
  245. package/lib/public/mate-avatars/C16.png +0 -0
  246. package/lib/public/mate-avatars/C17.png +0 -0
  247. package/lib/public/mate-avatars/C18.png +0 -0
  248. package/lib/public/mate-avatars/C19.png +0 -0
  249. package/lib/public/mate-avatars/C20.png +0 -0
  250. package/lib/public/mate-avatars/C21.png +0 -0
  251. package/lib/public/mate-avatars/C22.png +0 -0
  252. package/lib/public/mate-avatars/C23.png +0 -0
  253. package/lib/public/mate-avatars/C24.png +0 -0
  254. package/lib/public/mate-avatars/C25.png +0 -0
  255. package/lib/public/mate-avatars/C26.png +0 -0
  256. package/lib/public/mate-avatars/C27.png +0 -0
  257. package/lib/public/mate-avatars/C28.png +0 -0
  258. package/lib/public/mate-avatars/C29.png +0 -0
  259. package/lib/public/mate-avatars/C30.png +0 -0
  260. package/lib/public/mate-avatars/C31.png +0 -0
  261. package/lib/public/mate-avatars/C32.png +0 -0
  262. package/lib/public/mate-avatars/C33.png +0 -0
  263. package/lib/public/mate-avatars/C34.png +0 -0
  264. package/lib/public/mate-avatars/C35.png +0 -0
  265. package/lib/public/mate-avatars/C36.png +0 -0
  266. package/lib/public/mate-avatars/C37.png +0 -0
  267. package/lib/public/mate-avatars/C38.png +0 -0
  268. package/lib/public/mate-avatars/C39.png +0 -0
  269. package/lib/public/mate-avatars/C40.png +0 -0
  270. package/lib/public/mate-avatars/C41.png +0 -0
  271. package/lib/public/mate-avatars/C42.png +0 -0
  272. package/lib/public/mate-avatars/C43.png +0 -0
  273. package/lib/public/mate-avatars/C44.png +0 -0
  274. package/lib/public/mate-avatars/C45.png +0 -0
  275. package/lib/public/mate-avatars/C46.png +0 -0
  276. package/lib/public/mate-avatars/C47.png +0 -0
  277. package/lib/public/mate-avatars/C48.png +0 -0
  278. package/lib/public/mate-avatars/catalog.json +482 -0
  279. package/lib/public/mate-avatars/lead-clay.png +0 -0
  280. package/lib/public/modules/admin-settings-invitations.js +2 -2
  281. package/lib/public/modules/admin-settings-projects.js +8 -4
  282. package/lib/public/modules/admin-settings-ui.js +20 -4
  283. package/lib/public/modules/admin-settings-users.js +4 -4
  284. package/lib/public/modules/app-connection.js +11 -1
  285. package/lib/public/modules/app-favicon.js +18 -9
  286. package/lib/public/modules/app-header.js +7 -4
  287. package/lib/public/modules/app-home-hub.js +2 -1
  288. package/lib/public/modules/app-loop-ui.js +2 -0
  289. package/lib/public/modules/app-message-router.js +22 -1
  290. package/lib/public/modules/app-messages.js +125 -19
  291. package/lib/public/modules/app-misc.js +3 -1
  292. package/lib/public/modules/app-notifications.js +11 -5
  293. package/lib/public/modules/app-panels.js +61 -123
  294. package/lib/public/modules/app-projects.js +14 -407
  295. package/lib/public/modules/app-rate-limit.js +19 -28
  296. package/lib/public/modules/app-rendering.js +4 -3
  297. package/lib/public/modules/avatar.js +15 -41
  298. package/lib/public/modules/chat-render-runtime.js +20 -0
  299. package/lib/public/modules/clay-browser-links.js +47 -0
  300. package/lib/public/modules/clay-control-activity.js +214 -0
  301. package/lib/public/modules/clay-control-copy.js +36 -0
  302. package/lib/public/modules/clay-control-detail-targets.js +44 -0
  303. package/lib/public/modules/clay-control-object-targets.js +52 -0
  304. package/lib/public/modules/clay-control-target-visibility.js +12 -0
  305. package/lib/public/modules/clay-control-targets.js +44 -0
  306. package/lib/public/modules/clay-log-links.js +23 -0
  307. package/lib/public/modules/clay-navigation-render.js +52 -0
  308. package/lib/public/modules/clay-owner-navigation.js +33 -0
  309. package/lib/public/modules/clay-session-links.js +31 -0
  310. package/lib/public/modules/clay-studio-bridge.js +20 -0
  311. package/lib/public/modules/clay-studio-document.js +67 -0
  312. package/lib/public/modules/clay-studio-render.js +146 -0
  313. package/lib/public/modules/clay-tool-activity.js +140 -0
  314. package/lib/public/modules/clay-tool-presentation.js +343 -0
  315. package/lib/public/modules/command-palette.js +162 -40
  316. package/lib/public/modules/contextual-ask-session.js +26 -0
  317. package/lib/public/modules/contextual-clay-geometry.js +155 -0
  318. package/lib/public/modules/contextual-clay-session.js +130 -0
  319. package/lib/public/modules/contextual-clay-workspace.js +169 -0
  320. package/lib/public/modules/contextual-clay.js +252 -0
  321. package/lib/public/modules/contextual-composer.js +91 -0
  322. package/lib/public/modules/contextual-mate-context.js +117 -0
  323. package/lib/public/modules/contextual-mate-hint.js +40 -0
  324. package/lib/public/modules/contextual-mate-identity.js +23 -0
  325. package/lib/public/modules/contextual-mate-sessions.js +134 -0
  326. package/lib/public/modules/contextual-search.js +137 -0
  327. package/lib/public/modules/contextual-session-citations.js +58 -0
  328. package/lib/public/modules/debate.js +1 -0
  329. package/lib/public/modules/default-ai.js +11 -3
  330. package/lib/public/modules/elicitation-form.js +5 -0
  331. package/lib/public/modules/filebrowser.js +20 -1
  332. package/lib/public/modules/generation-stop.js +8 -5
  333. package/lib/public/modules/git-panel.js +3 -1
  334. package/lib/public/modules/github-star.js +66 -0
  335. package/lib/public/modules/history-replay-batch.js +13 -1
  336. package/lib/public/modules/home-debate-live.js +2 -0
  337. package/lib/public/modules/home-debate-planning.js +7 -7
  338. package/lib/public/modules/home-mate-avatar-editor.js +127 -208
  339. package/lib/public/modules/home-mate-chat.js +8 -6
  340. package/lib/public/modules/home-mate-creation.js +73 -1
  341. package/lib/public/modules/home-mate-settings.js +19 -12
  342. package/lib/public/modules/icon-strip-tooltip.js +2 -1
  343. package/lib/public/modules/input.js +88 -10
  344. package/lib/public/modules/issues-render.js +3 -2
  345. package/lib/public/modules/knowledge-db-workbench.js +4 -4
  346. package/lib/public/modules/markdown.js +6 -34
  347. package/lib/public/modules/mate-avatar-catalog.js +97 -0
  348. package/lib/public/modules/mate-knowledge-workbench.js +1 -1
  349. package/lib/public/modules/mate-loading-scene.js +17 -0
  350. package/lib/public/modules/mate-management.js +40 -7
  351. package/lib/public/modules/mcp-connection-view.js +31 -13
  352. package/lib/public/modules/mcp-discovery-view.js +31 -0
  353. package/lib/public/modules/mcp-ui.js +23 -4
  354. package/lib/public/modules/mention.js +3 -0
  355. package/lib/public/modules/message-delivery.js +20 -2
  356. package/lib/public/modules/model-picker.js +2 -0
  357. package/lib/public/modules/pane-activation.js +214 -0
  358. package/lib/public/modules/pane-binding.js +82 -0
  359. package/lib/public/modules/pane-bridge.js +7 -4
  360. package/lib/public/modules/pane-browser-bridge.js +22 -0
  361. package/lib/public/modules/pane-file-bridge.js +4 -0
  362. package/lib/public/modules/pane-issue-bridge.js +4 -1
  363. package/lib/public/modules/pending-message-queue.js +1 -0
  364. package/lib/public/modules/permission-control.js +5 -4
  365. package/lib/public/modules/personal-settings-dialog.js +7 -1
  366. package/lib/public/modules/product-review.js +26 -0
  367. package/lib/public/modules/profile.js +12 -241
  368. package/lib/public/modules/project-access-recovery.js +17 -0
  369. package/lib/public/modules/project-admin-overview.js +21 -0
  370. package/lib/public/modules/project-chat-identity.js +13 -4
  371. package/lib/public/modules/project-logs-render.js +3 -0
  372. package/lib/public/modules/project-mate-creation-workspace.js +13 -4
  373. package/lib/public/modules/project-mate-navigation.js +144 -22
  374. package/lib/public/modules/project-session-visibility-settings.js +19 -16
  375. package/lib/public/modules/project-settings.js +119 -129
  376. package/lib/public/modules/project-start-dialog.js +130 -0
  377. package/lib/public/modules/project-start-folder-state.js +78 -0
  378. package/lib/public/modules/project-start-folder-view.js +190 -0
  379. package/lib/public/modules/project-start-forms.js +66 -0
  380. package/lib/public/modules/project-start-path.js +60 -0
  381. package/lib/public/modules/project-start-state.js +54 -0
  382. package/lib/public/modules/project-start-submit.js +67 -0
  383. package/lib/public/modules/projects-hub-view.js +33 -14
  384. package/lib/public/modules/projects-hub.js +10 -6
  385. package/lib/public/modules/rewind.js +5 -0
  386. package/lib/public/modules/scheduled-tasks.js +2 -0
  387. package/lib/public/modules/scheduler.js +2 -0
  388. package/lib/public/modules/search-clay-chat.js +5 -440
  389. package/lib/public/modules/server-settings.js +36 -91
  390. package/lib/public/modules/session-actions.js +5 -2
  391. package/lib/public/modules/session-context-menu.js +5 -0
  392. package/lib/public/modules/session-create-form.js +10 -10
  393. package/lib/public/modules/session-folder-layout.js +1 -1
  394. package/lib/public/modules/session-folder-view-menu.js +9 -0
  395. package/lib/public/modules/session-github.js +85 -14
  396. package/lib/public/modules/session-rich-drafts.js +21 -0
  397. package/lib/public/modules/session-switch-defer.js +67 -0
  398. package/lib/public/modules/session-work-primary.js +51 -0
  399. package/lib/public/modules/settings-defaults.js +27 -28
  400. package/lib/public/modules/settings-project-target.js +56 -0
  401. package/lib/public/modules/settings-select.js +1 -1
  402. package/lib/public/modules/settings-shell-registry.js +88 -0
  403. package/lib/public/modules/settings-shell-view.js +104 -0
  404. package/lib/public/modules/settings-shell.js +342 -0
  405. package/lib/public/modules/shared-browser-settings.js +112 -0
  406. package/lib/public/modules/shared-browser-tabs.js +13 -3
  407. package/lib/public/modules/shared-browser.js +29 -6
  408. package/lib/public/modules/sidebar-mobile.js +12 -33
  409. package/lib/public/modules/sidebar-projects.js +3 -1
  410. package/lib/public/modules/sidebar-session-folders-ui.js +1 -1
  411. package/lib/public/modules/sidebar-sessions.js +1 -0
  412. package/lib/public/modules/split-group-restore.js +68 -0
  413. package/lib/public/modules/split-pair-ui.js +1 -0
  414. package/lib/public/modules/split-pane-reconciler.js +15 -0
  415. package/lib/public/modules/split-pane-renderer.js +87 -6
  416. package/lib/public/modules/split-session-boundary.js +1 -1
  417. package/lib/public/modules/split-view.js +60 -68
  418. package/lib/public/modules/sticky-note-markdown.js +72 -6
  419. package/lib/public/modules/sticky-notes-actions.js +30 -0
  420. package/lib/public/modules/sticky-notes-browser.js +9 -2
  421. package/lib/public/modules/sticky-notes-card.js +23 -7
  422. package/lib/public/modules/sticky-notes-checklist.js +116 -0
  423. package/lib/public/modules/sticky-notes-editor.js +108 -44
  424. package/lib/public/modules/sticky-notes-shared.js +50 -2
  425. package/lib/public/modules/sticky-notes-state.js +66 -0
  426. package/lib/public/modules/sticky-notes.js +34 -51
  427. package/lib/public/modules/stt-controller.js +4 -1
  428. package/lib/public/modules/stt-recording.js +1 -1
  429. package/lib/public/modules/stt.js +7 -2
  430. package/lib/public/modules/terminal.js +9 -0
  431. package/lib/public/modules/theme.js +18 -3
  432. package/lib/public/modules/thinking-lifecycle.js +33 -3
  433. package/lib/public/modules/thinking-view.js +23 -1
  434. package/lib/public/modules/tools.js +60 -11
  435. package/lib/public/modules/tui-grab.js +1 -2
  436. package/lib/public/modules/user-settings-appearance.js +10 -3
  437. package/lib/public/modules/user-settings-editors.js +1 -1
  438. package/lib/public/modules/user-settings-layout.js +14 -30
  439. package/lib/public/modules/user-settings-preferences.js +6 -2
  440. package/lib/public/modules/user-settings-profile.js +13 -3
  441. package/lib/public/modules/user-settings.js +18 -84
  442. package/lib/public/modules/vendor-login-browser.js +5 -3
  443. package/lib/public/modules/vendor-login.js +2 -2
  444. package/lib/public/modules/vendor-priority.js +15 -1
  445. package/lib/public/modules/vendor-usage-format.js +93 -0
  446. package/lib/public/modules/vendor-usage-list.js +45 -0
  447. package/lib/public/modules/vendor-usage-model.js +215 -0
  448. package/lib/public/modules/vendor-usage-overview.js +312 -0
  449. package/lib/public/modules/wireframe-workbench.js +5 -2
  450. package/lib/public/muse-avatar.svg +4 -0
  451. package/lib/public/style.css +14 -6
  452. package/lib/public/sw.js +3 -1
  453. package/lib/public/welcome-art/conversation-desk.png +0 -0
  454. package/lib/public/welcome-art/mate-clay.png +0 -0
  455. package/lib/public/welcome-art/project-clay.png +0 -0
  456. package/lib/safe-bash-commands.js +3 -45
  457. package/lib/scheduled-task-records.js +3 -1
  458. package/lib/scheduler.js +2 -0
  459. package/lib/sdk-bridge.js +348 -402
  460. package/lib/sdk-foreground-admission.js +16 -0
  461. package/lib/sdk-mcp-descriptors.js +2 -37
  462. package/lib/sdk-message-processor.js +61 -18
  463. package/lib/sdk-quota-snapshot.js +21 -3
  464. package/lib/sdk-response-recovery.js +1 -0
  465. package/lib/sdk-worker.js +2 -677
  466. package/lib/server-admin.js +63 -16
  467. package/lib/server-appearance-settings.js +13 -1
  468. package/lib/server-contextual-clay-settings.js +40 -0
  469. package/lib/server-contextual-mate-sessions.js +70 -0
  470. package/lib/server-dm.js +2 -1
  471. package/lib/server-github-stars.js +39 -0
  472. package/lib/server-global-ws.js +26 -32
  473. package/lib/server-home-chat-events.js +18 -4
  474. package/lib/server-home-chat.js +22 -20
  475. package/lib/server-home-clay-entry.js +23 -4
  476. package/lib/server-home-clay-session-links.js +29 -9
  477. package/lib/server-home-mate-creation.js +1 -0
  478. package/lib/server-home-mcp-consent.js +14 -0
  479. package/lib/server-mates.js +25 -62
  480. package/lib/server-owner-ui.js +118 -0
  481. package/lib/server-path-access.js +38 -0
  482. package/lib/server-project-removal.js +22 -0
  483. package/lib/server-settings.js +6 -1
  484. package/lib/server.js +167 -43
  485. package/lib/session-config-transition.js +45 -0
  486. package/lib/session-folders.js +7 -2
  487. package/lib/session-fork-history.js +21 -0
  488. package/lib/session-github.js +9 -2
  489. package/lib/session-hygiene.js +3 -35
  490. package/lib/session-notes-mcp-server.js +26 -4
  491. package/lib/session-notes-writes.js +88 -0
  492. package/lib/session-permission-mode.js +4 -3
  493. package/lib/session-skill-scope.js +11 -0
  494. package/lib/session-visibility.js +25 -10
  495. package/lib/session-work-primary.js +50 -0
  496. package/lib/sessions.js +66 -99
  497. package/lib/shared-browser-cookie-sync.js +32 -0
  498. package/lib/shared-browser-cookies.js +79 -0
  499. package/lib/shared-browser-runtime.js +25 -9
  500. package/lib/shared-browser-storage-sync.js +43 -0
  501. package/lib/shared-browser-storage.js +101 -0
  502. package/lib/shared-browser-tools.js +2 -1
  503. package/lib/shared-browser-worker.js +46 -10
  504. package/lib/terminal-manager.js +65 -6
  505. package/lib/tool-llm.js +1 -1
  506. package/lib/tui-transcript-index.js +1 -132
  507. package/lib/users-contextual-clay-preferences.js +31 -0
  508. package/lib/users-permissions.js +2 -8
  509. package/lib/users.js +7 -0
  510. package/lib/vendor-login-browser.js +1 -11
  511. package/lib/vendor-usage-acp-readers.js +97 -0
  512. package/lib/vendor-usage-capabilities.js +88 -0
  513. package/lib/vendor-usage-http-readers.js +214 -0
  514. package/lib/vendor-usage-metrics.js +47 -0
  515. package/lib/vendor-usage-native-readers.js +106 -0
  516. package/lib/vendor-usage-overview.js +406 -0
  517. package/lib/vendor-usage-service.js +281 -0
  518. package/lib/workspace-assignment-service.js +25 -0
  519. package/lib/workspace-query-access.js +4 -1
  520. package/lib/workspace-query-mcp-server.js +1 -1
  521. package/lib/workspace-query-service.js +13 -2
  522. package/lib/ws-request.js +1 -0
  523. package/lib/ws-schema.js +22 -4
  524. package/lib/yoke/acp-agent-profiles.js +7 -5
  525. package/lib/yoke/adapters/antigravity.js +3 -14
  526. package/lib/yoke/adapters/claude-query-contract.js +9 -0
  527. package/lib/yoke/adapters/claude.js +66 -50
  528. package/lib/yoke/adapters/codex-query-contract.js +25 -0
  529. package/lib/yoke/adapters/codex.js +98 -123
  530. package/lib/yoke/adapters/kiro.js +3 -1
  531. package/lib/yoke/adapters/muse-query-contract.js +7 -0
  532. package/lib/yoke/adapters/muse.js +122 -0
  533. package/lib/yoke/auth-events.js +17 -0
  534. package/lib/yoke/claude-hook-installer.js +272 -0
  535. package/lib/yoke/claude-jsonl-watcher.js +204 -0
  536. package/lib/yoke/claude-maintenance.js +55 -0
  537. package/lib/yoke/claude-permission-patterns.js +53 -0
  538. package/lib/yoke/claude-response-recovery.js +155 -0
  539. package/lib/yoke/claude-transcript-hygiene.js +34 -0
  540. package/lib/yoke/claude-worker-startup.js +28 -0
  541. package/lib/yoke/clay-skill-install.js +33 -0
  542. package/lib/yoke/cli-sessions.js +300 -0
  543. package/lib/yoke/codex-app-server.js +7 -12
  544. package/lib/yoke/codex-dynamic-tool-names.js +80 -0
  545. package/lib/yoke/codex-idle-recovery.js +81 -1
  546. package/lib/yoke/codex-model-catalog.js +37 -3
  547. package/lib/yoke/codex-recovery-admission.js +164 -0
  548. package/lib/yoke/configuration.js +13 -0
  549. package/lib/yoke/discovery.js +70 -0
  550. package/lib/yoke/errors.js +8 -0
  551. package/lib/yoke/host-environment.js +7 -0
  552. package/lib/yoke/index.js +9 -51
  553. package/lib/yoke/interface.js +116 -0
  554. package/lib/yoke/kiro-acp-server.js +5 -5
  555. package/lib/yoke/legacy-sessions.js +89 -0
  556. package/lib/yoke/login-protocol.js +25 -0
  557. package/lib/yoke/maintenance.js +12 -0
  558. package/lib/yoke/mcp-proxy.js +32 -0
  559. package/lib/yoke/mcp-source-adapters.js +91 -0
  560. package/lib/yoke/mcp-source-context.js +42 -0
  561. package/lib/yoke/mcp-source-discovery.js +169 -0
  562. package/lib/yoke/mcp-source-plugins.js +68 -0
  563. package/lib/yoke/mcp-source-restrictions.js +47 -0
  564. package/lib/yoke/mcp-tool-policy-normalize.js +28 -0
  565. package/lib/yoke/model-preferences.js +6 -0
  566. package/lib/yoke/muse-artifacts.js +36 -0
  567. package/lib/yoke/muse-controls.js +50 -0
  568. package/lib/yoke/muse-input.js +64 -0
  569. package/lib/yoke/muse-items.js +157 -0
  570. package/lib/yoke/muse-policy.js +52 -0
  571. package/lib/yoke/muse-query.js +428 -0
  572. package/lib/yoke/muse-runtime.js +211 -0
  573. package/lib/yoke/muse-services.js +92 -0
  574. package/lib/yoke/muse-skills.js +79 -0
  575. package/lib/yoke/muse-turns.js +71 -0
  576. package/lib/yoke/options/acp.js +2 -0
  577. package/lib/yoke/options/antigravity.js +2 -0
  578. package/lib/yoke/options/claude.js +21 -0
  579. package/lib/yoke/options/codex-defaults.js +29 -0
  580. package/lib/yoke/options/codex-settings.js +21 -0
  581. package/lib/yoke/options/codex.js +6 -0
  582. package/lib/yoke/options/kiro-defaults.js +22 -0
  583. package/lib/yoke/options/kiro.js +5 -0
  584. package/lib/yoke/options/muse.js +5 -0
  585. package/lib/yoke/provider-contract.js +5 -0
  586. package/lib/yoke/providers/antigravity-executable.js +8 -0
  587. package/lib/yoke/providers/claude-executable.js +40 -0
  588. package/lib/yoke/providers/claude-model-descriptions.js +34 -0
  589. package/lib/yoke/providers/claude-process.js +97 -0
  590. package/lib/yoke/providers/claude-provision.js +75 -0
  591. package/lib/yoke/providers/codex-errors.js +8 -0
  592. package/lib/yoke/providers/muse-executable.js +17 -0
  593. package/lib/yoke/provisioning.js +6 -0
  594. package/lib/yoke/query-options.js +10 -0
  595. package/lib/yoke/response-recovery.js +41 -0
  596. package/lib/yoke/sdk-mcp-descriptors.js +37 -0
  597. package/lib/yoke/sdk-server-factory.js +7 -0
  598. package/lib/yoke/session-history.js +8 -0
  599. package/lib/yoke/session-tool-transport.js +34 -13
  600. package/lib/yoke/settings.js +23 -0
  601. package/lib/yoke/source-locations.js +4 -0
  602. package/lib/yoke/title-query.js +18 -7
  603. package/lib/yoke/tui-transcript-index.js +132 -0
  604. package/lib/yoke/usage.js +48 -0
  605. package/lib/yoke/user-input.js +13 -0
  606. package/lib/yoke/vendor-registry.js +87 -3
  607. package/package.json +11 -4
  608. package/lib/public/modules/server-settings-navigation.js +0 -39
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) {
@@ -323,6 +311,7 @@ function createSDKBridge(opts) {
323
311
  }
324
312
 
325
313
  function stopIdleReaper() {
314
+ responseRecovery.dispose();
326
315
  if (_idleReaperTimer) {
327
316
  clearInterval(_idleReaperTimer);
328
317
  _idleReaperTimer = null;
@@ -389,6 +378,18 @@ function createSDKBridge(opts) {
389
378
  var sendAndRecord = msgProcessor.sendAndRecord;
390
379
  var sendToSession = msgProcessor.sendToSession;
391
380
 
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; },
383
+ sessions: sm.sessions,
384
+ changed: onProcessingChanged,
385
+ record: sendAndRecord,
386
+ send: sendToSession,
387
+ broadcast: function() { if (sm.broadcastSessionList) sm.broadcastSessionList(); },
388
+ restart: function(session, payload, state) {
389
+ return startQuery(session, payload.text, payload.images, payload.linuxUser, payload.beforePush, null, state);
390
+ },
391
+ }, opts.responseRecovery || {}));
392
+
392
393
  // --- MCP elicitation ---
393
394
 
394
395
  function handleElicitation(session, request, opts) {
@@ -417,6 +418,7 @@ function createSDKBridge(opts) {
417
418
 
418
419
  if (pushModule) {
419
420
  pushModule.sendPush({
421
+ sessionId: session.localId,
420
422
  type: "elicitation",
421
423
  slug: slug,
422
424
  title: (request.serverName || "MCP Server") + " needs input",
@@ -467,6 +469,7 @@ function createSDKBridge(opts) {
467
469
 
468
470
  if (pushModule) {
469
471
  pushModule.sendPush({
472
+ sessionId: session.localId,
470
473
  type: "user_dialog",
471
474
  slug: slug,
472
475
  title: "Claude needs a decision",
@@ -489,44 +492,7 @@ function createSDKBridge(opts) {
489
492
  // Ensures the linux user's .claude project directory exists and is writable,
490
493
  // then pre-copies CLI session file if needed. Called before starting a query
491
494
  // so the worker can resume from the correct session file.
492
- function ensureLinuxUserProjectDir(linuxUser, session) {
493
- try {
494
- var configMod = require("./config");
495
- var osUsersMod = require("./os-users");
496
- var originalHome = configMod.REAL_HOME || require("os").homedir();
497
- var linuxUserHome = osUsersMod.getLinuxUserHome(linuxUser);
498
- var uid = osUsersMod.getLinuxUserUid(linuxUser);
499
- if (originalHome !== linuxUserHome && uid != null) {
500
- var projectSlug = (cwd || "").replace(/\//g, "-");
501
- var dstDir = path.join(linuxUserHome, ".claude", "projects", projectSlug);
502
- // Create and chown the project directory once
503
- if (!fs.existsSync(dstDir)) {
504
- fs.mkdirSync(dstDir, { recursive: true });
505
- try { execFileSync("chown", ["-R", String(uid), path.join(linuxUserHome, ".claude")]); } catch (e2) {}
506
- } else {
507
- try {
508
- var dirStat = fs.statSync(dstDir);
509
- if (dirStat.uid !== uid) {
510
- execFileSync("chown", [String(uid), dstDir]);
511
- }
512
- } catch (e2) {}
513
- }
514
- // Pre-copy CLI session file so the worker can resume the conversation
515
- if (session.cliSessionId) {
516
- var sessionFileName = session.cliSessionId + ".jsonl";
517
- var srcFile = path.join(originalHome, ".claude", "projects", projectSlug, sessionFileName);
518
- var dstFile = path.join(dstDir, sessionFileName);
519
- if (fs.existsSync(srcFile) && !fs.existsSync(dstFile)) {
520
- fs.copyFileSync(srcFile, dstFile);
521
- try { execFileSync("chown", [String(uid), dstFile]); } catch (e2) {}
522
- console.log("[sdk-bridge] Pre-copied CLI session " + session.cliSessionId + " to " + linuxUser);
523
- }
524
- }
525
- }
526
- } catch (copyErr) {
527
- console.log("[sdk-bridge] Dir setup / session pre-copy skipped:", copyErr.message);
528
- }
529
- }
495
+
530
496
 
531
497
  // --- SDK query lifecycle ---
532
498
 
@@ -745,6 +711,39 @@ function createSDKBridge(opts) {
745
711
  // Workspace inspection and proposal posting are session-bound. Keep this
746
712
  // list exact so a future execution/mutation tool cannot inherit approval.
747
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,
718
+ "mcp__clay-workspace__read_owner_screen": true,
719
+ "mcp__clay-workspace__inspect_owner_screen_change": true,
720
+ "mcp__clay-workspace__control_owner_screen": true,
721
+ "mcp__clay-workspace__discover_product_controls": true,
722
+ "mcp__clay-workspace__list_owned_mates": true,
723
+ "mcp__clay-workspace__begin_schedule_control_interview": true,
724
+ "mcp__clay-workspace__read_schedule_controls": true,
725
+ "mcp__clay-workspace__inspect_schedule_change": true,
726
+ "mcp__clay-workspace__control_schedules": true,
727
+ "mcp__clay-workspace__read_mate_chat_control": true,
728
+ "mcp__clay-workspace__inspect_mate_chat_change": true,
729
+ "mcp__clay-workspace__control_mate_chat": true,
730
+ "mcp__clay-workspace__read_project_workspace": true,
731
+ "mcp__clay-workspace__read_workspace_receipt": true,
732
+ "mcp__clay-workspace__inspect_workspace_change": true,
733
+ "mcp__clay-workspace__control_project_workspace": true,
734
+ "mcp__clay-workspace__read_vendor_usage": true,
735
+ "mcp__clay-workspace__read_project_records": true,
736
+ "mcp__clay-workspace__inspect_project_record_change": true,
737
+ "mcp__clay-workspace__control_project_records": true,
738
+ "mcp__clay-workspace__read_mate_knowledge_control": true,
739
+ "mcp__clay-workspace__inspect_mate_knowledge_change": true,
740
+ "mcp__clay-workspace__control_mate_knowledge": true,
741
+ "mcp__clay-workspace__inspect_mate_control": true,
742
+ "mcp__clay-workspace__control_mate_lifecycle": true,
743
+ "mcp__clay-workspace__inspect_project_control": true,
744
+ "mcp__clay-workspace__control_project_lifecycle": true,
745
+ "mcp__clay-workspace__inspect_session_control": true,
746
+ "mcp__clay-workspace__control_project_session": true,
748
747
  "mcp__clay-workspace__list_projects": true,
749
748
  "mcp__clay-workspace__list_project_sessions": true,
750
749
  "mcp__clay-workspace__search_project_history": true,
@@ -902,6 +901,7 @@ function createSDKBridge(opts) {
902
901
 
903
902
  if (pushModule) {
904
903
  pushModule.sendPush({
904
+ sessionId: session.localId,
905
905
  type: "permission_request",
906
906
  slug: slug,
907
907
  requestId: requestId,
@@ -943,58 +943,13 @@ function createSDKBridge(opts) {
943
943
  * Only returns processes whose cwd matches our project directory.
944
944
  * Returns an array of { pid, command } for each conflicting process found.
945
945
  */
946
- function findConflictingClaude() {
947
- try {
948
- var output = execFileSync("ps", ["ax", "-o", "pid,command"], { encoding: "utf8", timeout: 5000, stdio: ["pipe", "pipe", "pipe"] });
949
- var lines = output.trim().split("\n");
950
- var candidates = [];
951
- for (var i = 1; i < lines.length; i++) { // skip header
952
- var line = lines[i].trim();
953
- var m = line.match(/^(\d+)\s+(.+)/);
954
- if (!m) continue;
955
- var pid = parseInt(m[1], 10);
956
- var cmd = m[2];
957
- // Skip our own process
958
- if (pid === process.pid) continue;
959
- // Skip node processes (our daemon, dev watchers, etc.)
960
- if (/\bnode\b/.test(cmd.split(/\s/)[0])) continue;
961
- // Match actual claude binary (e.g. /Users/x/.claude/local/claude, /usr/local/bin/claude)
962
- if (/\/claude(\s|$)/.test(cmd) || /^claude(\s|$)/.test(cmd)) {
963
- candidates.push({ pid: pid, command: cmd.substring(0, 200) });
964
- }
965
- }
966
-
967
- // Filter to only processes whose cwd matches our project
968
- var results = [];
969
- for (var j = 0; j < candidates.length; j++) {
970
- var c = candidates[j];
971
- try {
972
- // Use /proc/<pid>/cwd symlink (always available on Linux, no lsof dependency)
973
- var procCwd = fs.readlinkSync("/proc/" + c.pid + "/cwd");
974
- if (procCwd === cwd) {
975
- results.push(c);
976
- }
977
- } catch (e) {
978
- // /proc read failed — include as candidate anyway (conservative)
979
- results.push(c);
980
- }
981
- }
982
- return results;
983
- } catch (e) {
984
- return [];
985
- }
946
+ function findConflictingProcesses(session, error) {
947
+ var service = require("./yoke/provider-contract").resolve(getAdapterForSession(session)).processControl;
948
+ return service && typeof service.shouldInspect === "function" && service.shouldInspect(error) && typeof service.findConflicts === "function" ? service.findConflicts(cwd) : [];
986
949
  }
987
-
988
- /**
989
- * Verify that a PID is actually a claude binary process (not arbitrary).
990
- */
991
- function isClaudeProcess(pid) {
992
- try {
993
- var output = execFileSync("ps", ["-p", String(pid), "-o", "command="], { encoding: "utf8", timeout: 3000, stdio: ["pipe", "pipe", "pipe"] }).trim();
994
- return /\/claude(\s|$)/.test(output) || /^claude(\s|$)/.test(output);
995
- } catch (e) {
996
- return false;
997
- }
950
+ function isProviderProcess(pid, session) {
951
+ var service = require("./yoke/provider-contract").resolve(session ? getAdapterForSession(session) : adapter).processControl;
952
+ return !!(service && typeof service.verifyProcess === "function" && service.verifyProcess(pid));
998
953
  }
999
954
 
1000
955
  function ownsQueryRuntime(session, queryInstance, queryGeneration) {
@@ -1006,6 +961,7 @@ function createSDKBridge(opts) {
1006
961
  function recordRuntimeEffectivePermissionMode(session, queryInstance, queryGeneration, message) {
1007
962
  if (!message || (message.yokeType !== "init" && message.yokeType !== "status") || !message.permissionMode) return false;
1008
963
  if (!ownsQueryRuntime(session, queryInstance, queryGeneration)) return false;
964
+ session._permissionObservationRevision = Number(session._permissionObservationRevision || 0) + 1;
1009
965
  session.effectivePermissionMode = message.permissionMode;
1010
966
  session.permissionModeFallbackReason = null;
1011
967
  sm.saveSessionFile(session);
@@ -1036,7 +992,8 @@ function createSDKBridge(opts) {
1036
992
  return true;
1037
993
  }
1038
994
 
1039
- async function processQueryStream(session, codexCatalogCandidate) {
995
+ async function processQueryStream(session, catalogCandidate, queryContract) {
996
+ queryContract = queryContract || yoke.sessionTools.contract(getAdapterForSession(session));
1040
997
  // Capture references at start so we only clean up OUR resources in finally,
1041
998
  // not resources from a newer query that may have been created after an abort.
1042
999
  var myQueryInstance = session.queryInstance;
@@ -1047,7 +1004,7 @@ function createSDKBridge(opts) {
1047
1004
  try {
1048
1005
  for await (var msg of myQueryInstance) {
1049
1006
  // An old iterator may drain after Stop or after a replacement query starts.
1050
- if (!ownsQueryRuntime(session, myQueryInstance, myQueryGeneration)) continue;
1007
+ if (!ownsQueryRuntime(session, myQueryInstance, myQueryGeneration) || responseRecovery.stopping(session)) continue;
1051
1008
  if ((session.taskStopRequested || (myAbortController && myAbortController.signal.aborted))
1052
1009
  && msg && msg.yokeType !== "error" && msg.yokeType !== "interrupted") continue;
1053
1010
  if (msg && msg.yokeType !== "text_delta" && msg.yokeType !== "thinking_delta" && msg.yokeType !== "tool_input_delta") {
@@ -1091,6 +1048,7 @@ function createSDKBridge(opts) {
1091
1048
  if (continuationIdentity && !ownsQueryRuntime(session, myQueryInstance, myQueryGeneration)) {
1092
1049
  continue;
1093
1050
  }
1051
+ responseRecovery.event(session, msg);
1094
1052
  processSDKMessage(session, msg);
1095
1053
  if (continuationIdentity) {
1096
1054
  settleContinuationStartup(session, myQueryInstance, myQueryGeneration, true, null);
@@ -1101,26 +1059,21 @@ function createSDKBridge(opts) {
1101
1059
  // permission state. Setter acknowledgement deliberately does not
1102
1060
  // change this field.
1103
1061
  recordRuntimeEffectivePermissionMode(session, myQueryInstance, myQueryGeneration, msg);
1104
- if (codexCatalogCandidate && msg && msg.yokeType === "session_started" && msg.sessionId
1062
+ if (catalogCandidate && msg && msg.yokeType === "session_started" && msg.sessionId
1105
1063
  && session.queryInstance === myQueryInstance
1106
- && Number(session._sdkQueryGeneration || 0) === codexCatalogCandidate.generation
1064
+ && Number(session._sdkQueryGeneration || 0) === catalogCandidate.generation
1107
1065
  && session.cliSessionId === msg.sessionId
1108
1066
  && (!sm.sessions || typeof sm.sessions.get !== "function" || sm.sessions.get(session.localId) === session)) {
1109
- if (codexCatalogCandidate.version) session.codexDynamicToolCatalogVersion = codexCatalogCandidate.version;
1110
- if (codexCatalogCandidate.userInputVersion) session.codexUserInputToolCatalogVersion = codexCatalogCandidate.userInputVersion;
1111
- if (codexCatalogCandidate.sharedBrowserVersion) session.codexSharedBrowserToolCatalogVersion = codexCatalogCandidate.sharedBrowserVersion;
1112
- if (codexCatalogCandidate.issuesVersion) session.codexIssuesToolCatalogVersion = codexCatalogCandidate.issuesVersion;
1113
- if (codexCatalogCandidate.youVersion) session.codexYouToolCatalogVersion = codexCatalogCandidate.youVersion;
1114
- if (codexCatalogCandidate.linearVersion) session.codexLinearToolCatalogVersion = codexCatalogCandidate.linearVersion;
1067
+ queryContract.recordCatalog(session, catalogCandidate.versions);
1115
1068
  sm.saveSessionFile(session);
1116
- codexCatalogCandidate = null;
1069
+ catalogCandidate = null;
1117
1070
  }
1118
1071
  }
1119
1072
  // (getContextUsage moved to processSDKMessage result handler -- fire-and-forget)
1120
1073
  // Stream ended normally after a task stop — no "result" message was sent,
1121
1074
  // so the session is still marked as processing. Send interrupted feedback.
1122
1075
  console.log("[sdk-bridge] processQueryStream ended: isProcessing=" + session.isProcessing + " taskStopRequested=" + session.taskStopRequested);
1123
- var stillOwnsRuntime = session.queryInstance === myQueryInstance;
1076
+ var stillOwnsRuntime = session.queryInstance === myQueryInstance && !responseRecovery.stopping(session);
1124
1077
  settleContinuationStartup(session, myQueryInstance, myQueryGeneration, false, "The successor query ended before initialization.");
1125
1078
  if (session.isProcessing && session.taskStopRequested && stillOwnsRuntime) {
1126
1079
  session._lastTurnInterrupted = true;
@@ -1131,9 +1084,7 @@ function createSDKBridge(opts) {
1131
1084
  onProcessingChanged();
1132
1085
  send({ type: "status", processing: false });
1133
1086
  sendAndRecord(session, { type: "thinking_stop" });
1134
- var interruptMsg = (session.vendor && session.vendor !== "claude")
1135
- ? "\u25a0 Conversation interrupted - tell the model what to do differently."
1136
- : "Interrupted \u00b7 What should Claude do instead?";
1087
+ var interruptMsg = (queryContract && queryContract.interruptionMessage) || "■ Conversation interrupted - tell the model what to do differently.";
1137
1088
  sendAndRecord(session, { type: "info", text: interruptMsg });
1138
1089
  sendAndRecord(session, { type: "done", code: 0 });
1139
1090
  sm.broadcastSessionList();
@@ -1158,7 +1109,7 @@ function createSDKBridge(opts) {
1158
1109
  }
1159
1110
  } catch (err) {
1160
1111
  settleContinuationStartup(session, myQueryInstance, myQueryGeneration, false, err.message || "The successor query failed before initialization.");
1161
- if (session.isProcessing && session.queryInstance === myQueryInstance) {
1112
+ if (session.isProcessing && session.queryInstance === myQueryInstance && !responseRecovery.stopping(session)) {
1162
1113
  session.isProcessing = false;
1163
1114
  session._awaitingTurnResult = false;
1164
1115
  session._queuedTurnCount = 0;
@@ -1168,9 +1119,7 @@ function createSDKBridge(opts) {
1168
1119
  session._lastTurnInterrupted = true;
1169
1120
  if (!session.destroying) {
1170
1121
  sendAndRecord(session, { type: "thinking_stop" });
1171
- var interruptMsg2 = (session.vendor && session.vendor !== "claude")
1172
- ? "\u25a0 Conversation interrupted - tell the model what to do differently."
1173
- : "Interrupted \u00b7 What should Claude do instead?";
1122
+ var interruptMsg2 = (queryContract && queryContract.interruptionMessage) || "■ Conversation interrupted - tell the model what to do differently.";
1174
1123
  sendAndRecord(session, { type: "info", text: interruptMsg2 });
1175
1124
  sendAndRecord(session, { type: "done", code: 0 });
1176
1125
  }
@@ -1186,21 +1135,17 @@ function createSDKBridge(opts) {
1186
1135
  console.error("[sdk-bridge] Query stream error for session " + session.localId + ":", errDetail);
1187
1136
  console.error("[sdk-bridge] Stack:", err.stack || "(no stack)");
1188
1137
 
1189
- // Check for conflicting Claude processes only on exit code 1
1190
- var isExitCode1 = err.exitCode === 1 || (err.message && err.message.indexOf("exited with code 1") !== -1);
1191
- var conflicts = isExitCode1 ? findConflictingClaude() : [];
1138
+ var conflicts = findConflictingProcesses(session, err);
1192
1139
  if (conflicts.length > 0) {
1193
- console.error("[sdk-bridge] Found " + conflicts.length + " conflicting Claude process(es):", conflicts.map(function(c) { return "PID " + c.pid; }).join(", "));
1140
+ console.error("[sdk-bridge] Found " + conflicts.length + " conflicting provider process(es):", conflicts.map(function(c) { return "PID " + c.pid; }).join(", "));
1194
1141
  sendAndRecord(session, {
1195
1142
  type: "process_conflict",
1196
- text: "Another Claude Code process is already running in this project.",
1143
+ text: "Another provider process is already running in this project.",
1197
1144
  processes: conflicts,
1198
1145
  });
1199
1146
  } else {
1200
1147
  var errLower = errDetail.toLowerCase();
1201
- var isContextOverflow = errLower.indexOf("prompt is too long") !== -1
1202
- || errLower.indexOf("context_length") !== -1
1203
- || errLower.indexOf("maximum context length") !== -1;
1148
+ var isContextOverflow = require("./yoke/errors").contextOverflow(errDetail);
1204
1149
  var isAuthError = isAuthErrorMessage(errDetail);
1205
1150
  if (isContextOverflow) {
1206
1151
  sendAndRecord(session, {
@@ -1247,7 +1192,7 @@ function createSDKBridge(opts) {
1247
1192
  // chat message already sent above
1248
1193
  }
1249
1194
  } else {
1250
- sendAndRecord(session, { type: "error", text: "Claude process error: " + err.message });
1195
+ sendAndRecord(session, { type: "error", text: "Provider process error: " + err.message });
1251
1196
  }
1252
1197
  }
1253
1198
  sendAndRecord(session, { type: "done", code: 1 });
@@ -1272,7 +1217,8 @@ function createSDKBridge(opts) {
1272
1217
  }
1273
1218
  if (session.messageQueue === myMessageQueue) session.messageQueue = null;
1274
1219
  if (session.abortController === myAbortController) session.abortController = null;
1275
- if (ownsRuntime) {
1220
+ if (ownsRuntime && !responseRecovery.stopping(session)) {
1221
+ responseRecovery.cancel(session);
1276
1222
  session.taskStopRequested = false;
1277
1223
  session.pendingPermissions = {};
1278
1224
  // Preserve only restored legacy MCP entries. Canonical Yoke requests
@@ -1321,7 +1267,7 @@ function createSDKBridge(opts) {
1321
1267
  }
1322
1268
  }
1323
1269
  }
1324
- if (!session.isProcessing && typeof opts.onSessionAvailable === "function") opts.onSessionAvailable(session);
1270
+ if (!responseRecovery.stopping(session) && !session.isProcessing && typeof opts.onSessionAvailable === "function") opts.onSessionAvailable(session);
1325
1271
  }
1326
1272
  }
1327
1273
 
@@ -1329,20 +1275,16 @@ function createSDKBridge(opts) {
1329
1275
  if (session.queryInstance) return { query: session.queryInstance, isTemp: false, cleanup: function() {} };
1330
1276
 
1331
1277
  var handle;
1278
+ var selectedAdapter = getAdapterForSession(session);
1332
1279
  try {
1333
- handle = await adapter.createQuery({
1280
+ handle = await selectedAdapter.createQuery({
1334
1281
  cwd: cwd,
1335
1282
  resumeSessionId: session.cliSessionId,
1336
1283
  env: getRuntimeEnv(session),
1337
- adapterOptions: {
1338
- CLAUDE: {
1339
- settingSources: ["user", "project", "local"],
1340
- enableFileCheckpointing: true,
1341
- },
1342
- },
1284
+ adapterOptions: require("./yoke/query-options").map(getAdapterForSession(session), { nativePermissionMode: "default" }),
1343
1285
  });
1344
1286
  } catch (e) {
1345
- sendAndRecord(session, { type: "error", text: "Failed to load Claude SDK: " + (e.message || e) });
1287
+ sendAndRecord(session, { type: "error", text: "Failed to load provider session: " + (e.message || e) });
1346
1288
  throw e;
1347
1289
  }
1348
1290
 
@@ -1409,7 +1351,7 @@ function createSDKBridge(opts) {
1409
1351
  // abort the whole rewind instead of leaving local history trimmed
1410
1352
  // ahead of the real, un-rolled-back provider thread.
1411
1353
  if (!turnId) {
1412
- throw new Error("No turn id recorded for this turn; cannot roll back the Codex thread.");
1354
+ throw new Error("No turn id recorded for this turn; cannot roll back the provider thread.");
1413
1355
  }
1414
1356
  await sessionAdapter.rollbackThread(session.cliSessionId, turnId, {
1415
1357
  linuxUser: getSessionLinuxUser(session) || undefined,
@@ -1425,6 +1367,8 @@ function createSDKBridge(opts) {
1425
1367
 
1426
1368
  async function forkSessionUnified(session, uuid) {
1427
1369
  var sessionAdapter = getAdapterForSession(session);
1370
+ var localHistoryEndIndex = require("./session-fork-history").endIndex(session, uuid,
1371
+ require("./yoke/provider-contract").resolve(sessionAdapter).forkHistoryBoundary);
1428
1372
  var result = await sessionAdapter.forkSession(session.cliSessionId, {
1429
1373
  upToMessageId: uuid,
1430
1374
  dir: cwd,
@@ -1433,8 +1377,10 @@ function createSDKBridge(opts) {
1433
1377
  if (!result || !result.sessionId) throw new Error("Fork returned no session id");
1434
1378
 
1435
1379
  // Adapters with rollbackThread (e.g. Codex) use local history copy
1436
- if (typeof sessionAdapter.rollbackThread === "function") {
1437
- return { sessionId: result.sessionId, useLocalHistory: true };
1380
+ if (result.useLocalHistory === true || typeof sessionAdapter.rollbackThread === "function") {
1381
+ var localFork = { sessionId: result.sessionId, useLocalHistory: true };
1382
+ if (localHistoryEndIndex !== undefined) localFork.localHistoryEndIndex = localHistoryEndIndex;
1383
+ return localFork;
1438
1384
  }
1439
1385
  // Claude: read history from CLI session files
1440
1386
  return { sessionId: result.sessionId, useLocalHistory: false };
@@ -1442,22 +1388,69 @@ function createSDKBridge(opts) {
1442
1388
 
1443
1389
  // Wrapper: marks the boot window so pushMessage can buffer instead of
1444
1390
  // dropping messages that arrive while createQuery is still awaiting.
1445
- async function startQuery(session, text, images, linuxUser, beforePush, onAccepted) {
1391
+ async function startQuery(session, text, images, linuxUser, beforePush, onAccepted, retryState) {
1392
+ if (opts.isLifecycleExecutionPaused && opts.isLifecycleExecutionPaused()) {
1393
+ sendAndRecord(session, { type: "error", text: "Project execution is paused for lifecycle recovery. Reconcile the saved operation, or send a new owner message after restoring the registration." });
1394
+ session.isProcessing = false; onProcessingChanged(); sendAndRecord(session, { type: "done", code: 1 }); return false;
1395
+ }
1396
+ if (session._responseRecoveryStopPending && !retryState) {
1397
+ session.isProcessing = false;
1398
+ onProcessingChanged();
1399
+ sendAndRecord(session, { type: "error", text: "The previous Claude attempt has not confirmed stopped. Check the connection before sending this message again." });
1400
+ sendAndRecord(session, { type: "done", code: 1 });
1401
+ return false;
1402
+ }
1403
+ if (responseRecovery.stopping(session) && !retryState) return false;
1446
1404
  delete session._lastTurnInterrupted;
1405
+ var payload = { text: text, images: images, linuxUser: linuxUser, beforePush: beforePush };
1406
+ var recoveryState = responseRecovery.begin(session, payload, retryState);
1407
+ if (recoveryState) recoveryState.handle = null;
1408
+ var guardedPush = function() {
1409
+ return !session.destroying && !session.taskStopRequested
1410
+ && (!recoveryState || responseRecovery.valid(session, recoveryState) && !recoveryState.stopping)
1411
+ && (typeof beforePush !== "function" || beforePush() === true);
1412
+ };
1413
+ guardedPush.exactProductDelivery = !!(beforePush && beforePush.exactProductDelivery);
1447
1414
  session._queryGeneration = (session._queryGeneration || 0) + 1;
1448
1415
  session._queryStarting = true;
1416
+ var starting = startQueryInner(session, text, images, linuxUser, guardedPush, onAccepted, recoveryState);
1417
+ if (recoveryState) recoveryState.starting = starting;
1449
1418
  try {
1450
- return await startQueryInner(session, text, images, linuxUser, beforePush, onAccepted);
1419
+ var accepted = await starting;
1420
+ if (accepted === false && (!recoveryState || !recoveryState.stopping)) {
1421
+ responseRecovery.cancel(session);
1422
+ session.isProcessing = false;
1423
+ onProcessingChanged();
1424
+ }
1425
+ return accepted;
1426
+ } catch (error) {
1427
+ // Includes synchronous input queue failures, before stream consumption.
1428
+ var failedHandle = session.queryInstance;
1429
+ if (failedHandle) {
1430
+ try { if (typeof failedHandle.abort === "function") failedHandle.abort(); } catch (e) {}
1431
+ try { if (typeof failedHandle.close === "function") failedHandle.close(); } catch (e) {}
1432
+ }
1433
+ session.queryInstance = null;
1434
+ session.abortController = null;
1435
+ session.messageQueue = null;
1436
+ session.isProcessing = false;
1437
+ session._awaitingTurnResult = false;
1438
+ session._queuedTurnCount = 0;
1439
+ taskLifecycle.clearAll(session);
1440
+ if (!recoveryState || !recoveryState.stopping) {
1441
+ responseRecovery.cancel(session);
1442
+ sendAndRecord(session, { type: "error", text: "The agent connection failed while delivering your message. Please retry or check the connection." });
1443
+ sendAndRecord(session, { type: "done", code: 1 });
1444
+ onProcessingChanged();
1445
+ sm.broadcastSessionList();
1446
+ }
1447
+ return false;
1451
1448
  } finally {
1452
1449
  session._queryStarting = false;
1453
- if (!session.queryInstance && session.pendingPush && session.pendingPush.length) {
1454
- // The query never came up (auth/adapter failure already reported to
1455
- // the user). Drop the backlog rather than leaking it into a future
1456
- // unrelated query.
1457
- session.pendingPush = [];
1458
- }
1459
- if (!session.queryInstance && !session.isProcessing) session.taskStopRequested = false;
1460
- if (!session.isProcessing && typeof opts.onSessionAvailable === "function") opts.onSessionAvailable(session);
1450
+ delete session._providerRecoveryWaiting;
1451
+ if (!session.queryInstance && session.pendingPush && session.pendingPush.length) session.pendingPush = [];
1452
+ if (!session.queryInstance && !session.isProcessing && !session._pendingMessageDrainPaused && !responseRecovery.stopping(session)) session.taskStopRequested = false;
1453
+ if (!responseRecovery.stopping(session) && !session.isProcessing && typeof opts.onSessionAvailable === "function") opts.onSessionAvailable(session);
1461
1454
  }
1462
1455
  }
1463
1456
 
@@ -1501,7 +1494,7 @@ function createSDKBridge(opts) {
1501
1494
  };
1502
1495
  }
1503
1496
 
1504
- async function startQueryInner(session, text, images, linuxUser, beforePush, onAccepted) {
1497
+ async function startQueryInner(session, text, images, linuxUser, beforePush, onAccepted, recoveryState) {
1505
1498
  var sessionRuntimeEnv = getRuntimeEnv(session);
1506
1499
  var resumedAtQueryStart = !!session.cliSessionId;
1507
1500
  async function ensureVendorReady(vendor) {
@@ -1653,7 +1646,8 @@ function createSDKBridge(opts) {
1653
1646
 
1654
1647
  // Ensure Linux user project directory exists (runs in parallel with worker boot)
1655
1648
  if (linuxUser) {
1656
- ensureLinuxUserProjectDir(linuxUser, session);
1649
+ var preparation = require("./yoke/provider-contract").resolve(sessionAdapter).prepareIsolation;
1650
+ if (typeof preparation === "function") preparation(linuxUser, session, cwd);
1657
1651
  }
1658
1652
 
1659
1653
  session.blocks = {};
@@ -1670,100 +1664,27 @@ function createSDKBridge(opts) {
1670
1664
  session.abortController = new AbortController();
1671
1665
  }
1672
1666
 
1673
- // Build Claude-specific adapter options
1674
- var claudeOpts = {
1675
- settingSources: ["user", "project", "local"],
1676
- includePartialMessages: true,
1677
- enableFileCheckpointing: true,
1678
- extraArgs: { "replay-user-messages": null },
1679
- promptSuggestions: true,
1680
- agentProgressSummaries: true,
1681
- };
1682
-
1683
- // Per-loop settings override global defaults when present
1684
1667
  var ls = session.loopSettings || {};
1685
-
1686
- if (sm.currentBetas && sm.currentBetas.length > 0) {
1687
- claudeOpts.betas = sm.currentBetas;
1688
- }
1689
- var thinkingMode = ls.thinking || sm.currentThinking;
1690
- if (thinkingMode === "disabled") {
1691
- claudeOpts.thinking = { type: "disabled" };
1692
- } else if (thinkingMode === "budget") {
1693
- var budgetTokens = ls.thinkingBudget || sm.currentThinkingBudget;
1694
- if (budgetTokens) claudeOpts.thinking = { type: "enabled", budgetTokens: budgetTokens };
1695
- } else {
1696
- // Adaptive (the default). Request summarized display explicitly:
1697
- // current models default thinking.display to "omitted", which streams
1698
- // thinking blocks whose text is an EMPTY string -- the UI's thinking
1699
- // section stays blank even though thinking happens and is billed.
1700
- // (SDK ThinkingAdaptive: { type: "adaptive", display?: "summarized" | "omitted" })
1701
- claudeOpts.thinking = { type: "adaptive", display: "summarized" };
1702
- }
1703
-
1704
- if (ls.permissionMode) {
1705
- session._loopPermissionMode = ls.permissionMode;
1706
- }
1707
-
1708
- // Pass through any extra SDK settings from LOOP.json
1709
- if (ls.disableAllHooks !== undefined) {
1710
- claudeOpts.settings = Object.assign({}, claudeOpts.settings || {}, { disableAllHooks: ls.disableAllHooks });
1711
- }
1712
-
1713
- if (dangerouslySkipPermissions || (session.mode === "tui" && session.dangerouslySkipPermissions)) {
1714
- claudeOpts.allowDangerouslySkipPermissions = true;
1715
- claudeOpts.permissionMode = "bypassPermissions";
1716
- } else {
1717
- var globalMode = session.permissionMode || sm.currentPermissionMode || "default";
1718
- var effectiveDefault;
1719
- // GUI full access stays inside Clay's canUseTool callback. Passing the
1720
- // SDK bypass mode would require its process-level dangerous escape hatch
1721
- // and would skip Clay's permission policy entirely.
1722
- if (globalMode === "bypassPermissions") effectiveDefault = "default";
1723
- else if (session.acceptEditsAfterStart) effectiveDefault = "acceptEdits";
1724
- else effectiveDefault = globalMode;
1725
- var modeToApply = session._loopPermissionMode || effectiveDefault;
1726
- // Always set permissionMode explicitly, including "default". SDK
1727
- // 0.3.286+ leaves an omitted permissionMode to Claude Code itself,
1728
- // which can apply a settings defaultMode or start in auto mode on
1729
- // third-party providers / with telemetry off -- silently widening
1730
- // past Clay's Ask policy. Sending "default" keeps the SDK's own
1731
- // decision point at the same "default" behavior Clay has always run
1732
- // under: a tool the SDK's own allowed/denied rules or allowedTools
1733
- // list already settles is not re-asked, but anything left undecided
1734
- // still reaches Clay's canUseTool callback, as before.
1735
- claudeOpts.permissionMode = modeToApply || "default";
1736
- }
1737
- if (session.youCurator) {
1738
- claudeOpts.permissionMode = "default";
1739
- claudeOpts.allowDangerouslySkipPermissions = false;
1740
- }
1741
- // Clear one-shot acceptEditsAfterStart regardless of which branch ran above,
1742
- // so the flag does not linger into subsequent turns.
1743
- if (session.acceptEditsAfterStart) delete session.acceptEditsAfterStart;
1744
- if (session.cliSessionId && session.lastRewindUuid) {
1745
- claudeOpts.resumeSessionAt = session.lastRewindUuid;
1746
- delete session.lastRewindUuid;
1747
- sm.saveSessionFile(session);
1748
- }
1749
-
1750
- // Pass linuxUser to adapter for worker-based queries
1668
+ if (ls.permissionMode) session._loopPermissionMode = ls.permissionMode;
1669
+ var permissionMode = session.permissionMode || sm.currentPermissionMode || "default";
1670
+ var nativeMode = permissionMode === "bypassPermissions" ? "default" : session.acceptEditsAfterStart ? "acceptEdits" : permissionMode;
1671
+ var isolationHome = null;
1751
1672
  if (linuxUser) {
1752
- claudeOpts.linuxUser = linuxUser;
1753
- claudeOpts.singleTurn = !!session.singleTurn;
1754
- try {
1755
- claudeOpts.originalHome = require("./os-users").resolveOsUserInfo(linuxUser).home;
1756
- } catch (e) {
1757
- claudeOpts.originalHome = null;
1758
- }
1759
- claudeOpts.projectPath = session.cwd || null;
1760
- claudeOpts._perfT0 = t0;
1761
- // Pass previous worker state for reuse
1762
- if (session._adapterWorkerState) {
1763
- claudeOpts._workerState = session._adapterWorkerState;
1764
- session._adapterWorkerState = null;
1765
- }
1766
- }
1673
+ try { isolationHome = require("./os-users").resolveOsUserInfo(linuxUser).home; } catch (error) {}
1674
+ }
1675
+ var executionIntent = {
1676
+ settings: sm, permissionMode: permissionMode, nativePermissionMode: session._loopPermissionMode || nativeMode,
1677
+ nativeBypass: dangerouslySkipPermissions || session.mode === "tui" && session.dangerouslySkipPermissions,
1678
+ readOnly: !!session.youCurator, betas: sm.currentBetas,
1679
+ thinking: { mode: ls.thinking || sm.currentThinking, budget: ls.thinkingBudget || sm.currentThinkingBudget },
1680
+ disableHooks: ls.disableAllHooks, resumeAt: session.cliSessionId && session.lastRewindUuid || undefined,
1681
+ singleTurn: !!session.singleTurn, startedAt: t0,
1682
+ isolation: { user: linuxUser, home: isolationHome, projectPath: session.cwd || null, workerState: session._adapterWorkerState },
1683
+ sessionBridge: null,
1684
+ };
1685
+ if (session.acceptEditsAfterStart) delete session.acceptEditsAfterStart;
1686
+ if (executionIntent.resumeAt) { delete session.lastRewindUuid; sm.saveSessionFile(session); }
1687
+ if (linuxUser && session._adapterWorkerState) session._adapterWorkerState = null;
1767
1688
 
1768
1689
  // Pick a model from the session or its vendor-specific default, then
1769
1690
  // validate it against the session vendor's known model list.
@@ -1789,20 +1710,21 @@ function createSDKBridge(opts) {
1789
1710
  sm.saveSessionFile(session);
1790
1711
  }
1791
1712
 
1792
- var codexConfig = getCodexConfig(sm);
1793
- var kiroConfig = getKiroConfig(sm);
1794
1713
  session._sdkQueryGeneration = Number(session._sdkQueryGeneration || 0) + 1;
1795
1714
  // Individual feature modules own their transport capability checks. Do not
1796
1715
  // suppress unrelated session tools merely because one feature cannot be
1797
1716
  // delivered by this provider.
1798
1717
  var sessionToolDefs = getSessionToolDefs(session) || [];
1799
1718
  var queryGeneration = Number(session._sdkQueryGeneration || 0);
1719
+ executionIntent.sessionBridge = getAcpMcpServers(session);
1720
+ var queryContract = yoke.sessionTools.contract(sessionAdapter);
1721
+ function needsCatalogRecovery(family, version) {
1722
+ return yoke.sessionTools.needsRecovery(sessionAdapter, session, family, version);
1723
+ }
1800
1724
  var hasYouCatalog = sessionToolDefs.some(function (tool) { return tool.name === "search_user_knowledge"; });
1801
- var recoverCodexYouTools = sessionAdapter.vendor === "codex" && !!session.cliSessionId
1802
- && hasYouCatalog && Number(session.codexYouToolCatalogVersion || 0) < 1;
1725
+ var recoverYouTools = needsCatalogRecovery("knowledge", 1) && hasYouCatalog;
1803
1726
  var hasLinearCatalog = sessionToolDefs.some(function (tool) { return tool.name === "link_session_linear"; });
1804
- var recoverCodexLinearTools = sessionAdapter.vendor === "codex" && !!session.cliSessionId
1805
- && hasLinearCatalog && Number(session.codexLinearToolCatalogVersion || 0) < 1 && canUseSessionTools(session) === true;
1727
+ var recoverLinearTools = needsCatalogRecovery("linear", 1) && hasLinearCatalog && canUseSessionTools(session) === true;
1806
1728
  var hasIssueCatalog = false;
1807
1729
  for (var issueToolIndex = 0; issueToolIndex < sessionToolDefs.length; issueToolIndex++) {
1808
1730
  if (sessionToolDefs[issueToolIndex].name === "create_issue") {
@@ -1811,24 +1733,26 @@ function createSDKBridge(opts) {
1811
1733
  }
1812
1734
  }
1813
1735
  var hasSharedBrowserCatalog = sessionToolDefs.some(function (definition) { return definition.name === "shared_browser"; });
1814
- var recoverCodexBrowserTools = sessionAdapter.vendor === "codex" && !!session.cliSessionId
1815
- && hasSharedBrowserCatalog && Number(session.codexSharedBrowserToolCatalogVersion || 0) < 5;
1816
- var recoverCodexIssueTools = sessionAdapter.vendor === "codex"
1817
- && !!session.cliSessionId
1818
- && hasIssueCatalog
1819
- && Number(session.codexIssuesToolCatalogVersion || 0) < 1;
1736
+ var recoverBrowserTools = needsCatalogRecovery("browser", 6) && hasSharedBrowserCatalog;
1737
+ var recoverIssueTools = needsCatalogRecovery("issues", 1) && hasIssueCatalog;
1738
+ var notesCatalogVersion = require("./session-notes-mcp-server").CATALOG_VERSION;
1739
+ var hasNotesCatalog = sessionToolDefs.some(function (definition) { return definition.name === "write_note"; });
1740
+ var recoverNoteTools = needsCatalogRecovery("notes", notesCatalogVersion) && hasNotesCatalog;
1820
1741
  function queryContextAvailable() {
1742
+ if (queryRetired()) return false;
1821
1743
  if (Number(session._sdkQueryGeneration || 0) !== queryGeneration) return false;
1822
1744
  if (sm && sm.sessions && typeof sm.sessions.get === "function" && sm.sessions.get(session.localId) !== session) return false;
1823
1745
  return canUseSessionTools(session) === true;
1824
1746
  }
1747
+ var hasMateScheduleTool = sessionToolDefs.some(function (tool) { return yoke.sessionTools.MATE_SCHEDULE_TOOLS.indexOf(tool.name) !== -1; });
1748
+ var recoverMateSchedules = needsCatalogRecovery() && hasMateScheduleTool && queryContextAvailable();
1825
1749
  var hasScheduleTool = false;
1826
1750
  var hasScheduleInterviewTool = false;
1827
1751
  var scheduleInterviewActive = null;
1828
1752
  var scheduleQuestionLimit = null;
1829
1753
  for (var sessionToolIndex = 0; sessionToolIndex < sessionToolDefs.length; sessionToolIndex++) {
1830
1754
  if (yoke.sessionTools.SCHEDULE_TOOLS.indexOf(sessionToolDefs[sessionToolIndex].name) !== -1) hasScheduleTool = true;
1831
- if (sessionToolDefs[sessionToolIndex].name === "begin_scheduled_task_interview") {
1755
+ if (["begin_scheduled_task_interview", "begin_mate_schedule_interview", "begin_schedule_control_interview"].indexOf(sessionToolDefs[sessionToolIndex].name) >= 0) {
1832
1756
  hasScheduleInterviewTool = true;
1833
1757
  scheduleInterviewActive = sessionToolDefs[sessionToolIndex].scheduleInterviewActive || null;
1834
1758
  scheduleQuestionLimit = sessionToolDefs[sessionToolIndex].structuredQuestionLimit || null;
@@ -1838,10 +1762,10 @@ function createSDKBridge(opts) {
1838
1762
  var activeLoopInterview = !isDriverOperatedSession(session) && !session.loopInterviewHandoff && !activeRun && !!loopInterview.activeInterviewId(session);
1839
1763
  var structuredInputSession = hasScheduleInterviewTool || activeLoopInterview || (!isDriverOperatedSession(session) && session.ralphCraftingMode === true && session.loop && session.loop.role === "crafting");
1840
1764
  var requestedUserInputMode = session.debateSetupMode || session.mateCreationMode ? "fallback" : "auto";
1841
- if (structuredInputSession || sessionAdapter.vendor === "codex") requestedUserInputMode = "fallback";
1765
+ if (structuredInputSession) requestedUserInputMode = "fallback";
1766
+ if (requestedUserInputMode === "auto" && queryContract.defaultUserInputMode) requestedUserInputMode = queryContract.defaultUserInputMode;
1842
1767
  var selectedUserInputMode = yoke.userInput.selectMode(sessionAdapter, requestedUserInputMode);
1843
- var recoverCodexQuestionTools = sessionAdapter.vendor === "codex" && !!session.cliSessionId
1844
- && Number(session.codexUserInputToolCatalogVersion || 0) < 1 && queryContextAvailable();
1768
+ var recoverQuestionTools = needsCatalogRecovery("questions", 1) && queryContextAvailable();
1845
1769
  function structuredInputContextAvailable() {
1846
1770
  if (!queryContextAvailable()) return false;
1847
1771
  // A schedule-triggered fallback shares the schedule feature's live
@@ -1860,9 +1784,10 @@ function createSDKBridge(opts) {
1860
1784
  return "Ask exactly one question at a time, wait for the answer, then ask the next question.";
1861
1785
  }
1862
1786
  var sessionUserInputHandler = function(request, respond) {
1863
- if (!structuredInputContextAvailable()) {
1787
+ if (queryRetired() || !structuredInputContextAvailable()) {
1864
1788
  return respond.cancel("This structured-input request belongs to an unavailable or older query.");
1865
1789
  }
1790
+ responseRecovery.progress(session);
1866
1791
  var questionError = scheduleQuestionError(request);
1867
1792
  if (questionError) return respond.cancel(questionError);
1868
1793
  if (request.presentation === "elicitation" && request.diagnostics && request.diagnostics.elicitation) {
@@ -1882,21 +1807,33 @@ function createSDKBridge(opts) {
1882
1807
  return opts.onUserInputRequest(session, request, respond);
1883
1808
  };
1884
1809
  var localMcpServers = getMcpServers(session);
1885
- if (sessionAdapter.vendor === "claude" && hasScheduleTool && typeof sessionAdapter.createToolServer === "function") {
1810
+ if (queryContract.toolTransport === "sdk-mcp" && (hasScheduleTool || hasMateScheduleTool)
1811
+ && typeof sessionAdapter.createToolServer !== "function") {
1812
+ throw new Error("The adapter cannot mount native schedule tools without createToolServer.");
1813
+ }
1814
+ if (queryContract.toolTransport === "sdk-mcp" && hasScheduleTool) {
1886
1815
  var scheduledTaskToolDefs = [];
1887
1816
  for (var scheduledToolIndex = 0; scheduledToolIndex < sessionToolDefs.length; scheduledToolIndex++) {
1888
1817
  if (yoke.sessionTools.SCHEDULE_TOOLS.indexOf(sessionToolDefs[scheduledToolIndex].name) !== -1) scheduledTaskToolDefs.push(sessionToolDefs[scheduledToolIndex]);
1889
1818
  }
1890
1819
  if (scheduledTaskToolDefs.length) {
1891
1820
  var scheduledTaskServer = sessionAdapter.createToolServer({ name: "clay-scheduled-tasks", version: "1.0.0", tools: scheduledTaskToolDefs });
1892
- if (scheduledTaskServer) {
1893
- localMcpServers = Object.assign({}, localMcpServers || {});
1894
- localMcpServers["clay-scheduled-tasks"] = scheduledTaskServer;
1895
- }
1821
+ if (!scheduledTaskServer) throw new Error("The adapter could not mount scheduled-task tools.");
1822
+ localMcpServers = Object.assign({}, localMcpServers || {});
1823
+ localMcpServers["clay-scheduled-tasks"] = scheduledTaskServer;
1896
1824
  }
1897
1825
  }
1826
+ if (queryContract.toolTransport === "sdk-mcp" && hasMateScheduleTool) {
1827
+ localMcpServers = Object.assign({}, localMcpServers || {});
1828
+ localMcpServers["clay-mate-schedules"] = sessionAdapter.createToolServer({ name: "clay-mate-schedules", version: "1.0.0",
1829
+ tools: sessionToolDefs.filter(function (tool) { return yoke.sessionTools.MATE_SCHEDULE_TOOLS.indexOf(tool.name) !== -1; }) });
1830
+ if (!localMcpServers["clay-mate-schedules"]) throw new Error("The adapter could not mount Mate schedule tools.");
1831
+ }
1898
1832
  if (selectedUserInputMode === "fallback") {
1899
1833
  var userInputServer = yoke.userInput.createFallbackServer(sessionAdapter, sessionUserInputHandler, { force: true, maxQuestions: scheduleQuestionLimit });
1834
+ if (queryContract.toolTransport === "sdk-mcp" && !userInputServer) {
1835
+ throw new Error("The adapter could not mount structured-input tools.");
1836
+ }
1900
1837
  if (userInputServer) {
1901
1838
  localMcpServers = Object.assign({}, localMcpServers || {});
1902
1839
  localMcpServers[userInputServer.name || "yoke-user-input"] = userInputServer;
@@ -1931,6 +1868,12 @@ function createSDKBridge(opts) {
1931
1868
  return snapshotDef;
1932
1869
  }
1933
1870
 
1871
+ var externalQueryBridge = require('./mcp-query-bridge').createQueryBridge({ valid: queryContextAvailable,
1872
+ getServers: function () { return getRemoteMcpServers ? getRemoteMcpServers(session) || {} : {}; } });
1873
+ var externalToolDefs = queryContract.externalToolTransport === "dynamic" ? externalQueryBridge.dynamicDefs() : [];
1874
+ if (externalToolDefs.length) { sessionToolDefs = sessionToolDefs.concat(externalToolDefs); queryToolSnapshots.set(session, { generation: queryGeneration, defs: sessionToolDefs.slice() }); }
1875
+ var recoverExternalTools = needsCatalogRecovery() && (externalToolDefs.length > 0 || isMate);
1876
+
1934
1877
  // Give fresh queries an immediate provisional label. The visible user
1935
1878
  // message in history wins over `text`, which may contain an internal
1936
1879
  // provider wrapper rather than what the person typed.
@@ -1945,6 +1888,10 @@ function createSDKBridge(opts) {
1945
1888
  }
1946
1889
 
1947
1890
  var modelSwitchGeneration = session._queryGeneration;
1891
+ function queryRetired() {
1892
+ return Number(session._sdkQueryGeneration || 0) !== queryGeneration || session.taskStopRequested
1893
+ || session.destroying || responseRecovery.stopping(session) || (handle && session.queryInstance !== handle);
1894
+ }
1948
1895
  var queryOpts = {
1949
1896
  onModelSwitch: function(change) {
1950
1897
  if (session._queryGeneration !== modelSwitchGeneration || session.taskStopRequested) return;
@@ -1959,7 +1906,7 @@ function createSDKBridge(opts) {
1959
1906
  clayBridgeToken: clayBridgeToken,
1960
1907
  linuxUser: linuxUser || undefined,
1961
1908
  env: sessionRuntimeEnv,
1962
- skillOptions: getSessionSkillOptions(session),
1909
+ skillOptions: require("./session-skill-scope").bind(session, sm.sessions, getSessionSkillOptions(session), queryContextAvailable, usersModule.isMultiUser()),
1963
1910
  // appendSystemPrompt, NOT systemPrompt: a plain-string systemPrompt
1964
1911
  // REPLACES the entire Claude Code system prompt (sdk.d.ts:2096), which
1965
1912
  // would strip the driver session down to the two-sentence pair preset.
@@ -1967,7 +1914,7 @@ function createSDKBridge(opts) {
1967
1914
  // codex/kiro: prepend to the first message).
1968
1915
  appendSystemPrompt: [
1969
1916
  typeof opts.getSessionSystemPrompt === "function" ? opts.getSessionSystemPrompt(session) : "",
1970
- sessionAdapter.vendor === "codex" && queryContextAvailable() ? codexQuestionGuidance() : "",
1917
+ typeof queryContract.questionGuidance === "function" && queryContextAvailable() ? queryContract.questionGuidance() : "",
1971
1918
  ].filter(Boolean).join("\n\n") || undefined,
1972
1919
  model: queryModel,
1973
1920
  // Effort is remembered per vendor and clamped to the session vendor's
@@ -1984,10 +1931,22 @@ function createSDKBridge(opts) {
1984
1931
  toolServers: mergedMcpServers,
1985
1932
  toolServerDescriptors: extractMcpDescriptors(mergedMcpServers) || undefined,
1986
1933
  dynamicTools: sessionToolDefs,
1987
- // Codex persists dynamic tools with a thread. A stale authorized Driver
1934
+ // Conversation-scoped catalogs persist definitions. A stale authorized Driver
1988
1935
  // gets this exact-query bridge; its handlers still reauthorize live.
1989
- sessionMcpServer: (recoverCodexIssueTools || recoverCodexQuestionTools || recoverCodexBrowserTools || recoverCodexLinearTools || recoverCodexYouTools) ? getCodexSessionMcpServer(session, queryGeneration) : undefined,
1936
+ sessionMcpServer: (recoverMateSchedules || recoverIssueTools || recoverQuestionTools || recoverBrowserTools || recoverLinearTools || recoverYouTools || recoverExternalTools || recoverNoteTools) ? getSessionMcpServer(session, queryGeneration) : undefined,
1937
+ onRecoveryAdmission: function(waiting) {
1938
+ if (!queryContextAvailable()) return;
1939
+ if (waiting) session._providerRecoveryWaiting = queryGeneration;
1940
+ else delete session._providerRecoveryWaiting;
1941
+ },
1942
+ onRecoveryWaiting: function(waiting) {
1943
+ if (!queryContextAvailable()) return;
1944
+ sendAndRecord(session, { type: 'informational', level: 'info', content: waiting
1945
+ ? 'Tool recovery is waiting for this conversation’s runtime to become safely available. Your message is retained; Stop cancels the wait.'
1946
+ : 'Tool recovery is ready. Continuing your retained message.' });
1947
+ },
1990
1948
  callDynamicTool: function (toolName, args) {
1949
+ if (queryRetired() || !queryContextAvailable()) return Promise.reject(new Error("This session tool query is no longer available."));
1991
1950
  var toolDef = findSessionToolDef(toolName);
1992
1951
  if (toolDef && typeof toolDef.handler === "function") {
1993
1952
  return toolDef.handler(args || {});
@@ -1997,7 +1956,10 @@ function createSDKBridge(opts) {
1997
1956
  resumeSessionId: session.cliSessionId || undefined,
1998
1957
  abortController: linuxUser ? undefined : session.abortController,
1999
1958
  canUseTool: function(toolName, input, toolOpts) {
2000
- var nativeQuestionError = toolName === "AskUserQuestion" ? scheduleQuestionError(input) : null;
1959
+ if (queryRetired()) return { behavior: "deny", message: "This attempt has stopped." };
1960
+ responseRecovery.progress(session);
1961
+ var questionInput = yoke.userInput.toolQuestions(sessionAdapter, toolName, input);
1962
+ var nativeQuestionError = questionInput ? scheduleQuestionError(questionInput) : null;
2001
1963
  if (nativeQuestionError) return { behavior: "deny", message: nativeQuestionError };
2002
1964
  var permissionName = toolName;
2003
1965
  var permissionToolDef = findSessionToolDef(toolName);
@@ -2005,7 +1967,10 @@ function createSDKBridge(opts) {
2005
1967
  return handleCanUseTool(session, permissionName, input, toolOpts);
2006
1968
  },
2007
1969
  preToolUsePolicy: function(toolName, input) {
2008
- var nativeQuestionError = toolName === "AskUserQuestion" ? scheduleQuestionError(input) : null;
1970
+ if (queryRetired()) return Promise.resolve({ behavior: "deny", message: "This attempt has stopped." });
1971
+ responseRecovery.progress(session);
1972
+ var questionInput = yoke.userInput.toolQuestions(sessionAdapter, toolName, input);
1973
+ var nativeQuestionError = questionInput ? scheduleQuestionError(questionInput) : null;
2009
1974
  if (nativeQuestionError) return Promise.resolve({ behavior: "deny", message: nativeQuestionError });
2010
1975
  var permissionName = toolName;
2011
1976
  var permissionToolDef = findSessionToolDef(toolName);
@@ -2018,47 +1983,45 @@ function createSDKBridge(opts) {
2018
1983
  validateUserInputRequest: scheduleQuestionError,
2019
1984
  userInputMode: selectedUserInputMode,
2020
1985
  onElicitation: function(request, elicitOpts) {
1986
+ if (queryRetired()) return Promise.resolve({ action: "cancel" });
1987
+ responseRecovery.progress(session);
2021
1988
  return handleElicitation(session, request, elicitOpts);
2022
1989
  },
2023
1990
  onUserDialog: function(dialogRequest, dialogOpts) {
1991
+ if (queryRetired()) return Promise.resolve({ behavior: "cancelled" });
1992
+ responseRecovery.progress(session);
2024
1993
  return handleUserDialog(session, dialogRequest, dialogOpts);
2025
1994
  },
2026
1995
  callMcpTool: function(serverName, toolName, args) {
1996
+ if (queryRetired()) return Promise.reject(new Error("This attempt has stopped."));
1997
+ responseRecovery.progress(session);
2027
1998
  return callMcpToolHandler(mergedMcpServers, serverName, toolName, args);
2028
1999
  },
2029
- adapterOptions: {
2030
- CLAUDE: claudeOpts,
2031
- CODEX: {
2032
- // Always use "never" (auto-approve) because Clay handles tool
2033
- // permissions via its own UI (checkToolWhitelist + handleCanUseTool).
2034
- // Codex's native approval prompts are terminal-based and cannot be
2035
- // relayed through Clay's web UI, causing MCP tool calls to hang.
2036
- approvalPolicy: "never",
2037
- sandboxMode: session.youCurator ? "read-only" : codexConfig.sandbox,
2038
- webSearchMode: session.youCurator ? "disabled" : codexConfig.webSearch,
2039
- },
2040
- ACP: {
2041
- mcpServers: getAcpMcpServers(session),
2042
- },
2043
- ANTIGRAVITY: {
2044
- dangerouslySkipPermissions: dangerouslySkipPermissions
2045
- || session.permissionMode === "bypassPermissions",
2046
- },
2047
- KIRO: {
2048
- engine: kiroConfig.engine,
2049
- mode: kiroConfig.mode,
2050
- // ACP accepts additional stdio MCP servers per session. The bridge is
2051
- // bound to this Clay session so partner calls cannot leak across
2052
- // concurrent Kiro sessions or users.
2053
- mcpServers: getAcpMcpServers(session),
2054
- },
2055
- },
2000
+ adapterOptions: require("./yoke/query-options").map(sessionAdapter, executionIntent),
2056
2001
  };
2057
2002
 
2003
+ if (recoveryState) {
2004
+ if (!recoveryState.context) recoveryState.context = { appendSystemPrompt: queryOpts.appendSystemPrompt, model: queryOpts.model, effort: queryOpts.effort };
2005
+ Object.assign(queryOpts, recoveryState.context);
2006
+ }
2058
2007
  var handle;
2008
+ var startupHandle = session.queryInstance;
2059
2009
  console.log("[sdk-bridge] calling adapter.createQuery... vendor=" + sessionAdapter.vendor);
2060
2010
  try {
2061
2011
  handle = await sessionAdapter.createQuery(queryOpts);
2012
+ // A guarded delivery must still own the exact startup after provider
2013
+ // creation. Never replace a newer handle or publish to a deleted target.
2014
+ if (beforePush && beforePush.exactProductDelivery) {
2015
+ var startupCurrent = false;
2016
+ try { startupCurrent = sm.sessions.get(session.localId) === session && session.queryInstance === startupHandle && Number(session._sdkQueryGeneration || 0) === queryGeneration && beforePush() === true; } catch (guardError) {}
2017
+ if (!startupCurrent) {
2018
+ try { if (handle.abort) handle.abort(); } catch (abortError) {}
2019
+ try { if (handle.close) handle.close(); } catch (closeError) {}
2020
+ if (session.queryInstance === startupHandle) session.isProcessing = false;
2021
+ return false;
2022
+ }
2023
+ }
2024
+ if (recoveryState) recoveryState.handle = handle;
2062
2025
  console.log("[sdk-bridge] createQuery returned handle, vendor=" + sessionAdapter.vendor);
2063
2026
  // SDK accepted the explicit title. Provisional labels remain eligible
2064
2027
  // for semantic generation after the first Home exchange (or the normal
@@ -2078,6 +2041,7 @@ function createSDKBridge(opts) {
2078
2041
  console.error("[sdk-bridge] Failed to create query for session " + session.localId + ":", e.message || e);
2079
2042
  console.error("[sdk-bridge] cliSessionId:", session.cliSessionId, "resume:", !!session.cliSessionId);
2080
2043
  console.error("[sdk-bridge] Stack:", e.stack || "(no stack)");
2044
+ if (beforePush && beforePush.exactProductDelivery && (sm.sessions.get(session.localId) !== session || session.queryInstance !== startupHandle || Number(session._sdkQueryGeneration || 0) !== queryGeneration)) return false;
2081
2045
  session.isProcessing = false;
2082
2046
  session._awaitingTurnResult = false;
2083
2047
  session._queuedTurnCount = 0;
@@ -2112,11 +2076,9 @@ function createSDKBridge(opts) {
2112
2076
 
2113
2077
  // Store QueryHandle on session for iteration and control.
2114
2078
  session.queryInstance = handle;
2115
- // Capabilities come from the installed query handle, never a model-name
2116
- // table. `acceptEdits` and MCP overrides are Claude SDK semantics; a
2117
- // vendor may expose the generic setter without implementing either.
2118
- session.permissionCapabilities = require("./session-permission-mode").capabilities(session, sm.defaultVendor);
2119
- session.permissionCapabilities.mcpOverride = sessionAdapter.vendor === "claude" && typeof handle.setMcpPermissionModeOverride === "function";
2079
+ // An installed setter and the adapter's explicit protocol contract are
2080
+ // both required; generic handle stubs do not establish runtime support.
2081
+ session.permissionCapabilities = require("./session-permission-mode").capabilities(session, sm.defaultVendor, sessionAdapter);
2120
2082
  session.effectivePermissionMode = null;
2121
2083
  sm.saveSessionFile(session);
2122
2084
  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 || [] });
@@ -2132,6 +2094,7 @@ function createSDKBridge(opts) {
2132
2094
  }
2133
2095
 
2134
2096
  if (typeof beforePush === "function" && beforePush() !== true) {
2097
+ try { if (typeof handle.abort === "function") handle.abort(); } catch (e) {}
2135
2098
  try { handle.close(); } catch (e) {}
2136
2099
  if (session.queryInstance === handle) session.queryInstance = null;
2137
2100
  session.messageQueue = null;
@@ -2141,6 +2104,13 @@ function createSDKBridge(opts) {
2141
2104
  session._queuedTurnCount = 0;
2142
2105
  taskLifecycle.clearAll(session);
2143
2106
  onProcessingChanged();
2107
+ if (recoveryState && !responseRecovery.stopping(session)) {
2108
+ sendAndRecord(session, { type: "thinking_stop" });
2109
+ sendAndRecord(session, { type: session.taskStopRequested ? "info" : "error", text: session.taskStopRequested
2110
+ ? "Stopped before sending your message."
2111
+ : "This request is no longer eligible for delivery. Please review it and send it again." });
2112
+ sendAndRecord(session, { type: "done", code: session.taskStopRequested ? 0 : 1 });
2113
+ }
2144
2114
  sm.broadcastSessionList();
2145
2115
  return false;
2146
2116
  }
@@ -2192,32 +2162,28 @@ function createSDKBridge(opts) {
2192
2162
  }
2193
2163
 
2194
2164
  session.lastActivityAt = Date.now();
2195
- var codexCatalogCandidate = null;
2196
- if (sessionAdapter.vendor === "codex" && !resumedAtQueryStart) {
2165
+ var catalogCandidate = null;
2166
+ if (queryContract.catalogLifetime === "conversation" && typeof queryContract.recordCatalog === "function" && !resumedAtQueryStart) {
2197
2167
  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 };
2198
2168
  for (var catalogToolIndex = 0; catalogToolIndex < sessionToolDefs.length; catalogToolIndex++) {
2199
2169
  if (Object.prototype.hasOwnProperty.call(requiredCatalogTools, sessionToolDefs[catalogToolIndex].name)) requiredCatalogTools[sessionToolDefs[catalogToolIndex].name] = true;
2200
2170
  }
2201
2171
  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;
2202
2172
  if (completeInterviewCatalog || requiredCatalogTools.report_scheduled_task_outcome) {
2203
- codexCatalogCandidate = { generation: queryGeneration, version: yoke.sessionTools.CODEX_CATALOG_VERSION };
2204
- }
2205
- if (requiredCatalogTools.ask_user_questions) {
2206
- codexCatalogCandidate = Object.assign({}, codexCatalogCandidate || {}, { generation: queryGeneration, userInputVersion: 1 });
2173
+ catalogCandidate = { generation: queryGeneration, versions: { schedules: yoke.sessionTools.SCHEDULE_CATALOG_VERSION } };
2207
2174
  }
2208
- if (hasYouCatalog) codexCatalogCandidate = Object.assign({}, codexCatalogCandidate || {}, { generation: queryGeneration, youVersion: 1 });
2209
- if (hasLinearCatalog) codexCatalogCandidate = Object.assign({}, codexCatalogCandidate || {}, { generation: queryGeneration, linearVersion: 1 });
2210
- if (hasSharedBrowserCatalog) {
2211
- codexCatalogCandidate = Object.assign({}, codexCatalogCandidate || {}, { generation: queryGeneration, sharedBrowserVersion: 5 });
2175
+ function markCatalog(family, version) {
2176
+ if (!catalogCandidate) catalogCandidate = { generation: queryGeneration, versions: {} };
2177
+ catalogCandidate.versions[family] = version;
2212
2178
  }
2213
- if (hasIssueCatalog) {
2214
- codexCatalogCandidate = Object.assign({}, codexCatalogCandidate || {}, {
2215
- generation: queryGeneration,
2216
- issuesVersion: 1,
2217
- });
2218
- }
2219
- }
2220
- session.streamPromise = processQueryStream(session, codexCatalogCandidate).catch(function(err) {
2179
+ if (requiredCatalogTools.ask_user_questions) markCatalog("questions", 1);
2180
+ if (hasYouCatalog) markCatalog("knowledge", 1);
2181
+ if (hasLinearCatalog) markCatalog("linear", 1);
2182
+ if (hasSharedBrowserCatalog) markCatalog("browser", 6);
2183
+ if (hasIssueCatalog) markCatalog("issues", 1);
2184
+ if (hasNotesCatalog) markCatalog("notes", notesCatalogVersion);
2185
+ }
2186
+ session.streamPromise = processQueryStream(session, catalogCandidate, queryContract).catch(function(err) {
2221
2187
  });
2222
2188
  return true;
2223
2189
  }
@@ -2229,6 +2195,19 @@ function createSDKBridge(opts) {
2229
2195
  // dropped messages in both gap windows, which is what made a session show
2230
2196
  // the processing indicator without ever answering until a second send.
2231
2197
  function pushMessage(session, text, images, deliveryMeta) {
2198
+ if (session._responseRecoveryStopPending && !responseRecovery.stopping(session)) return false;
2199
+ function trackDelivery() {
2200
+ responseRecovery.enqueue(session, {
2201
+ text: text, images: images, linuxUser: session.lastLinuxUser || null,
2202
+ });
2203
+ }
2204
+ if (responseRecovery.stopping(session)) {
2205
+ // Keep admission serialized while the old execution is terminating.
2206
+ session.pendingPush = session.pendingPush || [];
2207
+ session.pendingPush.push(Object.assign({ text: text, images: images || null }, deliveryMeta || {}));
2208
+ trackDelivery();
2209
+ return true;
2210
+ }
2232
2211
  session.lastActivityAt = Date.now();
2233
2212
  if (session._runtimeRefreshRequested && !session._queryStarting) {
2234
2213
  session._runtimeRefreshRequested = false;
@@ -2255,6 +2234,8 @@ function createSDKBridge(opts) {
2255
2234
  console.error("[sdk-bridge] QueryHandle rejected message for session " + session.localId + ":", e.message || e);
2256
2235
  }
2257
2236
  if (delivered) {
2237
+ require('./sdk-foreground-admission').take(session);
2238
+ trackDelivery();
2258
2239
  delete session._lastTurnInterrupted;
2259
2240
  if (session._awaitingTurnResult) {
2260
2241
  session._queuedTurnCount = (session._queuedTurnCount || 0) + 1;
@@ -2279,6 +2260,7 @@ function createSDKBridge(opts) {
2279
2260
  if (session._queryStarting) {
2280
2261
  session.pendingPush = session.pendingPush || [];
2281
2262
  session.pendingPush.push(Object.assign({ text: text, images: images || null }, deliveryMeta || {}));
2263
+ trackDelivery();
2282
2264
  return true;
2283
2265
  }
2284
2266
  return false;
@@ -2359,65 +2341,8 @@ function createSDKBridge(opts) {
2359
2341
  // Detect which vendor binaries are installed for this user.
2360
2342
  // In multi-user mode, runs checks as the specific Linux user.
2361
2343
  function detectInstalledVendors(linuxUser) {
2362
- var execFileSync = require("child_process").execFileSync;
2363
- var fs = require("fs");
2364
- var result = [];
2365
-
2366
- function tryLookup(name) {
2367
- try {
2368
- if (linuxUser) {
2369
- execFileSync("su", ["-", linuxUser, "-c", "which " + name], { timeout: 3000, stdio: ["pipe", "pipe", "pipe"] });
2370
- } else {
2371
- if (process.platform === "win32") execFileSync("where", [name], { timeout: 3000, stdio: ["pipe", "pipe", "pipe"] });
2372
- else execFileSync("which", [name], { timeout: 3000, stdio: ["pipe", "pipe", "pipe"] });
2373
- }
2374
- return true;
2375
- } catch (e) {
2376
- return false;
2377
- }
2378
- }
2379
-
2380
- // Claude: check if binary is in PATH
2381
- if (tryLookup(yoke.getVendorInfo("claude").binaryName)) result.push("claude");
2382
-
2383
- // Codex: check bundled binary or PATH
2384
- var codexBin = null;
2385
- try {
2386
- codexBin = require("./yoke/codex-app-server").findCodexPath();
2387
- } catch (e) {}
2388
- if ((codexBin && fs.existsSync(codexBin)) || tryLookup(yoke.getVendorInfo("codex").binaryName)) result.push("codex");
2389
-
2390
- var subprocessVendorKeys = ["antigravity", "opencode", "kimi", "grok", "copilot", "qwen", "junie"];
2391
- var acpProfiles = require("./yoke/acp-agent-profiles");
2392
- for (var subprocessVendorIndex = 0; subprocessVendorIndex < subprocessVendorKeys.length; subprocessVendorIndex++) {
2393
- var subprocessVendor = subprocessVendorKeys[subprocessVendorIndex];
2394
- var subprocessInfo = yoke.getVendorInfo(subprocessVendor);
2395
- if (!linuxUser || subprocessInfo.osUserIsolation) {
2396
- if (subprocessVendor === "antigravity") {
2397
- if (tryLookup(subprocessInfo.binaryName)) result.push(subprocessVendor);
2398
- } else {
2399
- var acpProfile = acpProfiles.getAcpAgentProfile(subprocessVendor);
2400
- if (acpProfiles.findAcpAgentPath(acpProfile)) result.push(subprocessVendor);
2401
- }
2402
- } else {
2403
- console.log("[sdk-bridge] " + subprocessInfo.displayName + " hidden for OS-isolated user " + linuxUser + ": per-user spawning is not implemented");
2404
- }
2405
- }
2406
-
2407
- // Kiro has no per-user ACP spawn path yet. Do not advertise the daemon's
2408
- // binary to an OS-isolated user, even if that user also has Kiro installed.
2409
- var kiroInfo = yoke.getVendorInfo("kiro");
2410
- if (!linuxUser || kiroInfo.osUserIsolation) {
2411
- var kiroBin = null;
2412
- try {
2413
- kiroBin = require("./yoke/kiro-acp-server").findKiroPath();
2414
- } catch (e) {}
2415
- if ((kiroBin && fs.existsSync(kiroBin)) || tryLookup(kiroInfo.binaryName)) result.push("kiro");
2416
- } else {
2417
- console.log("[sdk-bridge] " + kiroInfo.displayName + " hidden for OS-isolated user " + linuxUser + ": per-user spawning is not implemented");
2418
- }
2419
-
2420
- return result;
2344
+ var installed = require("./yoke/discovery").installed({ user: linuxUser });
2345
+ return Object.keys(installed).filter(function(vendor) { return installed[vendor]; });
2421
2346
  }
2422
2347
 
2423
2348
  function getAvailableVendors(linuxUser) {
@@ -2489,7 +2414,20 @@ function createSDKBridge(opts) {
2489
2414
  sendModelInfoForVendor(null, defaultVendor, defaultModelForVendor(defaultVendor));
2490
2415
  }
2491
2416
 
2492
- async function setModel(session, model) {
2417
+ async function setModel(session, model, options) {
2418
+ if (options && typeof options.revalidate === "function") {
2419
+ var vendor = session.vendor || sm.defaultVendor || "claude";
2420
+ return require("./session-config-transition").applySessionConfig({
2421
+ sm: sm, session: session, revalidate: options.revalidate, method: "setModel", nativeValue: model, changes: { model: model },
2422
+ publish: function () {
2423
+ sm.defaultModelByVendor = sm.defaultModelByVendor || {};
2424
+ sm.defaultModelByVendor[vendor] = model;
2425
+ sm.broadcastSessionList();
2426
+ sendModelInfoForVendor(session, vendor, model);
2427
+ sendToSession(session, { type: "config_state", sessionId: session.localId, vendor: vendor, model: model, effort: session.effort || sm.currentEffort || "medium", mode: session.permissionMode || sm.currentPermissionMode || "default", betas: sm.currentBetas || [] });
2428
+ },
2429
+ });
2430
+ }
2493
2431
  // Normalize to string id in case a { value, displayName } object slips in
2494
2432
  if (model && typeof model !== "string") {
2495
2433
  model = modelEntryValue(model);
@@ -2525,7 +2463,22 @@ function createSDKBridge(opts) {
2525
2463
  }
2526
2464
  }
2527
2465
 
2528
- async function setEffort(session, effort) {
2466
+ async function setEffort(session, effort, options) {
2467
+ if (options && typeof options.revalidate === "function") {
2468
+ var vendor = session.vendor || sm.defaultVendor || "claude";
2469
+ var value = yoke.clampEffort(vendor, effort);
2470
+ if (!value || value !== effort) throw new Error("Unsupported reasoning effort.");
2471
+ return require("./session-config-transition").applySessionConfig({
2472
+ sm: sm, session: session, revalidate: options.revalidate, method: "setEffort", nativeValue: value, changes: { effort: value },
2473
+ publish: function () {
2474
+ sm.currentEffortByVendor = sm.currentEffortByVendor || {};
2475
+ sm.currentEffortByVendor[vendor] = value;
2476
+ sm.currentEffort = value;
2477
+ sm.broadcastSessionList();
2478
+ sendToSession(session, { type: "config_state", sessionId: session.localId, vendor: vendor, effort: value, model: session.model || "", mode: session.permissionMode || sm.currentPermissionMode || "default", betas: sm.currentBetas || [] });
2479
+ },
2480
+ });
2481
+ }
2529
2482
  // Remember the pick per vendor (clamped) so vendor switches don't leak
2530
2483
  // unsupported levels across sessions; sm.currentEffort stays as the
2531
2484
  // display value for the session the user just configured.
@@ -2646,16 +2599,7 @@ function createSDKBridge(opts) {
2646
2599
  message: "Read-only access. You cannot make changes via @mention.",
2647
2600
  });
2648
2601
  },
2649
- adapterOptions: {
2650
- CLAUDE: {
2651
- settingSources: ["user"],
2652
- includePartialMessages: true,
2653
- // Explicit, not omitted: SDK 0.3.286+ leaves an omitted
2654
- // permissionMode to Claude Code's own default/auto behavior,
2655
- // which could bypass the deny-by-default canUseTool above.
2656
- permissionMode: "default",
2657
- },
2658
- },
2602
+ adapterOptions: require("./yoke/query-options").map(mentionAdapter, { instructionSources: ["user"], readOnly: true }),
2659
2603
  });
2660
2604
  } catch (e) {
2661
2605
  opts.onError("Failed to create mention query: " + (e.message || e));
@@ -2794,12 +2738,14 @@ function createSDKBridge(opts) {
2794
2738
  startQuery: startQuery,
2795
2739
  startQueryWithAcceptance: startQueryWithAcceptance,
2796
2740
  pushMessage: pushMessage,
2741
+ cancelResponseRecovery: responseRecovery.cancel,
2797
2742
  refreshSessionRuntime: refreshSessionRuntime,
2798
2743
  refreshEnvironmentRuntime: refreshEnvironmentRuntime,
2799
2744
  setModel: setModel,
2800
2745
  setEffort: setEffort,
2801
2746
  setPermissionMode: setPermissionMode,
2802
- isClaudeProcess: isClaudeProcess,
2747
+ isProviderProcess: isProviderProcess,
2748
+ isClaudeProcess: isProviderProcess, // Compatibility export; production uses the provider contract.
2803
2749
  permissionPushTitle: permissionPushTitle,
2804
2750
  permissionPushBody: permissionPushBody,
2805
2751
  warmup: warmup,