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

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 (339) 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 +28 -0
  7. package/lib/clay-mate-chat-receipts.js +26 -0
  8. package/lib/clay-mate-chat.js +87 -0
  9. package/lib/clay-mate-knowledge-input.js +26 -0
  10. package/lib/clay-mate-knowledge.js +95 -0
  11. package/lib/clay-mate-lifecycle.js +95 -0
  12. package/lib/clay-product-control-input.js +33 -0
  13. package/lib/clay-product-control-tools.js +51 -0
  14. package/lib/clay-product-control.js +237 -0
  15. package/lib/clay-project-lifecycle.js +154 -0
  16. package/lib/clay-project-record-input.js +44 -0
  17. package/lib/clay-project-records.js +83 -0
  18. package/lib/clay-scheduling-input.js +21 -0
  19. package/lib/clay-scheduling-receipts.js +26 -0
  20. package/lib/clay-scheduling.js +119 -0
  21. package/lib/clay-screen-controls.js +96 -0
  22. package/lib/clay-workspace-controls.js +83 -0
  23. package/lib/clay-workspace-input.js +17 -0
  24. package/lib/clay-workspace-receipts.js +26 -0
  25. package/lib/daemon-project-access.js +11 -0
  26. package/lib/daemon-project-lifecycle-callbacks.js +15 -0
  27. package/lib/daemon-project-lifecycle.js +262 -0
  28. package/lib/daemon-projects.js +1 -1
  29. package/lib/daemon.js +73 -221
  30. package/lib/issues-service.js +29 -15
  31. package/lib/knowledge-db-import.js +5 -4
  32. package/lib/knowledge-db-service.js +9 -6
  33. package/lib/knowledge-document-index.js +2 -1
  34. package/lib/knowledge-file-store.js +7 -1
  35. package/lib/knowledge-product-operations.js +46 -0
  36. package/lib/mate-avatar-catalog.js +12 -0
  37. package/lib/mate-avatar-migration.js +41 -0
  38. package/lib/mate-creation-mcp-server.js +2 -0
  39. package/lib/mate-knowledge-canonical-access.js +10 -0
  40. package/lib/mate-knowledge-service.js +7 -4
  41. package/lib/mate-lifecycle-service.js +94 -0
  42. package/lib/mate-message-delivery.js +43 -0
  43. package/lib/mate-ready-creation.js +9 -1
  44. package/lib/mate-schedule-input.js +29 -0
  45. package/lib/mate-schedule-lifecycle.js +151 -0
  46. package/lib/mate-scheduling.js +103 -0
  47. package/lib/mates.js +18 -6
  48. package/lib/mcp-agent-tools.js +117 -0
  49. package/lib/mcp-connection-store.js +119 -15
  50. package/lib/mcp-import-credentials.js +31 -0
  51. package/lib/mcp-local-transport.js +13 -0
  52. package/lib/mcp-oauth.js +8 -6
  53. package/lib/mcp-query-bridge.js +30 -0
  54. package/lib/mcp-remote-connections.js +74 -20
  55. package/lib/mcp-source-adapters.js +91 -0
  56. package/lib/mcp-source-context.js +42 -0
  57. package/lib/mcp-source-discovery.js +169 -0
  58. package/lib/mcp-source-plugins.js +68 -0
  59. package/lib/mcp-source-restrictions.js +47 -0
  60. package/lib/mcp-tool-policy.js +34 -0
  61. package/lib/notes-checklist.js +50 -0
  62. package/lib/notes.js +49 -3
  63. package/lib/os-users.js +20 -5
  64. package/lib/project-admin-overview.js +51 -0
  65. package/lib/project-ask-user.js +8 -4
  66. package/lib/project-debate.js +1 -0
  67. package/lib/project-delegated-follow-up.js +2 -1
  68. package/lib/project-directory-claim.js +65 -0
  69. package/lib/project-file-operations.js +61 -0
  70. package/lib/project-filesystem.js +13 -87
  71. package/lib/project-full-access.js +23 -2
  72. package/lib/project-knowledge.js +32 -18
  73. package/lib/project-lifecycle-operations.js +98 -0
  74. package/lib/project-lifecycle-process-witness.js +32 -0
  75. package/lib/project-lifecycle-runtime.js +50 -0
  76. package/lib/project-linux-access.js +22 -0
  77. package/lib/project-logs-canonical-clay.js +27 -0
  78. package/lib/project-logs-clay-query.js +16 -0
  79. package/lib/project-logs-schema.js +4 -2
  80. package/lib/project-logs-service.js +8 -7
  81. package/lib/project-logs.js +1 -0
  82. package/lib/project-loop.js +1 -0
  83. package/lib/project-mate-chat-control.js +55 -0
  84. package/lib/project-mate-creation-proposal.js +18 -3
  85. package/lib/project-mate-interaction.js +8 -0
  86. package/lib/project-mcp.js +54 -16
  87. package/lib/project-models.js +14 -31
  88. package/lib/project-notifications.js +74 -16
  89. package/lib/project-ownership.js +77 -0
  90. package/lib/project-pair-result-delivery.js +14 -3
  91. package/lib/project-pending-message-queue.js +1 -0
  92. package/lib/project-product-control.js +87 -0
  93. package/lib/project-product-session-queue.js +46 -0
  94. package/lib/project-push-access.js +28 -0
  95. package/lib/project-scheduled-message-queue.js +30 -0
  96. package/lib/project-scheduled-messages.js +63 -64
  97. package/lib/project-scheduled-task-control.js +83 -0
  98. package/lib/project-scheduled-tasks.js +13 -2
  99. package/lib/project-scheduling-access.js +10 -0
  100. package/lib/project-session-actions.js +65 -0
  101. package/lib/project-session-delete.js +8 -1
  102. package/lib/project-session-document.js +8 -3
  103. package/lib/project-session-github.js +63 -19
  104. package/lib/project-session-history-actions.js +165 -0
  105. package/lib/project-session-linear.js +14 -6
  106. package/lib/project-session-notes.js +7 -3
  107. package/lib/project-session-runtime-actions.js +63 -0
  108. package/lib/project-session-vendor.js +54 -0
  109. package/lib/project-sessions.js +68 -241
  110. package/lib/project-shared-browser.js +56 -12
  111. package/lib/project-user-message.js +30 -1
  112. package/lib/project-workspace-operations.js +126 -0
  113. package/lib/project-workspace-query.js +9 -2
  114. package/lib/project.js +144 -37
  115. package/lib/public/app.js +5 -0
  116. package/lib/public/appearance-bootstrap.js +14 -0
  117. package/lib/public/css/clay-tool-activity.css +51 -0
  118. package/lib/public/css/clay-workspace.css +29 -0
  119. package/lib/public/css/home-debate-planning.css +2 -0
  120. package/lib/public/css/home-mate-creation.css +5 -0
  121. package/lib/public/css/mate-avatar-gallery.css +12 -0
  122. package/lib/public/css/mate-conversation.css +10 -5
  123. package/lib/public/css/overlays.css +4 -2
  124. package/lib/public/css/project-admin-overview.css +13 -0
  125. package/lib/public/css/session-folders.css +11 -0
  126. package/lib/public/css/session-github.css +46 -1
  127. package/lib/public/css/shared-browser.css +35 -1
  128. package/lib/public/css/sticky-notes.css +61 -1
  129. package/lib/public/index.html +11 -5
  130. package/lib/public/mate-avatars/B01.png +0 -0
  131. package/lib/public/mate-avatars/B02.png +0 -0
  132. package/lib/public/mate-avatars/B03.png +0 -0
  133. package/lib/public/mate-avatars/B04.png +0 -0
  134. package/lib/public/mate-avatars/B05.png +0 -0
  135. package/lib/public/mate-avatars/B06.png +0 -0
  136. package/lib/public/mate-avatars/B07.png +0 -0
  137. package/lib/public/mate-avatars/B08.png +0 -0
  138. package/lib/public/mate-avatars/B09.png +0 -0
  139. package/lib/public/mate-avatars/B10.png +0 -0
  140. package/lib/public/mate-avatars/B11.png +0 -0
  141. package/lib/public/mate-avatars/B12.png +0 -0
  142. package/lib/public/mate-avatars/B13.png +0 -0
  143. package/lib/public/mate-avatars/B14.png +0 -0
  144. package/lib/public/mate-avatars/B15.png +0 -0
  145. package/lib/public/mate-avatars/B16.png +0 -0
  146. package/lib/public/mate-avatars/B17.png +0 -0
  147. package/lib/public/mate-avatars/B18.png +0 -0
  148. package/lib/public/mate-avatars/B19.png +0 -0
  149. package/lib/public/mate-avatars/B20.png +0 -0
  150. package/lib/public/mate-avatars/B21.png +0 -0
  151. package/lib/public/mate-avatars/B22.png +0 -0
  152. package/lib/public/mate-avatars/B23.png +0 -0
  153. package/lib/public/mate-avatars/B24.png +0 -0
  154. package/lib/public/mate-avatars/B25.png +0 -0
  155. package/lib/public/mate-avatars/B26.png +0 -0
  156. package/lib/public/mate-avatars/B27.png +0 -0
  157. package/lib/public/mate-avatars/B28.png +0 -0
  158. package/lib/public/mate-avatars/B29.png +0 -0
  159. package/lib/public/mate-avatars/B30.png +0 -0
  160. package/lib/public/mate-avatars/B31.png +0 -0
  161. package/lib/public/mate-avatars/B32.png +0 -0
  162. package/lib/public/mate-avatars/B33.png +0 -0
  163. package/lib/public/mate-avatars/B34.png +0 -0
  164. package/lib/public/mate-avatars/B35.png +0 -0
  165. package/lib/public/mate-avatars/B36.png +0 -0
  166. package/lib/public/mate-avatars/B37.png +0 -0
  167. package/lib/public/mate-avatars/B38.png +0 -0
  168. package/lib/public/mate-avatars/B39.png +0 -0
  169. package/lib/public/mate-avatars/B40.png +0 -0
  170. package/lib/public/mate-avatars/B41.png +0 -0
  171. package/lib/public/mate-avatars/B42.png +0 -0
  172. package/lib/public/mate-avatars/B43.png +0 -0
  173. package/lib/public/mate-avatars/B44.png +0 -0
  174. package/lib/public/mate-avatars/B45.png +0 -0
  175. package/lib/public/mate-avatars/B46.png +0 -0
  176. package/lib/public/mate-avatars/B47.png +0 -0
  177. package/lib/public/mate-avatars/B48.png +0 -0
  178. package/lib/public/mate-avatars/C01.png +0 -0
  179. package/lib/public/mate-avatars/C02.png +0 -0
  180. package/lib/public/mate-avatars/C03.png +0 -0
  181. package/lib/public/mate-avatars/C04.png +0 -0
  182. package/lib/public/mate-avatars/C05.png +0 -0
  183. package/lib/public/mate-avatars/C06.png +0 -0
  184. package/lib/public/mate-avatars/C07.png +0 -0
  185. package/lib/public/mate-avatars/C08.png +0 -0
  186. package/lib/public/mate-avatars/C09.png +0 -0
  187. package/lib/public/mate-avatars/C10.png +0 -0
  188. package/lib/public/mate-avatars/C11.png +0 -0
  189. package/lib/public/mate-avatars/C12.png +0 -0
  190. package/lib/public/mate-avatars/C13.png +0 -0
  191. package/lib/public/mate-avatars/C14.png +0 -0
  192. package/lib/public/mate-avatars/C15.png +0 -0
  193. package/lib/public/mate-avatars/C16.png +0 -0
  194. package/lib/public/mate-avatars/C17.png +0 -0
  195. package/lib/public/mate-avatars/C18.png +0 -0
  196. package/lib/public/mate-avatars/C19.png +0 -0
  197. package/lib/public/mate-avatars/C20.png +0 -0
  198. package/lib/public/mate-avatars/C21.png +0 -0
  199. package/lib/public/mate-avatars/C22.png +0 -0
  200. package/lib/public/mate-avatars/C23.png +0 -0
  201. package/lib/public/mate-avatars/C24.png +0 -0
  202. package/lib/public/mate-avatars/C25.png +0 -0
  203. package/lib/public/mate-avatars/C26.png +0 -0
  204. package/lib/public/mate-avatars/C27.png +0 -0
  205. package/lib/public/mate-avatars/C28.png +0 -0
  206. package/lib/public/mate-avatars/C29.png +0 -0
  207. package/lib/public/mate-avatars/C30.png +0 -0
  208. package/lib/public/mate-avatars/C31.png +0 -0
  209. package/lib/public/mate-avatars/C32.png +0 -0
  210. package/lib/public/mate-avatars/C33.png +0 -0
  211. package/lib/public/mate-avatars/C34.png +0 -0
  212. package/lib/public/mate-avatars/C35.png +0 -0
  213. package/lib/public/mate-avatars/C36.png +0 -0
  214. package/lib/public/mate-avatars/C37.png +0 -0
  215. package/lib/public/mate-avatars/C38.png +0 -0
  216. package/lib/public/mate-avatars/C39.png +0 -0
  217. package/lib/public/mate-avatars/C40.png +0 -0
  218. package/lib/public/mate-avatars/C41.png +0 -0
  219. package/lib/public/mate-avatars/C42.png +0 -0
  220. package/lib/public/mate-avatars/C43.png +0 -0
  221. package/lib/public/mate-avatars/C44.png +0 -0
  222. package/lib/public/mate-avatars/C45.png +0 -0
  223. package/lib/public/mate-avatars/C46.png +0 -0
  224. package/lib/public/mate-avatars/C47.png +0 -0
  225. package/lib/public/mate-avatars/C48.png +0 -0
  226. package/lib/public/mate-avatars/catalog.json +482 -0
  227. package/lib/public/mate-avatars/lead-clay.png +0 -0
  228. package/lib/public/modules/admin-settings-projects.js +6 -2
  229. package/lib/public/modules/app-connection.js +8 -1
  230. package/lib/public/modules/app-message-router.js +4 -0
  231. package/lib/public/modules/app-messages.js +31 -5
  232. package/lib/public/modules/app-projects.js +5 -0
  233. package/lib/public/modules/app-rendering.js +1 -1
  234. package/lib/public/modules/avatar.js +15 -41
  235. package/lib/public/modules/chat-render-runtime.js +10 -1
  236. package/lib/public/modules/clay-browser-links.js +47 -0
  237. package/lib/public/modules/clay-log-links.js +1 -1
  238. package/lib/public/modules/clay-owner-navigation.js +27 -0
  239. package/lib/public/modules/clay-tool-activity.js +140 -0
  240. package/lib/public/modules/clay-tool-presentation.js +338 -0
  241. package/lib/public/modules/command-palette.js +2 -2
  242. package/lib/public/modules/debate.js +1 -0
  243. package/lib/public/modules/default-ai.js +11 -3
  244. package/lib/public/modules/history-replay-batch.js +5 -1
  245. package/lib/public/modules/home-debate-live.js +2 -0
  246. package/lib/public/modules/home-debate-planning.js +7 -7
  247. package/lib/public/modules/home-mate-avatar-editor.js +127 -208
  248. package/lib/public/modules/home-mate-chat.js +8 -6
  249. package/lib/public/modules/home-mate-creation.js +73 -1
  250. package/lib/public/modules/home-mate-settings.js +19 -12
  251. package/lib/public/modules/markdown.js +4 -0
  252. package/lib/public/modules/mate-avatar-catalog.js +97 -0
  253. package/lib/public/modules/mate-management.js +40 -7
  254. package/lib/public/modules/mcp-connection-view.js +31 -13
  255. package/lib/public/modules/mcp-discovery-view.js +31 -0
  256. package/lib/public/modules/mcp-ui.js +23 -4
  257. package/lib/public/modules/mention.js +3 -0
  258. package/lib/public/modules/pane-browser-bridge.js +20 -0
  259. package/lib/public/modules/personal-settings-dialog.js +7 -1
  260. package/lib/public/modules/profile.js +1 -238
  261. package/lib/public/modules/project-access-recovery.js +17 -0
  262. package/lib/public/modules/project-admin-overview.js +21 -0
  263. package/lib/public/modules/project-mate-navigation.js +99 -14
  264. package/lib/public/modules/search-clay-chat.js +16 -2
  265. package/lib/public/modules/session-folder-layout.js +1 -1
  266. package/lib/public/modules/session-folder-view-menu.js +9 -0
  267. package/lib/public/modules/session-github.js +85 -14
  268. package/lib/public/modules/session-work-primary.js +51 -0
  269. package/lib/public/modules/shared-browser-settings.js +112 -0
  270. package/lib/public/modules/shared-browser-tabs.js +13 -3
  271. package/lib/public/modules/shared-browser.js +27 -6
  272. package/lib/public/modules/sidebar-session-folders-ui.js +1 -1
  273. package/lib/public/modules/split-view.js +2 -0
  274. package/lib/public/modules/sticky-note-markdown.js +72 -6
  275. package/lib/public/modules/sticky-notes-actions.js +30 -0
  276. package/lib/public/modules/sticky-notes-browser.js +9 -2
  277. package/lib/public/modules/sticky-notes-card.js +23 -7
  278. package/lib/public/modules/sticky-notes-checklist.js +71 -0
  279. package/lib/public/modules/sticky-notes-editor.js +108 -44
  280. package/lib/public/modules/sticky-notes-shared.js +50 -2
  281. package/lib/public/modules/sticky-notes-state.js +66 -0
  282. package/lib/public/modules/sticky-notes.js +34 -51
  283. package/lib/public/modules/theme.js +8 -1
  284. package/lib/public/modules/thinking-lifecycle.js +33 -3
  285. package/lib/public/modules/thinking-view.js +23 -1
  286. package/lib/public/modules/tools.js +36 -7
  287. package/lib/public/modules/user-settings-appearance.js +10 -3
  288. package/lib/public/modules/user-settings-editors.js +1 -1
  289. package/lib/public/modules/user-settings-layout.js +11 -2
  290. package/lib/public/modules/user-settings-preferences.js +6 -2
  291. package/lib/public/modules/user-settings-profile.js +12 -2
  292. package/lib/public/modules/user-settings.js +3 -2
  293. package/lib/public/style.css +3 -0
  294. package/lib/public/sw.js +3 -1
  295. package/lib/scheduled-task-records.js +3 -1
  296. package/lib/scheduler.js +2 -0
  297. package/lib/sdk-bridge.js +228 -23
  298. package/lib/sdk-message-processor.js +2 -0
  299. package/lib/sdk-response-recovery.js +155 -0
  300. package/lib/server-admin.js +63 -16
  301. package/lib/server-appearance-settings.js +13 -1
  302. package/lib/server-dm.js +2 -1
  303. package/lib/server-global-ws.js +10 -20
  304. package/lib/server-home-chat-events.js +18 -4
  305. package/lib/server-home-chat.js +12 -16
  306. package/lib/server-home-mate-creation.js +1 -0
  307. package/lib/server-home-mcp-consent.js +14 -0
  308. package/lib/server-mates.js +25 -62
  309. package/lib/server-owner-ui.js +77 -0
  310. package/lib/server-path-access.js +38 -0
  311. package/lib/server-project-removal.js +22 -0
  312. package/lib/server-settings.js +4 -1
  313. package/lib/server.js +144 -41
  314. package/lib/session-config-transition.js +45 -0
  315. package/lib/session-folders.js +7 -2
  316. package/lib/session-github.js +9 -2
  317. package/lib/session-visibility.js +5 -2
  318. package/lib/session-work-primary.js +50 -0
  319. package/lib/sessions.js +13 -0
  320. package/lib/shared-browser-cookie-sync.js +32 -0
  321. package/lib/shared-browser-cookies.js +79 -0
  322. package/lib/shared-browser-runtime.js +25 -9
  323. package/lib/shared-browser-storage-sync.js +43 -0
  324. package/lib/shared-browser-storage.js +101 -0
  325. package/lib/shared-browser-tools.js +2 -1
  326. package/lib/shared-browser-worker.js +46 -10
  327. package/lib/terminal-manager.js +52 -0
  328. package/lib/users-permissions.js +2 -8
  329. package/lib/users.js +4 -0
  330. package/lib/workspace-query-access.js +4 -1
  331. package/lib/ws-schema.js +8 -2
  332. package/lib/yoke/acp-agent-profiles.js +2 -2
  333. package/lib/yoke/adapters/claude.js +47 -8
  334. package/lib/yoke/adapters/codex.js +52 -111
  335. package/lib/yoke/claude-worker-startup.js +28 -0
  336. package/lib/yoke/codex-idle-recovery.js +81 -1
  337. package/lib/yoke/codex-recovery-admission.js +164 -0
  338. package/lib/yoke/session-tool-transport.js +3 -0
  339. package/package.json +5 -1
@@ -59,6 +59,7 @@
59
59
  @import url("css/home-debate-planning.css");
60
60
  @import url("css/home-mate-creation.css");
61
61
  @import url("css/home-mate-settings.css");
62
+ @import url("css/mate-avatar-gallery.css");
62
63
  @import url("css/home-mate-model-picker.css");
63
64
  @import url("css/project-mate-creation.css");
64
65
  @import url("css/home-debate-live.css");
@@ -87,3 +88,5 @@
87
88
  @import url("css/server-settings-system.css");
88
89
 
89
90
  @import url("css/project-settings-modal.css");
91
+
92
+ @import url("css/clay-tool-activity.css");
package/lib/public/sw.js CHANGED
@@ -1,4 +1,4 @@
1
- var CACHE_NAME = "clay-offline-v4";
1
+ var CACHE_NAME = "clay-offline-v5";
2
2
 
3
3
  self.addEventListener("install", function (event) {
4
4
  event.waitUntil(self.skipWaiting());
@@ -23,6 +23,8 @@ self.addEventListener("activate", function (event) {
23
23
  function shouldCache(request, response) {
24
24
  if (request.method !== "GET") return false;
25
25
  if (!response || !response.ok) return false;
26
+ var cacheControl = response.headers && response.headers.get("Cache-Control") || "";
27
+ if (/(^|,)\s*(private|no-store)(?:\s*=\s*[^,]*)?(?=\s*(?:,|$))/i.test(cacheControl)) return false;
26
28
  // Cache same-origin static assets and CDN resources (jsdelivr, fonts)
27
29
  var url = new URL(request.url);
28
30
  if (url.origin === self.location.origin) return true;
@@ -18,6 +18,7 @@ function createScheduledTaskRecords(ctx) {
18
18
  var runs = Array.isArray(record.runs) ? record.runs : [];
19
19
  var readiness = ctx.runtimeStatus ? ctx.runtimeStatus(record) : null;
20
20
  return {
21
+ createdBy: record.createdBy || null, updatedBy: record.updatedBy || null,
21
22
  id: record.id, revision: Number(record.updatedAt || 0), name: record.name || "",
22
23
  instructions: record.task || record.prompt || record.instructions || "",
23
24
  cron: record.cron || null, date: record.date || null, time: record.time || null,
@@ -49,7 +50,7 @@ function createScheduledTaskRecords(ctx) {
49
50
  return { ok: true, record: publicRecord(record) };
50
51
  }
51
52
 
52
- function update(session, id, revision, patch) {
53
+ function update(session, id, revision, patch, actor) {
53
54
  if (typeof id !== "string" || !ID_PATTERN.test(id)) return { ok: false, error: "Scheduled task id is invalid." };
54
55
  var record = ctx.registry.getById(id);
55
56
  if (!record) return { ok: false, error: "Scheduled task not found." };
@@ -94,6 +95,7 @@ function createScheduledTaskRecords(ctx) {
94
95
  }
95
96
  try {
96
97
  var updateData = Object.assign({}, checked.value);
98
+ if (actor) updateData.updatedBy = actor;
97
99
  if (claimOwner) { updateData.ownerId = session.ownerId; updateData.enabled = true; }
98
100
  if (updateData.cron) {
99
101
  updateData.date = record.cron && !record.scheduleRule ? record.date || null : null;
package/lib/scheduler.js CHANGED
@@ -239,6 +239,7 @@ function createLoopRegistry(opts) {
239
239
 
240
240
  function register(data) {
241
241
  var rec = {
242
+ createdBy: data.createdBy || null, updatedBy: data.updatedBy || null, creationFingerprint: data.creationFingerprint || null,
242
243
  id: data.id || ("loop_" + Date.now() + "_" + require("crypto").randomBytes(3).toString("hex")),
243
244
  name: data.name || "Untitled",
244
245
  task: data.task || "",
@@ -297,6 +298,7 @@ function createLoopRegistry(opts) {
297
298
  if (!rec) return null;
298
299
  var previous = Object.assign({}, rec);
299
300
 
301
+ if (data.updatedBy !== undefined) rec.updatedBy = data.updatedBy;
300
302
  if (data.name !== undefined) rec.name = data.name;
301
303
  if (data.cron !== undefined) rec.cron = data.cron;
302
304
  if (data.enabled !== undefined) rec.enabled = data.enabled;
package/lib/sdk-bridge.js CHANGED
@@ -323,6 +323,7 @@ function createSDKBridge(opts) {
323
323
  }
324
324
 
325
325
  function stopIdleReaper() {
326
+ responseRecovery.dispose();
326
327
  if (_idleReaperTimer) {
327
328
  clearInterval(_idleReaperTimer);
328
329
  _idleReaperTimer = null;
@@ -389,6 +390,17 @@ function createSDKBridge(opts) {
389
390
  var sendAndRecord = msgProcessor.sendAndRecord;
390
391
  var sendToSession = msgProcessor.sendToSession;
391
392
 
393
+ var responseRecovery = require("./sdk-response-recovery").attachResponseRecovery(Object.assign({
394
+ sessions: sm.sessions,
395
+ changed: onProcessingChanged,
396
+ record: sendAndRecord,
397
+ send: sendToSession,
398
+ broadcast: function() { if (sm.broadcastSessionList) sm.broadcastSessionList(); },
399
+ restart: function(session, payload, state) {
400
+ return startQuery(session, payload.text, payload.images, payload.linuxUser, payload.beforePush, null, state);
401
+ },
402
+ }, opts.responseRecovery || {}));
403
+
392
404
  // --- MCP elicitation ---
393
405
 
394
406
  function handleElicitation(session, request, opts) {
@@ -417,6 +429,7 @@ function createSDKBridge(opts) {
417
429
 
418
430
  if (pushModule) {
419
431
  pushModule.sendPush({
432
+ sessionId: session.localId,
420
433
  type: "elicitation",
421
434
  slug: slug,
422
435
  title: (request.serverName || "MCP Server") + " needs input",
@@ -467,6 +480,7 @@ function createSDKBridge(opts) {
467
480
 
468
481
  if (pushModule) {
469
482
  pushModule.sendPush({
483
+ sessionId: session.localId,
470
484
  type: "user_dialog",
471
485
  slug: slug,
472
486
  title: "Claude needs a decision",
@@ -745,6 +759,34 @@ function createSDKBridge(opts) {
745
759
  // Workspace inspection and proposal posting are session-bound. Keep this
746
760
  // list exact so a future execution/mutation tool cannot inherit approval.
747
761
  var safeWorkspaceTools = {
762
+ "mcp__clay-workspace__read_owner_screen": true,
763
+ "mcp__clay-workspace__inspect_owner_screen_change": true,
764
+ "mcp__clay-workspace__control_owner_screen": true,
765
+ "mcp__clay-workspace__discover_product_controls": true,
766
+ "mcp__clay-workspace__list_owned_mates": true,
767
+ "mcp__clay-workspace__begin_schedule_control_interview": true,
768
+ "mcp__clay-workspace__read_schedule_controls": true,
769
+ "mcp__clay-workspace__inspect_schedule_change": true,
770
+ "mcp__clay-workspace__control_schedules": true,
771
+ "mcp__clay-workspace__read_mate_chat_control": true,
772
+ "mcp__clay-workspace__inspect_mate_chat_change": true,
773
+ "mcp__clay-workspace__control_mate_chat": true,
774
+ "mcp__clay-workspace__read_project_workspace": true,
775
+ "mcp__clay-workspace__read_workspace_receipt": true,
776
+ "mcp__clay-workspace__inspect_workspace_change": true,
777
+ "mcp__clay-workspace__control_project_workspace": true,
778
+ "mcp__clay-workspace__read_project_records": true,
779
+ "mcp__clay-workspace__inspect_project_record_change": true,
780
+ "mcp__clay-workspace__control_project_records": true,
781
+ "mcp__clay-workspace__read_mate_knowledge_control": true,
782
+ "mcp__clay-workspace__inspect_mate_knowledge_change": true,
783
+ "mcp__clay-workspace__control_mate_knowledge": true,
784
+ "mcp__clay-workspace__inspect_mate_control": true,
785
+ "mcp__clay-workspace__control_mate_lifecycle": true,
786
+ "mcp__clay-workspace__inspect_project_control": true,
787
+ "mcp__clay-workspace__control_project_lifecycle": true,
788
+ "mcp__clay-workspace__inspect_session_control": true,
789
+ "mcp__clay-workspace__control_project_session": true,
748
790
  "mcp__clay-workspace__list_projects": true,
749
791
  "mcp__clay-workspace__list_project_sessions": true,
750
792
  "mcp__clay-workspace__search_project_history": true,
@@ -902,6 +944,7 @@ function createSDKBridge(opts) {
902
944
 
903
945
  if (pushModule) {
904
946
  pushModule.sendPush({
947
+ sessionId: session.localId,
905
948
  type: "permission_request",
906
949
  slug: slug,
907
950
  requestId: requestId,
@@ -1006,6 +1049,7 @@ function createSDKBridge(opts) {
1006
1049
  function recordRuntimeEffectivePermissionMode(session, queryInstance, queryGeneration, message) {
1007
1050
  if (!message || (message.yokeType !== "init" && message.yokeType !== "status") || !message.permissionMode) return false;
1008
1051
  if (!ownsQueryRuntime(session, queryInstance, queryGeneration)) return false;
1052
+ session._permissionObservationRevision = Number(session._permissionObservationRevision || 0) + 1;
1009
1053
  session.effectivePermissionMode = message.permissionMode;
1010
1054
  session.permissionModeFallbackReason = null;
1011
1055
  sm.saveSessionFile(session);
@@ -1047,7 +1091,7 @@ function createSDKBridge(opts) {
1047
1091
  try {
1048
1092
  for await (var msg of myQueryInstance) {
1049
1093
  // An old iterator may drain after Stop or after a replacement query starts.
1050
- if (!ownsQueryRuntime(session, myQueryInstance, myQueryGeneration)) continue;
1094
+ if (!ownsQueryRuntime(session, myQueryInstance, myQueryGeneration) || responseRecovery.stopping(session)) continue;
1051
1095
  if ((session.taskStopRequested || (myAbortController && myAbortController.signal.aborted))
1052
1096
  && msg && msg.yokeType !== "error" && msg.yokeType !== "interrupted") continue;
1053
1097
  if (msg && msg.yokeType !== "text_delta" && msg.yokeType !== "thinking_delta" && msg.yokeType !== "tool_input_delta") {
@@ -1091,6 +1135,7 @@ function createSDKBridge(opts) {
1091
1135
  if (continuationIdentity && !ownsQueryRuntime(session, myQueryInstance, myQueryGeneration)) {
1092
1136
  continue;
1093
1137
  }
1138
+ responseRecovery.event(session, msg);
1094
1139
  processSDKMessage(session, msg);
1095
1140
  if (continuationIdentity) {
1096
1141
  settleContinuationStartup(session, myQueryInstance, myQueryGeneration, true, null);
@@ -1120,7 +1165,7 @@ function createSDKBridge(opts) {
1120
1165
  // Stream ended normally after a task stop — no "result" message was sent,
1121
1166
  // so the session is still marked as processing. Send interrupted feedback.
1122
1167
  console.log("[sdk-bridge] processQueryStream ended: isProcessing=" + session.isProcessing + " taskStopRequested=" + session.taskStopRequested);
1123
- var stillOwnsRuntime = session.queryInstance === myQueryInstance;
1168
+ var stillOwnsRuntime = session.queryInstance === myQueryInstance && !responseRecovery.stopping(session);
1124
1169
  settleContinuationStartup(session, myQueryInstance, myQueryGeneration, false, "The successor query ended before initialization.");
1125
1170
  if (session.isProcessing && session.taskStopRequested && stillOwnsRuntime) {
1126
1171
  session._lastTurnInterrupted = true;
@@ -1158,7 +1203,7 @@ function createSDKBridge(opts) {
1158
1203
  }
1159
1204
  } catch (err) {
1160
1205
  settleContinuationStartup(session, myQueryInstance, myQueryGeneration, false, err.message || "The successor query failed before initialization.");
1161
- if (session.isProcessing && session.queryInstance === myQueryInstance) {
1206
+ if (session.isProcessing && session.queryInstance === myQueryInstance && !responseRecovery.stopping(session)) {
1162
1207
  session.isProcessing = false;
1163
1208
  session._awaitingTurnResult = false;
1164
1209
  session._queuedTurnCount = 0;
@@ -1272,7 +1317,8 @@ function createSDKBridge(opts) {
1272
1317
  }
1273
1318
  if (session.messageQueue === myMessageQueue) session.messageQueue = null;
1274
1319
  if (session.abortController === myAbortController) session.abortController = null;
1275
- if (ownsRuntime) {
1320
+ if (ownsRuntime && !responseRecovery.stopping(session)) {
1321
+ responseRecovery.cancel(session);
1276
1322
  session.taskStopRequested = false;
1277
1323
  session.pendingPermissions = {};
1278
1324
  // Preserve only restored legacy MCP entries. Canonical Yoke requests
@@ -1321,7 +1367,7 @@ function createSDKBridge(opts) {
1321
1367
  }
1322
1368
  }
1323
1369
  }
1324
- if (!session.isProcessing && typeof opts.onSessionAvailable === "function") opts.onSessionAvailable(session);
1370
+ if (!responseRecovery.stopping(session) && !session.isProcessing && typeof opts.onSessionAvailable === "function") opts.onSessionAvailable(session);
1325
1371
  }
1326
1372
  }
1327
1373
 
@@ -1442,22 +1488,70 @@ function createSDKBridge(opts) {
1442
1488
 
1443
1489
  // Wrapper: marks the boot window so pushMessage can buffer instead of
1444
1490
  // dropping messages that arrive while createQuery is still awaiting.
1445
- async function startQuery(session, text, images, linuxUser, beforePush, onAccepted) {
1491
+ async function startQuery(session, text, images, linuxUser, beforePush, onAccepted, retryState) {
1492
+ if (opts.isLifecycleExecutionPaused && opts.isLifecycleExecutionPaused()) {
1493
+ 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." });
1494
+ session.isProcessing = false; onProcessingChanged(); sendAndRecord(session, { type: "done", code: 1 }); return false;
1495
+ }
1496
+ if (session._responseRecoveryStopPending && !retryState) {
1497
+ session.isProcessing = false;
1498
+ onProcessingChanged();
1499
+ sendAndRecord(session, { type: "error", text: "The previous Claude attempt has not confirmed stopped. Check the connection before sending this message again." });
1500
+ sendAndRecord(session, { type: "done", code: 1 });
1501
+ return false;
1502
+ }
1503
+ if (responseRecovery.stopping(session) && !retryState) return false;
1446
1504
  delete session._lastTurnInterrupted;
1505
+ var claude = (session.vendor || adapter.vendor) === "claude";
1506
+ var payload = { text: text, images: images, linuxUser: linuxUser, beforePush: beforePush };
1507
+ var recoveryState = claude ? responseRecovery.begin(session, payload, retryState) : null;
1508
+ if (recoveryState) recoveryState.handle = null;
1509
+ var guardedPush = function() {
1510
+ return !session.destroying && !session.taskStopRequested
1511
+ && (!recoveryState || responseRecovery.valid(session, recoveryState) && !recoveryState.stopping)
1512
+ && (typeof beforePush !== "function" || beforePush() === true);
1513
+ };
1514
+ guardedPush.exactProductDelivery = !!(beforePush && beforePush.exactProductDelivery);
1447
1515
  session._queryGeneration = (session._queryGeneration || 0) + 1;
1448
1516
  session._queryStarting = true;
1517
+ var starting = startQueryInner(session, text, images, linuxUser, guardedPush, onAccepted, recoveryState);
1518
+ if (recoveryState) recoveryState.starting = starting;
1449
1519
  try {
1450
- return await startQueryInner(session, text, images, linuxUser, beforePush, onAccepted);
1520
+ var accepted = await starting;
1521
+ if (accepted === false && (!recoveryState || !recoveryState.stopping)) {
1522
+ responseRecovery.cancel(session);
1523
+ session.isProcessing = false;
1524
+ onProcessingChanged();
1525
+ }
1526
+ return accepted;
1527
+ } catch (error) {
1528
+ // Includes synchronous input queue failures, before stream consumption.
1529
+ var failedHandle = session.queryInstance;
1530
+ if (failedHandle) {
1531
+ try { if (typeof failedHandle.abort === "function") failedHandle.abort(); } catch (e) {}
1532
+ try { if (typeof failedHandle.close === "function") failedHandle.close(); } catch (e) {}
1533
+ }
1534
+ session.queryInstance = null;
1535
+ session.abortController = null;
1536
+ session.messageQueue = null;
1537
+ session.isProcessing = false;
1538
+ session._awaitingTurnResult = false;
1539
+ session._queuedTurnCount = 0;
1540
+ taskLifecycle.clearAll(session);
1541
+ if (!recoveryState || !recoveryState.stopping) {
1542
+ responseRecovery.cancel(session);
1543
+ sendAndRecord(session, { type: "error", text: "The agent connection failed while delivering your message. Please retry or check the connection." });
1544
+ sendAndRecord(session, { type: "done", code: 1 });
1545
+ onProcessingChanged();
1546
+ sm.broadcastSessionList();
1547
+ }
1548
+ return false;
1451
1549
  } finally {
1452
1550
  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);
1551
+ delete session._codexRecoveryWaiting;
1552
+ if (!session.queryInstance && session.pendingPush && session.pendingPush.length) session.pendingPush = [];
1553
+ if (!session.queryInstance && !session.isProcessing && !session._pendingMessageDrainPaused && !responseRecovery.stopping(session)) session.taskStopRequested = false;
1554
+ if (!responseRecovery.stopping(session) && !session.isProcessing && typeof opts.onSessionAvailable === "function") opts.onSessionAvailable(session);
1461
1555
  }
1462
1556
  }
1463
1557
 
@@ -1501,7 +1595,7 @@ function createSDKBridge(opts) {
1501
1595
  };
1502
1596
  }
1503
1597
 
1504
- async function startQueryInner(session, text, images, linuxUser, beforePush, onAccepted) {
1598
+ async function startQueryInner(session, text, images, linuxUser, beforePush, onAccepted, recoveryState) {
1505
1599
  var sessionRuntimeEnv = getRuntimeEnv(session);
1506
1600
  var resumedAtQueryStart = !!session.cliSessionId;
1507
1601
  async function ensureVendorReady(vendor) {
@@ -1812,7 +1906,7 @@ function createSDKBridge(opts) {
1812
1906
  }
1813
1907
  var hasSharedBrowserCatalog = sessionToolDefs.some(function (definition) { return definition.name === "shared_browser"; });
1814
1908
  var recoverCodexBrowserTools = sessionAdapter.vendor === "codex" && !!session.cliSessionId
1815
- && hasSharedBrowserCatalog && Number(session.codexSharedBrowserToolCatalogVersion || 0) < 5;
1909
+ && hasSharedBrowserCatalog && Number(session.codexSharedBrowserToolCatalogVersion || 0) < 6;
1816
1910
  var recoverCodexIssueTools = sessionAdapter.vendor === "codex"
1817
1911
  && !!session.cliSessionId
1818
1912
  && hasIssueCatalog
@@ -1822,13 +1916,15 @@ function createSDKBridge(opts) {
1822
1916
  if (sm && sm.sessions && typeof sm.sessions.get === "function" && sm.sessions.get(session.localId) !== session) return false;
1823
1917
  return canUseSessionTools(session) === true;
1824
1918
  }
1919
+ var hasMateScheduleTool = sessionToolDefs.some(function (tool) { return yoke.sessionTools.MATE_SCHEDULE_TOOLS.indexOf(tool.name) !== -1; });
1920
+ var recoverCodexMateSchedules = sessionAdapter.vendor === "codex" && !!session.cliSessionId && hasMateScheduleTool && queryContextAvailable();
1825
1921
  var hasScheduleTool = false;
1826
1922
  var hasScheduleInterviewTool = false;
1827
1923
  var scheduleInterviewActive = null;
1828
1924
  var scheduleQuestionLimit = null;
1829
1925
  for (var sessionToolIndex = 0; sessionToolIndex < sessionToolDefs.length; sessionToolIndex++) {
1830
1926
  if (yoke.sessionTools.SCHEDULE_TOOLS.indexOf(sessionToolDefs[sessionToolIndex].name) !== -1) hasScheduleTool = true;
1831
- if (sessionToolDefs[sessionToolIndex].name === "begin_scheduled_task_interview") {
1927
+ if (["begin_scheduled_task_interview", "begin_mate_schedule_interview", "begin_schedule_control_interview"].indexOf(sessionToolDefs[sessionToolIndex].name) >= 0) {
1832
1928
  hasScheduleInterviewTool = true;
1833
1929
  scheduleInterviewActive = sessionToolDefs[sessionToolIndex].scheduleInterviewActive || null;
1834
1930
  scheduleQuestionLimit = sessionToolDefs[sessionToolIndex].structuredQuestionLimit || null;
@@ -1860,9 +1956,10 @@ function createSDKBridge(opts) {
1860
1956
  return "Ask exactly one question at a time, wait for the answer, then ask the next question.";
1861
1957
  }
1862
1958
  var sessionUserInputHandler = function(request, respond) {
1863
- if (!structuredInputContextAvailable()) {
1959
+ if (queryRetired() || !structuredInputContextAvailable()) {
1864
1960
  return respond.cancel("This structured-input request belongs to an unavailable or older query.");
1865
1961
  }
1962
+ responseRecovery.progress(session);
1866
1963
  var questionError = scheduleQuestionError(request);
1867
1964
  if (questionError) return respond.cancel(questionError);
1868
1965
  if (request.presentation === "elicitation" && request.diagnostics && request.diagnostics.elicitation) {
@@ -1895,6 +1992,11 @@ function createSDKBridge(opts) {
1895
1992
  }
1896
1993
  }
1897
1994
  }
1995
+ if (sessionAdapter.vendor === "claude" && hasMateScheduleTool && typeof sessionAdapter.createToolServer === "function") {
1996
+ localMcpServers = Object.assign({}, localMcpServers || {});
1997
+ localMcpServers["clay-mate-schedules"] = sessionAdapter.createToolServer({ name: "clay-mate-schedules", version: "1.0.0",
1998
+ tools: sessionToolDefs.filter(function (tool) { return yoke.sessionTools.MATE_SCHEDULE_TOOLS.indexOf(tool.name) !== -1; }) });
1999
+ }
1898
2000
  if (selectedUserInputMode === "fallback") {
1899
2001
  var userInputServer = yoke.userInput.createFallbackServer(sessionAdapter, sessionUserInputHandler, { force: true, maxQuestions: scheduleQuestionLimit });
1900
2002
  if (userInputServer) {
@@ -1931,6 +2033,12 @@ function createSDKBridge(opts) {
1931
2033
  return snapshotDef;
1932
2034
  }
1933
2035
 
2036
+ var externalQueryBridge = require('./mcp-query-bridge').createQueryBridge({ valid: queryContextAvailable,
2037
+ getServers: function () { return getRemoteMcpServers ? getRemoteMcpServers(session) || {} : {}; } });
2038
+ var externalToolDefs = sessionAdapter.vendor === 'codex' ? externalQueryBridge.dynamicDefs() : [];
2039
+ if (externalToolDefs.length) { sessionToolDefs = sessionToolDefs.concat(externalToolDefs); queryToolSnapshots.set(session, { generation: queryGeneration, defs: sessionToolDefs.slice() }); }
2040
+ var recoverCodexExternalTools = sessionAdapter.vendor === 'codex' && !!session.cliSessionId && (externalToolDefs.length > 0 || isMate);
2041
+
1934
2042
  // Give fresh queries an immediate provisional label. The visible user
1935
2043
  // message in history wins over `text`, which may contain an internal
1936
2044
  // provider wrapper rather than what the person typed.
@@ -1945,6 +2053,11 @@ function createSDKBridge(opts) {
1945
2053
  }
1946
2054
 
1947
2055
  var modelSwitchGeneration = session._queryGeneration;
2056
+ function queryRetired() {
2057
+ if (sessionAdapter.vendor !== "claude") return false;
2058
+ return Number(session._sdkQueryGeneration || 0) !== queryGeneration || session.taskStopRequested
2059
+ || session.destroying || responseRecovery.stopping(session) || (handle && session.queryInstance !== handle);
2060
+ }
1948
2061
  var queryOpts = {
1949
2062
  onModelSwitch: function(change) {
1950
2063
  if (session._queryGeneration !== modelSwitchGeneration || session.taskStopRequested) return;
@@ -1986,7 +2099,18 @@ function createSDKBridge(opts) {
1986
2099
  dynamicTools: sessionToolDefs,
1987
2100
  // Codex persists dynamic tools with a thread. A stale authorized Driver
1988
2101
  // gets this exact-query bridge; its handlers still reauthorize live.
1989
- sessionMcpServer: (recoverCodexIssueTools || recoverCodexQuestionTools || recoverCodexBrowserTools || recoverCodexLinearTools || recoverCodexYouTools) ? getCodexSessionMcpServer(session, queryGeneration) : undefined,
2102
+ sessionMcpServer: (recoverCodexMateSchedules || recoverCodexIssueTools || recoverCodexQuestionTools || recoverCodexBrowserTools || recoverCodexLinearTools || recoverCodexYouTools || recoverCodexExternalTools) ? getCodexSessionMcpServer(session, queryGeneration) : undefined,
2103
+ onRecoveryAdmission: function(waiting) {
2104
+ if (!queryContextAvailable()) return;
2105
+ if (waiting) session._codexRecoveryWaiting = queryGeneration;
2106
+ else delete session._codexRecoveryWaiting;
2107
+ },
2108
+ onRecoveryWaiting: function(waiting) {
2109
+ if (!queryContextAvailable()) return;
2110
+ sendAndRecord(session, { type: 'informational', level: 'info', content: waiting
2111
+ ? 'Codex recovery is waiting for this conversation’s runtime to become safely available. Your message is retained; Stop cancels the wait.'
2112
+ : 'Codex recovery is ready. Continuing your retained message.' });
2113
+ },
1990
2114
  callDynamicTool: function (toolName, args) {
1991
2115
  var toolDef = findSessionToolDef(toolName);
1992
2116
  if (toolDef && typeof toolDef.handler === "function") {
@@ -1997,6 +2121,8 @@ function createSDKBridge(opts) {
1997
2121
  resumeSessionId: session.cliSessionId || undefined,
1998
2122
  abortController: linuxUser ? undefined : session.abortController,
1999
2123
  canUseTool: function(toolName, input, toolOpts) {
2124
+ if (queryRetired()) return { behavior: "deny", message: "This attempt has stopped." };
2125
+ responseRecovery.progress(session);
2000
2126
  var nativeQuestionError = toolName === "AskUserQuestion" ? scheduleQuestionError(input) : null;
2001
2127
  if (nativeQuestionError) return { behavior: "deny", message: nativeQuestionError };
2002
2128
  var permissionName = toolName;
@@ -2005,6 +2131,8 @@ function createSDKBridge(opts) {
2005
2131
  return handleCanUseTool(session, permissionName, input, toolOpts);
2006
2132
  },
2007
2133
  preToolUsePolicy: function(toolName, input) {
2134
+ if (queryRetired()) return Promise.resolve({ behavior: "deny", message: "This attempt has stopped." });
2135
+ responseRecovery.progress(session);
2008
2136
  var nativeQuestionError = toolName === "AskUserQuestion" ? scheduleQuestionError(input) : null;
2009
2137
  if (nativeQuestionError) return Promise.resolve({ behavior: "deny", message: nativeQuestionError });
2010
2138
  var permissionName = toolName;
@@ -2018,12 +2146,18 @@ function createSDKBridge(opts) {
2018
2146
  validateUserInputRequest: scheduleQuestionError,
2019
2147
  userInputMode: selectedUserInputMode,
2020
2148
  onElicitation: function(request, elicitOpts) {
2149
+ if (queryRetired()) return Promise.resolve({ action: "cancel" });
2150
+ responseRecovery.progress(session);
2021
2151
  return handleElicitation(session, request, elicitOpts);
2022
2152
  },
2023
2153
  onUserDialog: function(dialogRequest, dialogOpts) {
2154
+ if (queryRetired()) return Promise.resolve({ behavior: "cancelled" });
2155
+ responseRecovery.progress(session);
2024
2156
  return handleUserDialog(session, dialogRequest, dialogOpts);
2025
2157
  },
2026
2158
  callMcpTool: function(serverName, toolName, args) {
2159
+ if (queryRetired()) return Promise.reject(new Error("This attempt has stopped."));
2160
+ responseRecovery.progress(session);
2027
2161
  return callMcpToolHandler(mergedMcpServers, serverName, toolName, args);
2028
2162
  },
2029
2163
  adapterOptions: {
@@ -2055,10 +2189,28 @@ function createSDKBridge(opts) {
2055
2189
  },
2056
2190
  };
2057
2191
 
2192
+ if (recoveryState) {
2193
+ if (!recoveryState.context) recoveryState.context = { appendSystemPrompt: queryOpts.appendSystemPrompt, model: queryOpts.model, effort: queryOpts.effort };
2194
+ Object.assign(queryOpts, recoveryState.context);
2195
+ }
2058
2196
  var handle;
2197
+ var startupHandle = session.queryInstance;
2059
2198
  console.log("[sdk-bridge] calling adapter.createQuery... vendor=" + sessionAdapter.vendor);
2060
2199
  try {
2061
2200
  handle = await sessionAdapter.createQuery(queryOpts);
2201
+ // A guarded delivery must still own the exact startup after provider
2202
+ // creation. Never replace a newer handle or publish to a deleted target.
2203
+ if (beforePush && beforePush.exactProductDelivery) {
2204
+ var startupCurrent = false;
2205
+ try { startupCurrent = sm.sessions.get(session.localId) === session && session.queryInstance === startupHandle && Number(session._sdkQueryGeneration || 0) === queryGeneration && beforePush() === true; } catch (guardError) {}
2206
+ if (!startupCurrent) {
2207
+ try { if (handle.abort) handle.abort(); } catch (abortError) {}
2208
+ try { if (handle.close) handle.close(); } catch (closeError) {}
2209
+ if (session.queryInstance === startupHandle) session.isProcessing = false;
2210
+ return false;
2211
+ }
2212
+ }
2213
+ if (recoveryState) recoveryState.handle = handle;
2062
2214
  console.log("[sdk-bridge] createQuery returned handle, vendor=" + sessionAdapter.vendor);
2063
2215
  // SDK accepted the explicit title. Provisional labels remain eligible
2064
2216
  // for semantic generation after the first Home exchange (or the normal
@@ -2078,6 +2230,7 @@ function createSDKBridge(opts) {
2078
2230
  console.error("[sdk-bridge] Failed to create query for session " + session.localId + ":", e.message || e);
2079
2231
  console.error("[sdk-bridge] cliSessionId:", session.cliSessionId, "resume:", !!session.cliSessionId);
2080
2232
  console.error("[sdk-bridge] Stack:", e.stack || "(no stack)");
2233
+ if (beforePush && beforePush.exactProductDelivery && (sm.sessions.get(session.localId) !== session || session.queryInstance !== startupHandle || Number(session._sdkQueryGeneration || 0) !== queryGeneration)) return false;
2081
2234
  session.isProcessing = false;
2082
2235
  session._awaitingTurnResult = false;
2083
2236
  session._queuedTurnCount = 0;
@@ -2132,6 +2285,7 @@ function createSDKBridge(opts) {
2132
2285
  }
2133
2286
 
2134
2287
  if (typeof beforePush === "function" && beforePush() !== true) {
2288
+ try { if (typeof handle.abort === "function") handle.abort(); } catch (e) {}
2135
2289
  try { handle.close(); } catch (e) {}
2136
2290
  if (session.queryInstance === handle) session.queryInstance = null;
2137
2291
  session.messageQueue = null;
@@ -2141,6 +2295,13 @@ function createSDKBridge(opts) {
2141
2295
  session._queuedTurnCount = 0;
2142
2296
  taskLifecycle.clearAll(session);
2143
2297
  onProcessingChanged();
2298
+ if (recoveryState && !responseRecovery.stopping(session)) {
2299
+ sendAndRecord(session, { type: "thinking_stop" });
2300
+ sendAndRecord(session, { type: session.taskStopRequested ? "info" : "error", text: session.taskStopRequested
2301
+ ? "Stopped before sending your message."
2302
+ : "This request is no longer eligible for delivery. Please review it and send it again." });
2303
+ sendAndRecord(session, { type: "done", code: session.taskStopRequested ? 0 : 1 });
2304
+ }
2144
2305
  sm.broadcastSessionList();
2145
2306
  return false;
2146
2307
  }
@@ -2208,7 +2369,7 @@ function createSDKBridge(opts) {
2208
2369
  if (hasYouCatalog) codexCatalogCandidate = Object.assign({}, codexCatalogCandidate || {}, { generation: queryGeneration, youVersion: 1 });
2209
2370
  if (hasLinearCatalog) codexCatalogCandidate = Object.assign({}, codexCatalogCandidate || {}, { generation: queryGeneration, linearVersion: 1 });
2210
2371
  if (hasSharedBrowserCatalog) {
2211
- codexCatalogCandidate = Object.assign({}, codexCatalogCandidate || {}, { generation: queryGeneration, sharedBrowserVersion: 5 });
2372
+ codexCatalogCandidate = Object.assign({}, codexCatalogCandidate || {}, { generation: queryGeneration, sharedBrowserVersion: 6 });
2212
2373
  }
2213
2374
  if (hasIssueCatalog) {
2214
2375
  codexCatalogCandidate = Object.assign({}, codexCatalogCandidate || {}, {
@@ -2229,6 +2390,19 @@ function createSDKBridge(opts) {
2229
2390
  // dropped messages in both gap windows, which is what made a session show
2230
2391
  // the processing indicator without ever answering until a second send.
2231
2392
  function pushMessage(session, text, images, deliveryMeta) {
2393
+ if (session._responseRecoveryStopPending && !responseRecovery.stopping(session)) return false;
2394
+ function trackDelivery() {
2395
+ if ((session.vendor || adapter.vendor) === "claude") responseRecovery.enqueue(session, {
2396
+ text: text, images: images, linuxUser: session.lastLinuxUser || null,
2397
+ });
2398
+ }
2399
+ if (responseRecovery.stopping(session)) {
2400
+ // Keep admission serialized while the old execution is terminating.
2401
+ session.pendingPush = session.pendingPush || [];
2402
+ session.pendingPush.push(Object.assign({ text: text, images: images || null }, deliveryMeta || {}));
2403
+ trackDelivery();
2404
+ return true;
2405
+ }
2232
2406
  session.lastActivityAt = Date.now();
2233
2407
  if (session._runtimeRefreshRequested && !session._queryStarting) {
2234
2408
  session._runtimeRefreshRequested = false;
@@ -2255,6 +2429,7 @@ function createSDKBridge(opts) {
2255
2429
  console.error("[sdk-bridge] QueryHandle rejected message for session " + session.localId + ":", e.message || e);
2256
2430
  }
2257
2431
  if (delivered) {
2432
+ trackDelivery();
2258
2433
  delete session._lastTurnInterrupted;
2259
2434
  if (session._awaitingTurnResult) {
2260
2435
  session._queuedTurnCount = (session._queuedTurnCount || 0) + 1;
@@ -2279,6 +2454,7 @@ function createSDKBridge(opts) {
2279
2454
  if (session._queryStarting) {
2280
2455
  session.pendingPush = session.pendingPush || [];
2281
2456
  session.pendingPush.push(Object.assign({ text: text, images: images || null }, deliveryMeta || {}));
2457
+ trackDelivery();
2282
2458
  return true;
2283
2459
  }
2284
2460
  return false;
@@ -2489,7 +2665,20 @@ function createSDKBridge(opts) {
2489
2665
  sendModelInfoForVendor(null, defaultVendor, defaultModelForVendor(defaultVendor));
2490
2666
  }
2491
2667
 
2492
- async function setModel(session, model) {
2668
+ async function setModel(session, model, options) {
2669
+ if (options && typeof options.revalidate === "function") {
2670
+ var vendor = session.vendor || sm.defaultVendor || "claude";
2671
+ return require("./session-config-transition").applySessionConfig({
2672
+ sm: sm, session: session, revalidate: options.revalidate, method: "setModel", nativeValue: model, changes: { model: model },
2673
+ publish: function () {
2674
+ sm.defaultModelByVendor = sm.defaultModelByVendor || {};
2675
+ sm.defaultModelByVendor[vendor] = model;
2676
+ sm.broadcastSessionList();
2677
+ sendModelInfoForVendor(session, vendor, model);
2678
+ 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 || [] });
2679
+ },
2680
+ });
2681
+ }
2493
2682
  // Normalize to string id in case a { value, displayName } object slips in
2494
2683
  if (model && typeof model !== "string") {
2495
2684
  model = modelEntryValue(model);
@@ -2525,7 +2714,22 @@ function createSDKBridge(opts) {
2525
2714
  }
2526
2715
  }
2527
2716
 
2528
- async function setEffort(session, effort) {
2717
+ async function setEffort(session, effort, options) {
2718
+ if (options && typeof options.revalidate === "function") {
2719
+ var vendor = session.vendor || sm.defaultVendor || "claude";
2720
+ var value = yoke.clampEffort(vendor, effort);
2721
+ if (!value || value !== effort) throw new Error("Unsupported reasoning effort.");
2722
+ return require("./session-config-transition").applySessionConfig({
2723
+ sm: sm, session: session, revalidate: options.revalidate, method: "setEffort", nativeValue: value, changes: { effort: value },
2724
+ publish: function () {
2725
+ sm.currentEffortByVendor = sm.currentEffortByVendor || {};
2726
+ sm.currentEffortByVendor[vendor] = value;
2727
+ sm.currentEffort = value;
2728
+ sm.broadcastSessionList();
2729
+ sendToSession(session, { type: "config_state", sessionId: session.localId, vendor: vendor, effort: value, model: session.model || "", mode: session.permissionMode || sm.currentPermissionMode || "default", betas: sm.currentBetas || [] });
2730
+ },
2731
+ });
2732
+ }
2529
2733
  // Remember the pick per vendor (clamped) so vendor switches don't leak
2530
2734
  // unsupported levels across sessions; sm.currentEffort stays as the
2531
2735
  // display value for the session the user just configured.
@@ -2794,6 +2998,7 @@ function createSDKBridge(opts) {
2794
2998
  startQuery: startQuery,
2795
2999
  startQueryWithAcceptance: startQueryWithAcceptance,
2796
3000
  pushMessage: pushMessage,
3001
+ cancelResponseRecovery: responseRecovery.cancel,
2797
3002
  refreshSessionRuntime: refreshSessionRuntime,
2798
3003
  refreshEnvironmentRuntime: refreshEnvironmentRuntime,
2799
3004
  setModel: setModel,
@@ -430,6 +430,7 @@ function attachMessageProcessor(ctx) {
430
430
  if (pushModule && block.name === "AskUserQuestion" && input.questions) {
431
431
  var q = input.questions[0];
432
432
  pushModule.sendPush({
433
+ sessionId: session.localId,
433
434
  type: "ask_user",
434
435
  slug: slug,
435
436
  title: (mateDisplayName || "Claude") + " has a question",
@@ -715,6 +716,7 @@ function attachMessageProcessor(ctx) {
715
716
 
716
717
  if (pushModule && !session._delegatedBy && !_suppressResponseNotification) {
717
718
  pushModule.sendPush({
719
+ sessionId: session.localId,
718
720
  type: "done",
719
721
  slug: slug,
720
722
  title: _doneTitle,