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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (608) hide show
  1. package/README.md +0 -4
  2. package/lib/account-auth-socket.js +1 -0
  3. package/lib/builtin-mates.js +19 -16
  4. package/lib/bundled-skills/clay-agent-browser/SKILL.md +6 -2
  5. package/lib/canonical-clay-record-authority.js +15 -0
  6. package/lib/canonical-clay-scheduling-authority.js +19 -0
  7. package/lib/claude-hook-installer.js +1 -239
  8. package/lib/claude-jsonl-watcher.js +1 -204
  9. package/lib/clay-control-presentation-binding.js +96 -0
  10. package/lib/clay-control-presentation.js +128 -0
  11. package/lib/clay-control-target-projection.js +26 -0
  12. package/lib/clay-mate-chat-receipts.js +26 -0
  13. package/lib/clay-mate-chat.js +88 -0
  14. package/lib/clay-mate-knowledge-input.js +26 -0
  15. package/lib/clay-mate-knowledge.js +95 -0
  16. package/lib/clay-mate-lifecycle.js +95 -0
  17. package/lib/clay-product-control-input.js +33 -0
  18. package/lib/clay-product-control-tools.js +52 -0
  19. package/lib/clay-product-control.js +250 -0
  20. package/lib/clay-project-lifecycle.js +154 -0
  21. package/lib/clay-project-record-input.js +44 -0
  22. package/lib/clay-project-records.js +84 -0
  23. package/lib/clay-scheduling-input.js +21 -0
  24. package/lib/clay-scheduling-receipts.js +26 -0
  25. package/lib/clay-scheduling.js +119 -0
  26. package/lib/clay-screen-controls.js +104 -0
  27. package/lib/clay-skill-install.js +1 -33
  28. package/lib/clay-vendor-usage.js +44 -0
  29. package/lib/clay-workspace-controls.js +84 -0
  30. package/lib/clay-workspace-input.js +17 -0
  31. package/lib/clay-workspace-receipts.js +26 -0
  32. package/lib/cli-sessions.js +1 -300
  33. package/lib/codex-defaults.js +1 -28
  34. package/lib/context-usage.js +3 -2
  35. package/lib/contextual-screen-snapshot.js +38 -0
  36. package/lib/daemon-project-access.js +11 -0
  37. package/lib/daemon-project-lifecycle-callbacks.js +15 -0
  38. package/lib/daemon-project-lifecycle.js +262 -0
  39. package/lib/daemon-projects.js +1 -1
  40. package/lib/daemon.js +78 -276
  41. package/lib/default-ai-runtime.js +4 -8
  42. package/lib/dm-digest-cursor.js +120 -0
  43. package/lib/email-mcp-server.js +1 -1
  44. package/lib/github-star-count.js +147 -0
  45. package/lib/issues-service.js +29 -15
  46. package/lib/kiro-defaults.js +1 -22
  47. package/lib/knowledge-db-import-validation.js +46 -0
  48. package/lib/knowledge-db-import.js +99 -0
  49. package/lib/knowledge-db-service.js +24 -33
  50. package/lib/knowledge-db-storage-nedb-maintenance.js +99 -0
  51. package/lib/knowledge-db-storage-nedb.js +66 -0
  52. package/lib/knowledge-db-storage.js +72 -0
  53. package/lib/knowledge-db-validation.js +0 -0
  54. package/lib/knowledge-document-index.js +2 -1
  55. package/lib/knowledge-file-store.js +7 -1
  56. package/lib/knowledge-product-operations.js +46 -0
  57. package/lib/mate-avatar-catalog.js +12 -0
  58. package/lib/mate-avatar-migration.js +41 -0
  59. package/lib/mate-context-service.js +138 -0
  60. package/lib/mate-context-tools.js +21 -0
  61. package/lib/mate-creation-mcp-server.js +2 -0
  62. package/lib/mate-knowledge-canonical-access.js +10 -0
  63. package/lib/mate-knowledge-mate-bound.js +53 -0
  64. package/lib/mate-knowledge-mcp-server.js +55 -8
  65. package/lib/mate-knowledge-service.js +16 -34
  66. package/lib/mate-knowledge-text-tools.js +106 -0
  67. package/lib/mate-lifecycle-service.js +94 -0
  68. package/lib/mate-message-delivery.js +43 -0
  69. package/lib/mate-ready-creation.js +9 -1
  70. package/lib/mate-schedule-input.js +29 -0
  71. package/lib/mate-schedule-lifecycle.js +151 -0
  72. package/lib/mate-scheduling.js +103 -0
  73. package/lib/mates.js +18 -6
  74. package/lib/mcp-agent-tools.js +117 -0
  75. package/lib/mcp-connection-store.js +119 -15
  76. package/lib/mcp-import-credentials.js +31 -0
  77. package/lib/mcp-local-transport.js +13 -0
  78. package/lib/mcp-oauth.js +8 -6
  79. package/lib/mcp-proxy.js +2 -32
  80. package/lib/mcp-query-bridge.js +30 -0
  81. package/lib/mcp-remote-connections.js +74 -20
  82. package/lib/mcp-source-adapters.js +1 -0
  83. package/lib/mcp-source-context.js +1 -0
  84. package/lib/mcp-source-discovery.js +1 -0
  85. package/lib/mcp-source-plugins.js +1 -0
  86. package/lib/mcp-source-restrictions.js +1 -0
  87. package/lib/mcp-tool-policy.js +9 -0
  88. package/lib/notes-checklist.js +92 -0
  89. package/lib/notes.js +51 -4
  90. package/lib/os-user-diagnostics.js +2 -4
  91. package/lib/os-users.js +24 -70
  92. package/lib/pane-pending-interactions.js +20 -0
  93. package/lib/pane-session-activation.js +114 -0
  94. package/lib/pane-session-transport.js +26 -0
  95. package/lib/project-admin-overview.js +51 -0
  96. package/lib/project-ask-user.js +8 -4
  97. package/lib/project-connection.js +14 -10
  98. package/lib/project-control-git-view.js +12 -0
  99. package/lib/project-debate.js +1 -0
  100. package/lib/project-delegated-follow-up.js +2 -1
  101. package/lib/project-directory-claim.js +65 -0
  102. package/lib/project-file-operations.js +61 -0
  103. package/lib/project-filesystem.js +15 -85
  104. package/lib/project-full-access.js +23 -2
  105. package/lib/project-http.js +7 -4
  106. package/lib/project-knowledge.js +32 -18
  107. package/lib/project-lifecycle-operations.js +98 -0
  108. package/lib/project-lifecycle-process-witness.js +32 -0
  109. package/lib/project-lifecycle-runtime.js +50 -0
  110. package/lib/project-linux-access.js +22 -0
  111. package/lib/project-logs-canonical-clay.js +27 -0
  112. package/lib/project-logs-clay-query.js +16 -0
  113. package/lib/project-logs-schema.js +4 -2
  114. package/lib/project-logs-service.js +8 -7
  115. package/lib/project-logs.js +1 -0
  116. package/lib/project-loop.js +1 -0
  117. package/lib/project-mate-chat-control.js +55 -0
  118. package/lib/project-mate-creation-proposal.js +18 -3
  119. package/lib/project-mate-interaction.js +40 -62
  120. package/lib/project-mcp.js +54 -16
  121. package/lib/project-models.js +14 -31
  122. package/lib/project-notifications.js +76 -17
  123. package/lib/project-ownership.js +77 -0
  124. package/lib/project-pair-result-delivery.js +14 -3
  125. package/lib/project-pair-usage.js +12 -13
  126. package/lib/project-pending-message-queue.js +4 -1
  127. package/lib/project-product-control.js +89 -0
  128. package/lib/project-product-session-queue.js +46 -0
  129. package/lib/project-push-access.js +28 -0
  130. package/lib/project-scheduled-message-queue.js +30 -0
  131. package/lib/project-scheduled-messages.js +67 -68
  132. package/lib/project-scheduled-task-control.js +83 -0
  133. package/lib/project-scheduled-tasks.js +13 -2
  134. package/lib/project-scheduling-access.js +10 -0
  135. package/lib/project-session-actions.js +65 -0
  136. package/lib/project-session-delete.js +8 -1
  137. package/lib/project-session-document.js +27 -7
  138. package/lib/project-session-github.js +63 -19
  139. package/lib/project-session-history-actions.js +167 -0
  140. package/lib/project-session-linear.js +14 -6
  141. package/lib/project-session-notes.js +40 -11
  142. package/lib/project-session-pair.js +1 -1
  143. package/lib/project-session-runtime-actions.js +64 -0
  144. package/lib/project-session-spawn.js +2 -3
  145. package/lib/project-session-vendor.js +55 -0
  146. package/lib/project-sessions.js +160 -290
  147. package/lib/project-shared-browser.js +78 -17
  148. package/lib/project-start-protocol.js +29 -0
  149. package/lib/project-studio-presentation.js +58 -0
  150. package/lib/project-user-message.js +67 -1
  151. package/lib/project-vendor-login.js +8 -36
  152. package/lib/project-workspace-operations.js +126 -0
  153. package/lib/project-workspace-query.js +19 -2
  154. package/lib/project.js +181 -45
  155. package/lib/public/app.js +71 -17
  156. package/lib/public/appearance-bootstrap.js +16 -0
  157. package/lib/public/css/clay-tool-activity.css +51 -0
  158. package/lib/public/css/clay-workspace.css +29 -0
  159. package/lib/public/css/command-palette.css +12 -33
  160. package/lib/public/css/contextual-clay.css +240 -0
  161. package/lib/public/css/filebrowser.css +1 -1
  162. package/lib/public/css/home-debate-planning.css +2 -0
  163. package/lib/public/css/home-mate-creation.css +5 -0
  164. package/lib/public/css/input.css +6 -0
  165. package/lib/public/css/mate-avatar-gallery.css +12 -0
  166. package/lib/public/css/mate-conversation.css +10 -5
  167. package/lib/public/css/mate-loading-scene.css +21 -0
  168. package/lib/public/css/messages.css +6 -2
  169. package/lib/public/css/overlays.css +68 -258
  170. package/lib/public/css/project-admin-overview.css +13 -0
  171. package/lib/public/css/project-mate-creation.css +5 -6
  172. package/lib/public/css/project-start.css +108 -0
  173. package/lib/public/css/projects-hub.css +53 -14
  174. package/lib/public/css/session-folders.css +11 -0
  175. package/lib/public/css/session-github.css +46 -1
  176. package/lib/public/css/settings-shell.css +103 -0
  177. package/lib/public/css/shared-browser.css +35 -1
  178. package/lib/public/css/sidebar.css +7 -0
  179. package/lib/public/css/sticky-notes.css +87 -1
  180. package/lib/public/css/title-bar.css +79 -0
  181. package/lib/public/index.html +61 -68
  182. package/lib/public/mate-avatars/B01.png +0 -0
  183. package/lib/public/mate-avatars/B02.png +0 -0
  184. package/lib/public/mate-avatars/B03.png +0 -0
  185. package/lib/public/mate-avatars/B04.png +0 -0
  186. package/lib/public/mate-avatars/B05.png +0 -0
  187. package/lib/public/mate-avatars/B06.png +0 -0
  188. package/lib/public/mate-avatars/B07.png +0 -0
  189. package/lib/public/mate-avatars/B08.png +0 -0
  190. package/lib/public/mate-avatars/B09.png +0 -0
  191. package/lib/public/mate-avatars/B10.png +0 -0
  192. package/lib/public/mate-avatars/B11.png +0 -0
  193. package/lib/public/mate-avatars/B12.png +0 -0
  194. package/lib/public/mate-avatars/B13.png +0 -0
  195. package/lib/public/mate-avatars/B14.png +0 -0
  196. package/lib/public/mate-avatars/B15.png +0 -0
  197. package/lib/public/mate-avatars/B16.png +0 -0
  198. package/lib/public/mate-avatars/B17.png +0 -0
  199. package/lib/public/mate-avatars/B18.png +0 -0
  200. package/lib/public/mate-avatars/B19.png +0 -0
  201. package/lib/public/mate-avatars/B20.png +0 -0
  202. package/lib/public/mate-avatars/B21.png +0 -0
  203. package/lib/public/mate-avatars/B22.png +0 -0
  204. package/lib/public/mate-avatars/B23.png +0 -0
  205. package/lib/public/mate-avatars/B24.png +0 -0
  206. package/lib/public/mate-avatars/B25.png +0 -0
  207. package/lib/public/mate-avatars/B26.png +0 -0
  208. package/lib/public/mate-avatars/B27.png +0 -0
  209. package/lib/public/mate-avatars/B28.png +0 -0
  210. package/lib/public/mate-avatars/B29.png +0 -0
  211. package/lib/public/mate-avatars/B30.png +0 -0
  212. package/lib/public/mate-avatars/B31.png +0 -0
  213. package/lib/public/mate-avatars/B32.png +0 -0
  214. package/lib/public/mate-avatars/B33.png +0 -0
  215. package/lib/public/mate-avatars/B34.png +0 -0
  216. package/lib/public/mate-avatars/B35.png +0 -0
  217. package/lib/public/mate-avatars/B36.png +0 -0
  218. package/lib/public/mate-avatars/B37.png +0 -0
  219. package/lib/public/mate-avatars/B38.png +0 -0
  220. package/lib/public/mate-avatars/B39.png +0 -0
  221. package/lib/public/mate-avatars/B40.png +0 -0
  222. package/lib/public/mate-avatars/B41.png +0 -0
  223. package/lib/public/mate-avatars/B42.png +0 -0
  224. package/lib/public/mate-avatars/B43.png +0 -0
  225. package/lib/public/mate-avatars/B44.png +0 -0
  226. package/lib/public/mate-avatars/B45.png +0 -0
  227. package/lib/public/mate-avatars/B46.png +0 -0
  228. package/lib/public/mate-avatars/B47.png +0 -0
  229. package/lib/public/mate-avatars/B48.png +0 -0
  230. package/lib/public/mate-avatars/C01.png +0 -0
  231. package/lib/public/mate-avatars/C02.png +0 -0
  232. package/lib/public/mate-avatars/C03.png +0 -0
  233. package/lib/public/mate-avatars/C04.png +0 -0
  234. package/lib/public/mate-avatars/C05.png +0 -0
  235. package/lib/public/mate-avatars/C06.png +0 -0
  236. package/lib/public/mate-avatars/C07.png +0 -0
  237. package/lib/public/mate-avatars/C08.png +0 -0
  238. package/lib/public/mate-avatars/C09.png +0 -0
  239. package/lib/public/mate-avatars/C10.png +0 -0
  240. package/lib/public/mate-avatars/C11.png +0 -0
  241. package/lib/public/mate-avatars/C12.png +0 -0
  242. package/lib/public/mate-avatars/C13.png +0 -0
  243. package/lib/public/mate-avatars/C14.png +0 -0
  244. package/lib/public/mate-avatars/C15.png +0 -0
  245. package/lib/public/mate-avatars/C16.png +0 -0
  246. package/lib/public/mate-avatars/C17.png +0 -0
  247. package/lib/public/mate-avatars/C18.png +0 -0
  248. package/lib/public/mate-avatars/C19.png +0 -0
  249. package/lib/public/mate-avatars/C20.png +0 -0
  250. package/lib/public/mate-avatars/C21.png +0 -0
  251. package/lib/public/mate-avatars/C22.png +0 -0
  252. package/lib/public/mate-avatars/C23.png +0 -0
  253. package/lib/public/mate-avatars/C24.png +0 -0
  254. package/lib/public/mate-avatars/C25.png +0 -0
  255. package/lib/public/mate-avatars/C26.png +0 -0
  256. package/lib/public/mate-avatars/C27.png +0 -0
  257. package/lib/public/mate-avatars/C28.png +0 -0
  258. package/lib/public/mate-avatars/C29.png +0 -0
  259. package/lib/public/mate-avatars/C30.png +0 -0
  260. package/lib/public/mate-avatars/C31.png +0 -0
  261. package/lib/public/mate-avatars/C32.png +0 -0
  262. package/lib/public/mate-avatars/C33.png +0 -0
  263. package/lib/public/mate-avatars/C34.png +0 -0
  264. package/lib/public/mate-avatars/C35.png +0 -0
  265. package/lib/public/mate-avatars/C36.png +0 -0
  266. package/lib/public/mate-avatars/C37.png +0 -0
  267. package/lib/public/mate-avatars/C38.png +0 -0
  268. package/lib/public/mate-avatars/C39.png +0 -0
  269. package/lib/public/mate-avatars/C40.png +0 -0
  270. package/lib/public/mate-avatars/C41.png +0 -0
  271. package/lib/public/mate-avatars/C42.png +0 -0
  272. package/lib/public/mate-avatars/C43.png +0 -0
  273. package/lib/public/mate-avatars/C44.png +0 -0
  274. package/lib/public/mate-avatars/C45.png +0 -0
  275. package/lib/public/mate-avatars/C46.png +0 -0
  276. package/lib/public/mate-avatars/C47.png +0 -0
  277. package/lib/public/mate-avatars/C48.png +0 -0
  278. package/lib/public/mate-avatars/catalog.json +482 -0
  279. package/lib/public/mate-avatars/lead-clay.png +0 -0
  280. package/lib/public/modules/admin-settings-invitations.js +2 -2
  281. package/lib/public/modules/admin-settings-projects.js +8 -4
  282. package/lib/public/modules/admin-settings-ui.js +20 -4
  283. package/lib/public/modules/admin-settings-users.js +4 -4
  284. package/lib/public/modules/app-connection.js +11 -1
  285. package/lib/public/modules/app-favicon.js +18 -9
  286. package/lib/public/modules/app-header.js +7 -4
  287. package/lib/public/modules/app-home-hub.js +2 -1
  288. package/lib/public/modules/app-loop-ui.js +2 -0
  289. package/lib/public/modules/app-message-router.js +22 -1
  290. package/lib/public/modules/app-messages.js +125 -19
  291. package/lib/public/modules/app-misc.js +3 -1
  292. package/lib/public/modules/app-notifications.js +11 -5
  293. package/lib/public/modules/app-panels.js +61 -123
  294. package/lib/public/modules/app-projects.js +14 -407
  295. package/lib/public/modules/app-rate-limit.js +19 -28
  296. package/lib/public/modules/app-rendering.js +4 -3
  297. package/lib/public/modules/avatar.js +15 -41
  298. package/lib/public/modules/chat-render-runtime.js +20 -0
  299. package/lib/public/modules/clay-browser-links.js +47 -0
  300. package/lib/public/modules/clay-control-activity.js +214 -0
  301. package/lib/public/modules/clay-control-copy.js +36 -0
  302. package/lib/public/modules/clay-control-detail-targets.js +44 -0
  303. package/lib/public/modules/clay-control-object-targets.js +52 -0
  304. package/lib/public/modules/clay-control-target-visibility.js +12 -0
  305. package/lib/public/modules/clay-control-targets.js +44 -0
  306. package/lib/public/modules/clay-log-links.js +23 -0
  307. package/lib/public/modules/clay-navigation-render.js +52 -0
  308. package/lib/public/modules/clay-owner-navigation.js +33 -0
  309. package/lib/public/modules/clay-session-links.js +31 -0
  310. package/lib/public/modules/clay-studio-bridge.js +20 -0
  311. package/lib/public/modules/clay-studio-document.js +67 -0
  312. package/lib/public/modules/clay-studio-render.js +146 -0
  313. package/lib/public/modules/clay-tool-activity.js +140 -0
  314. package/lib/public/modules/clay-tool-presentation.js +343 -0
  315. package/lib/public/modules/command-palette.js +162 -40
  316. package/lib/public/modules/contextual-ask-session.js +26 -0
  317. package/lib/public/modules/contextual-clay-geometry.js +155 -0
  318. package/lib/public/modules/contextual-clay-session.js +130 -0
  319. package/lib/public/modules/contextual-clay-workspace.js +169 -0
  320. package/lib/public/modules/contextual-clay.js +252 -0
  321. package/lib/public/modules/contextual-composer.js +91 -0
  322. package/lib/public/modules/contextual-mate-context.js +117 -0
  323. package/lib/public/modules/contextual-mate-hint.js +40 -0
  324. package/lib/public/modules/contextual-mate-identity.js +23 -0
  325. package/lib/public/modules/contextual-mate-sessions.js +134 -0
  326. package/lib/public/modules/contextual-search.js +137 -0
  327. package/lib/public/modules/contextual-session-citations.js +58 -0
  328. package/lib/public/modules/debate.js +1 -0
  329. package/lib/public/modules/default-ai.js +11 -3
  330. package/lib/public/modules/elicitation-form.js +5 -0
  331. package/lib/public/modules/filebrowser.js +20 -1
  332. package/lib/public/modules/generation-stop.js +8 -5
  333. package/lib/public/modules/git-panel.js +3 -1
  334. package/lib/public/modules/github-star.js +66 -0
  335. package/lib/public/modules/history-replay-batch.js +13 -1
  336. package/lib/public/modules/home-debate-live.js +2 -0
  337. package/lib/public/modules/home-debate-planning.js +7 -7
  338. package/lib/public/modules/home-mate-avatar-editor.js +127 -208
  339. package/lib/public/modules/home-mate-chat.js +8 -6
  340. package/lib/public/modules/home-mate-creation.js +73 -1
  341. package/lib/public/modules/home-mate-settings.js +19 -12
  342. package/lib/public/modules/icon-strip-tooltip.js +2 -1
  343. package/lib/public/modules/input.js +88 -10
  344. package/lib/public/modules/issues-render.js +3 -2
  345. package/lib/public/modules/knowledge-db-workbench.js +4 -4
  346. package/lib/public/modules/markdown.js +6 -34
  347. package/lib/public/modules/mate-avatar-catalog.js +97 -0
  348. package/lib/public/modules/mate-knowledge-workbench.js +1 -1
  349. package/lib/public/modules/mate-loading-scene.js +17 -0
  350. package/lib/public/modules/mate-management.js +40 -7
  351. package/lib/public/modules/mcp-connection-view.js +31 -13
  352. package/lib/public/modules/mcp-discovery-view.js +31 -0
  353. package/lib/public/modules/mcp-ui.js +23 -4
  354. package/lib/public/modules/mention.js +3 -0
  355. package/lib/public/modules/message-delivery.js +20 -2
  356. package/lib/public/modules/model-picker.js +2 -0
  357. package/lib/public/modules/pane-activation.js +214 -0
  358. package/lib/public/modules/pane-binding.js +82 -0
  359. package/lib/public/modules/pane-bridge.js +7 -4
  360. package/lib/public/modules/pane-browser-bridge.js +22 -0
  361. package/lib/public/modules/pane-file-bridge.js +4 -0
  362. package/lib/public/modules/pane-issue-bridge.js +4 -1
  363. package/lib/public/modules/pending-message-queue.js +1 -0
  364. package/lib/public/modules/permission-control.js +5 -4
  365. package/lib/public/modules/personal-settings-dialog.js +7 -1
  366. package/lib/public/modules/product-review.js +26 -0
  367. package/lib/public/modules/profile.js +12 -241
  368. package/lib/public/modules/project-access-recovery.js +17 -0
  369. package/lib/public/modules/project-admin-overview.js +21 -0
  370. package/lib/public/modules/project-chat-identity.js +13 -4
  371. package/lib/public/modules/project-logs-render.js +3 -0
  372. package/lib/public/modules/project-mate-creation-workspace.js +13 -4
  373. package/lib/public/modules/project-mate-navigation.js +144 -22
  374. package/lib/public/modules/project-session-visibility-settings.js +19 -16
  375. package/lib/public/modules/project-settings.js +119 -129
  376. package/lib/public/modules/project-start-dialog.js +130 -0
  377. package/lib/public/modules/project-start-folder-state.js +78 -0
  378. package/lib/public/modules/project-start-folder-view.js +190 -0
  379. package/lib/public/modules/project-start-forms.js +66 -0
  380. package/lib/public/modules/project-start-path.js +60 -0
  381. package/lib/public/modules/project-start-state.js +54 -0
  382. package/lib/public/modules/project-start-submit.js +67 -0
  383. package/lib/public/modules/projects-hub-view.js +33 -14
  384. package/lib/public/modules/projects-hub.js +10 -6
  385. package/lib/public/modules/rewind.js +5 -0
  386. package/lib/public/modules/scheduled-tasks.js +2 -0
  387. package/lib/public/modules/scheduler.js +2 -0
  388. package/lib/public/modules/search-clay-chat.js +5 -440
  389. package/lib/public/modules/server-settings.js +36 -91
  390. package/lib/public/modules/session-actions.js +5 -2
  391. package/lib/public/modules/session-context-menu.js +5 -0
  392. package/lib/public/modules/session-create-form.js +10 -10
  393. package/lib/public/modules/session-folder-layout.js +1 -1
  394. package/lib/public/modules/session-folder-view-menu.js +9 -0
  395. package/lib/public/modules/session-github.js +85 -14
  396. package/lib/public/modules/session-rich-drafts.js +21 -0
  397. package/lib/public/modules/session-switch-defer.js +67 -0
  398. package/lib/public/modules/session-work-primary.js +51 -0
  399. package/lib/public/modules/settings-defaults.js +27 -28
  400. package/lib/public/modules/settings-project-target.js +56 -0
  401. package/lib/public/modules/settings-select.js +1 -1
  402. package/lib/public/modules/settings-shell-registry.js +88 -0
  403. package/lib/public/modules/settings-shell-view.js +104 -0
  404. package/lib/public/modules/settings-shell.js +342 -0
  405. package/lib/public/modules/shared-browser-settings.js +112 -0
  406. package/lib/public/modules/shared-browser-tabs.js +13 -3
  407. package/lib/public/modules/shared-browser.js +29 -6
  408. package/lib/public/modules/sidebar-mobile.js +12 -33
  409. package/lib/public/modules/sidebar-projects.js +3 -1
  410. package/lib/public/modules/sidebar-session-folders-ui.js +1 -1
  411. package/lib/public/modules/sidebar-sessions.js +1 -0
  412. package/lib/public/modules/split-group-restore.js +68 -0
  413. package/lib/public/modules/split-pair-ui.js +1 -0
  414. package/lib/public/modules/split-pane-reconciler.js +15 -0
  415. package/lib/public/modules/split-pane-renderer.js +87 -6
  416. package/lib/public/modules/split-session-boundary.js +1 -1
  417. package/lib/public/modules/split-view.js +60 -68
  418. package/lib/public/modules/sticky-note-markdown.js +72 -6
  419. package/lib/public/modules/sticky-notes-actions.js +30 -0
  420. package/lib/public/modules/sticky-notes-browser.js +9 -2
  421. package/lib/public/modules/sticky-notes-card.js +23 -7
  422. package/lib/public/modules/sticky-notes-checklist.js +116 -0
  423. package/lib/public/modules/sticky-notes-editor.js +108 -44
  424. package/lib/public/modules/sticky-notes-shared.js +50 -2
  425. package/lib/public/modules/sticky-notes-state.js +66 -0
  426. package/lib/public/modules/sticky-notes.js +34 -51
  427. package/lib/public/modules/stt-controller.js +4 -1
  428. package/lib/public/modules/stt-recording.js +1 -1
  429. package/lib/public/modules/stt.js +7 -2
  430. package/lib/public/modules/terminal.js +9 -0
  431. package/lib/public/modules/theme.js +18 -3
  432. package/lib/public/modules/thinking-lifecycle.js +33 -3
  433. package/lib/public/modules/thinking-view.js +23 -1
  434. package/lib/public/modules/tools.js +60 -11
  435. package/lib/public/modules/tui-grab.js +1 -2
  436. package/lib/public/modules/user-settings-appearance.js +10 -3
  437. package/lib/public/modules/user-settings-editors.js +1 -1
  438. package/lib/public/modules/user-settings-layout.js +14 -30
  439. package/lib/public/modules/user-settings-preferences.js +6 -2
  440. package/lib/public/modules/user-settings-profile.js +13 -3
  441. package/lib/public/modules/user-settings.js +18 -84
  442. package/lib/public/modules/vendor-login-browser.js +5 -3
  443. package/lib/public/modules/vendor-login.js +2 -2
  444. package/lib/public/modules/vendor-priority.js +15 -1
  445. package/lib/public/modules/vendor-usage-format.js +93 -0
  446. package/lib/public/modules/vendor-usage-list.js +45 -0
  447. package/lib/public/modules/vendor-usage-model.js +215 -0
  448. package/lib/public/modules/vendor-usage-overview.js +312 -0
  449. package/lib/public/modules/wireframe-workbench.js +5 -2
  450. package/lib/public/muse-avatar.svg +4 -0
  451. package/lib/public/style.css +14 -6
  452. package/lib/public/sw.js +3 -1
  453. package/lib/public/welcome-art/conversation-desk.png +0 -0
  454. package/lib/public/welcome-art/mate-clay.png +0 -0
  455. package/lib/public/welcome-art/project-clay.png +0 -0
  456. package/lib/safe-bash-commands.js +3 -45
  457. package/lib/scheduled-task-records.js +3 -1
  458. package/lib/scheduler.js +2 -0
  459. package/lib/sdk-bridge.js +348 -402
  460. package/lib/sdk-foreground-admission.js +16 -0
  461. package/lib/sdk-mcp-descriptors.js +2 -37
  462. package/lib/sdk-message-processor.js +61 -18
  463. package/lib/sdk-quota-snapshot.js +21 -3
  464. package/lib/sdk-response-recovery.js +1 -0
  465. package/lib/sdk-worker.js +2 -677
  466. package/lib/server-admin.js +63 -16
  467. package/lib/server-appearance-settings.js +13 -1
  468. package/lib/server-contextual-clay-settings.js +40 -0
  469. package/lib/server-contextual-mate-sessions.js +70 -0
  470. package/lib/server-dm.js +2 -1
  471. package/lib/server-github-stars.js +39 -0
  472. package/lib/server-global-ws.js +26 -32
  473. package/lib/server-home-chat-events.js +18 -4
  474. package/lib/server-home-chat.js +22 -20
  475. package/lib/server-home-clay-entry.js +23 -4
  476. package/lib/server-home-clay-session-links.js +29 -9
  477. package/lib/server-home-mate-creation.js +1 -0
  478. package/lib/server-home-mcp-consent.js +14 -0
  479. package/lib/server-mates.js +25 -62
  480. package/lib/server-owner-ui.js +118 -0
  481. package/lib/server-path-access.js +38 -0
  482. package/lib/server-project-removal.js +22 -0
  483. package/lib/server-settings.js +6 -1
  484. package/lib/server.js +167 -43
  485. package/lib/session-config-transition.js +45 -0
  486. package/lib/session-folders.js +7 -2
  487. package/lib/session-fork-history.js +21 -0
  488. package/lib/session-github.js +9 -2
  489. package/lib/session-hygiene.js +3 -35
  490. package/lib/session-notes-mcp-server.js +26 -4
  491. package/lib/session-notes-writes.js +88 -0
  492. package/lib/session-permission-mode.js +4 -3
  493. package/lib/session-skill-scope.js +11 -0
  494. package/lib/session-visibility.js +25 -10
  495. package/lib/session-work-primary.js +50 -0
  496. package/lib/sessions.js +66 -99
  497. package/lib/shared-browser-cookie-sync.js +32 -0
  498. package/lib/shared-browser-cookies.js +79 -0
  499. package/lib/shared-browser-runtime.js +25 -9
  500. package/lib/shared-browser-storage-sync.js +43 -0
  501. package/lib/shared-browser-storage.js +101 -0
  502. package/lib/shared-browser-tools.js +2 -1
  503. package/lib/shared-browser-worker.js +46 -10
  504. package/lib/terminal-manager.js +65 -6
  505. package/lib/tool-llm.js +1 -1
  506. package/lib/tui-transcript-index.js +1 -132
  507. package/lib/users-contextual-clay-preferences.js +31 -0
  508. package/lib/users-permissions.js +2 -8
  509. package/lib/users.js +7 -0
  510. package/lib/vendor-login-browser.js +1 -11
  511. package/lib/vendor-usage-acp-readers.js +97 -0
  512. package/lib/vendor-usage-capabilities.js +88 -0
  513. package/lib/vendor-usage-http-readers.js +214 -0
  514. package/lib/vendor-usage-metrics.js +47 -0
  515. package/lib/vendor-usage-native-readers.js +106 -0
  516. package/lib/vendor-usage-overview.js +406 -0
  517. package/lib/vendor-usage-service.js +281 -0
  518. package/lib/workspace-assignment-service.js +25 -0
  519. package/lib/workspace-query-access.js +4 -1
  520. package/lib/workspace-query-mcp-server.js +1 -1
  521. package/lib/workspace-query-service.js +13 -2
  522. package/lib/ws-request.js +1 -0
  523. package/lib/ws-schema.js +22 -4
  524. package/lib/yoke/acp-agent-profiles.js +7 -5
  525. package/lib/yoke/adapters/antigravity.js +3 -14
  526. package/lib/yoke/adapters/claude-query-contract.js +9 -0
  527. package/lib/yoke/adapters/claude.js +66 -50
  528. package/lib/yoke/adapters/codex-query-contract.js +25 -0
  529. package/lib/yoke/adapters/codex.js +98 -123
  530. package/lib/yoke/adapters/kiro.js +3 -1
  531. package/lib/yoke/adapters/muse-query-contract.js +7 -0
  532. package/lib/yoke/adapters/muse.js +122 -0
  533. package/lib/yoke/auth-events.js +17 -0
  534. package/lib/yoke/claude-hook-installer.js +272 -0
  535. package/lib/yoke/claude-jsonl-watcher.js +204 -0
  536. package/lib/yoke/claude-maintenance.js +55 -0
  537. package/lib/yoke/claude-permission-patterns.js +53 -0
  538. package/lib/yoke/claude-response-recovery.js +155 -0
  539. package/lib/yoke/claude-transcript-hygiene.js +34 -0
  540. package/lib/yoke/claude-worker-startup.js +28 -0
  541. package/lib/yoke/clay-skill-install.js +33 -0
  542. package/lib/yoke/cli-sessions.js +300 -0
  543. package/lib/yoke/codex-app-server.js +7 -12
  544. package/lib/yoke/codex-dynamic-tool-names.js +80 -0
  545. package/lib/yoke/codex-idle-recovery.js +81 -1
  546. package/lib/yoke/codex-model-catalog.js +37 -3
  547. package/lib/yoke/codex-recovery-admission.js +164 -0
  548. package/lib/yoke/configuration.js +13 -0
  549. package/lib/yoke/discovery.js +70 -0
  550. package/lib/yoke/errors.js +8 -0
  551. package/lib/yoke/host-environment.js +7 -0
  552. package/lib/yoke/index.js +9 -51
  553. package/lib/yoke/interface.js +116 -0
  554. package/lib/yoke/kiro-acp-server.js +5 -5
  555. package/lib/yoke/legacy-sessions.js +89 -0
  556. package/lib/yoke/login-protocol.js +25 -0
  557. package/lib/yoke/maintenance.js +12 -0
  558. package/lib/yoke/mcp-proxy.js +32 -0
  559. package/lib/yoke/mcp-source-adapters.js +91 -0
  560. package/lib/yoke/mcp-source-context.js +42 -0
  561. package/lib/yoke/mcp-source-discovery.js +169 -0
  562. package/lib/yoke/mcp-source-plugins.js +68 -0
  563. package/lib/yoke/mcp-source-restrictions.js +47 -0
  564. package/lib/yoke/mcp-tool-policy-normalize.js +28 -0
  565. package/lib/yoke/model-preferences.js +6 -0
  566. package/lib/yoke/muse-artifacts.js +36 -0
  567. package/lib/yoke/muse-controls.js +50 -0
  568. package/lib/yoke/muse-input.js +64 -0
  569. package/lib/yoke/muse-items.js +157 -0
  570. package/lib/yoke/muse-policy.js +52 -0
  571. package/lib/yoke/muse-query.js +428 -0
  572. package/lib/yoke/muse-runtime.js +211 -0
  573. package/lib/yoke/muse-services.js +92 -0
  574. package/lib/yoke/muse-skills.js +79 -0
  575. package/lib/yoke/muse-turns.js +71 -0
  576. package/lib/yoke/options/acp.js +2 -0
  577. package/lib/yoke/options/antigravity.js +2 -0
  578. package/lib/yoke/options/claude.js +21 -0
  579. package/lib/yoke/options/codex-defaults.js +29 -0
  580. package/lib/yoke/options/codex-settings.js +21 -0
  581. package/lib/yoke/options/codex.js +6 -0
  582. package/lib/yoke/options/kiro-defaults.js +22 -0
  583. package/lib/yoke/options/kiro.js +5 -0
  584. package/lib/yoke/options/muse.js +5 -0
  585. package/lib/yoke/provider-contract.js +5 -0
  586. package/lib/yoke/providers/antigravity-executable.js +8 -0
  587. package/lib/yoke/providers/claude-executable.js +40 -0
  588. package/lib/yoke/providers/claude-model-descriptions.js +34 -0
  589. package/lib/yoke/providers/claude-process.js +97 -0
  590. package/lib/yoke/providers/claude-provision.js +75 -0
  591. package/lib/yoke/providers/codex-errors.js +8 -0
  592. package/lib/yoke/providers/muse-executable.js +17 -0
  593. package/lib/yoke/provisioning.js +6 -0
  594. package/lib/yoke/query-options.js +10 -0
  595. package/lib/yoke/response-recovery.js +41 -0
  596. package/lib/yoke/sdk-mcp-descriptors.js +37 -0
  597. package/lib/yoke/sdk-server-factory.js +7 -0
  598. package/lib/yoke/session-history.js +8 -0
  599. package/lib/yoke/session-tool-transport.js +34 -13
  600. package/lib/yoke/settings.js +23 -0
  601. package/lib/yoke/source-locations.js +4 -0
  602. package/lib/yoke/title-query.js +18 -7
  603. package/lib/yoke/tui-transcript-index.js +132 -0
  604. package/lib/yoke/usage.js +48 -0
  605. package/lib/yoke/user-input.js +13 -0
  606. package/lib/yoke/vendor-registry.js +87 -3
  607. package/package.json +11 -4
  608. package/lib/public/modules/server-settings-navigation.js +0 -39
@@ -2,15 +2,19 @@ var serverSettingsMessages = require("./project-server-settings");
2
2
  var fs = require("fs");
3
3
  var path = require("path");
4
4
  var { execFileSync } = require("child_process");
5
- var { CODEX_DEFAULTS, getCodexConfig } = require("./codex-defaults");
5
+ var providerSettings = require("./yoke/settings");
6
6
  var updateSnooze = require("./update-snooze");
7
7
  var yoke = require("./yoke");
8
+ var startProtocol = require("./project-start-protocol");
9
+ var startReply = startProtocol.withRequestId;
10
+ var browseMeta = startProtocol.browseMeta;
8
11
  var sessionVisibility = require("./session-visibility");
9
12
  var contextUsageModule = require("./context-usage");
10
13
  var sessionDeleteModule = require("./project-session-delete");
11
14
  var sessionFoldersModule = require("./project-session-folders");
12
15
  var elicitationResponse = require("./project-elicitation-response");
13
16
  var sessionCreationModule = require("./project-session-creation");
17
+ var vendorUsageOverviewModule = require("./vendor-usage-overview");
14
18
 
15
19
  function vendorSupportsTui(vendor) {
16
20
  var info = yoke.getVendorInfo(vendor);
@@ -118,14 +122,20 @@ function attachSessions(ctx) {
118
122
  var ensureProjectAccessForSession = ctx.ensureProjectAccessForSession;
119
123
  var getOsUserInfoForWs = ctx.getOsUserInfoForWs;
120
124
  var hydrateSkillCatalog = ctx.hydrateSkillCatalog || function () { return []; };
125
+ var sessionActions = require("./project-session-actions").attachSessionActions(ctx);
126
+ var sessionVendor = require("./project-session-vendor").attachSessionVendor(ctx);
127
+ var sessionRuntime = require("./project-session-runtime-actions").attachSessionRuntimeActions({ sm: sm, tm: tm, prepareGui: prepareTuiSessionForGuiView, isPendingQueuePaused: ctx.isPendingQueuePaused,
128
+ publishGui: function (session) {
129
+ clients.forEach(function (ws) {
130
+ if (ws.readyState !== 1 || ws._clayActiveSession !== session.localId) return;
131
+ if (usersModule.isMultiUser() && (!ws._clayUser || !usersModule.findUserById(ws._clayUser.id) || !usersModule.canAccessProject(ws._clayUser.id, getProjectAccess()) || !usersModule.canAccessSession(ws._clayUser.id, session, getProjectAccess()))) return;
132
+ sm.switchSession(session.localId, ws, ctx.hydrateImageRefs);
133
+ });
134
+ },
135
+ });
121
136
  function stopSession(session, actor) {
122
137
  if (!session) return false;
123
- session._pendingMessageDrainPaused = true;
124
- if (typeof ctx.onPendingQueuePause === "function") ctx.onPendingQueuePause(session, actor || null);
125
- if (!session.isProcessing && !session._queryStarting) return true;
126
- if (typeof ctx.onHumanPairStop === "function") ctx.onHumanPairStop(session);
127
- session.taskStopRequested = true;
128
- if (session.abortController) session.abortController.abort();
138
+ sessionActions.stop(session, actor);
129
139
  return true;
130
140
  }
131
141
  var hydrateImageRefs = ctx.hydrateImageRefs;
@@ -143,12 +153,16 @@ function attachSessions(ctx) {
143
153
  return !session.ownerId;
144
154
  }
145
155
 
146
- function respondToAskUser(session, msg) {
156
+ function respondToAskUser(session, msg, ws) {
147
157
  if (!session || !msg || !msg.toolId) return false;
148
158
  var toolId = msg.toolId;
149
159
  var answers = msg.answers || {};
150
160
  var pending = session.pendingAskUser && session.pendingAskUser[toolId];
151
161
  if (!pending) return false;
162
+ if (pending.request && ["mcp-import", "product-control"].indexOf(pending.request.source) >= 0 &&
163
+ (typeof pending.request.authorizeResponse !== "function" || !pending.request.authorizeResponse(ws))) return false;
164
+ if (pending.request && ["work-primary", "mate-schedule"].indexOf(pending.request.source) >= 0 && usersModule.isMultiUser() &&
165
+ (!ws || !ws._clayUser || ws._clayUser.id !== session.ownerId)) return false;
152
166
  if (pending.mode === "yoke") {
153
167
  try { yoke.userInput.normalizeAnswers(pending.request.questions, answers); }
154
168
  catch (e) { return false; }
@@ -209,6 +223,14 @@ function attachSessions(ctx) {
209
223
  var setLatestVersion = ctx.setLatestVersion;
210
224
  var loadContextSources = ctx.loadContextSources;
211
225
 
226
+ var sessionHistory = require("./project-session-history-actions").attachSessionHistoryActions(Object.assign({}, ctx, {
227
+ resolveSessionHome: resolveSessionHome, cancelPendingAskUser: cancelPendingAskUser,
228
+ }));
229
+
230
+ function boundedRequestId(msg) {
231
+ return typeof msg.requestId === "string" ? msg.requestId.substring(0, 200) : undefined;
232
+ }
233
+
212
234
  function canAccessProjectSlug(ws, targetSlug) {
213
235
  if (!targetSlug) return false;
214
236
  if (!usersModule || typeof usersModule.isMultiUser !== "function" || !usersModule.isMultiUser()) return true;
@@ -244,7 +266,7 @@ function attachSessions(ctx) {
244
266
  if (!session || !session.cliSessionId) return;
245
267
  if (session._titleWatcherStop) return; // already watching
246
268
  var watcher;
247
- try { watcher = require("./claude-jsonl-watcher"); } catch (e) { return; }
269
+ try { watcher = require("./yoke/claude-jsonl-watcher"); } catch (e) { return; }
248
270
  var home = resolveSessionHome(session);
249
271
  var jsonlPath = watcher.jsonlPathFor(home, cwd, session.cliSessionId);
250
272
  if (!jsonlPath) return;
@@ -312,7 +334,7 @@ function attachSessions(ctx) {
312
334
  // message right away. The transcript is small enough that a full
313
335
  // re-send beats maintaining a delta protocol.
314
336
  try {
315
- var newIndex = require("./tui-transcript-index").readAssistantIndex(resolveSessionHome(s), cwd, s.cliSessionId);
337
+ var newIndex = require("./yoke/tui-transcript-index").readAssistantIndex(resolveSessionHome(s), cwd, s.cliSessionId);
316
338
  send({
317
339
  type: "tui_transcript_state",
318
340
  id: s.localId,
@@ -347,6 +369,11 @@ function attachSessions(ctx) {
347
369
  getProjectAccess: getProjectAccess,
348
370
  getVendorAvailability: function (ws) { return ctx.getVendorAvailability(ws); },
349
371
  });
372
+ var vendorUsage = vendorUsageOverviewModule.attachVendorUsageOverview({
373
+ slug: slug, sm: sm, usersModule: usersModule, clients: clients, sendTo: sendTo, getProjectAccess: getProjectAccess,
374
+ getVendorAvailability: function (ws) { return ctx.getVendorAvailability(ws); },
375
+ adapters: ctx.adapters, osUsers: osUsers, getLinuxUserForWs: ctx.getLinuxUserForWs,
376
+ });
350
377
  // Kick off watchers for any TUI sessions already loaded from disk so
351
378
  // their titles refresh as soon as Claude Code rewrites the jsonl, even
352
379
  // before the user clicks them.
@@ -436,10 +463,10 @@ function attachSessions(ctx) {
436
463
  // Idempotent: if the PTY is already gone and history is populated, this
437
464
  // is a no-op (avoids re-reading jsonl and re-writing the session file on
438
465
  // every click while the user is reading the session in GUI mode).
439
- function prepareTuiSessionForGuiView(session) {
466
+ function prepareTuiSessionForGuiView(session, options) {
440
467
  if (!session || session.cliSessionId == null) return;
441
468
  var cliSess;
442
- try { cliSess = require("./cli-sessions"); } catch (e) { return; }
469
+ try { cliSess = require("./yoke/cli-sessions"); } catch (e) { return; }
443
470
  // Re-read whenever the jsonl has changed since the last hydrate (a TUI
444
471
  // turn appended messages), not just when history is empty. The earlier
445
472
  // "already hydrated" short-circuit kept stale history after a
@@ -463,7 +490,7 @@ function attachSessions(ctx) {
463
490
  try { tm.close(session.terminalId); } catch (e) {}
464
491
  }
465
492
  session.terminalId = null;
466
- try { sm.saveSessionFile(session); } catch (e) {}
493
+ if (!options || !options.deferSave) { try { sm.saveSessionFile(session); } catch (e) {} }
467
494
  }
468
495
 
469
496
  // Resolve how a session should be presented to a viewer WITHOUT spawning a
@@ -588,7 +615,9 @@ function attachSessions(ctx) {
588
615
  var targetFrom = typeof msg.target === "number" ? msg.target : before - sm.HISTORY_PAGE_SIZE;
589
616
  var from = sm.findTurnBoundary(session.history, Math.max(0, targetFrom));
590
617
  var to = before;
591
- var items = session.history.slice(from, to).map(hydrateImageRefs);
618
+ var items = session.history.slice(from, to).map(function (item, index) {
619
+ return Object.assign({}, hydrateImageRefs(require("./yoke/usage").clientEvent(item, session.vendor || "claude")), { _historyIndex: from + index });
620
+ });
592
621
  sendTo(ws, {
593
622
  type: "history_prepend",
594
623
  items: items,
@@ -739,13 +768,15 @@ function attachSessions(ctx) {
739
768
  if (msg.type === "set_project_session_visibility_default") {
740
769
  sessionVisibility.handleDefaultChange({
741
770
  slug: slug, isMate: isMate, osUsers: osUsers, usersModule: usersModule, getProjectAccess: getProjectAccess,
742
- opts: opts, sendTo: sendTo,
771
+ getTargetAccess: typeof opts.onGetProjectAccess === "function" ? opts.onGetProjectAccess : null,
772
+ canAccessProjectSlug: canAccessProjectSlug, opts: opts, sendTo: sendTo,
743
773
  }, ws, msg);
744
774
  return true;
745
775
  }
746
776
 
747
777
  if (sessionFolders.handleMessage(ws, msg)) return true;
748
778
  if (sessionCreation.handleMessage(ws, msg)) return true;
779
+ if (vendorUsage.handleMessage(ws, msg)) return true;
749
780
 
750
781
  if (msg.type === "bulk_delete_sessions") {
751
782
  sessionDelete.bulkDeleteSessions(ws, msg);
@@ -762,11 +793,10 @@ function attachSessions(ctx) {
762
793
  sendTo(ws, { type: "error", text: "Cannot transfer ownership of home directory projects." });
763
794
  return true;
764
795
  }
765
- var projectOwnerId = getProjectOwnerId();
766
- var isAdmin = ws._clayUser && ws._clayUser.role === "admin";
796
+ var projectOwnerId = getProjectAccess().ownerId;
767
797
  var isProjectOwner = ws._clayUser && projectOwnerId && ws._clayUser.id === projectOwnerId;
768
- if (!ws._clayUser || (!isAdmin && !isProjectOwner)) {
769
- sendTo(ws, { type: "error", text: "Only project owners or admins can transfer ownership." });
798
+ if (!ws._clayUser || !isProjectOwner) {
799
+ sendTo(ws, { type: "error", text: "Only project owners can transfer ownership." });
770
800
  return true;
771
801
  }
772
802
  var targetUser = msg.userId ? usersModule.findUserById(msg.userId) : null;
@@ -774,11 +804,11 @@ function attachSessions(ctx) {
774
804
  sendTo(ws, { type: "error", text: "User not found." });
775
805
  return true;
776
806
  }
807
+ // Persist before publishing runtime ownership.
808
+ if (!opts.onProjectOwnerChanged) return true;
809
+ var savedOwner = opts.onProjectOwnerChanged(slug, targetUser.id);
810
+ if (savedOwner && savedOwner.error) { sendTo(ws, { type: "error", text: savedOwner.error }); return true; }
777
811
  setProjectOwnerId(targetUser.id);
778
- // Persist via daemon callback
779
- if (opts.onProjectOwnerChanged) {
780
- opts.onProjectOwnerChanged(slug, targetUser.id);
781
- }
782
812
  send({ type: "project_owner_changed", ownerId: targetUser.id, ownerName: targetUser.displayName || targetUser.username });
783
813
  return true;
784
814
  }
@@ -789,18 +819,22 @@ function attachSessions(ctx) {
789
819
  // adoptOrphanedCliSessions). Click a session in the sidebar instead.
790
820
 
791
821
  if (msg.type === "switch_session") {
822
+ var paneActivation = require('./pane-session-activation').begin(ctx, ws, msg);
823
+ if (paneActivation === false) return true;
792
824
  if (msg.id && sm.sessions.has(msg.id)) {
793
825
  // resolveSessionForView sets runtimeMode / runtimeTerminalId /
794
826
  // tuiSuspended (and hydrates the transcript) before sm.switchSession
795
827
  // broadcasts session_switched. A live runtime keeps its actual mode
796
828
  // for every viewer; idle sessions open in chat. Nothing is spawned here.
797
829
  var xmTarget = sm.sessions.get(msg.id);
830
+ if (usersModule.isMultiUser() && (!ws._clayUser || !usersModule.canAccessSession(ws._clayUser.id, xmTarget, getProjectAccess()))) return true;
798
831
  if (xmTarget && (!xmTarget.vendor || vendorSupportsTui(xmTarget.vendor))) {
799
832
  // Single source of truth: live runtime wins (tui stays tui, gui stays
800
833
  // gui); idle legacy sessions hydrate their transcript into chat.
801
834
  resolveSessionForView(xmTarget, ws);
802
835
  }
803
836
  var switchTargetSess = sm.sessions.get(msg.id);
837
+ if (paneActivation && !paneActivation.validate()) return true;
804
838
  var switchTargetVendor = switchTargetSess && (switchTargetSess.vendor || sm.defaultVendor || "claude");
805
839
  if (switchTargetSess && !switchTargetSess.effort) {
806
840
  switchTargetSess.effort = yoke.clampEffort(
@@ -813,20 +847,20 @@ function attachSessions(ctx) {
813
847
  if (usersModule.isMultiUser() && ws._clayUser) {
814
848
  var switchTarget = sm.sessions.get(msg.id);
815
849
  if (!usersModule.canAccessSession(ws._clayUser.id, switchTarget, { visibility: "public" })) return true;
816
- ws._clayActiveSession = msg.id;
817
- sm.switchSession(msg.id, ws, hydrateImageRefs);
850
+ sm.switchSession(msg.id, ws, hydrateImageRefs, paneActivation && paneActivation.send, paneActivation && paneActivation.validate);
818
851
  if (!ws._clayPane) broadcastPresence();
819
852
  } else {
820
- ws._clayActiveSession = msg.id;
821
- sm.switchSession(msg.id, ws, hydrateImageRefs);
853
+ sm.switchSession(msg.id, ws, hydrateImageRefs, paneActivation && paneActivation.send, paneActivation && paneActivation.validate);
822
854
  }
823
855
  // Send per-session context sources
824
856
  if (typeof loadContextSources === "function") {
825
857
  var switchedSources = loadContextSources(slug, msg.id);
826
- sendTo(ws, { type: "context_sources_state", active: switchedSources });
858
+ if (paneActivation) paneActivation.send({ type: "context_sources_state", active: switchedSources });
859
+ else sendTo(ws, { type: "context_sources_state", active: switchedSources });
827
860
  }
828
861
  var swPresKey = ws._clayUser ? ws._clayUser.id : "_default";
829
862
  if (!ws._clayPane) userPresence.setPresence(slug, swPresKey, userPresence.sessionIdForPersistence(switchTargetSess), null);
863
+ if (paneActivation) paneActivation.finish();
830
864
  }
831
865
  return true;
832
866
  }
@@ -902,7 +936,7 @@ function attachSessions(ctx) {
902
936
  return true;
903
937
  }
904
938
  try {
905
- var tprIndex = require("./tui-transcript-index").readAssistantIndex(resolveSessionHome(tprSess), cwd, tprSess.cliSessionId);
939
+ var tprIndex = require("./yoke/tui-transcript-index").readAssistantIndex(resolveSessionHome(tprSess), cwd, tprSess.cliSessionId);
906
940
  sendTo(ws, {
907
941
  type: "tui_transcript_state",
908
942
  id: tprId,
@@ -933,19 +967,7 @@ function attachSessions(ctx) {
933
967
  var s = sm.sessions.get(msg.id);
934
968
  if (usersModule.isMultiUser() && (!ws._clayUser ||
935
969
  !usersModule.canAccessSession(ws._clayUser.id, s, getProjectAccess()))) return true;
936
- s.title = String(msg.title).substring(0, 100);
937
- s.titleProvisional = false;
938
- s.titleAutoGenerated = false;
939
- s.titleManuallySet = true;
940
- sm.saveSessionFile(s);
941
- sm.notifySessionRenamed(s.localId);
942
- sm.broadcastSessionList();
943
- // Sync title to SDK session
944
- if (s.cliSessionId) {
945
- adapter.renameSession(s.cliSessionId, s.title, { dir: cwd }).catch(function(e) {
946
- console.error("[project] SDK renameSession failed:", e.message);
947
- });
948
- }
970
+ sessionActions.rename(s, String(msg.title).substring(0, 100));
949
971
  }
950
972
  return true;
951
973
  }
@@ -1080,7 +1102,12 @@ function attachSessions(ctx) {
1080
1102
 
1081
1103
  if (msg.type === "stop") {
1082
1104
  var session = getSessionForWs(ws);
1083
- if (autonomousRun && session && session.autonomousRun) autonomousRun.finish(session, "stopped", "Stopped by the user.");
1105
+ if (msg.sessionId != null || msg.sessionOriginId != null) {
1106
+ session = sm.sessions.get(msg.sessionId);
1107
+ if (!session || session.sessionOriginId !== msg.sessionOriginId ||
1108
+ !usersModule.isMultiUser() && session.ownerId ||
1109
+ usersModule.isMultiUser() && (!ws._clayUser || !usersModule.canAccessSession(ws._clayUser.id, session, getProjectAccess()))) return true;
1110
+ }
1084
1111
  stopSession(session, ws && ws._clayUser);
1085
1112
  return true;
1086
1113
  }
@@ -1096,14 +1123,14 @@ function attachSessions(ctx) {
1096
1123
  var pid = msg.pid;
1097
1124
  if (!pid || typeof pid !== "number") return true;
1098
1125
  // Verify target is actually a claude process before killing
1099
- if (!sdk.isClaudeProcess(pid)) {
1100
- console.error("[project] Refused to kill PID " + pid + ": not a claude process");
1101
- sendTo(ws, { type: "error", text: "Process " + pid + " is not a Claude process." });
1126
+ if (!sdk.isProviderProcess(pid, getSessionForWs(ws))) {
1127
+ console.error("[project] Refused to kill PID " + pid + ": not a verified provider process");
1128
+ sendTo(ws, { type: "error", text: "Process " + pid + " is not a verified provider process." });
1102
1129
  return true;
1103
1130
  }
1104
1131
  try {
1105
1132
  process.kill(pid, "SIGTERM");
1106
- console.log("[project] Sent SIGTERM to conflicting Claude process PID " + pid);
1133
+ console.log("[project] Sent SIGTERM to conflicting provider process PID " + pid);
1107
1134
  sendTo(ws, { type: "process_killed", pid: pid });
1108
1135
  } catch (e) {
1109
1136
  console.error("[project] Failed to kill PID " + pid + ":", e.message);
@@ -1181,38 +1208,14 @@ function attachSessions(ctx) {
1181
1208
 
1182
1209
  if (msg.type === "set_vendor" && msg.vendor) {
1183
1210
  var vendorSession = getSessionForWs(ws);
1184
- if (vendorSession) {
1185
- // Refuse to rebind vendor on a session that is already bound to a
1186
- // different CLI (cliSessionId is vendor-specific). This prevents a
1187
- // stale client-side vendor state from clobbering the persisted vendor
1188
- // on page reload / server restart.
1189
- var alreadyBound = vendorSession.cliSessionId && vendorSession.vendor && vendorSession.vendor !== msg.vendor;
1190
- if (alreadyBound) {
1191
- console.warn("[project] set_vendor ignored: session " + vendorSession.localId +
1192
- " is bound to '" + vendorSession.vendor + "', refused rebind to '" + msg.vendor + "'");
1193
- } else {
1194
- vendorSession.vendor = msg.vendor;
1195
- if (vendorSession.permissionMode === "auto" && msg.vendor !== "claude") {
1196
- vendorSession.permissionMode = "default";
1197
- vendorSession.effectivePermissionMode = null;
1198
- }
1199
- if (vendorSession.vendor !== msg.vendor) vendorSession.model = "";
1200
- sm.saveSessionFile(vendorSession);
1201
- sm.broadcastSessionList();
1202
- }
1203
- }
1204
- if (msg.vendor) {
1205
- var vendorModels = (sm.modelsByVendor && sm.modelsByVendor[msg.vendor]) || [];
1206
- sendTo(ws, {
1207
- type: "model_info",
1208
- model: "",
1209
- models: vendorModels,
1210
- vendor: msg.vendor, sessionId: vendorSession ? vendorSession.localId : null,
1211
- availableVendors: sm.availableVendors || [],
1212
- installedVendors: sm.installedVendors || [],
1213
- });
1214
- send({ type: "config_state", vendor: null, sessionId: null, mode: sm.currentPermissionMode || "default", effort: sm.currentEffort || "medium", betas: sm.currentBetas || [], thinking: sm.currentThinking || "adaptive", thinkingBudget: sm.currentThinkingBudget || 10000 });
1211
+ if (!vendorSession) return true;
1212
+ // Re-selecting the provider of an established thread does not rebind it.
1213
+ if (vendorSession.cliSessionId && vendorSession.vendor === msg.vendor) return true;
1214
+ try {
1215
+ var vendorResult = sessionVendor.setVendor(vendorSession, msg.vendor);
1216
+ if (vendorResult.status === "failed") sendTo(ws, { type: "error", text: vendorResult.error });
1215
1217
  }
1218
+ catch (error) { sendTo(ws, { type: "error", text: error.message }); }
1216
1219
  return true;
1217
1220
  }
1218
1221
 
@@ -1280,15 +1283,7 @@ function attachSessions(ctx) {
1280
1283
  if (!valid.ok) { permissionResult(false, valid.error, true); return true; }
1281
1284
  (async function() {
1282
1285
  try {
1283
- if (session.permissionMode === "bypassPermissions" && valid.mode !== "bypassPermissions" && autonomousRun) {
1284
- await autonomousRun.stopForPermissionChange(session);
1285
- }
1286
- await fullAccessService.setMode(session, valid.mode);
1287
- var state = permissionModes.clientState(session, sm.currentPermissionMode, sm.defaultVendor);
1288
- sm.sendToSession(session, { type: "config_state", sessionId: session.localId, vendor: session.vendor || sm.defaultVendor || "claude", mode: state.requestedPermissionMode,
1289
- effectivePermissionMode: state.effectivePermissionMode, permissionRequestId: requestId,
1290
- permissionCapabilities: state.permissionCapabilities, mcpPermissionModeOverrides: state.mcpPermissionModeOverrides,
1291
- effort: session.effort || sm.currentEffort || "medium", betas: sm.currentBetas || [] });
1286
+ await sessionActions.permission(session, valid.mode, requestId);
1292
1287
  permissionResult(true, null, true);
1293
1288
  } catch (error) {
1294
1289
  permissionResult(false, "Failed to change permission mode: " + (error.message || error), true);
@@ -1358,10 +1353,9 @@ function attachSessions(ctx) {
1358
1353
  sendTo(ws, { type: "error", text: "This effort level is not supported by the selected vendor." });
1359
1354
  return true;
1360
1355
  }
1361
- sdk.setEffort(session, sessionEffort).catch(function (e) {
1356
+ sessionActions.effort(session, sessionEffort).catch(function (e) {
1362
1357
  sendTo(ws, { type: "error", text: "Failed to change reasoning effort: " + (e.message || e) });
1363
1358
  });
1364
- sm.sendToSession(session, { type: "config_state", model: session.model || ((sm.defaultModelByVendor || {})[effortVendor]) || "", vendor: effortVendor, sessionId: session.localId, mode: session.permissionMode || sm.currentPermissionMode || "default", effort: sessionEffort, betas: sm.currentBetas || [], thinking: sm.currentThinking || "adaptive", thinkingBudget: sm.currentThinkingBudget || 10000 });
1365
1359
  }
1366
1360
  return true;
1367
1361
  }
@@ -1397,20 +1391,12 @@ function attachSessions(ctx) {
1397
1391
  return true;
1398
1392
  }
1399
1393
 
1400
- // Codex-specific settings (stored on sessionManager, passed to adapter via adapterOptions)
1401
- if (msg.type === "set_codex_approval") {
1402
- sm.codexApproval = getCodexConfig({ codexApproval: msg.approval }).approval;
1403
- send(Object.assign({ type: "codex_config" }, getCodexConfig(sm)));
1404
- return true;
1405
- }
1406
- if (msg.type === "set_codex_sandbox") {
1407
- sm.codexSandbox = msg.sandbox || CODEX_DEFAULTS.sandbox;
1408
- send(Object.assign({ type: "codex_config" }, getCodexConfig(sm)));
1409
- return true;
1410
- }
1411
- if (msg.type === "set_codex_websearch") {
1412
- sm.codexWebSearch = msg.webSearch || CODEX_DEFAULTS.webSearch;
1413
- send(Object.assign({ type: "codex_config" }, getCodexConfig(sm)));
1394
+ var providerChange;
1395
+ try { providerChange = providerSettings.change(msg, sm); }
1396
+ catch (error) { sendTo(ws, { type: "error", text: error.message }); return true; }
1397
+ if (providerChange) {
1398
+ Object.keys(providerChange.patch).forEach(function(key) { sm[key] = providerChange.patch[key]; });
1399
+ providerChange.messages.forEach(function(message) { send(message); });
1414
1400
  return true;
1415
1401
  }
1416
1402
 
@@ -1438,92 +1424,9 @@ function attachSessions(ctx) {
1438
1424
  sendTo(ws, { type: "rewind_error", text: "Rewind already in progress." });
1439
1425
  return true;
1440
1426
  }
1441
- session._rewindInProgress = true;
1442
- var mode = msg.mode || "both";
1443
-
1444
- (async function () {
1445
- try {
1446
- // File restoration (delegated to adapter via sdk-bridge)
1447
- if (mode !== "chat") {
1448
- await sdk.rewindExecuteFiles(session, msg.uuid);
1449
- }
1450
-
1451
- // Conversation rollback (skip for files-only mode)
1452
- if (mode !== "files") {
1453
- var targetIdx = -1;
1454
- for (var i = 0; i < session.messageUUIDs.length; i++) {
1455
- if (session.messageUUIDs[i].uuid === msg.uuid) {
1456
- targetIdx = i;
1457
- break;
1458
- }
1459
- }
1460
-
1461
- // Codex's thread/revert cuts at a turn id, not a count. Read the
1462
- // target entry's turnId BEFORE trimming local history below.
1463
- var hasTurnsToRollBack = false;
1464
- var targetTurnId = null;
1465
- if (targetIdx >= 0) {
1466
- targetTurnId = session.messageUUIDs[targetIdx].turnId || null;
1467
- for (var ri = targetIdx; ri < session.messageUUIDs.length; ri++) {
1468
- if (session.messageUUIDs[ri].type === "user") { hasTurnsToRollBack = true; break; }
1469
- }
1470
- }
1471
-
1472
- // Notify adapter of conversation rollback (e.g. Codex thread/revert)
1473
- // BEFORE trimming local history below. Deliberately not caught
1474
- // here: a failed or impossible (missing turnId) provider-side
1475
- // rollback must abort the whole rewind via the outer catch, never
1476
- // leave local history trimmed ahead of the real provider thread.
1477
- if (hasTurnsToRollBack) {
1478
- await sdk.rollbackConversation(session, targetTurnId);
1479
- }
1480
-
1481
- if (targetIdx >= 0) {
1482
- var trimTo = session.messageUUIDs[targetIdx].historyIndex;
1483
- for (var k = trimTo - 1; k >= 0; k--) {
1484
- if (session.history[k].type === "user_message") {
1485
- trimTo = k;
1486
- break;
1487
- }
1488
- }
1489
- session.history = session.history.slice(0, trimTo);
1490
- session.messageUUIDs = session.messageUUIDs.slice(0, targetIdx);
1491
- // Reset digest checkpoint if it points past the trimmed history
1492
- if (typeof session._dmLastDigestedIndex === "number" && session._dmLastDigestedIndex > trimTo) {
1493
- session._dmLastDigestedIndex = trimTo;
1494
- }
1495
- }
1496
-
1497
- var kept = session.messageUUIDs;
1498
- session.lastRewindUuid = kept.length > 0 ? kept[kept.length - 1].uuid : null;
1499
- }
1500
-
1501
- if (session.abortController) {
1502
- try { session.abortController.abort(); } catch (e) {}
1503
- }
1504
- if (session.messageQueue) {
1505
- try { session.messageQueue.end(); } catch (e) {}
1506
- }
1507
- session.queryInstance = null;
1508
- session.messageQueue = null;
1509
- session.abortController = null;
1510
- session.blocks = {};
1511
- session.sentToolResults = {};
1512
- session.pendingPermissions = {};
1513
- cancelPendingAskUser(session, "Conversation rewound");
1514
- session.isProcessing = false;
1515
- onProcessingChanged();
1516
-
1517
- sm.saveSessionFile(session);
1518
- sm.switchSession(session.localId, ws, hydrateImageRefs);
1519
- sm.sendAndRecord(session, { type: "rewind_complete", mode: mode });
1520
- sm.broadcastSessionList();
1521
- } catch (err) {
1522
- sendTo(ws, { type: "rewind_error", text: "Rewind failed: " + err.message });
1523
- } finally {
1524
- session._rewindInProgress = false;
1525
- }
1526
- })();
1427
+ sessionHistory.rewind(session, msg.uuid, msg.mode || "both", ws, function () {}).catch(function (error) {
1428
+ sendTo(ws, { type: "rewind_error", text: "Rewind failed: " + error.message });
1429
+ });
1527
1430
  return true;
1528
1431
  }
1529
1432
 
@@ -1533,69 +1436,17 @@ function attachSessions(ctx) {
1533
1436
  sendTo(ws, { type: "error", text: "Cannot fork: no CLI session" });
1534
1437
  return true;
1535
1438
  }
1536
- var forkTitle = (session.title || "New Session") + " (fork)";
1537
-
1538
- sdk.forkSession(session, msg.uuid).then(function(result) {
1539
- if (result.useLocalHistory) {
1540
- // Copy local history up to the target UUID
1541
- var targetIdx = -1;
1542
- for (var fi = 0; fi < session.messageUUIDs.length; fi++) {
1543
- if (session.messageUUIDs[fi].uuid === msg.uuid) { targetIdx = fi; break; }
1544
- }
1545
- var forkHistory = [];
1546
- if (targetIdx >= 0) {
1547
- var trimTo = session.messageUUIDs[targetIdx].historyIndex;
1548
- forkHistory = session.history.slice(0, trimTo);
1549
- } else {
1550
- forkHistory = session.history.slice();
1551
- }
1552
- var forked = sm.createSession({
1553
- vendor: session.vendor,
1554
- ownerId: session.ownerId || null,
1555
- userKnowledgeOwnerId: session.userKnowledgeOwnerId || null,
1556
- sessionVisibility: sessionVisibility.normalize(session.sessionVisibility),
1557
- }, ws);
1558
- forked.cliSessionId = result.sessionId;
1559
- forked.title = forkTitle;
1560
- forked.history = forkHistory;
1561
- forked.messageUUIDs = [];
1562
- for (var hi = 0; hi < forkHistory.length; hi++) {
1563
- if (forkHistory[hi].type === "message_uuid") {
1564
- forked.messageUUIDs.push({ uuid: forkHistory[hi].uuid, type: forkHistory[hi].messageType, historyIndex: hi, turnId: forkHistory[hi].turnId || null });
1565
- }
1566
- }
1567
- sm.saveSessionFile(forked);
1568
- sm.switchSession(forked.localId, ws, hydrateImageRefs);
1569
- sendTo(ws, { type: "fork_complete", sessionId: forked.localId });
1570
- sm.broadcastSessionList();
1571
- } else {
1572
- // Read history from CLI session files
1573
- var cliSess = require("./cli-sessions");
1574
- return cliSess.readCliSessionHistory(resolveSessionHome(session), cwd, result.sessionId).then(function(history) {
1575
- var forked = sm.resumeSession(result.sessionId, {
1576
- history: history,
1577
- title: forkTitle,
1578
- vendor: session.vendor,
1579
- ownerId: session.ownerId || null,
1580
- userKnowledgeOwnerId: session.userKnowledgeOwnerId || null,
1581
- sessionVisibility: sessionVisibility.normalize(session.sessionVisibility),
1582
- sessionVisibilityExplicit: session.sessionVisibilityExplicit === true,
1583
- }, ws);
1584
- if (forked) {
1585
- ws._clayActiveSession = forked.localId;
1586
- sendTo(ws, { type: "fork_complete", sessionId: forked.localId });
1587
- }
1588
- });
1589
- }
1590
- }).catch(function(e) {
1591
- sendTo(ws, { type: "error", text: "Fork failed: " + (e.message || e) });
1439
+ sessionHistory.fork(session, msg.uuid, ws, function () {}).then(function (forked) {
1440
+ if (forked) sendTo(ws, { type: "fork_complete", sessionId: forked.localId });
1441
+ }).catch(function (error) {
1442
+ sendTo(ws, { type: "error", text: "Fork failed: " + error.message });
1592
1443
  });
1593
1444
  return true;
1594
1445
  }
1595
1446
 
1596
1447
  if (msg.type === "ask_user_response") {
1597
1448
  var session = getSessionForWs(ws);
1598
- if (session) respondToAskUser(session, msg);
1449
+ if (session) respondToAskUser(session, msg, ws);
1599
1450
  return true;
1600
1451
  }
1601
1452
 
@@ -1638,6 +1489,7 @@ function attachSessions(ctx) {
1638
1489
  var sessionId = sm.permissionRequestIndex[requestId];
1639
1490
  var session = sessionId ? sm.sessions.get(sessionId) : getSessionForWs(ws);
1640
1491
  if (!session) return true;
1492
+ if (ws._clayPaneActivation && session.localId !== ws._clayPaneActivation.sessionId) return true;
1641
1493
  var pending = session.pendingPermissions[requestId];
1642
1494
  if (!pending) return true;
1643
1495
  delete sm.permissionRequestIndex[requestId];
@@ -1803,14 +1655,6 @@ function attachSessions(ctx) {
1803
1655
  if (msg.type === "browse_dir") {
1804
1656
  var rawPath = (msg.path || "").replace(/^~/, require("./config").REAL_HOME);
1805
1657
  var absTarget = path.resolve(rawPath);
1806
- // Multi-user mode: non-admins can only browse their home directory
1807
- if (osUsers && osUsers.length > 0 && ws._clayUser && ws._clayUser.role !== "admin") {
1808
- var browseHome = ws._clayUser.linuxUser ? "/home/" + ws._clayUser.linuxUser : null;
1809
- if (!browseHome || (absTarget !== browseHome && (absTarget + "/").indexOf(browseHome + "/") !== 0)) {
1810
- sendTo(ws, { type: "browse_dir_result", path: msg.path, entries: [], error: "Access restricted to your home directory" });
1811
- return true;
1812
- }
1813
- }
1814
1658
  var parentDir, prefix;
1815
1659
  try {
1816
1660
  var stat = fs.statSync(absTarget);
@@ -1827,21 +1671,27 @@ function attachSessions(ctx) {
1827
1671
  parentDir = path.dirname(absTarget);
1828
1672
  prefix = path.basename(absTarget).toLowerCase();
1829
1673
  }
1674
+ var canList = function (candidate) { return !opts.canUsePath || opts.canUsePath(ws._clayUser && ws._clayUser.id, candidate); };
1675
+ if (!canList(parentDir)) {
1676
+ sendTo(ws, startReply(msg, { type: "browse_dir_result", path: msg.path, entries: [], error: "Directory access is not permitted" }));
1677
+ return true;
1678
+ }
1830
1679
  try {
1831
1680
  var dirItems = fs.readdirSync(parentDir, { withFileTypes: true });
1832
1681
  var dirEntries = [];
1833
1682
  for (var di = 0; di < dirItems.length; di++) {
1834
1683
  var d = dirItems[di];
1835
1684
  if (!d.isDirectory()) continue;
1685
+ if (opts.canUsePath && !opts.canUsePath(ws._clayUser && ws._clayUser.id, path.join(parentDir, d.name))) continue;
1836
1686
  if (d.name.charAt(0) === ".") continue;
1837
1687
  if (IGNORED_DIRS.has(d.name)) continue;
1838
1688
  if (prefix && !d.name.toLowerCase().startsWith(prefix)) continue;
1839
1689
  dirEntries.push({ name: d.name, path: path.join(parentDir, d.name) });
1840
1690
  }
1841
1691
  dirEntries.sort(function (a, b) { return a.name.localeCompare(b.name); });
1842
- sendTo(ws, { type: "browse_dir_result", path: msg.path, entries: dirEntries });
1692
+ sendTo(ws, Object.assign(browseMeta(msg, parentDir, prefix, canList), { type: "browse_dir_result", path: msg.path, entries: dirEntries }));
1843
1693
  } catch (e) {
1844
- sendTo(ws, { type: "browse_dir_result", path: msg.path, entries: [], error: e.message });
1694
+ sendTo(ws, startReply(msg, { type: "browse_dir_result", path: msg.path, entries: [], error: e.message }));
1845
1695
  }
1846
1696
  return true;
1847
1697
  }
@@ -1850,33 +1700,25 @@ function attachSessions(ctx) {
1850
1700
  if (msg.type === "add_project") {
1851
1701
  var addPath = (msg.path || "").replace(/^~/, require("./config").REAL_HOME);
1852
1702
  var addAbs = path.resolve(addPath);
1853
- // Multi-user mode: normal users restricted to their home directory
1854
- if (osUsers && osUsers.length > 0 && ws._clayUser && ws._clayUser.role !== "admin") {
1855
- if (!ws._clayUser.linuxUser) {
1856
- sendTo(ws, { type: "add_project_result", ok: false, error: "No Linux user assigned" });
1857
- return true;
1858
- }
1859
- var userHome = "/home/" + ws._clayUser.linuxUser;
1860
- if (addAbs !== userHome && (addAbs + "/").indexOf(userHome + "/") !== 0) {
1861
- sendTo(ws, { type: "add_project_result", ok: false, error: "Path not allowed. You can only add directories under " + userHome });
1862
- return true;
1863
- }
1703
+ if (opts.canUsePath && !opts.canUsePath(ws._clayUser && ws._clayUser.id, addAbs)) {
1704
+ sendTo(ws, startReply(msg, { type: "add_project_result", ok: false, error: "Directory access is not permitted" }));
1705
+ return true;
1864
1706
  }
1865
1707
  try {
1866
1708
  var addStat = fs.statSync(addAbs);
1867
1709
  if (!addStat.isDirectory()) {
1868
- sendTo(ws, { type: "add_project_result", ok: false, error: "Not a directory" });
1710
+ sendTo(ws, startReply(msg, { type: "add_project_result", ok: false, error: "Not a directory" }));
1869
1711
  return true;
1870
1712
  }
1871
1713
  } catch (e) {
1872
- sendTo(ws, { type: "add_project_result", ok: false, error: "Directory not found" });
1714
+ sendTo(ws, startReply(msg, { type: "add_project_result", ok: false, error: "Directory not found" }));
1873
1715
  return true;
1874
1716
  }
1875
1717
  if (typeof opts.onAddProject === "function") {
1876
1718
  var result = opts.onAddProject(addAbs, ws._clayUser);
1877
- sendTo(ws, { type: "add_project_result", ok: result.ok, slug: result.slug, error: result.error, existing: result.existing });
1719
+ sendTo(ws, startReply(msg, Object.assign({}, result, { type: "add_project_result" })));
1878
1720
  } else {
1879
- sendTo(ws, { type: "add_project_result", ok: false, error: "Not supported" });
1721
+ sendTo(ws, startReply(msg, { type: "add_project_result", ok: false, error: "Not supported" }));
1880
1722
  }
1881
1723
  return true;
1882
1724
  }
@@ -1886,7 +1728,7 @@ function attachSessions(ctx) {
1886
1728
  if (ws._clayUser) {
1887
1729
  var cpPerms = usersModule.getEffectivePermissions(ws._clayUser, osUsers);
1888
1730
  if (!cpPerms.createProject) {
1889
- sendTo(ws, { type: "add_project_result", ok: false, error: "You do not have permission to create projects" });
1731
+ sendTo(ws, startReply(msg, { type: "add_project_result", ok: false, error: "You do not have permission to create projects" }));
1890
1732
  return true;
1891
1733
  }
1892
1734
  }
@@ -1894,14 +1736,14 @@ function attachSessions(ctx) {
1894
1736
  if (msg.type === "create_project") {
1895
1737
  var createName = (msg.name || "").trim();
1896
1738
  if (!createName || !/^[a-zA-Z0-9_-]+$/.test(createName)) {
1897
- sendTo(ws, { type: "add_project_result", ok: false, error: "Invalid name. Use only letters, numbers, dashes, and underscores." });
1739
+ sendTo(ws, startReply(msg, { type: "add_project_result", ok: false, error: "Invalid name. Use only letters, numbers, dashes, and underscores." }));
1898
1740
  return true;
1899
1741
  }
1900
1742
  if (typeof opts.onCreateProject === "function") {
1901
1743
  var createResult = opts.onCreateProject(createName, ws._clayUser);
1902
- sendTo(ws, { type: "add_project_result", ok: createResult.ok, slug: createResult.slug, error: createResult.error });
1744
+ sendTo(ws, startReply(msg, Object.assign({}, createResult, { type: "add_project_result" })));
1903
1745
  } else {
1904
- sendTo(ws, { type: "add_project_result", ok: false, error: "Not supported" });
1746
+ sendTo(ws, startReply(msg, { type: "add_project_result", ok: false, error: "Not supported" }));
1905
1747
  }
1906
1748
  return true;
1907
1749
  }
@@ -1910,16 +1752,16 @@ function attachSessions(ctx) {
1910
1752
  if (msg.type === "clone_project") {
1911
1753
  var cloneUrl = (msg.url || "").trim();
1912
1754
  if (!cloneUrl || (!/^https?:\/\//.test(cloneUrl) && !/^git@/.test(cloneUrl))) {
1913
- sendTo(ws, { type: "add_project_result", ok: false, error: "Invalid URL. Use https:// or git@ format." });
1755
+ sendTo(ws, startReply(msg, { type: "add_project_result", ok: false, error: "Invalid URL. Use https:// or git@ format." }));
1914
1756
  return true;
1915
1757
  }
1916
- sendTo(ws, { type: "clone_project_progress", status: "cloning" });
1758
+ sendTo(ws, startReply(msg, { type: "clone_project_progress", status: "cloning" }));
1917
1759
  if (typeof opts.onCloneProject === "function") {
1918
1760
  opts.onCloneProject(cloneUrl, ws._clayUser, function (cloneResult) {
1919
- sendTo(ws, { type: "add_project_result", ok: cloneResult.ok, slug: cloneResult.slug, error: cloneResult.error });
1761
+ sendTo(ws, startReply(msg, { type: "add_project_result", ok: cloneResult.ok, slug: cloneResult.slug, error: cloneResult.error }));
1920
1762
  });
1921
1763
  } else {
1922
- sendTo(ws, { type: "add_project_result", ok: false, error: "Not supported" });
1764
+ sendTo(ws, startReply(msg, { type: "add_project_result", ok: false, error: "Not supported" }));
1923
1765
  }
1924
1766
  return true;
1925
1767
  }
@@ -1989,10 +1831,10 @@ function attachSessions(ctx) {
1989
1831
  moveAllSchedulesToProject(removeSlug, msg.moveTasksTo);
1990
1832
  }
1991
1833
  if (typeof opts.onRemoveProject === "function") {
1992
- // Send result before removing so the WS is still open
1993
- sendTo(ws, { type: "remove_project_result", ok: true, slug: removeSlug });
1994
1834
  var removeUserId = ws._clayUser ? ws._clayUser.id : null;
1995
- opts.onRemoveProject(removeSlug, removeUserId);
1835
+ Promise.resolve().then(function () { return opts.onRemoveProject(removeSlug, removeUserId); }).then(function (result) {
1836
+ sendTo(ws, { type: "remove_project_result", ok: !!result && result.ok === true, slug: removeSlug, error: result && result.error });
1837
+ }, function (error) { sendTo(ws, { type: "remove_project_result", ok: false, slug: removeSlug, error: error.message }); });
1996
1838
  } else {
1997
1839
  sendTo(ws, { type: "remove_project_result", ok: false, error: "Not supported" });
1998
1840
  }
@@ -2032,30 +1874,41 @@ function attachSessions(ctx) {
2032
1874
 
2033
1875
  // --- Set project title (rename) ---
2034
1876
  if (msg.type === "set_project_title") {
1877
+ var titleRequestId = boundedRequestId(msg);
2035
1878
  if (!msg.slug) {
2036
- sendTo(ws, { type: "set_project_title_result", ok: false, error: "Missing slug" });
1879
+ sendTo(ws, { type: "set_project_title_result", ok: false, error: "Missing slug", requestId: titleRequestId });
1880
+ return true;
1881
+ }
1882
+ // The target is named by the client; it must be a project this account can access.
1883
+ if (!canAccessProjectSlug(ws, msg.slug)) {
1884
+ sendTo(ws, { type: "set_project_title_result", ok: false, error: "Project access is not permitted", slug: msg.slug, requestId: titleRequestId });
2037
1885
  return true;
2038
1886
  }
2039
1887
  if (typeof opts.onSetProjectTitle === "function") {
2040
1888
  var titleResult = opts.onSetProjectTitle(msg.slug, msg.title || null);
2041
- sendTo(ws, { type: "set_project_title_result", ok: titleResult.ok, slug: msg.slug, error: titleResult.error });
1889
+ sendTo(ws, Object.assign({}, titleResult, { type: "set_project_title_result", slug: msg.slug, requestId: titleRequestId }));
2042
1890
  } else {
2043
- sendTo(ws, { type: "set_project_title_result", ok: false, error: "Not supported" });
1891
+ sendTo(ws, { type: "set_project_title_result", ok: false, error: "Not supported", requestId: titleRequestId });
2044
1892
  }
2045
1893
  return true;
2046
1894
  }
2047
1895
 
2048
1896
  // --- Set project icon (emoji) ---
2049
1897
  if (msg.type === "set_project_icon") {
1898
+ var iconRequestId = boundedRequestId(msg);
2050
1899
  if (!msg.slug) {
2051
- sendTo(ws, { type: "set_project_icon_result", ok: false, error: "Missing slug" });
1900
+ sendTo(ws, { type: "set_project_icon_result", ok: false, error: "Missing slug", requestId: iconRequestId });
1901
+ return true;
1902
+ }
1903
+ if (!canAccessProjectSlug(ws, msg.slug)) {
1904
+ sendTo(ws, { type: "set_project_icon_result", ok: false, error: "Project access is not permitted", slug: msg.slug, requestId: iconRequestId });
2052
1905
  return true;
2053
1906
  }
2054
1907
  if (typeof opts.onSetProjectIcon === "function") {
2055
1908
  var iconResult = opts.onSetProjectIcon(msg.slug, msg.icon || null);
2056
- sendTo(ws, { type: "set_project_icon_result", ok: iconResult.ok, slug: msg.slug, error: iconResult.error });
1909
+ sendTo(ws, { type: "set_project_icon_result", ok: iconResult.ok, slug: msg.slug, error: iconResult.error, requestId: iconRequestId });
2057
1910
  } else {
2058
- sendTo(ws, { type: "set_project_icon_result", ok: false, error: "Not supported" });
1911
+ sendTo(ws, { type: "set_project_icon_result", ok: false, error: "Not supported", requestId: iconRequestId });
2059
1912
  }
2060
1913
  return true;
2061
1914
  }
@@ -2100,7 +1953,7 @@ function attachSessions(ctx) {
2100
1953
  var galManaged = [];
2101
1954
  var galUser = [];
2102
1955
  try {
2103
- var galInstaller = require("./claude-hook-installer");
1956
+ var galInstaller = require("./yoke/claude-hook-installer");
2104
1957
  galManaged = galInstaller.CLAY_MANAGED_ALLOW || [];
2105
1958
  } catch (e) {}
2106
1959
  if (galUid) {
@@ -2126,7 +1979,7 @@ function attachSessions(ctx) {
2126
1979
  // home (OS-mode: getent passwd; single-user: os.homedir()).
2127
1980
  var salEffective = false;
2128
1981
  try {
2129
- var salInstaller = require("./claude-hook-installer");
1982
+ var salInstaller = require("./yoke/claude-hook-installer");
2130
1983
  var salHome = null;
2131
1984
  if (osUsers && ws._clayUser && ws._clayUser.linuxUser) {
2132
1985
  try {
@@ -2181,6 +2034,18 @@ function attachSessions(ctx) {
2181
2034
  }
2182
2035
 
2183
2036
  return {
2037
+ invalidateVendorUsage: function () { vendorUsage.invalidateAll(); },
2038
+ // Canonical read-only inspection: the caller has already proven the owner and
2039
+ // project access; the OS user is re-derived from the live user record.
2040
+ readVendorUsage: function (request) {
2041
+ var installed = null;
2042
+ if (Array.isArray(sm.installedVendors)) {
2043
+ installed = Object.create(null);
2044
+ sm.installedVendors.forEach(function (vendor) { installed[vendor] = true; });
2045
+ }
2046
+ var linuxUser = osUsers && request.owner !== "default" ? ctx.getLinuxUserForSession({ ownerId: request.owner }) : null;
2047
+ return vendorUsage.readForOwner({ owner: request.owner, linuxUser: linuxUser, installed: installed, isCurrent: request.isCurrent });
2048
+ },
2184
2049
  handleSessionsMessage: handleSessionsMessage,
2185
2050
  resolveSessionForView: resolveSessionForView,
2186
2051
  sendSessionFoldersState: sessionFolders.sendStateTo,
@@ -2192,6 +2057,11 @@ function attachSessions(ctx) {
2192
2057
  return handleSessionsMessage(ws, { type: "permission_response", requestId: msg.requestId, decision: msg.decision });
2193
2058
  },
2194
2059
  stopHomeSession: stopSession,
2060
+ sessionActions: sessionActions,
2061
+ sessionHistoryActions: sessionHistory,
2062
+ sessionVendorActions: sessionVendor,
2063
+ sessionRuntimeActions: sessionRuntime,
2064
+ deleteProductControlPlan: function (plan) { sessionDelete.executeOwnedDeletion(plan); },
2195
2065
  };
2196
2066
  }
2197
2067