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

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 (552) hide show
  1. package/lib/account-auth-socket.js +1 -0
  2. package/lib/builtin-mates.js +19 -16
  3. package/lib/bundled-skills/clay-agent-browser/SKILL.md +6 -2
  4. package/lib/canonical-clay-record-authority.js +15 -0
  5. package/lib/canonical-clay-scheduling-authority.js +19 -0
  6. package/lib/claude-hook-installer.js +1 -239
  7. package/lib/claude-jsonl-watcher.js +1 -204
  8. package/lib/clay-control-presentation-binding.js +96 -0
  9. package/lib/clay-control-presentation.js +128 -0
  10. package/lib/clay-control-target-projection.js +26 -0
  11. package/lib/clay-mate-chat-receipts.js +26 -0
  12. package/lib/clay-mate-chat.js +88 -0
  13. package/lib/clay-mate-knowledge-input.js +26 -0
  14. package/lib/clay-mate-knowledge.js +95 -0
  15. package/lib/clay-mate-lifecycle.js +95 -0
  16. package/lib/clay-product-control-input.js +33 -0
  17. package/lib/clay-product-control-tools.js +51 -0
  18. package/lib/clay-product-control.js +247 -0
  19. package/lib/clay-project-lifecycle.js +154 -0
  20. package/lib/clay-project-record-input.js +44 -0
  21. package/lib/clay-project-records.js +84 -0
  22. package/lib/clay-scheduling-input.js +21 -0
  23. package/lib/clay-scheduling-receipts.js +26 -0
  24. package/lib/clay-scheduling.js +119 -0
  25. package/lib/clay-screen-controls.js +104 -0
  26. package/lib/clay-skill-install.js +1 -33
  27. package/lib/clay-workspace-controls.js +84 -0
  28. package/lib/clay-workspace-input.js +17 -0
  29. package/lib/clay-workspace-receipts.js +26 -0
  30. package/lib/cli-sessions.js +1 -300
  31. package/lib/codex-defaults.js +1 -28
  32. package/lib/context-usage.js +3 -2
  33. package/lib/contextual-screen-snapshot.js +38 -0
  34. package/lib/daemon-project-access.js +11 -0
  35. package/lib/daemon-project-lifecycle-callbacks.js +15 -0
  36. package/lib/daemon-project-lifecycle.js +262 -0
  37. package/lib/daemon-projects.js +1 -1
  38. package/lib/daemon.js +78 -276
  39. package/lib/default-ai-runtime.js +4 -8
  40. package/lib/dm-digest-cursor.js +120 -0
  41. package/lib/email-mcp-server.js +1 -1
  42. package/lib/issues-service.js +29 -15
  43. package/lib/kiro-defaults.js +1 -22
  44. package/lib/knowledge-db-import-validation.js +46 -0
  45. package/lib/knowledge-db-import.js +99 -0
  46. package/lib/knowledge-db-service.js +24 -33
  47. package/lib/knowledge-db-storage-nedb-maintenance.js +99 -0
  48. package/lib/knowledge-db-storage-nedb.js +66 -0
  49. package/lib/knowledge-db-storage.js +72 -0
  50. package/lib/knowledge-db-validation.js +0 -0
  51. package/lib/knowledge-document-index.js +2 -1
  52. package/lib/knowledge-file-store.js +7 -1
  53. package/lib/knowledge-product-operations.js +46 -0
  54. package/lib/mate-avatar-catalog.js +12 -0
  55. package/lib/mate-avatar-migration.js +41 -0
  56. package/lib/mate-context-service.js +138 -0
  57. package/lib/mate-context-tools.js +21 -0
  58. package/lib/mate-creation-mcp-server.js +2 -0
  59. package/lib/mate-knowledge-canonical-access.js +10 -0
  60. package/lib/mate-knowledge-mate-bound.js +53 -0
  61. package/lib/mate-knowledge-mcp-server.js +55 -8
  62. package/lib/mate-knowledge-service.js +16 -34
  63. package/lib/mate-knowledge-text-tools.js +106 -0
  64. package/lib/mate-lifecycle-service.js +94 -0
  65. package/lib/mate-message-delivery.js +43 -0
  66. package/lib/mate-ready-creation.js +9 -1
  67. package/lib/mate-schedule-input.js +29 -0
  68. package/lib/mate-schedule-lifecycle.js +151 -0
  69. package/lib/mate-scheduling.js +103 -0
  70. package/lib/mates.js +18 -6
  71. package/lib/mcp-agent-tools.js +117 -0
  72. package/lib/mcp-connection-store.js +119 -15
  73. package/lib/mcp-import-credentials.js +31 -0
  74. package/lib/mcp-local-transport.js +13 -0
  75. package/lib/mcp-oauth.js +8 -6
  76. package/lib/mcp-proxy.js +2 -32
  77. package/lib/mcp-query-bridge.js +30 -0
  78. package/lib/mcp-remote-connections.js +74 -20
  79. package/lib/mcp-source-adapters.js +1 -0
  80. package/lib/mcp-source-context.js +1 -0
  81. package/lib/mcp-source-discovery.js +1 -0
  82. package/lib/mcp-source-plugins.js +1 -0
  83. package/lib/mcp-source-restrictions.js +1 -0
  84. package/lib/mcp-tool-policy.js +9 -0
  85. package/lib/notes-checklist.js +50 -0
  86. package/lib/notes.js +49 -3
  87. package/lib/os-user-diagnostics.js +2 -4
  88. package/lib/os-users.js +24 -70
  89. package/lib/pane-pending-interactions.js +20 -0
  90. package/lib/pane-session-activation.js +114 -0
  91. package/lib/pane-session-transport.js +26 -0
  92. package/lib/project-admin-overview.js +51 -0
  93. package/lib/project-ask-user.js +8 -4
  94. package/lib/project-connection.js +14 -10
  95. package/lib/project-control-git-view.js +12 -0
  96. package/lib/project-debate.js +1 -0
  97. package/lib/project-delegated-follow-up.js +2 -1
  98. package/lib/project-directory-claim.js +65 -0
  99. package/lib/project-file-operations.js +61 -0
  100. package/lib/project-filesystem.js +15 -85
  101. package/lib/project-full-access.js +23 -2
  102. package/lib/project-http.js +7 -4
  103. package/lib/project-knowledge.js +32 -18
  104. package/lib/project-lifecycle-operations.js +98 -0
  105. package/lib/project-lifecycle-process-witness.js +32 -0
  106. package/lib/project-lifecycle-runtime.js +50 -0
  107. package/lib/project-linux-access.js +22 -0
  108. package/lib/project-logs-canonical-clay.js +27 -0
  109. package/lib/project-logs-clay-query.js +16 -0
  110. package/lib/project-logs-schema.js +4 -2
  111. package/lib/project-logs-service.js +8 -7
  112. package/lib/project-logs.js +1 -0
  113. package/lib/project-loop.js +1 -0
  114. package/lib/project-mate-chat-control.js +55 -0
  115. package/lib/project-mate-creation-proposal.js +18 -3
  116. package/lib/project-mate-interaction.js +40 -62
  117. package/lib/project-mcp.js +54 -16
  118. package/lib/project-models.js +14 -31
  119. package/lib/project-notifications.js +76 -17
  120. package/lib/project-ownership.js +77 -0
  121. package/lib/project-pair-result-delivery.js +14 -3
  122. package/lib/project-pair-usage.js +12 -13
  123. package/lib/project-pending-message-queue.js +4 -1
  124. package/lib/project-product-control.js +89 -0
  125. package/lib/project-product-session-queue.js +46 -0
  126. package/lib/project-push-access.js +28 -0
  127. package/lib/project-scheduled-message-queue.js +30 -0
  128. package/lib/project-scheduled-messages.js +67 -68
  129. package/lib/project-scheduled-task-control.js +83 -0
  130. package/lib/project-scheduled-tasks.js +13 -2
  131. package/lib/project-scheduling-access.js +10 -0
  132. package/lib/project-session-actions.js +65 -0
  133. package/lib/project-session-delete.js +8 -1
  134. package/lib/project-session-document.js +27 -7
  135. package/lib/project-session-github.js +63 -19
  136. package/lib/project-session-history-actions.js +167 -0
  137. package/lib/project-session-linear.js +14 -6
  138. package/lib/project-session-notes.js +7 -3
  139. package/lib/project-session-pair.js +1 -1
  140. package/lib/project-session-runtime-actions.js +64 -0
  141. package/lib/project-session-spawn.js +2 -3
  142. package/lib/project-session-vendor.js +55 -0
  143. package/lib/project-sessions.js +103 -272
  144. package/lib/project-shared-browser.js +78 -17
  145. package/lib/project-studio-presentation.js +58 -0
  146. package/lib/project-user-message.js +67 -1
  147. package/lib/project-vendor-login.js +6 -36
  148. package/lib/project-workspace-operations.js +126 -0
  149. package/lib/project-workspace-query.js +19 -2
  150. package/lib/project.js +178 -45
  151. package/lib/public/app.js +46 -0
  152. package/lib/public/appearance-bootstrap.js +16 -0
  153. package/lib/public/css/clay-tool-activity.css +51 -0
  154. package/lib/public/css/clay-workspace.css +29 -0
  155. package/lib/public/css/command-palette.css +12 -33
  156. package/lib/public/css/contextual-clay.css +240 -0
  157. package/lib/public/css/home-debate-planning.css +2 -0
  158. package/lib/public/css/home-mate-creation.css +5 -0
  159. package/lib/public/css/input.css +6 -0
  160. package/lib/public/css/mate-avatar-gallery.css +12 -0
  161. package/lib/public/css/mate-conversation.css +10 -5
  162. package/lib/public/css/messages.css +6 -2
  163. package/lib/public/css/overlays.css +4 -2
  164. package/lib/public/css/project-admin-overview.css +13 -0
  165. package/lib/public/css/session-folders.css +11 -0
  166. package/lib/public/css/session-github.css +46 -1
  167. package/lib/public/css/shared-browser.css +35 -1
  168. package/lib/public/css/sticky-notes.css +87 -1
  169. package/lib/public/index.html +11 -5
  170. package/lib/public/mate-avatars/B01.png +0 -0
  171. package/lib/public/mate-avatars/B02.png +0 -0
  172. package/lib/public/mate-avatars/B03.png +0 -0
  173. package/lib/public/mate-avatars/B04.png +0 -0
  174. package/lib/public/mate-avatars/B05.png +0 -0
  175. package/lib/public/mate-avatars/B06.png +0 -0
  176. package/lib/public/mate-avatars/B07.png +0 -0
  177. package/lib/public/mate-avatars/B08.png +0 -0
  178. package/lib/public/mate-avatars/B09.png +0 -0
  179. package/lib/public/mate-avatars/B10.png +0 -0
  180. package/lib/public/mate-avatars/B11.png +0 -0
  181. package/lib/public/mate-avatars/B12.png +0 -0
  182. package/lib/public/mate-avatars/B13.png +0 -0
  183. package/lib/public/mate-avatars/B14.png +0 -0
  184. package/lib/public/mate-avatars/B15.png +0 -0
  185. package/lib/public/mate-avatars/B16.png +0 -0
  186. package/lib/public/mate-avatars/B17.png +0 -0
  187. package/lib/public/mate-avatars/B18.png +0 -0
  188. package/lib/public/mate-avatars/B19.png +0 -0
  189. package/lib/public/mate-avatars/B20.png +0 -0
  190. package/lib/public/mate-avatars/B21.png +0 -0
  191. package/lib/public/mate-avatars/B22.png +0 -0
  192. package/lib/public/mate-avatars/B23.png +0 -0
  193. package/lib/public/mate-avatars/B24.png +0 -0
  194. package/lib/public/mate-avatars/B25.png +0 -0
  195. package/lib/public/mate-avatars/B26.png +0 -0
  196. package/lib/public/mate-avatars/B27.png +0 -0
  197. package/lib/public/mate-avatars/B28.png +0 -0
  198. package/lib/public/mate-avatars/B29.png +0 -0
  199. package/lib/public/mate-avatars/B30.png +0 -0
  200. package/lib/public/mate-avatars/B31.png +0 -0
  201. package/lib/public/mate-avatars/B32.png +0 -0
  202. package/lib/public/mate-avatars/B33.png +0 -0
  203. package/lib/public/mate-avatars/B34.png +0 -0
  204. package/lib/public/mate-avatars/B35.png +0 -0
  205. package/lib/public/mate-avatars/B36.png +0 -0
  206. package/lib/public/mate-avatars/B37.png +0 -0
  207. package/lib/public/mate-avatars/B38.png +0 -0
  208. package/lib/public/mate-avatars/B39.png +0 -0
  209. package/lib/public/mate-avatars/B40.png +0 -0
  210. package/lib/public/mate-avatars/B41.png +0 -0
  211. package/lib/public/mate-avatars/B42.png +0 -0
  212. package/lib/public/mate-avatars/B43.png +0 -0
  213. package/lib/public/mate-avatars/B44.png +0 -0
  214. package/lib/public/mate-avatars/B45.png +0 -0
  215. package/lib/public/mate-avatars/B46.png +0 -0
  216. package/lib/public/mate-avatars/B47.png +0 -0
  217. package/lib/public/mate-avatars/B48.png +0 -0
  218. package/lib/public/mate-avatars/C01.png +0 -0
  219. package/lib/public/mate-avatars/C02.png +0 -0
  220. package/lib/public/mate-avatars/C03.png +0 -0
  221. package/lib/public/mate-avatars/C04.png +0 -0
  222. package/lib/public/mate-avatars/C05.png +0 -0
  223. package/lib/public/mate-avatars/C06.png +0 -0
  224. package/lib/public/mate-avatars/C07.png +0 -0
  225. package/lib/public/mate-avatars/C08.png +0 -0
  226. package/lib/public/mate-avatars/C09.png +0 -0
  227. package/lib/public/mate-avatars/C10.png +0 -0
  228. package/lib/public/mate-avatars/C11.png +0 -0
  229. package/lib/public/mate-avatars/C12.png +0 -0
  230. package/lib/public/mate-avatars/C13.png +0 -0
  231. package/lib/public/mate-avatars/C14.png +0 -0
  232. package/lib/public/mate-avatars/C15.png +0 -0
  233. package/lib/public/mate-avatars/C16.png +0 -0
  234. package/lib/public/mate-avatars/C17.png +0 -0
  235. package/lib/public/mate-avatars/C18.png +0 -0
  236. package/lib/public/mate-avatars/C19.png +0 -0
  237. package/lib/public/mate-avatars/C20.png +0 -0
  238. package/lib/public/mate-avatars/C21.png +0 -0
  239. package/lib/public/mate-avatars/C22.png +0 -0
  240. package/lib/public/mate-avatars/C23.png +0 -0
  241. package/lib/public/mate-avatars/C24.png +0 -0
  242. package/lib/public/mate-avatars/C25.png +0 -0
  243. package/lib/public/mate-avatars/C26.png +0 -0
  244. package/lib/public/mate-avatars/C27.png +0 -0
  245. package/lib/public/mate-avatars/C28.png +0 -0
  246. package/lib/public/mate-avatars/C29.png +0 -0
  247. package/lib/public/mate-avatars/C30.png +0 -0
  248. package/lib/public/mate-avatars/C31.png +0 -0
  249. package/lib/public/mate-avatars/C32.png +0 -0
  250. package/lib/public/mate-avatars/C33.png +0 -0
  251. package/lib/public/mate-avatars/C34.png +0 -0
  252. package/lib/public/mate-avatars/C35.png +0 -0
  253. package/lib/public/mate-avatars/C36.png +0 -0
  254. package/lib/public/mate-avatars/C37.png +0 -0
  255. package/lib/public/mate-avatars/C38.png +0 -0
  256. package/lib/public/mate-avatars/C39.png +0 -0
  257. package/lib/public/mate-avatars/C40.png +0 -0
  258. package/lib/public/mate-avatars/C41.png +0 -0
  259. package/lib/public/mate-avatars/C42.png +0 -0
  260. package/lib/public/mate-avatars/C43.png +0 -0
  261. package/lib/public/mate-avatars/C44.png +0 -0
  262. package/lib/public/mate-avatars/C45.png +0 -0
  263. package/lib/public/mate-avatars/C46.png +0 -0
  264. package/lib/public/mate-avatars/C47.png +0 -0
  265. package/lib/public/mate-avatars/C48.png +0 -0
  266. package/lib/public/mate-avatars/catalog.json +482 -0
  267. package/lib/public/mate-avatars/lead-clay.png +0 -0
  268. package/lib/public/modules/admin-settings-projects.js +6 -2
  269. package/lib/public/modules/app-connection.js +11 -1
  270. package/lib/public/modules/app-favicon.js +18 -9
  271. package/lib/public/modules/app-header.js +7 -4
  272. package/lib/public/modules/app-loop-ui.js +2 -0
  273. package/lib/public/modules/app-message-router.js +22 -1
  274. package/lib/public/modules/app-messages.js +115 -17
  275. package/lib/public/modules/app-misc.js +3 -1
  276. package/lib/public/modules/app-notifications.js +11 -5
  277. package/lib/public/modules/app-panels.js +61 -123
  278. package/lib/public/modules/app-projects.js +13 -0
  279. package/lib/public/modules/app-rate-limit.js +7 -27
  280. package/lib/public/modules/app-rendering.js +4 -3
  281. package/lib/public/modules/avatar.js +15 -41
  282. package/lib/public/modules/chat-render-runtime.js +20 -0
  283. package/lib/public/modules/clay-browser-links.js +47 -0
  284. package/lib/public/modules/clay-control-activity.js +214 -0
  285. package/lib/public/modules/clay-control-copy.js +36 -0
  286. package/lib/public/modules/clay-control-detail-targets.js +44 -0
  287. package/lib/public/modules/clay-control-object-targets.js +52 -0
  288. package/lib/public/modules/clay-control-target-visibility.js +12 -0
  289. package/lib/public/modules/clay-control-targets.js +44 -0
  290. package/lib/public/modules/clay-log-links.js +23 -0
  291. package/lib/public/modules/clay-navigation-render.js +52 -0
  292. package/lib/public/modules/clay-owner-navigation.js +33 -0
  293. package/lib/public/modules/clay-session-links.js +31 -0
  294. package/lib/public/modules/clay-studio-bridge.js +20 -0
  295. package/lib/public/modules/clay-studio-document.js +67 -0
  296. package/lib/public/modules/clay-studio-render.js +146 -0
  297. package/lib/public/modules/clay-tool-activity.js +140 -0
  298. package/lib/public/modules/clay-tool-presentation.js +342 -0
  299. package/lib/public/modules/command-palette.js +162 -40
  300. package/lib/public/modules/contextual-ask-session.js +26 -0
  301. package/lib/public/modules/contextual-clay-geometry.js +155 -0
  302. package/lib/public/modules/contextual-clay-session.js +130 -0
  303. package/lib/public/modules/contextual-clay-workspace.js +169 -0
  304. package/lib/public/modules/contextual-clay.js +250 -0
  305. package/lib/public/modules/contextual-composer.js +91 -0
  306. package/lib/public/modules/contextual-mate-context.js +117 -0
  307. package/lib/public/modules/contextual-mate-hint.js +40 -0
  308. package/lib/public/modules/contextual-mate-identity.js +23 -0
  309. package/lib/public/modules/contextual-mate-sessions.js +134 -0
  310. package/lib/public/modules/contextual-search.js +137 -0
  311. package/lib/public/modules/contextual-session-citations.js +58 -0
  312. package/lib/public/modules/debate.js +1 -0
  313. package/lib/public/modules/default-ai.js +11 -3
  314. package/lib/public/modules/elicitation-form.js +5 -0
  315. package/lib/public/modules/filebrowser.js +20 -1
  316. package/lib/public/modules/generation-stop.js +8 -5
  317. package/lib/public/modules/git-panel.js +3 -1
  318. package/lib/public/modules/history-replay-batch.js +13 -1
  319. package/lib/public/modules/home-debate-live.js +2 -0
  320. package/lib/public/modules/home-debate-planning.js +7 -7
  321. package/lib/public/modules/home-mate-avatar-editor.js +127 -208
  322. package/lib/public/modules/home-mate-chat.js +8 -6
  323. package/lib/public/modules/home-mate-creation.js +73 -1
  324. package/lib/public/modules/home-mate-settings.js +19 -12
  325. package/lib/public/modules/icon-strip-tooltip.js +2 -1
  326. package/lib/public/modules/input.js +88 -10
  327. package/lib/public/modules/issues-render.js +3 -2
  328. package/lib/public/modules/knowledge-db-workbench.js +4 -4
  329. package/lib/public/modules/markdown.js +6 -34
  330. package/lib/public/modules/mate-avatar-catalog.js +97 -0
  331. package/lib/public/modules/mate-knowledge-workbench.js +1 -1
  332. package/lib/public/modules/mate-management.js +40 -7
  333. package/lib/public/modules/mcp-connection-view.js +31 -13
  334. package/lib/public/modules/mcp-discovery-view.js +31 -0
  335. package/lib/public/modules/mcp-ui.js +23 -4
  336. package/lib/public/modules/mention.js +3 -0
  337. package/lib/public/modules/message-delivery.js +20 -2
  338. package/lib/public/modules/model-picker.js +2 -0
  339. package/lib/public/modules/pane-activation.js +214 -0
  340. package/lib/public/modules/pane-binding.js +82 -0
  341. package/lib/public/modules/pane-bridge.js +7 -4
  342. package/lib/public/modules/pane-browser-bridge.js +22 -0
  343. package/lib/public/modules/pane-file-bridge.js +4 -0
  344. package/lib/public/modules/pane-issue-bridge.js +4 -1
  345. package/lib/public/modules/pending-message-queue.js +1 -0
  346. package/lib/public/modules/permission-control.js +5 -4
  347. package/lib/public/modules/personal-settings-dialog.js +7 -1
  348. package/lib/public/modules/product-review.js +26 -0
  349. package/lib/public/modules/profile.js +1 -238
  350. package/lib/public/modules/project-access-recovery.js +17 -0
  351. package/lib/public/modules/project-admin-overview.js +21 -0
  352. package/lib/public/modules/project-chat-identity.js +13 -4
  353. package/lib/public/modules/project-logs-render.js +3 -0
  354. package/lib/public/modules/project-mate-navigation.js +144 -22
  355. package/lib/public/modules/rewind.js +5 -0
  356. package/lib/public/modules/scheduled-tasks.js +2 -0
  357. package/lib/public/modules/scheduler.js +2 -0
  358. package/lib/public/modules/search-clay-chat.js +5 -440
  359. package/lib/public/modules/session-actions.js +5 -2
  360. package/lib/public/modules/session-context-menu.js +5 -0
  361. package/lib/public/modules/session-create-form.js +10 -10
  362. package/lib/public/modules/session-folder-layout.js +1 -1
  363. package/lib/public/modules/session-folder-view-menu.js +9 -0
  364. package/lib/public/modules/session-github.js +85 -14
  365. package/lib/public/modules/session-rich-drafts.js +21 -0
  366. package/lib/public/modules/session-switch-defer.js +67 -0
  367. package/lib/public/modules/session-work-primary.js +51 -0
  368. package/lib/public/modules/settings-defaults.js +27 -28
  369. package/lib/public/modules/shared-browser-settings.js +112 -0
  370. package/lib/public/modules/shared-browser-tabs.js +13 -3
  371. package/lib/public/modules/shared-browser.js +29 -6
  372. package/lib/public/modules/sidebar-mobile.js +4 -3
  373. package/lib/public/modules/sidebar-projects.js +1 -0
  374. package/lib/public/modules/sidebar-session-folders-ui.js +1 -1
  375. package/lib/public/modules/sidebar-sessions.js +1 -0
  376. package/lib/public/modules/split-group-restore.js +68 -0
  377. package/lib/public/modules/split-pair-ui.js +1 -0
  378. package/lib/public/modules/split-pane-reconciler.js +15 -0
  379. package/lib/public/modules/split-pane-renderer.js +87 -6
  380. package/lib/public/modules/split-session-boundary.js +1 -1
  381. package/lib/public/modules/split-view.js +60 -68
  382. package/lib/public/modules/sticky-note-markdown.js +72 -6
  383. package/lib/public/modules/sticky-notes-actions.js +30 -0
  384. package/lib/public/modules/sticky-notes-browser.js +9 -2
  385. package/lib/public/modules/sticky-notes-card.js +23 -7
  386. package/lib/public/modules/sticky-notes-checklist.js +116 -0
  387. package/lib/public/modules/sticky-notes-editor.js +108 -44
  388. package/lib/public/modules/sticky-notes-shared.js +50 -2
  389. package/lib/public/modules/sticky-notes-state.js +66 -0
  390. package/lib/public/modules/sticky-notes.js +34 -51
  391. package/lib/public/modules/stt-controller.js +4 -1
  392. package/lib/public/modules/stt-recording.js +1 -1
  393. package/lib/public/modules/stt.js +7 -2
  394. package/lib/public/modules/terminal.js +9 -0
  395. package/lib/public/modules/theme.js +8 -1
  396. package/lib/public/modules/thinking-lifecycle.js +33 -3
  397. package/lib/public/modules/thinking-view.js +23 -1
  398. package/lib/public/modules/tools.js +60 -11
  399. package/lib/public/modules/tui-grab.js +1 -2
  400. package/lib/public/modules/user-settings-appearance.js +10 -3
  401. package/lib/public/modules/user-settings-editors.js +1 -1
  402. package/lib/public/modules/user-settings-layout.js +11 -2
  403. package/lib/public/modules/user-settings-preferences.js +6 -2
  404. package/lib/public/modules/user-settings-profile.js +12 -2
  405. package/lib/public/modules/user-settings.js +3 -2
  406. package/lib/public/modules/vendor-login-browser.js +5 -3
  407. package/lib/public/modules/vendor-login.js +2 -2
  408. package/lib/public/modules/vendor-priority.js +15 -1
  409. package/lib/public/modules/wireframe-workbench.js +5 -2
  410. package/lib/public/muse-avatar.svg +4 -0
  411. package/lib/public/style.css +5 -0
  412. package/lib/public/sw.js +3 -1
  413. package/lib/safe-bash-commands.js +3 -45
  414. package/lib/scheduled-task-records.js +3 -1
  415. package/lib/scheduler.js +2 -0
  416. package/lib/sdk-bridge.js +342 -401
  417. package/lib/sdk-foreground-admission.js +16 -0
  418. package/lib/sdk-mcp-descriptors.js +2 -37
  419. package/lib/sdk-message-processor.js +52 -17
  420. package/lib/sdk-quota-snapshot.js +1 -1
  421. package/lib/sdk-response-recovery.js +1 -0
  422. package/lib/sdk-worker.js +2 -677
  423. package/lib/server-admin.js +63 -16
  424. package/lib/server-appearance-settings.js +13 -1
  425. package/lib/server-contextual-clay-settings.js +40 -0
  426. package/lib/server-contextual-mate-sessions.js +70 -0
  427. package/lib/server-dm.js +2 -1
  428. package/lib/server-global-ws.js +10 -20
  429. package/lib/server-home-chat-events.js +18 -4
  430. package/lib/server-home-chat.js +22 -20
  431. package/lib/server-home-clay-entry.js +23 -4
  432. package/lib/server-home-clay-session-links.js +29 -9
  433. package/lib/server-home-mate-creation.js +1 -0
  434. package/lib/server-home-mcp-consent.js +14 -0
  435. package/lib/server-mates.js +25 -62
  436. package/lib/server-owner-ui.js +118 -0
  437. package/lib/server-path-access.js +38 -0
  438. package/lib/server-project-removal.js +22 -0
  439. package/lib/server-settings.js +6 -1
  440. package/lib/server.js +163 -43
  441. package/lib/session-config-transition.js +45 -0
  442. package/lib/session-folders.js +7 -2
  443. package/lib/session-fork-history.js +21 -0
  444. package/lib/session-github.js +9 -2
  445. package/lib/session-hygiene.js +3 -35
  446. package/lib/session-permission-mode.js +4 -3
  447. package/lib/session-skill-scope.js +11 -0
  448. package/lib/session-visibility.js +5 -2
  449. package/lib/session-work-primary.js +50 -0
  450. package/lib/sessions.js +64 -99
  451. package/lib/shared-browser-cookie-sync.js +32 -0
  452. package/lib/shared-browser-cookies.js +79 -0
  453. package/lib/shared-browser-runtime.js +25 -9
  454. package/lib/shared-browser-storage-sync.js +43 -0
  455. package/lib/shared-browser-storage.js +101 -0
  456. package/lib/shared-browser-tools.js +2 -1
  457. package/lib/shared-browser-worker.js +46 -10
  458. package/lib/terminal-manager.js +65 -6
  459. package/lib/tool-llm.js +1 -1
  460. package/lib/tui-transcript-index.js +1 -132
  461. package/lib/users-contextual-clay-preferences.js +31 -0
  462. package/lib/users-permissions.js +2 -8
  463. package/lib/users.js +7 -0
  464. package/lib/vendor-login-browser.js +1 -11
  465. package/lib/workspace-assignment-service.js +25 -0
  466. package/lib/workspace-query-access.js +4 -1
  467. package/lib/workspace-query-mcp-server.js +1 -1
  468. package/lib/workspace-query-service.js +13 -2
  469. package/lib/ws-request.js +1 -0
  470. package/lib/ws-schema.js +18 -2
  471. package/lib/yoke/acp-agent-profiles.js +7 -5
  472. package/lib/yoke/adapters/antigravity.js +3 -14
  473. package/lib/yoke/adapters/claude-query-contract.js +9 -0
  474. package/lib/yoke/adapters/claude.js +54 -50
  475. package/lib/yoke/adapters/codex-query-contract.js +24 -0
  476. package/lib/yoke/adapters/codex.js +53 -111
  477. package/lib/yoke/adapters/kiro.js +3 -1
  478. package/lib/yoke/adapters/muse-query-contract.js +7 -0
  479. package/lib/yoke/adapters/muse.js +122 -0
  480. package/lib/yoke/auth-events.js +17 -0
  481. package/lib/yoke/claude-hook-installer.js +271 -0
  482. package/lib/yoke/claude-jsonl-watcher.js +204 -0
  483. package/lib/yoke/claude-maintenance.js +55 -0
  484. package/lib/yoke/claude-permission-patterns.js +53 -0
  485. package/lib/yoke/claude-response-recovery.js +155 -0
  486. package/lib/yoke/claude-transcript-hygiene.js +34 -0
  487. package/lib/yoke/claude-worker-startup.js +28 -0
  488. package/lib/yoke/clay-skill-install.js +33 -0
  489. package/lib/yoke/cli-sessions.js +300 -0
  490. package/lib/yoke/codex-app-server.js +7 -12
  491. package/lib/yoke/codex-idle-recovery.js +81 -1
  492. package/lib/yoke/codex-recovery-admission.js +164 -0
  493. package/lib/yoke/configuration.js +13 -0
  494. package/lib/yoke/discovery.js +70 -0
  495. package/lib/yoke/errors.js +8 -0
  496. package/lib/yoke/host-environment.js +7 -0
  497. package/lib/yoke/index.js +9 -51
  498. package/lib/yoke/interface.js +116 -0
  499. package/lib/yoke/kiro-acp-server.js +5 -5
  500. package/lib/yoke/legacy-sessions.js +89 -0
  501. package/lib/yoke/login-protocol.js +25 -0
  502. package/lib/yoke/maintenance.js +12 -0
  503. package/lib/yoke/mcp-proxy.js +32 -0
  504. package/lib/yoke/mcp-source-adapters.js +91 -0
  505. package/lib/yoke/mcp-source-context.js +42 -0
  506. package/lib/yoke/mcp-source-discovery.js +169 -0
  507. package/lib/yoke/mcp-source-plugins.js +68 -0
  508. package/lib/yoke/mcp-source-restrictions.js +47 -0
  509. package/lib/yoke/mcp-tool-policy-normalize.js +28 -0
  510. package/lib/yoke/model-preferences.js +6 -0
  511. package/lib/yoke/muse-artifacts.js +36 -0
  512. package/lib/yoke/muse-controls.js +50 -0
  513. package/lib/yoke/muse-input.js +64 -0
  514. package/lib/yoke/muse-items.js +157 -0
  515. package/lib/yoke/muse-policy.js +52 -0
  516. package/lib/yoke/muse-query.js +428 -0
  517. package/lib/yoke/muse-runtime.js +211 -0
  518. package/lib/yoke/muse-services.js +79 -0
  519. package/lib/yoke/muse-skills.js +79 -0
  520. package/lib/yoke/muse-turns.js +71 -0
  521. package/lib/yoke/options/acp.js +2 -0
  522. package/lib/yoke/options/antigravity.js +2 -0
  523. package/lib/yoke/options/claude.js +21 -0
  524. package/lib/yoke/options/codex-defaults.js +29 -0
  525. package/lib/yoke/options/codex-settings.js +21 -0
  526. package/lib/yoke/options/codex.js +6 -0
  527. package/lib/yoke/options/kiro-defaults.js +22 -0
  528. package/lib/yoke/options/kiro.js +5 -0
  529. package/lib/yoke/options/muse.js +5 -0
  530. package/lib/yoke/provider-contract.js +5 -0
  531. package/lib/yoke/providers/antigravity-executable.js +8 -0
  532. package/lib/yoke/providers/claude-executable.js +40 -0
  533. package/lib/yoke/providers/claude-model-descriptions.js +14 -0
  534. package/lib/yoke/providers/claude-process.js +97 -0
  535. package/lib/yoke/providers/claude-provision.js +75 -0
  536. package/lib/yoke/providers/codex-errors.js +8 -0
  537. package/lib/yoke/providers/muse-executable.js +17 -0
  538. package/lib/yoke/provisioning.js +6 -0
  539. package/lib/yoke/query-options.js +10 -0
  540. package/lib/yoke/response-recovery.js +41 -0
  541. package/lib/yoke/sdk-mcp-descriptors.js +37 -0
  542. package/lib/yoke/sdk-server-factory.js +7 -0
  543. package/lib/yoke/session-history.js +8 -0
  544. package/lib/yoke/session-tool-transport.js +34 -13
  545. package/lib/yoke/settings.js +23 -0
  546. package/lib/yoke/source-locations.js +4 -0
  547. package/lib/yoke/title-query.js +18 -7
  548. package/lib/yoke/tui-transcript-index.js +132 -0
  549. package/lib/yoke/usage.js +48 -0
  550. package/lib/yoke/user-input.js +13 -0
  551. package/lib/yoke/vendor-registry.js +81 -1
  552. package/package.json +10 -4
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,38 @@ 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_project_records": true,
735
+ "mcp__clay-workspace__inspect_project_record_change": true,
736
+ "mcp__clay-workspace__control_project_records": true,
737
+ "mcp__clay-workspace__read_mate_knowledge_control": true,
738
+ "mcp__clay-workspace__inspect_mate_knowledge_change": true,
739
+ "mcp__clay-workspace__control_mate_knowledge": true,
740
+ "mcp__clay-workspace__inspect_mate_control": true,
741
+ "mcp__clay-workspace__control_mate_lifecycle": true,
742
+ "mcp__clay-workspace__inspect_project_control": true,
743
+ "mcp__clay-workspace__control_project_lifecycle": true,
744
+ "mcp__clay-workspace__inspect_session_control": true,
745
+ "mcp__clay-workspace__control_project_session": true,
748
746
  "mcp__clay-workspace__list_projects": true,
749
747
  "mcp__clay-workspace__list_project_sessions": true,
750
748
  "mcp__clay-workspace__search_project_history": true,
@@ -902,6 +900,7 @@ function createSDKBridge(opts) {
902
900
 
903
901
  if (pushModule) {
904
902
  pushModule.sendPush({
903
+ sessionId: session.localId,
905
904
  type: "permission_request",
906
905
  slug: slug,
907
906
  requestId: requestId,
@@ -943,58 +942,13 @@ function createSDKBridge(opts) {
943
942
  * Only returns processes whose cwd matches our project directory.
944
943
  * Returns an array of { pid, command } for each conflicting process found.
945
944
  */
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
- }
945
+ function findConflictingProcesses(session, error) {
946
+ var service = require("./yoke/provider-contract").resolve(getAdapterForSession(session)).processControl;
947
+ return service && typeof service.shouldInspect === "function" && service.shouldInspect(error) && typeof service.findConflicts === "function" ? service.findConflicts(cwd) : [];
986
948
  }
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
- }
949
+ function isProviderProcess(pid, session) {
950
+ var service = require("./yoke/provider-contract").resolve(session ? getAdapterForSession(session) : adapter).processControl;
951
+ return !!(service && typeof service.verifyProcess === "function" && service.verifyProcess(pid));
998
952
  }
999
953
 
1000
954
  function ownsQueryRuntime(session, queryInstance, queryGeneration) {
@@ -1006,6 +960,7 @@ function createSDKBridge(opts) {
1006
960
  function recordRuntimeEffectivePermissionMode(session, queryInstance, queryGeneration, message) {
1007
961
  if (!message || (message.yokeType !== "init" && message.yokeType !== "status") || !message.permissionMode) return false;
1008
962
  if (!ownsQueryRuntime(session, queryInstance, queryGeneration)) return false;
963
+ session._permissionObservationRevision = Number(session._permissionObservationRevision || 0) + 1;
1009
964
  session.effectivePermissionMode = message.permissionMode;
1010
965
  session.permissionModeFallbackReason = null;
1011
966
  sm.saveSessionFile(session);
@@ -1036,7 +991,8 @@ function createSDKBridge(opts) {
1036
991
  return true;
1037
992
  }
1038
993
 
1039
- async function processQueryStream(session, codexCatalogCandidate) {
994
+ async function processQueryStream(session, catalogCandidate, queryContract) {
995
+ queryContract = queryContract || yoke.sessionTools.contract(getAdapterForSession(session));
1040
996
  // Capture references at start so we only clean up OUR resources in finally,
1041
997
  // not resources from a newer query that may have been created after an abort.
1042
998
  var myQueryInstance = session.queryInstance;
@@ -1047,7 +1003,7 @@ function createSDKBridge(opts) {
1047
1003
  try {
1048
1004
  for await (var msg of myQueryInstance) {
1049
1005
  // An old iterator may drain after Stop or after a replacement query starts.
1050
- if (!ownsQueryRuntime(session, myQueryInstance, myQueryGeneration)) continue;
1006
+ if (!ownsQueryRuntime(session, myQueryInstance, myQueryGeneration) || responseRecovery.stopping(session)) continue;
1051
1007
  if ((session.taskStopRequested || (myAbortController && myAbortController.signal.aborted))
1052
1008
  && msg && msg.yokeType !== "error" && msg.yokeType !== "interrupted") continue;
1053
1009
  if (msg && msg.yokeType !== "text_delta" && msg.yokeType !== "thinking_delta" && msg.yokeType !== "tool_input_delta") {
@@ -1091,6 +1047,7 @@ function createSDKBridge(opts) {
1091
1047
  if (continuationIdentity && !ownsQueryRuntime(session, myQueryInstance, myQueryGeneration)) {
1092
1048
  continue;
1093
1049
  }
1050
+ responseRecovery.event(session, msg);
1094
1051
  processSDKMessage(session, msg);
1095
1052
  if (continuationIdentity) {
1096
1053
  settleContinuationStartup(session, myQueryInstance, myQueryGeneration, true, null);
@@ -1101,26 +1058,21 @@ function createSDKBridge(opts) {
1101
1058
  // permission state. Setter acknowledgement deliberately does not
1102
1059
  // change this field.
1103
1060
  recordRuntimeEffectivePermissionMode(session, myQueryInstance, myQueryGeneration, msg);
1104
- if (codexCatalogCandidate && msg && msg.yokeType === "session_started" && msg.sessionId
1061
+ if (catalogCandidate && msg && msg.yokeType === "session_started" && msg.sessionId
1105
1062
  && session.queryInstance === myQueryInstance
1106
- && Number(session._sdkQueryGeneration || 0) === codexCatalogCandidate.generation
1063
+ && Number(session._sdkQueryGeneration || 0) === catalogCandidate.generation
1107
1064
  && session.cliSessionId === msg.sessionId
1108
1065
  && (!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;
1066
+ queryContract.recordCatalog(session, catalogCandidate.versions);
1115
1067
  sm.saveSessionFile(session);
1116
- codexCatalogCandidate = null;
1068
+ catalogCandidate = null;
1117
1069
  }
1118
1070
  }
1119
1071
  // (getContextUsage moved to processSDKMessage result handler -- fire-and-forget)
1120
1072
  // Stream ended normally after a task stop — no "result" message was sent,
1121
1073
  // so the session is still marked as processing. Send interrupted feedback.
1122
1074
  console.log("[sdk-bridge] processQueryStream ended: isProcessing=" + session.isProcessing + " taskStopRequested=" + session.taskStopRequested);
1123
- var stillOwnsRuntime = session.queryInstance === myQueryInstance;
1075
+ var stillOwnsRuntime = session.queryInstance === myQueryInstance && !responseRecovery.stopping(session);
1124
1076
  settleContinuationStartup(session, myQueryInstance, myQueryGeneration, false, "The successor query ended before initialization.");
1125
1077
  if (session.isProcessing && session.taskStopRequested && stillOwnsRuntime) {
1126
1078
  session._lastTurnInterrupted = true;
@@ -1131,9 +1083,7 @@ function createSDKBridge(opts) {
1131
1083
  onProcessingChanged();
1132
1084
  send({ type: "status", processing: false });
1133
1085
  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?";
1086
+ var interruptMsg = (queryContract && queryContract.interruptionMessage) || "■ Conversation interrupted - tell the model what to do differently.";
1137
1087
  sendAndRecord(session, { type: "info", text: interruptMsg });
1138
1088
  sendAndRecord(session, { type: "done", code: 0 });
1139
1089
  sm.broadcastSessionList();
@@ -1158,7 +1108,7 @@ function createSDKBridge(opts) {
1158
1108
  }
1159
1109
  } catch (err) {
1160
1110
  settleContinuationStartup(session, myQueryInstance, myQueryGeneration, false, err.message || "The successor query failed before initialization.");
1161
- if (session.isProcessing && session.queryInstance === myQueryInstance) {
1111
+ if (session.isProcessing && session.queryInstance === myQueryInstance && !responseRecovery.stopping(session)) {
1162
1112
  session.isProcessing = false;
1163
1113
  session._awaitingTurnResult = false;
1164
1114
  session._queuedTurnCount = 0;
@@ -1168,9 +1118,7 @@ function createSDKBridge(opts) {
1168
1118
  session._lastTurnInterrupted = true;
1169
1119
  if (!session.destroying) {
1170
1120
  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?";
1121
+ var interruptMsg2 = (queryContract && queryContract.interruptionMessage) || "■ Conversation interrupted - tell the model what to do differently.";
1174
1122
  sendAndRecord(session, { type: "info", text: interruptMsg2 });
1175
1123
  sendAndRecord(session, { type: "done", code: 0 });
1176
1124
  }
@@ -1186,21 +1134,17 @@ function createSDKBridge(opts) {
1186
1134
  console.error("[sdk-bridge] Query stream error for session " + session.localId + ":", errDetail);
1187
1135
  console.error("[sdk-bridge] Stack:", err.stack || "(no stack)");
1188
1136
 
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() : [];
1137
+ var conflicts = findConflictingProcesses(session, err);
1192
1138
  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(", "));
1139
+ console.error("[sdk-bridge] Found " + conflicts.length + " conflicting provider process(es):", conflicts.map(function(c) { return "PID " + c.pid; }).join(", "));
1194
1140
  sendAndRecord(session, {
1195
1141
  type: "process_conflict",
1196
- text: "Another Claude Code process is already running in this project.",
1142
+ text: "Another provider process is already running in this project.",
1197
1143
  processes: conflicts,
1198
1144
  });
1199
1145
  } else {
1200
1146
  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;
1147
+ var isContextOverflow = require("./yoke/errors").contextOverflow(errDetail);
1204
1148
  var isAuthError = isAuthErrorMessage(errDetail);
1205
1149
  if (isContextOverflow) {
1206
1150
  sendAndRecord(session, {
@@ -1247,7 +1191,7 @@ function createSDKBridge(opts) {
1247
1191
  // chat message already sent above
1248
1192
  }
1249
1193
  } else {
1250
- sendAndRecord(session, { type: "error", text: "Claude process error: " + err.message });
1194
+ sendAndRecord(session, { type: "error", text: "Provider process error: " + err.message });
1251
1195
  }
1252
1196
  }
1253
1197
  sendAndRecord(session, { type: "done", code: 1 });
@@ -1272,7 +1216,8 @@ function createSDKBridge(opts) {
1272
1216
  }
1273
1217
  if (session.messageQueue === myMessageQueue) session.messageQueue = null;
1274
1218
  if (session.abortController === myAbortController) session.abortController = null;
1275
- if (ownsRuntime) {
1219
+ if (ownsRuntime && !responseRecovery.stopping(session)) {
1220
+ responseRecovery.cancel(session);
1276
1221
  session.taskStopRequested = false;
1277
1222
  session.pendingPermissions = {};
1278
1223
  // Preserve only restored legacy MCP entries. Canonical Yoke requests
@@ -1321,7 +1266,7 @@ function createSDKBridge(opts) {
1321
1266
  }
1322
1267
  }
1323
1268
  }
1324
- if (!session.isProcessing && typeof opts.onSessionAvailable === "function") opts.onSessionAvailable(session);
1269
+ if (!responseRecovery.stopping(session) && !session.isProcessing && typeof opts.onSessionAvailable === "function") opts.onSessionAvailable(session);
1325
1270
  }
1326
1271
  }
1327
1272
 
@@ -1329,20 +1274,16 @@ function createSDKBridge(opts) {
1329
1274
  if (session.queryInstance) return { query: session.queryInstance, isTemp: false, cleanup: function() {} };
1330
1275
 
1331
1276
  var handle;
1277
+ var selectedAdapter = getAdapterForSession(session);
1332
1278
  try {
1333
- handle = await adapter.createQuery({
1279
+ handle = await selectedAdapter.createQuery({
1334
1280
  cwd: cwd,
1335
1281
  resumeSessionId: session.cliSessionId,
1336
1282
  env: getRuntimeEnv(session),
1337
- adapterOptions: {
1338
- CLAUDE: {
1339
- settingSources: ["user", "project", "local"],
1340
- enableFileCheckpointing: true,
1341
- },
1342
- },
1283
+ adapterOptions: require("./yoke/query-options").map(getAdapterForSession(session), { nativePermissionMode: "default" }),
1343
1284
  });
1344
1285
  } catch (e) {
1345
- sendAndRecord(session, { type: "error", text: "Failed to load Claude SDK: " + (e.message || e) });
1286
+ sendAndRecord(session, { type: "error", text: "Failed to load provider session: " + (e.message || e) });
1346
1287
  throw e;
1347
1288
  }
1348
1289
 
@@ -1409,7 +1350,7 @@ function createSDKBridge(opts) {
1409
1350
  // abort the whole rewind instead of leaving local history trimmed
1410
1351
  // ahead of the real, un-rolled-back provider thread.
1411
1352
  if (!turnId) {
1412
- throw new Error("No turn id recorded for this turn; cannot roll back the Codex thread.");
1353
+ throw new Error("No turn id recorded for this turn; cannot roll back the provider thread.");
1413
1354
  }
1414
1355
  await sessionAdapter.rollbackThread(session.cliSessionId, turnId, {
1415
1356
  linuxUser: getSessionLinuxUser(session) || undefined,
@@ -1425,6 +1366,8 @@ function createSDKBridge(opts) {
1425
1366
 
1426
1367
  async function forkSessionUnified(session, uuid) {
1427
1368
  var sessionAdapter = getAdapterForSession(session);
1369
+ var localHistoryEndIndex = require("./session-fork-history").endIndex(session, uuid,
1370
+ require("./yoke/provider-contract").resolve(sessionAdapter).forkHistoryBoundary);
1428
1371
  var result = await sessionAdapter.forkSession(session.cliSessionId, {
1429
1372
  upToMessageId: uuid,
1430
1373
  dir: cwd,
@@ -1433,8 +1376,10 @@ function createSDKBridge(opts) {
1433
1376
  if (!result || !result.sessionId) throw new Error("Fork returned no session id");
1434
1377
 
1435
1378
  // Adapters with rollbackThread (e.g. Codex) use local history copy
1436
- if (typeof sessionAdapter.rollbackThread === "function") {
1437
- return { sessionId: result.sessionId, useLocalHistory: true };
1379
+ if (result.useLocalHistory === true || typeof sessionAdapter.rollbackThread === "function") {
1380
+ var localFork = { sessionId: result.sessionId, useLocalHistory: true };
1381
+ if (localHistoryEndIndex !== undefined) localFork.localHistoryEndIndex = localHistoryEndIndex;
1382
+ return localFork;
1438
1383
  }
1439
1384
  // Claude: read history from CLI session files
1440
1385
  return { sessionId: result.sessionId, useLocalHistory: false };
@@ -1442,22 +1387,69 @@ function createSDKBridge(opts) {
1442
1387
 
1443
1388
  // Wrapper: marks the boot window so pushMessage can buffer instead of
1444
1389
  // dropping messages that arrive while createQuery is still awaiting.
1445
- async function startQuery(session, text, images, linuxUser, beforePush, onAccepted) {
1390
+ async function startQuery(session, text, images, linuxUser, beforePush, onAccepted, retryState) {
1391
+ if (opts.isLifecycleExecutionPaused && opts.isLifecycleExecutionPaused()) {
1392
+ 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." });
1393
+ session.isProcessing = false; onProcessingChanged(); sendAndRecord(session, { type: "done", code: 1 }); return false;
1394
+ }
1395
+ if (session._responseRecoveryStopPending && !retryState) {
1396
+ session.isProcessing = false;
1397
+ onProcessingChanged();
1398
+ sendAndRecord(session, { type: "error", text: "The previous Claude attempt has not confirmed stopped. Check the connection before sending this message again." });
1399
+ sendAndRecord(session, { type: "done", code: 1 });
1400
+ return false;
1401
+ }
1402
+ if (responseRecovery.stopping(session) && !retryState) return false;
1446
1403
  delete session._lastTurnInterrupted;
1404
+ var payload = { text: text, images: images, linuxUser: linuxUser, beforePush: beforePush };
1405
+ var recoveryState = responseRecovery.begin(session, payload, retryState);
1406
+ if (recoveryState) recoveryState.handle = null;
1407
+ var guardedPush = function() {
1408
+ return !session.destroying && !session.taskStopRequested
1409
+ && (!recoveryState || responseRecovery.valid(session, recoveryState) && !recoveryState.stopping)
1410
+ && (typeof beforePush !== "function" || beforePush() === true);
1411
+ };
1412
+ guardedPush.exactProductDelivery = !!(beforePush && beforePush.exactProductDelivery);
1447
1413
  session._queryGeneration = (session._queryGeneration || 0) + 1;
1448
1414
  session._queryStarting = true;
1415
+ var starting = startQueryInner(session, text, images, linuxUser, guardedPush, onAccepted, recoveryState);
1416
+ if (recoveryState) recoveryState.starting = starting;
1449
1417
  try {
1450
- return await startQueryInner(session, text, images, linuxUser, beforePush, onAccepted);
1418
+ var accepted = await starting;
1419
+ if (accepted === false && (!recoveryState || !recoveryState.stopping)) {
1420
+ responseRecovery.cancel(session);
1421
+ session.isProcessing = false;
1422
+ onProcessingChanged();
1423
+ }
1424
+ return accepted;
1425
+ } catch (error) {
1426
+ // Includes synchronous input queue failures, before stream consumption.
1427
+ var failedHandle = session.queryInstance;
1428
+ if (failedHandle) {
1429
+ try { if (typeof failedHandle.abort === "function") failedHandle.abort(); } catch (e) {}
1430
+ try { if (typeof failedHandle.close === "function") failedHandle.close(); } catch (e) {}
1431
+ }
1432
+ session.queryInstance = null;
1433
+ session.abortController = null;
1434
+ session.messageQueue = null;
1435
+ session.isProcessing = false;
1436
+ session._awaitingTurnResult = false;
1437
+ session._queuedTurnCount = 0;
1438
+ taskLifecycle.clearAll(session);
1439
+ if (!recoveryState || !recoveryState.stopping) {
1440
+ responseRecovery.cancel(session);
1441
+ sendAndRecord(session, { type: "error", text: "The agent connection failed while delivering your message. Please retry or check the connection." });
1442
+ sendAndRecord(session, { type: "done", code: 1 });
1443
+ onProcessingChanged();
1444
+ sm.broadcastSessionList();
1445
+ }
1446
+ return false;
1451
1447
  } finally {
1452
1448
  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);
1449
+ delete session._providerRecoveryWaiting;
1450
+ if (!session.queryInstance && session.pendingPush && session.pendingPush.length) session.pendingPush = [];
1451
+ if (!session.queryInstance && !session.isProcessing && !session._pendingMessageDrainPaused && !responseRecovery.stopping(session)) session.taskStopRequested = false;
1452
+ if (!responseRecovery.stopping(session) && !session.isProcessing && typeof opts.onSessionAvailable === "function") opts.onSessionAvailable(session);
1461
1453
  }
1462
1454
  }
1463
1455
 
@@ -1501,7 +1493,7 @@ function createSDKBridge(opts) {
1501
1493
  };
1502
1494
  }
1503
1495
 
1504
- async function startQueryInner(session, text, images, linuxUser, beforePush, onAccepted) {
1496
+ async function startQueryInner(session, text, images, linuxUser, beforePush, onAccepted, recoveryState) {
1505
1497
  var sessionRuntimeEnv = getRuntimeEnv(session);
1506
1498
  var resumedAtQueryStart = !!session.cliSessionId;
1507
1499
  async function ensureVendorReady(vendor) {
@@ -1653,7 +1645,8 @@ function createSDKBridge(opts) {
1653
1645
 
1654
1646
  // Ensure Linux user project directory exists (runs in parallel with worker boot)
1655
1647
  if (linuxUser) {
1656
- ensureLinuxUserProjectDir(linuxUser, session);
1648
+ var preparation = require("./yoke/provider-contract").resolve(sessionAdapter).prepareIsolation;
1649
+ if (typeof preparation === "function") preparation(linuxUser, session, cwd);
1657
1650
  }
1658
1651
 
1659
1652
  session.blocks = {};
@@ -1670,100 +1663,27 @@ function createSDKBridge(opts) {
1670
1663
  session.abortController = new AbortController();
1671
1664
  }
1672
1665
 
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
1666
  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
1667
+ if (ls.permissionMode) session._loopPermissionMode = ls.permissionMode;
1668
+ var permissionMode = session.permissionMode || sm.currentPermissionMode || "default";
1669
+ var nativeMode = permissionMode === "bypassPermissions" ? "default" : session.acceptEditsAfterStart ? "acceptEdits" : permissionMode;
1670
+ var isolationHome = null;
1751
1671
  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
- }
1672
+ try { isolationHome = require("./os-users").resolveOsUserInfo(linuxUser).home; } catch (error) {}
1673
+ }
1674
+ var executionIntent = {
1675
+ settings: sm, permissionMode: permissionMode, nativePermissionMode: session._loopPermissionMode || nativeMode,
1676
+ nativeBypass: dangerouslySkipPermissions || session.mode === "tui" && session.dangerouslySkipPermissions,
1677
+ readOnly: !!session.youCurator, betas: sm.currentBetas,
1678
+ thinking: { mode: ls.thinking || sm.currentThinking, budget: ls.thinkingBudget || sm.currentThinkingBudget },
1679
+ disableHooks: ls.disableAllHooks, resumeAt: session.cliSessionId && session.lastRewindUuid || undefined,
1680
+ singleTurn: !!session.singleTurn, startedAt: t0,
1681
+ isolation: { user: linuxUser, home: isolationHome, projectPath: session.cwd || null, workerState: session._adapterWorkerState },
1682
+ sessionBridge: null,
1683
+ };
1684
+ if (session.acceptEditsAfterStart) delete session.acceptEditsAfterStart;
1685
+ if (executionIntent.resumeAt) { delete session.lastRewindUuid; sm.saveSessionFile(session); }
1686
+ if (linuxUser && session._adapterWorkerState) session._adapterWorkerState = null;
1767
1687
 
1768
1688
  // Pick a model from the session or its vendor-specific default, then
1769
1689
  // validate it against the session vendor's known model list.
@@ -1789,20 +1709,21 @@ function createSDKBridge(opts) {
1789
1709
  sm.saveSessionFile(session);
1790
1710
  }
1791
1711
 
1792
- var codexConfig = getCodexConfig(sm);
1793
- var kiroConfig = getKiroConfig(sm);
1794
1712
  session._sdkQueryGeneration = Number(session._sdkQueryGeneration || 0) + 1;
1795
1713
  // Individual feature modules own their transport capability checks. Do not
1796
1714
  // suppress unrelated session tools merely because one feature cannot be
1797
1715
  // delivered by this provider.
1798
1716
  var sessionToolDefs = getSessionToolDefs(session) || [];
1799
1717
  var queryGeneration = Number(session._sdkQueryGeneration || 0);
1718
+ executionIntent.sessionBridge = getAcpMcpServers(session);
1719
+ var queryContract = yoke.sessionTools.contract(sessionAdapter);
1720
+ function needsCatalogRecovery(family, version) {
1721
+ return yoke.sessionTools.needsRecovery(sessionAdapter, session, family, version);
1722
+ }
1800
1723
  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;
1724
+ var recoverYouTools = needsCatalogRecovery("knowledge", 1) && hasYouCatalog;
1803
1725
  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;
1726
+ var recoverLinearTools = needsCatalogRecovery("linear", 1) && hasLinearCatalog && canUseSessionTools(session) === true;
1806
1727
  var hasIssueCatalog = false;
1807
1728
  for (var issueToolIndex = 0; issueToolIndex < sessionToolDefs.length; issueToolIndex++) {
1808
1729
  if (sessionToolDefs[issueToolIndex].name === "create_issue") {
@@ -1811,24 +1732,23 @@ function createSDKBridge(opts) {
1811
1732
  }
1812
1733
  }
1813
1734
  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;
1735
+ var recoverBrowserTools = needsCatalogRecovery("browser", 6) && hasSharedBrowserCatalog;
1736
+ var recoverIssueTools = needsCatalogRecovery("issues", 1) && hasIssueCatalog;
1820
1737
  function queryContextAvailable() {
1738
+ if (queryRetired()) return false;
1821
1739
  if (Number(session._sdkQueryGeneration || 0) !== queryGeneration) return false;
1822
1740
  if (sm && sm.sessions && typeof sm.sessions.get === "function" && sm.sessions.get(session.localId) !== session) return false;
1823
1741
  return canUseSessionTools(session) === true;
1824
1742
  }
1743
+ var hasMateScheduleTool = sessionToolDefs.some(function (tool) { return yoke.sessionTools.MATE_SCHEDULE_TOOLS.indexOf(tool.name) !== -1; });
1744
+ var recoverMateSchedules = needsCatalogRecovery() && hasMateScheduleTool && queryContextAvailable();
1825
1745
  var hasScheduleTool = false;
1826
1746
  var hasScheduleInterviewTool = false;
1827
1747
  var scheduleInterviewActive = null;
1828
1748
  var scheduleQuestionLimit = null;
1829
1749
  for (var sessionToolIndex = 0; sessionToolIndex < sessionToolDefs.length; sessionToolIndex++) {
1830
1750
  if (yoke.sessionTools.SCHEDULE_TOOLS.indexOf(sessionToolDefs[sessionToolIndex].name) !== -1) hasScheduleTool = true;
1831
- if (sessionToolDefs[sessionToolIndex].name === "begin_scheduled_task_interview") {
1751
+ if (["begin_scheduled_task_interview", "begin_mate_schedule_interview", "begin_schedule_control_interview"].indexOf(sessionToolDefs[sessionToolIndex].name) >= 0) {
1832
1752
  hasScheduleInterviewTool = true;
1833
1753
  scheduleInterviewActive = sessionToolDefs[sessionToolIndex].scheduleInterviewActive || null;
1834
1754
  scheduleQuestionLimit = sessionToolDefs[sessionToolIndex].structuredQuestionLimit || null;
@@ -1838,10 +1758,10 @@ function createSDKBridge(opts) {
1838
1758
  var activeLoopInterview = !isDriverOperatedSession(session) && !session.loopInterviewHandoff && !activeRun && !!loopInterview.activeInterviewId(session);
1839
1759
  var structuredInputSession = hasScheduleInterviewTool || activeLoopInterview || (!isDriverOperatedSession(session) && session.ralphCraftingMode === true && session.loop && session.loop.role === "crafting");
1840
1760
  var requestedUserInputMode = session.debateSetupMode || session.mateCreationMode ? "fallback" : "auto";
1841
- if (structuredInputSession || sessionAdapter.vendor === "codex") requestedUserInputMode = "fallback";
1761
+ if (structuredInputSession) requestedUserInputMode = "fallback";
1762
+ if (requestedUserInputMode === "auto" && queryContract.defaultUserInputMode) requestedUserInputMode = queryContract.defaultUserInputMode;
1842
1763
  var selectedUserInputMode = yoke.userInput.selectMode(sessionAdapter, requestedUserInputMode);
1843
- var recoverCodexQuestionTools = sessionAdapter.vendor === "codex" && !!session.cliSessionId
1844
- && Number(session.codexUserInputToolCatalogVersion || 0) < 1 && queryContextAvailable();
1764
+ var recoverQuestionTools = needsCatalogRecovery("questions", 1) && queryContextAvailable();
1845
1765
  function structuredInputContextAvailable() {
1846
1766
  if (!queryContextAvailable()) return false;
1847
1767
  // A schedule-triggered fallback shares the schedule feature's live
@@ -1860,9 +1780,10 @@ function createSDKBridge(opts) {
1860
1780
  return "Ask exactly one question at a time, wait for the answer, then ask the next question.";
1861
1781
  }
1862
1782
  var sessionUserInputHandler = function(request, respond) {
1863
- if (!structuredInputContextAvailable()) {
1783
+ if (queryRetired() || !structuredInputContextAvailable()) {
1864
1784
  return respond.cancel("This structured-input request belongs to an unavailable or older query.");
1865
1785
  }
1786
+ responseRecovery.progress(session);
1866
1787
  var questionError = scheduleQuestionError(request);
1867
1788
  if (questionError) return respond.cancel(questionError);
1868
1789
  if (request.presentation === "elicitation" && request.diagnostics && request.diagnostics.elicitation) {
@@ -1882,21 +1803,33 @@ function createSDKBridge(opts) {
1882
1803
  return opts.onUserInputRequest(session, request, respond);
1883
1804
  };
1884
1805
  var localMcpServers = getMcpServers(session);
1885
- if (sessionAdapter.vendor === "claude" && hasScheduleTool && typeof sessionAdapter.createToolServer === "function") {
1806
+ if (queryContract.toolTransport === "sdk-mcp" && (hasScheduleTool || hasMateScheduleTool)
1807
+ && typeof sessionAdapter.createToolServer !== "function") {
1808
+ throw new Error("The adapter cannot mount native schedule tools without createToolServer.");
1809
+ }
1810
+ if (queryContract.toolTransport === "sdk-mcp" && hasScheduleTool) {
1886
1811
  var scheduledTaskToolDefs = [];
1887
1812
  for (var scheduledToolIndex = 0; scheduledToolIndex < sessionToolDefs.length; scheduledToolIndex++) {
1888
1813
  if (yoke.sessionTools.SCHEDULE_TOOLS.indexOf(sessionToolDefs[scheduledToolIndex].name) !== -1) scheduledTaskToolDefs.push(sessionToolDefs[scheduledToolIndex]);
1889
1814
  }
1890
1815
  if (scheduledTaskToolDefs.length) {
1891
1816
  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
- }
1817
+ if (!scheduledTaskServer) throw new Error("The adapter could not mount scheduled-task tools.");
1818
+ localMcpServers = Object.assign({}, localMcpServers || {});
1819
+ localMcpServers["clay-scheduled-tasks"] = scheduledTaskServer;
1896
1820
  }
1897
1821
  }
1822
+ if (queryContract.toolTransport === "sdk-mcp" && hasMateScheduleTool) {
1823
+ localMcpServers = Object.assign({}, localMcpServers || {});
1824
+ localMcpServers["clay-mate-schedules"] = sessionAdapter.createToolServer({ name: "clay-mate-schedules", version: "1.0.0",
1825
+ tools: sessionToolDefs.filter(function (tool) { return yoke.sessionTools.MATE_SCHEDULE_TOOLS.indexOf(tool.name) !== -1; }) });
1826
+ if (!localMcpServers["clay-mate-schedules"]) throw new Error("The adapter could not mount Mate schedule tools.");
1827
+ }
1898
1828
  if (selectedUserInputMode === "fallback") {
1899
1829
  var userInputServer = yoke.userInput.createFallbackServer(sessionAdapter, sessionUserInputHandler, { force: true, maxQuestions: scheduleQuestionLimit });
1830
+ if (queryContract.toolTransport === "sdk-mcp" && !userInputServer) {
1831
+ throw new Error("The adapter could not mount structured-input tools.");
1832
+ }
1900
1833
  if (userInputServer) {
1901
1834
  localMcpServers = Object.assign({}, localMcpServers || {});
1902
1835
  localMcpServers[userInputServer.name || "yoke-user-input"] = userInputServer;
@@ -1931,6 +1864,12 @@ function createSDKBridge(opts) {
1931
1864
  return snapshotDef;
1932
1865
  }
1933
1866
 
1867
+ var externalQueryBridge = require('./mcp-query-bridge').createQueryBridge({ valid: queryContextAvailable,
1868
+ getServers: function () { return getRemoteMcpServers ? getRemoteMcpServers(session) || {} : {}; } });
1869
+ var externalToolDefs = queryContract.externalToolTransport === "dynamic" ? externalQueryBridge.dynamicDefs() : [];
1870
+ if (externalToolDefs.length) { sessionToolDefs = sessionToolDefs.concat(externalToolDefs); queryToolSnapshots.set(session, { generation: queryGeneration, defs: sessionToolDefs.slice() }); }
1871
+ var recoverExternalTools = needsCatalogRecovery() && (externalToolDefs.length > 0 || isMate);
1872
+
1934
1873
  // Give fresh queries an immediate provisional label. The visible user
1935
1874
  // message in history wins over `text`, which may contain an internal
1936
1875
  // provider wrapper rather than what the person typed.
@@ -1945,6 +1884,10 @@ function createSDKBridge(opts) {
1945
1884
  }
1946
1885
 
1947
1886
  var modelSwitchGeneration = session._queryGeneration;
1887
+ function queryRetired() {
1888
+ return Number(session._sdkQueryGeneration || 0) !== queryGeneration || session.taskStopRequested
1889
+ || session.destroying || responseRecovery.stopping(session) || (handle && session.queryInstance !== handle);
1890
+ }
1948
1891
  var queryOpts = {
1949
1892
  onModelSwitch: function(change) {
1950
1893
  if (session._queryGeneration !== modelSwitchGeneration || session.taskStopRequested) return;
@@ -1959,7 +1902,7 @@ function createSDKBridge(opts) {
1959
1902
  clayBridgeToken: clayBridgeToken,
1960
1903
  linuxUser: linuxUser || undefined,
1961
1904
  env: sessionRuntimeEnv,
1962
- skillOptions: getSessionSkillOptions(session),
1905
+ skillOptions: require("./session-skill-scope").bind(session, sm.sessions, getSessionSkillOptions(session), queryContextAvailable, usersModule.isMultiUser()),
1963
1906
  // appendSystemPrompt, NOT systemPrompt: a plain-string systemPrompt
1964
1907
  // REPLACES the entire Claude Code system prompt (sdk.d.ts:2096), which
1965
1908
  // would strip the driver session down to the two-sentence pair preset.
@@ -1967,7 +1910,7 @@ function createSDKBridge(opts) {
1967
1910
  // codex/kiro: prepend to the first message).
1968
1911
  appendSystemPrompt: [
1969
1912
  typeof opts.getSessionSystemPrompt === "function" ? opts.getSessionSystemPrompt(session) : "",
1970
- sessionAdapter.vendor === "codex" && queryContextAvailable() ? codexQuestionGuidance() : "",
1913
+ typeof queryContract.questionGuidance === "function" && queryContextAvailable() ? queryContract.questionGuidance() : "",
1971
1914
  ].filter(Boolean).join("\n\n") || undefined,
1972
1915
  model: queryModel,
1973
1916
  // Effort is remembered per vendor and clamped to the session vendor's
@@ -1984,10 +1927,22 @@ function createSDKBridge(opts) {
1984
1927
  toolServers: mergedMcpServers,
1985
1928
  toolServerDescriptors: extractMcpDescriptors(mergedMcpServers) || undefined,
1986
1929
  dynamicTools: sessionToolDefs,
1987
- // Codex persists dynamic tools with a thread. A stale authorized Driver
1930
+ // Conversation-scoped catalogs persist definitions. A stale authorized Driver
1988
1931
  // gets this exact-query bridge; its handlers still reauthorize live.
1989
- sessionMcpServer: (recoverCodexIssueTools || recoverCodexQuestionTools || recoverCodexBrowserTools || recoverCodexLinearTools || recoverCodexYouTools) ? getCodexSessionMcpServer(session, queryGeneration) : undefined,
1932
+ sessionMcpServer: (recoverMateSchedules || recoverIssueTools || recoverQuestionTools || recoverBrowserTools || recoverLinearTools || recoverYouTools || recoverExternalTools) ? getSessionMcpServer(session, queryGeneration) : undefined,
1933
+ onRecoveryAdmission: function(waiting) {
1934
+ if (!queryContextAvailable()) return;
1935
+ if (waiting) session._providerRecoveryWaiting = queryGeneration;
1936
+ else delete session._providerRecoveryWaiting;
1937
+ },
1938
+ onRecoveryWaiting: function(waiting) {
1939
+ if (!queryContextAvailable()) return;
1940
+ sendAndRecord(session, { type: 'informational', level: 'info', content: waiting
1941
+ ? 'Tool recovery is waiting for this conversation’s runtime to become safely available. Your message is retained; Stop cancels the wait.'
1942
+ : 'Tool recovery is ready. Continuing your retained message.' });
1943
+ },
1990
1944
  callDynamicTool: function (toolName, args) {
1945
+ if (queryRetired() || !queryContextAvailable()) return Promise.reject(new Error("This session tool query is no longer available."));
1991
1946
  var toolDef = findSessionToolDef(toolName);
1992
1947
  if (toolDef && typeof toolDef.handler === "function") {
1993
1948
  return toolDef.handler(args || {});
@@ -1997,7 +1952,10 @@ function createSDKBridge(opts) {
1997
1952
  resumeSessionId: session.cliSessionId || undefined,
1998
1953
  abortController: linuxUser ? undefined : session.abortController,
1999
1954
  canUseTool: function(toolName, input, toolOpts) {
2000
- var nativeQuestionError = toolName === "AskUserQuestion" ? scheduleQuestionError(input) : null;
1955
+ if (queryRetired()) return { behavior: "deny", message: "This attempt has stopped." };
1956
+ responseRecovery.progress(session);
1957
+ var questionInput = yoke.userInput.toolQuestions(sessionAdapter, toolName, input);
1958
+ var nativeQuestionError = questionInput ? scheduleQuestionError(questionInput) : null;
2001
1959
  if (nativeQuestionError) return { behavior: "deny", message: nativeQuestionError };
2002
1960
  var permissionName = toolName;
2003
1961
  var permissionToolDef = findSessionToolDef(toolName);
@@ -2005,7 +1963,10 @@ function createSDKBridge(opts) {
2005
1963
  return handleCanUseTool(session, permissionName, input, toolOpts);
2006
1964
  },
2007
1965
  preToolUsePolicy: function(toolName, input) {
2008
- var nativeQuestionError = toolName === "AskUserQuestion" ? scheduleQuestionError(input) : null;
1966
+ if (queryRetired()) return Promise.resolve({ behavior: "deny", message: "This attempt has stopped." });
1967
+ responseRecovery.progress(session);
1968
+ var questionInput = yoke.userInput.toolQuestions(sessionAdapter, toolName, input);
1969
+ var nativeQuestionError = questionInput ? scheduleQuestionError(questionInput) : null;
2009
1970
  if (nativeQuestionError) return Promise.resolve({ behavior: "deny", message: nativeQuestionError });
2010
1971
  var permissionName = toolName;
2011
1972
  var permissionToolDef = findSessionToolDef(toolName);
@@ -2018,47 +1979,45 @@ function createSDKBridge(opts) {
2018
1979
  validateUserInputRequest: scheduleQuestionError,
2019
1980
  userInputMode: selectedUserInputMode,
2020
1981
  onElicitation: function(request, elicitOpts) {
1982
+ if (queryRetired()) return Promise.resolve({ action: "cancel" });
1983
+ responseRecovery.progress(session);
2021
1984
  return handleElicitation(session, request, elicitOpts);
2022
1985
  },
2023
1986
  onUserDialog: function(dialogRequest, dialogOpts) {
1987
+ if (queryRetired()) return Promise.resolve({ behavior: "cancelled" });
1988
+ responseRecovery.progress(session);
2024
1989
  return handleUserDialog(session, dialogRequest, dialogOpts);
2025
1990
  },
2026
1991
  callMcpTool: function(serverName, toolName, args) {
1992
+ if (queryRetired()) return Promise.reject(new Error("This attempt has stopped."));
1993
+ responseRecovery.progress(session);
2027
1994
  return callMcpToolHandler(mergedMcpServers, serverName, toolName, args);
2028
1995
  },
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
- },
1996
+ adapterOptions: require("./yoke/query-options").map(sessionAdapter, executionIntent),
2056
1997
  };
2057
1998
 
1999
+ if (recoveryState) {
2000
+ if (!recoveryState.context) recoveryState.context = { appendSystemPrompt: queryOpts.appendSystemPrompt, model: queryOpts.model, effort: queryOpts.effort };
2001
+ Object.assign(queryOpts, recoveryState.context);
2002
+ }
2058
2003
  var handle;
2004
+ var startupHandle = session.queryInstance;
2059
2005
  console.log("[sdk-bridge] calling adapter.createQuery... vendor=" + sessionAdapter.vendor);
2060
2006
  try {
2061
2007
  handle = await sessionAdapter.createQuery(queryOpts);
2008
+ // A guarded delivery must still own the exact startup after provider
2009
+ // creation. Never replace a newer handle or publish to a deleted target.
2010
+ if (beforePush && beforePush.exactProductDelivery) {
2011
+ var startupCurrent = false;
2012
+ try { startupCurrent = sm.sessions.get(session.localId) === session && session.queryInstance === startupHandle && Number(session._sdkQueryGeneration || 0) === queryGeneration && beforePush() === true; } catch (guardError) {}
2013
+ if (!startupCurrent) {
2014
+ try { if (handle.abort) handle.abort(); } catch (abortError) {}
2015
+ try { if (handle.close) handle.close(); } catch (closeError) {}
2016
+ if (session.queryInstance === startupHandle) session.isProcessing = false;
2017
+ return false;
2018
+ }
2019
+ }
2020
+ if (recoveryState) recoveryState.handle = handle;
2062
2021
  console.log("[sdk-bridge] createQuery returned handle, vendor=" + sessionAdapter.vendor);
2063
2022
  // SDK accepted the explicit title. Provisional labels remain eligible
2064
2023
  // for semantic generation after the first Home exchange (or the normal
@@ -2078,6 +2037,7 @@ function createSDKBridge(opts) {
2078
2037
  console.error("[sdk-bridge] Failed to create query for session " + session.localId + ":", e.message || e);
2079
2038
  console.error("[sdk-bridge] cliSessionId:", session.cliSessionId, "resume:", !!session.cliSessionId);
2080
2039
  console.error("[sdk-bridge] Stack:", e.stack || "(no stack)");
2040
+ if (beforePush && beforePush.exactProductDelivery && (sm.sessions.get(session.localId) !== session || session.queryInstance !== startupHandle || Number(session._sdkQueryGeneration || 0) !== queryGeneration)) return false;
2081
2041
  session.isProcessing = false;
2082
2042
  session._awaitingTurnResult = false;
2083
2043
  session._queuedTurnCount = 0;
@@ -2112,11 +2072,9 @@ function createSDKBridge(opts) {
2112
2072
 
2113
2073
  // Store QueryHandle on session for iteration and control.
2114
2074
  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";
2075
+ // An installed setter and the adapter's explicit protocol contract are
2076
+ // both required; generic handle stubs do not establish runtime support.
2077
+ session.permissionCapabilities = require("./session-permission-mode").capabilities(session, sm.defaultVendor, sessionAdapter);
2120
2078
  session.effectivePermissionMode = null;
2121
2079
  sm.saveSessionFile(session);
2122
2080
  sendToSession(session, { type: "config_state", model: session.model || defaultModelForVendor(session.vendor || "claude"), vendor: session.vendor || "claude", sessionId: session.localId, mode: session.permissionMode || sm.currentPermissionMode || "default", effectivePermissionMode: null, permissionCapabilities: session.permissionCapabilities, mcpPermissionModeOverrides: session.mcpPermissionModeOverrides || {}, effort: session.effort || sm.currentEffort || "medium", betas: sm.currentBetas || [] });
@@ -2132,6 +2090,7 @@ function createSDKBridge(opts) {
2132
2090
  }
2133
2091
 
2134
2092
  if (typeof beforePush === "function" && beforePush() !== true) {
2093
+ try { if (typeof handle.abort === "function") handle.abort(); } catch (e) {}
2135
2094
  try { handle.close(); } catch (e) {}
2136
2095
  if (session.queryInstance === handle) session.queryInstance = null;
2137
2096
  session.messageQueue = null;
@@ -2141,6 +2100,13 @@ function createSDKBridge(opts) {
2141
2100
  session._queuedTurnCount = 0;
2142
2101
  taskLifecycle.clearAll(session);
2143
2102
  onProcessingChanged();
2103
+ if (recoveryState && !responseRecovery.stopping(session)) {
2104
+ sendAndRecord(session, { type: "thinking_stop" });
2105
+ sendAndRecord(session, { type: session.taskStopRequested ? "info" : "error", text: session.taskStopRequested
2106
+ ? "Stopped before sending your message."
2107
+ : "This request is no longer eligible for delivery. Please review it and send it again." });
2108
+ sendAndRecord(session, { type: "done", code: session.taskStopRequested ? 0 : 1 });
2109
+ }
2144
2110
  sm.broadcastSessionList();
2145
2111
  return false;
2146
2112
  }
@@ -2192,32 +2158,27 @@ function createSDKBridge(opts) {
2192
2158
  }
2193
2159
 
2194
2160
  session.lastActivityAt = Date.now();
2195
- var codexCatalogCandidate = null;
2196
- if (sessionAdapter.vendor === "codex" && !resumedAtQueryStart) {
2161
+ var catalogCandidate = null;
2162
+ if (queryContract.catalogLifetime === "conversation" && typeof queryContract.recordCatalog === "function" && !resumedAtQueryStart) {
2197
2163
  var requiredCatalogTools = { begin_scheduled_task_interview: false, propose_scheduled_task: false, list_scheduled_tasks: false, read_scheduled_task: false, update_scheduled_task: false, report_scheduled_task_outcome: false, ask_user_questions: false };
2198
2164
  for (var catalogToolIndex = 0; catalogToolIndex < sessionToolDefs.length; catalogToolIndex++) {
2199
2165
  if (Object.prototype.hasOwnProperty.call(requiredCatalogTools, sessionToolDefs[catalogToolIndex].name)) requiredCatalogTools[sessionToolDefs[catalogToolIndex].name] = true;
2200
2166
  }
2201
2167
  var completeInterviewCatalog = requiredCatalogTools.begin_scheduled_task_interview && requiredCatalogTools.propose_scheduled_task && requiredCatalogTools.list_scheduled_tasks && requiredCatalogTools.read_scheduled_task && requiredCatalogTools.update_scheduled_task && requiredCatalogTools.ask_user_questions;
2202
2168
  if (completeInterviewCatalog || requiredCatalogTools.report_scheduled_task_outcome) {
2203
- codexCatalogCandidate = { generation: queryGeneration, version: yoke.sessionTools.CODEX_CATALOG_VERSION };
2169
+ catalogCandidate = { generation: queryGeneration, versions: { schedules: yoke.sessionTools.SCHEDULE_CATALOG_VERSION } };
2204
2170
  }
2205
- if (requiredCatalogTools.ask_user_questions) {
2206
- codexCatalogCandidate = Object.assign({}, codexCatalogCandidate || {}, { generation: queryGeneration, userInputVersion: 1 });
2207
- }
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 });
2212
- }
2213
- if (hasIssueCatalog) {
2214
- codexCatalogCandidate = Object.assign({}, codexCatalogCandidate || {}, {
2215
- generation: queryGeneration,
2216
- issuesVersion: 1,
2217
- });
2171
+ function markCatalog(family, version) {
2172
+ if (!catalogCandidate) catalogCandidate = { generation: queryGeneration, versions: {} };
2173
+ catalogCandidate.versions[family] = version;
2218
2174
  }
2175
+ if (requiredCatalogTools.ask_user_questions) markCatalog("questions", 1);
2176
+ if (hasYouCatalog) markCatalog("knowledge", 1);
2177
+ if (hasLinearCatalog) markCatalog("linear", 1);
2178
+ if (hasSharedBrowserCatalog) markCatalog("browser", 6);
2179
+ if (hasIssueCatalog) markCatalog("issues", 1);
2219
2180
  }
2220
- session.streamPromise = processQueryStream(session, codexCatalogCandidate).catch(function(err) {
2181
+ session.streamPromise = processQueryStream(session, catalogCandidate, queryContract).catch(function(err) {
2221
2182
  });
2222
2183
  return true;
2223
2184
  }
@@ -2229,6 +2190,19 @@ function createSDKBridge(opts) {
2229
2190
  // dropped messages in both gap windows, which is what made a session show
2230
2191
  // the processing indicator without ever answering until a second send.
2231
2192
  function pushMessage(session, text, images, deliveryMeta) {
2193
+ if (session._responseRecoveryStopPending && !responseRecovery.stopping(session)) return false;
2194
+ function trackDelivery() {
2195
+ responseRecovery.enqueue(session, {
2196
+ text: text, images: images, linuxUser: session.lastLinuxUser || null,
2197
+ });
2198
+ }
2199
+ if (responseRecovery.stopping(session)) {
2200
+ // Keep admission serialized while the old execution is terminating.
2201
+ session.pendingPush = session.pendingPush || [];
2202
+ session.pendingPush.push(Object.assign({ text: text, images: images || null }, deliveryMeta || {}));
2203
+ trackDelivery();
2204
+ return true;
2205
+ }
2232
2206
  session.lastActivityAt = Date.now();
2233
2207
  if (session._runtimeRefreshRequested && !session._queryStarting) {
2234
2208
  session._runtimeRefreshRequested = false;
@@ -2255,6 +2229,8 @@ function createSDKBridge(opts) {
2255
2229
  console.error("[sdk-bridge] QueryHandle rejected message for session " + session.localId + ":", e.message || e);
2256
2230
  }
2257
2231
  if (delivered) {
2232
+ require('./sdk-foreground-admission').take(session);
2233
+ trackDelivery();
2258
2234
  delete session._lastTurnInterrupted;
2259
2235
  if (session._awaitingTurnResult) {
2260
2236
  session._queuedTurnCount = (session._queuedTurnCount || 0) + 1;
@@ -2279,6 +2255,7 @@ function createSDKBridge(opts) {
2279
2255
  if (session._queryStarting) {
2280
2256
  session.pendingPush = session.pendingPush || [];
2281
2257
  session.pendingPush.push(Object.assign({ text: text, images: images || null }, deliveryMeta || {}));
2258
+ trackDelivery();
2282
2259
  return true;
2283
2260
  }
2284
2261
  return false;
@@ -2359,65 +2336,8 @@ function createSDKBridge(opts) {
2359
2336
  // Detect which vendor binaries are installed for this user.
2360
2337
  // In multi-user mode, runs checks as the specific Linux user.
2361
2338
  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;
2339
+ var installed = require("./yoke/discovery").installed({ user: linuxUser });
2340
+ return Object.keys(installed).filter(function(vendor) { return installed[vendor]; });
2421
2341
  }
2422
2342
 
2423
2343
  function getAvailableVendors(linuxUser) {
@@ -2489,7 +2409,20 @@ function createSDKBridge(opts) {
2489
2409
  sendModelInfoForVendor(null, defaultVendor, defaultModelForVendor(defaultVendor));
2490
2410
  }
2491
2411
 
2492
- async function setModel(session, model) {
2412
+ async function setModel(session, model, options) {
2413
+ if (options && typeof options.revalidate === "function") {
2414
+ var vendor = session.vendor || sm.defaultVendor || "claude";
2415
+ return require("./session-config-transition").applySessionConfig({
2416
+ sm: sm, session: session, revalidate: options.revalidate, method: "setModel", nativeValue: model, changes: { model: model },
2417
+ publish: function () {
2418
+ sm.defaultModelByVendor = sm.defaultModelByVendor || {};
2419
+ sm.defaultModelByVendor[vendor] = model;
2420
+ sm.broadcastSessionList();
2421
+ sendModelInfoForVendor(session, vendor, model);
2422
+ 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 || [] });
2423
+ },
2424
+ });
2425
+ }
2493
2426
  // Normalize to string id in case a { value, displayName } object slips in
2494
2427
  if (model && typeof model !== "string") {
2495
2428
  model = modelEntryValue(model);
@@ -2525,7 +2458,22 @@ function createSDKBridge(opts) {
2525
2458
  }
2526
2459
  }
2527
2460
 
2528
- async function setEffort(session, effort) {
2461
+ async function setEffort(session, effort, options) {
2462
+ if (options && typeof options.revalidate === "function") {
2463
+ var vendor = session.vendor || sm.defaultVendor || "claude";
2464
+ var value = yoke.clampEffort(vendor, effort);
2465
+ if (!value || value !== effort) throw new Error("Unsupported reasoning effort.");
2466
+ return require("./session-config-transition").applySessionConfig({
2467
+ sm: sm, session: session, revalidate: options.revalidate, method: "setEffort", nativeValue: value, changes: { effort: value },
2468
+ publish: function () {
2469
+ sm.currentEffortByVendor = sm.currentEffortByVendor || {};
2470
+ sm.currentEffortByVendor[vendor] = value;
2471
+ sm.currentEffort = value;
2472
+ sm.broadcastSessionList();
2473
+ sendToSession(session, { type: "config_state", sessionId: session.localId, vendor: vendor, effort: value, model: session.model || "", mode: session.permissionMode || sm.currentPermissionMode || "default", betas: sm.currentBetas || [] });
2474
+ },
2475
+ });
2476
+ }
2529
2477
  // Remember the pick per vendor (clamped) so vendor switches don't leak
2530
2478
  // unsupported levels across sessions; sm.currentEffort stays as the
2531
2479
  // display value for the session the user just configured.
@@ -2646,16 +2594,7 @@ function createSDKBridge(opts) {
2646
2594
  message: "Read-only access. You cannot make changes via @mention.",
2647
2595
  });
2648
2596
  },
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
- },
2597
+ adapterOptions: require("./yoke/query-options").map(mentionAdapter, { instructionSources: ["user"], readOnly: true }),
2659
2598
  });
2660
2599
  } catch (e) {
2661
2600
  opts.onError("Failed to create mention query: " + (e.message || e));
@@ -2794,12 +2733,14 @@ function createSDKBridge(opts) {
2794
2733
  startQuery: startQuery,
2795
2734
  startQueryWithAcceptance: startQueryWithAcceptance,
2796
2735
  pushMessage: pushMessage,
2736
+ cancelResponseRecovery: responseRecovery.cancel,
2797
2737
  refreshSessionRuntime: refreshSessionRuntime,
2798
2738
  refreshEnvironmentRuntime: refreshEnvironmentRuntime,
2799
2739
  setModel: setModel,
2800
2740
  setEffort: setEffort,
2801
2741
  setPermissionMode: setPermissionMode,
2802
- isClaudeProcess: isClaudeProcess,
2742
+ isProviderProcess: isProviderProcess,
2743
+ isClaudeProcess: isProviderProcess, // Compatibility export; production uses the provider contract.
2803
2744
  permissionPushTitle: permissionPushTitle,
2804
2745
  permissionPushBody: permissionPushBody,
2805
2746
  warmup: warmup,