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
@@ -0,0 +1,114 @@
1
+ var provenance = require('./session-provenance');
2
+
3
+ // This opt-in protocol only describes an authenticated ordinary pane snapshot.
4
+ // It grants no action authority and is never a visible-render acknowledgement.
5
+ function begin(ctx, ws, msg) {
6
+ if (!Object.prototype.hasOwnProperty.call(msg, 'paneActivation')) {
7
+ ws._clayPaneActivation = null;
8
+ return null;
9
+ }
10
+ var request = msg.paneActivation;
11
+ var session = ctx.sm.sessions.get(msg.id);
12
+ var userId = ws._clayUser && ws._clayUser.id || null;
13
+ var worker = provenance.isWorker(session);
14
+ var parentSession = worker && Array.from(ctx.sm.sessions.values()).find(function (parent) { return parent.sessionOriginId === session.sessionProvenance.parentSessionOriginId && !provenance.isWorker(parent); });
15
+ var identity = session && {
16
+ requestId: request && request.requestId,
17
+ generation: request && request.generation,
18
+ projectSlug: ctx.slug,
19
+ viewerId: userId,
20
+ ownerId: session.ownerId || null,
21
+ sessionId: session.localId,
22
+ sessionOriginId: session.sessionOriginId,
23
+ role: worker ? 'worker' : 'driver',
24
+ workerGeneration: worker ? session.sessionProvenance.generation : null,
25
+ parentSessionOriginId: worker ? session.sessionProvenance.parentSessionOriginId : null,
26
+ groupStamp: groupStamp(),
27
+ };
28
+ function groupStamp() {
29
+ var group = ctx.splitStore && ctx.splitStore.groupForMember(msg.id);
30
+ return group ? JSON.stringify({ id: group.id, members: group.members, pair: group.pair || null }) : null;
31
+ }
32
+ function allowed() {
33
+ if (typeof ws._clayAuthCheck === 'function' && !ws._clayAuthCheck()) return false;
34
+ var access = ctx.getProjectAccess ? ctx.getProjectAccess() : { visibility: 'public' };
35
+ if (!ctx.usersModule.isMultiUser()) return !session.ownerId;
36
+ return !!userId &&
37
+ !!ctx.usersModule.findUserById(userId) &&
38
+ ctx.usersModule.canAccessProject(userId, access) &&
39
+ ctx.usersModule.canAccessSession(userId, session, access);
40
+ }
41
+ var valid = request && typeof request === 'object' &&
42
+ typeof request.requestId === 'string' && /^[a-zA-Z0-9_-]{1,96}$/.test(request.requestId) &&
43
+ Number.isSafeInteger(request.generation) && request.generation > (ws._clayPaneActivationGeneration || 0) &&
44
+ ws._clayPane === true && !ws._clayContextual && !ctx.isMate && ws.readyState === 1 &&
45
+ session && session.sessionOriginId && !session.isMate &&
46
+ (session.runtimeMode || session.mode || 'gui') === 'gui' && !session.tuiSuspended &&
47
+ request.projectSlug === identity.projectSlug && request.viewerId === identity.viewerId &&
48
+ request.ownerId === identity.ownerId && request.sessionOriginId === identity.sessionOriginId &&
49
+ request.role === identity.role && request.workerGeneration === identity.workerGeneration &&
50
+ (!ctx.splitStore || request.groupStamp === identity.groupStamp && request.parentSessionOriginId === identity.parentSessionOriginId) &&
51
+ (!worker || Number.isSafeInteger(identity.workerGeneration) && identity.workerGeneration > 0) && allowed();
52
+ if (!valid) {
53
+ // Do not disclose whether a foreign or removed session exists.
54
+ ctx.sendTo(ws, { type: 'pane_activation_rejected', requestId: request && typeof request.requestId === 'string' ? request.requestId.slice(0, 96) : null });
55
+ return false;
56
+ }
57
+ ws._clayPaneActivationGeneration = identity.generation;
58
+ ws._clayPaneActivation = identity;
59
+ function current() {
60
+ return ws.readyState === 1 && ws._clayPane === true && !ws._clayContextual && !ctx.isMate && !session.isMate &&
61
+ ws._clayPaneActivation === identity &&
62
+ ctx.sm.sessions.get(msg.id) === session && session.sessionOriginId === identity.sessionOriginId &&
63
+ (ws._clayUser && ws._clayUser.id || null) === userId &&
64
+ (session.ownerId || null) === identity.ownerId &&
65
+ (session.runtimeMode || session.mode || 'gui') === 'gui' && !session.tuiSuspended &&
66
+ (provenance.isWorker(session) ? 'worker' : 'driver') === identity.role &&
67
+ (provenance.isWorker(session) ? session.sessionProvenance.generation : null) === identity.workerGeneration &&
68
+ groupStamp() === identity.groupStamp &&
69
+ (!worker || parentSession && ctx.sm.sessions.get(parentSession.localId) === parentSession &&
70
+ session.sessionProvenance.parentSessionOriginId === identity.parentSessionOriginId && parentSession.sessionOriginId === identity.parentSessionOriginId &&
71
+ !provenance.isWorker(parentSession) && (parentSession.ownerId || null) === identity.ownerId) &&
72
+ allowed();
73
+ }
74
+ function send(message) {
75
+ if (current() && ws._clayActiveSession === session.localId) ctx.sendTo(ws, Object.assign({}, message, { paneActivation: identity }));
76
+ }
77
+ var historyItems = [], historyText = 0;
78
+ // Only synchronous replay uses this bounded envelope. Each actual disclosure
79
+ // rechecks live authorization; nothing is cached across a send or async work.
80
+ send.history = function (message) {
81
+ if (message.type === 'history_done') {
82
+ flushHistory();
83
+ send(message);
84
+ return;
85
+ }
86
+ historyItems.push(message);
87
+ historyText += typeof message.text === 'string' ? message.text.length : 0;
88
+ if (historyItems.length >= 100 || historyText >= 65536) flushHistory();
89
+ };
90
+ function flushHistory() {
91
+ if (!historyItems.length) return;
92
+ var items = historyItems;
93
+ historyItems = []; historyText = 0;
94
+ send({ type: 'pane_history_batch', items: items });
95
+ }
96
+ if (current()) ctx.sendTo(ws, { type: 'pane_activation_started', paneActivation: identity });
97
+ ws._clayPaneActivationValid = function () { return current() && ws._clayActiveSession === identity.sessionId; };
98
+ return {
99
+ validate: function () {
100
+ if (current()) return true;
101
+ ctx.sendTo(ws, { type: 'pane_activation_rejected', requestId: identity.requestId });
102
+ ws._clayPaneActivation = null;
103
+ return false;
104
+ },
105
+ send: send,
106
+ finish: function () {
107
+ // Includes history listeners, status, permissions and context sources.
108
+ // Consumers still need to observe their own DOM/composer readiness.
109
+ if (ws._clayActiveSession === session.localId) send({ type: 'pane_activation_snapshot_done' });
110
+ },
111
+ };
112
+ }
113
+
114
+ module.exports = { begin: begin };
@@ -0,0 +1,26 @@
1
+ var AsyncLocalStorage = require('node:async_hooks').AsyncLocalStorage;
2
+ var requests = new AsyncLocalStorage();
3
+
4
+ function same(a, b) {
5
+ return !!a && !!b && ['requestId', 'generation', 'projectSlug', 'viewerId', 'ownerId', 'sessionId', 'sessionOriginId', 'role', 'workerGeneration', 'groupStamp', 'parentSessionOriginId'].every(function (key) { return a[key] === b[key]; });
6
+ }
7
+ function run(ws, msg, dispatch) {
8
+ var binding = ws._clayPaneActivation;
9
+ if (!binding && msg.paneBinding || binding && msg.type === 'switch_session' && !msg.paneActivation) return;
10
+ if (binding && ['switch_session', 'ping', 'pong', 'clay_ui_ready'].indexOf(msg.type) < 0) {
11
+ if (!same(binding, msg.paneBinding) || !ws._clayPaneActivationValid || !ws._clayPaneActivationValid()) return;
12
+ }
13
+ return requests.run({ ws: ws, binding: binding || null }, function () { return dispatch(ws, msg); });
14
+ }
15
+ // A direct asynchronous reply retains the request's original binding. Live
16
+ // provider events instead supply their authoritative source session explicitly.
17
+ function outbound(ws, message, sessionId) {
18
+ if (!message || message.paneActivation || /^pane_activation_/.test(message.type)) return message;
19
+ var scope = requests.getStore();
20
+ var binding = sessionId === undefined ? scope && scope.ws === ws && scope.binding : ws._clayPaneActivation;
21
+ if (!binding) return message;
22
+ if (sessionId !== undefined && binding.sessionId !== sessionId) return null;
23
+ if (!same(binding, ws._clayPaneActivation) || !ws._clayPaneActivationValid || !ws._clayPaneActivationValid()) return null;
24
+ return Object.assign({}, message, { paneActivation: binding });
25
+ }
26
+ module.exports = { run: run, outbound: outbound, same: same };
@@ -0,0 +1,51 @@
1
+ // This projection is an allowlist. Never add runtime/session/path objects here.
2
+ function projectMetadata(status, access, users) {
3
+ if (!status || status.isMate || !access || access.error) return null;
4
+ var owner = access.ownerId && users.findUserById(access.ownerId);
5
+ return {
6
+ slug: status.slug,
7
+ project: status.title || status.project || status.slug,
8
+ title: status.title || status.project || status.slug,
9
+ projectOwnerId: owner ? owner.id : null,
10
+ ownerName: owner ? (owner.displayName || owner.username) : "Ownership needs recovery",
11
+ visibility: access.visibility,
12
+ contentAccess: false,
13
+ administrativeAccess: true,
14
+ ownershipRecovery: access.ownershipRecovery === "required" || !owner,
15
+ runtimeHealth: "Registered",
16
+ };
17
+ }
18
+
19
+ function metadataFor(user, status, access, users) {
20
+ if (!user || !users.findUserById(user.id) || user.role !== "admin" || !status || status.isMate || /^mate-/.test(status.slug)) return null;
21
+ return projectMetadata(status, access, users);
22
+ }
23
+
24
+ function escape(value) {
25
+ return String(value || "").replace(/[&<>"']/g, function (character) {
26
+ return { "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" }[character];
27
+ });
28
+ }
29
+
30
+ function page(metadata, projects) {
31
+ var privacyText = metadata.ownershipRecovery
32
+ ? "Content remains restricted because no verified owner is available. Ask the server operator to verify the account’s OS UID and real home/project mapping, then run the documented ownership repair."
33
+ : "Conversations, files and terminals are private. Ask the owner to share project access.";
34
+ var accessText = metadata.ownershipRecovery
35
+ ? "Ownership needs recovery. The server operator must independently verify or repair the authoritative account-to-OS mapping before ownership repair can succeed. Ambiguous records stay restricted; administrator status cannot claim this project. See the Project ownership and administrator access guide."
36
+ : "Only the project owner can change ownership, visibility and sharing. Administrator status does not grant access to private content.";
37
+ var strip = projects.filter(function (project) { return !project.isMate; }).map(function (project) {
38
+ return '<a class="icon-strip-item' + (project.slug === metadata.slug ? ' active' : '') + '" href="/p/' + encodeURIComponent(project.slug) + '/" title="' + escape(project.title || project.project) + '"><span class="icon-strip-name">' + escape((project.title || project.project || project.slug).substring(0, 2)) + '</span></a>';
39
+ }).join("");
40
+ return '<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>' + escape(metadata.title) + ' · Clay</title>' +
41
+ '<script src="/appearance-bootstrap.js"></script><link rel="stylesheet" href="/style.css"><link rel="stylesheet" href="/css/project-admin-overview.css"></head><body>' +
42
+ '<div id="layout"><header id="top-bar"><a href="/" class="top-bar-brand-link" aria-label="Clay home"><span class="top-bar-brand-wordmark"></span></a></header>' +
43
+ '<div id="layout-body"><nav id="icon-strip" aria-label="Projects"><div class="icon-strip-projects">' + strip + '</div></nav>' +
44
+ '<main id="main-area" class="project-admin-overview"><div class="project-admin-canvas"><h1>' + escape(metadata.title) + '</h1>' +
45
+ '<div role="tablist" aria-label="Project information"><button type="button" role="tab" aria-selected="true" id="overview-tab" aria-controls="overview-panel">Overview</button><button type="button" role="tab" aria-selected="false" id="access-tab" aria-controls="access-panel">Access</button></div>' +
46
+ '<section id="overview-panel" role="tabpanel" aria-labelledby="overview-tab"><dl><dt>Owner</dt><dd>' + escape(metadata.ownerName) + '</dd><dt>Visibility</dt><dd>' + escape(metadata.visibility) + '</dd><dt>Status</dt><dd>' + (metadata.ownershipRecovery ? 'Ownership needs recovery' : escape(metadata.runtimeHealth)) + '</dd><dt>Your access</dt><dd>Administrative metadata only</dd></dl><p>' + escape(privacyText) + '</p><p class="project-admin-secondary">Resource usage is unavailable for this project.</p></section>' +
47
+ '<section id="access-panel" role="tabpanel" aria-labelledby="access-tab" hidden><h2>Administrative metadata only</h2><p>' + escape(accessText) + '</p></section></div></main></div></div>' +
48
+ '<script type="module" src="/modules/project-admin-overview.js"></script></body></html>';
49
+ }
50
+
51
+ module.exports = { metadataFor: metadataFor, page: page };
@@ -25,8 +25,9 @@ function attachAskUser(ctx) {
25
25
  respond.cancel("Autonomous mode. Make your own decision.");
26
26
  return;
27
27
  }
28
- var questions = planningQuestions(boundSession, request.questions);
28
+ var questions = ["mcp-import", "product-control"].indexOf(request.source) >= 0 ? request.questions : planningQuestions(boundSession, request.questions);
29
29
  var input = { questions: questions };
30
+ if (request.source === 'mcp-import') input.source = 'mcp-import';
30
31
  if (!boundSession.pendingAskUser) boundSession.pendingAskUser = {};
31
32
  var pending = {
32
33
  input: input,
@@ -38,15 +39,18 @@ function attachAskUser(ctx) {
38
39
  };
39
40
  boundSession.pendingAskUser[request.id] = pending;
40
41
  if (respond && typeof respond.onSettle === "function") {
41
- respond.onSettle(function () {
42
+ respond.onSettle(function (result) {
43
+ if (request.source === 'product-control' && result.status !== 'submitted') ctx.record(boundSession, { type: 'product_control_question_expired', toolId: request.id, error: 'This exact change review expired or was cancelled. No change was authorized.' });
44
+ if (request.source === 'mate-schedule' && result.status !== 'submitted') ctx.record(boundSession, { type: 'mate_schedule_question_expired', toolId: request.id, error: 'This schedule review expired. Request a new review.' });
45
+ if (request.source === 'mcp-import' && result.status !== 'submitted') ctx.record(boundSession, { type: 'mcp_import_consent_expired', toolId: request.id, source: 'mcp-import', error: 'Import consent expired or was cancelled. No connection was imported.' });
42
46
  if (boundSession.pendingAskUser && boundSession.pendingAskUser[request.id] === pending) delete boundSession.pendingAskUser[request.id];
43
47
  });
44
48
  }
45
49
  if (!askUserIdentity.hasRecordedAskUserRequest(boundSession.history, request.id, input)) {
46
- ctx.record(boundSession, { type: "tool_executing", id: request.id, name: "AskUserQuestion", input: input });
50
+ ctx.record(boundSession, { type: "tool_executing", id: request.id, name: "AskUserQuestion", input: input, source: request.source });
47
51
  }
48
52
  var router = ctx.getWorkerQuestionRouter && ctx.getWorkerQuestionRouter();
49
- if (router) router.routeQuestion(boundSession, request, respond, function (answers, driverId) {
53
+ if (router && request.source !== "mcp-import" && request.source !== "mate-schedule" && request.source !== "product-control") router.routeQuestion(boundSession, request, respond, function (answers, driverId) {
50
54
  var displayAnswers = {};
51
55
  request.questions.forEach(function (question, index) { displayAnswers[index] = answers[question.id].join(", "); });
52
56
  ctx.record(boundSession, { type: "ask_user_answered", toolId: request.id, answers: displayAnswers, answeredByDriver: driverId });
@@ -3,7 +3,7 @@ var path = require("path");
3
3
  var usersModule = require("./users");
4
4
  var userPresence = require("./user-presence");
5
5
  var emailAccounts = require("./email-accounts");
6
- var { getCodexConfig } = require("./codex-defaults");
6
+ var providerSettings = require("./yoke/settings");
7
7
  var yoke = require("./yoke");
8
8
  var permissionModes = require("./session-permission-mode");
9
9
  var sessionProvenance = require("./session-provenance");
@@ -143,6 +143,9 @@ function attachConnection(ctx) {
143
143
  var _warmedUp = false;
144
144
 
145
145
  function findRestoredActiveSession(ws, wsUser, allSessions) {
146
+ if (ws._clayContextual && ws._clayPaneSession) {
147
+ return { active: allSessions.find(function (session) { return session.localId === ws._clayPaneSession; }) || null, storedPresence: null };
148
+ }
146
149
  var active = null;
147
150
  var presenceKey = wsUser ? wsUser.id : "_default";
148
151
  var storedPresence = userPresence.getPresence(slug, presenceKey);
@@ -202,7 +205,7 @@ function attachConnection(ctx) {
202
205
  } else if (!usersModule.isMultiUser()) {
203
206
  allSessions = allSessions.filter(function (s) { return !s.ownerId; });
204
207
  }
205
- var restoredState = ws._clayPane
208
+ var restoredState = ws._clayPane && !ws._clayContextual
206
209
  ? { active: null, storedPresence: null }
207
210
  : findRestoredActiveSession(ws, wsUser, allSessions);
208
211
  var restoredActive = restoredState.active;
@@ -217,7 +220,7 @@ function attachConnection(ctx) {
217
220
  sm.saveSessionFile(restoredActive);
218
221
  }
219
222
  var initialModels = (sm.modelsByVendor && sm.modelsByVendor[initialVendor]) || sm.availableModels || [];
220
- sendTo(ws, { type: "info", cwd: cwd, slug: slug, project: title || project, version: currentVersion, debug: !!debug, dangerouslySkipPermissions: dangerouslySkipPermissions, osUsers: osUsers, lanHost: lanHost, projectCount: _filteredProjects.length, projects: _filteredProjects, projectOwnerId: projectOwnerId, ownerLocked: ownerLocked, vendors: yoke.VENDOR_REGISTRY, isMate: isMate, mateId: isMate ? mateId : null, mateDisplayName: isMate ? (mateDisplayName || title || project || "Mate") : "" });
223
+ sendTo(ws, { type: "info", controlRuntimeId: require("./clay-control-target-projection").runtime(sm), cwd: cwd, slug: slug, project: title || project, version: currentVersion, debug: !!debug, dangerouslySkipPermissions: dangerouslySkipPermissions, osUsers: osUsers, lanHost: lanHost, projectCount: _filteredProjects.length, projects: _filteredProjects, projectOwnerId: projectOwnerId, ownerLocked: ownerLocked, vendors: require("./yoke/vendor-registry").publicInfo(), isMate: isMate, mateId: isMate ? mateId : null, mateDisplayName: isMate ? (mateDisplayName || title || project || "Mate") : "" });
221
224
  if (typeof sendCursorSharingState === "function") sendCursorSharingState(ws);
222
225
  // Update notifications are pushed on a scheduled interval (see
223
226
  // scheduleUpdateBroadcast). We no longer push on connect to avoid
@@ -234,8 +237,8 @@ function attachConnection(ctx) {
234
237
  var initialPermissionState = permissionModes.clientState(restoredActive, sm.currentPermissionMode, sm.defaultVendor);
235
238
  sendTo(ws, { type: "config_state", model: initialModelInfo.model, vendor: initialVendor, sessionId: restoredActive ? restoredActive.localId : null, mode: initialPermissionState.requestedPermissionMode, effectivePermissionMode: initialPermissionState.effectivePermissionMode, permissionCapabilities: initialPermissionState.permissionCapabilities, mcpPermissionModeOverrides: initialPermissionState.mcpPermissionModeOverrides, effort: initialEffort || "medium", betas: sm.currentBetas || [], thinking: sm.currentThinking || "adaptive", thinkingBudget: sm.currentThinkingBudget || 10000 });
236
239
  sendTo(ws, { type: "last_vendor", vendor: sm.lastVendor || "" });
237
- sendTo(ws, Object.assign({ type: "codex_config" }, getCodexConfig(sm)));
238
- sendTo(ws, { type: "term_list", terminals: tm.list() });
240
+ providerSettings.messages(sm).forEach(function(message) { sendTo(ws, message); });
241
+ sendTo(ws, { type: "term_list", terminals: tm.list(true) });
239
242
  // Any login flow already running in this project, so a reconnecting client
240
243
  // re-attaches to the existing login terminal instead of starting another.
241
244
  if (_vendorLogin) _vendorLogin.sendStateTo(ws);
@@ -267,7 +270,7 @@ function attachConnection(ctx) {
267
270
  var presenceKey = wsUser ? wsUser.id : "_default";
268
271
  var storedPresence = restoredState.storedPresence;
269
272
  var autoCreated = false;
270
- if (!active && !ws._clayPane) {
273
+ if (!active && (!ws._clayPane || (ws._clayContextual && !ws._clayPaneSession))) {
271
274
  var autoOpts = {};
272
275
  if (wsUser && usersModule.isMultiUser()) autoOpts.ownerId = wsUser.id;
273
276
  active = sm.createSession(autoOpts, ws);
@@ -301,12 +304,13 @@ function attachConnection(ctx) {
301
304
  }
302
305
  sendTo(ws, { type: "history_meta", total: total, from: fromIndex });
303
306
  for (var i = fromIndex; i < total; i++) {
304
- sendTo(ws, hydrateImageRefs(active.history[i]));
307
+ sendTo(ws, Object.assign({}, hydrateImageRefs(require("./yoke/usage").clientEvent(active.history[i], active.vendor || "claude")), { _historyIndex: i }));
305
308
  }
306
- var _lastUsage = null, _lastModelUsage = null, _lastCost = null, _lastStreamInputTokens = null;
309
+ var _lastContext = null, _lastUsage = null, _lastModelUsage = null, _lastCost = null, _lastStreamInputTokens = null;
307
310
  for (var _ri = total - 1; _ri >= 0; _ri--) {
308
311
  if (active.history[_ri].type === "result") {
309
- var _r = active.history[_ri];
312
+ var _r = require("./yoke/usage").clientEvent(active.history[_ri], active.vendor || "claude");
313
+ _lastContext = _r.context;
310
314
  _lastUsage = _r.usage || null;
311
315
  _lastModelUsage = _r.modelUsage || null;
312
316
  _lastCost = _r.cost != null ? _r.cost : null;
@@ -314,7 +318,7 @@ function attachConnection(ctx) {
314
318
  break;
315
319
  }
316
320
  }
317
- sendTo(ws, { type: "history_done", lastUsage: _lastUsage, lastModelUsage: _lastModelUsage, lastCost: _lastCost, lastStreamInputTokens: _lastStreamInputTokens, contextUsage: active.lastContextUsage || null });
321
+ sendTo(ws, { type: "history_done", controlHistoryRevision: require("./clay-control-target-projection").digest(active.history || []), sessionId: active.localId, sessionOriginId: active.sessionOriginId || null, lastUsage: _lastUsage, lastModelUsage: _lastModelUsage, lastCost: _lastCost, lastStreamInputTokens: _lastStreamInputTokens, context: _lastContext, contextUsage: require("./yoke/usage").context(active.lastContextUsage, active.vendor || "claude") });
318
322
  if (scheduledMessages) scheduledMessages.sendState(active, ws);
319
323
  if (pairResultRecovery) pairResultRecovery.sendState(active, ws);
320
324
 
@@ -0,0 +1,12 @@
1
+ var fs = require('fs'), path = require('path');
2
+ var projection = require('./clay-control-target-projection');
3
+ function project(service, principal, status, manager) {
4
+ var before = service.rootIdentity(principal, 'git');
5
+ var directory = fs.realpathSync(status.gitDir), stat = fs.statSync(directory), indexPath = path.join(directory, 'index');
6
+ var index = fs.existsSync(indexPath) ? require('crypto').createHash('sha256').update(fs.readFileSync(indexPath)).digest('hex') : null;
7
+ var view = { runtimeId: projection.runtime(manager), repository: projection.digest([directory, [stat.dev, stat.ino, stat.mode, stat.uid, stat.gid]]),
8
+ files: status.files.slice(0, 200).map(function (file) { return { path: file.path, revision: projection.digest([status.oid, index, file]) }; }) };
9
+ if (service.rootIdentity(principal, 'git') !== before) throw new Error('Workspace identity changed during projection.');
10
+ return view;
11
+ }
12
+ module.exports = { project: project };
@@ -774,6 +774,7 @@ function attachDebate(ctx) {
774
774
  var identityMate = matesModule.getMate(debate.mateCtx, msg.mateId);
775
775
  var identityProfile = identityMate && identityMate.profile ? identityMate.profile : (identityMate || {});
776
776
  if (!msg.mateName) msg.mateName = identity.name;
777
+ if (!msg.avatarCatalogId) msg.avatarCatalogId = identityProfile.avatarCatalogId || "";
777
778
  if (!msg.avatarStyle) msg.avatarStyle = identity.avatarStyle;
778
779
  if (!msg.avatarSeed) msg.avatarSeed = identity.avatarSeed;
779
780
  if (!msg.avatarColor) msg.avatarColor = identity.avatarColor;
@@ -12,7 +12,7 @@ function unsafeReason(session, isPaired) {
12
12
  if (session.homeDebatePlanning || session.homeDebatePhase || session.debateSetupMode || session.debateState || session._debate) return "Debate sessions cannot receive project follow-ups.";
13
13
  if (session.loop || session.spawn || session.handoff || session.assignment || session.singleTurn || session._pairDelegation || isPaired) return "Orchestrated sessions cannot receive project follow-ups.";
14
14
  if (session.dangerouslySkipPermissions || session.permissionMode === "bypassPermissions") return "Full-access sessions cannot receive project follow-ups.";
15
- if (session.destroying || session.scheduledMessage || session.rateLimitAutoContinuePending || session.taskStopRequested || session._runtimeRefreshRequested || session.pendingPush && session.pendingPush.length) return "Target session is not safely resumable right now.";
15
+ if (session.destroying || session.scheduledMessage || session.rateLimitAutoContinuePending || session.taskStopRequested || session._pendingMessageDrainPaused || session._runtimeRefreshRequested || session.pendingPush && session.pendingPush.length) return "Target session is not safely resumable right now.";
16
16
  return "";
17
17
  }
18
18
 
@@ -36,6 +36,7 @@ function attachDelegatedFollowUp(ctx) {
36
36
  var paired = !!(ctx.isPairedSession && ctx.isPairedSession(session));
37
37
  var reason = unsafeReason(session, paired);
38
38
  if (reason) throw new Error(reason);
39
+ if (ctx.isPendingQueuePaused && ctx.isPendingQueuePaused(session)) throw new Error("The Driver queue is stopped; explicit owner resume is required.");
39
40
  if (!ctx.canResumeVendor(session.vendor)) throw new Error("Target session provider cannot safely resume this conversation.");
40
41
  return { session: session, sessionRef: targetSessionRef, title: session.title || "Existing session" };
41
42
  }
@@ -0,0 +1,65 @@
1
+ // Reserve a new project directory before running Git or changing ownership.
2
+ // Filesystem identity checks also keep failed attempts from deleting targets
3
+ // replaced by another actor. Hostile OS/root mutation is outside this boundary.
4
+ var fs = require("fs");
5
+ var path = require("path");
6
+
7
+ function canonical(candidate) {
8
+ var current = path.resolve(candidate);
9
+ var missing = [];
10
+ while (true) {
11
+ try { return path.join.apply(path, [fs.realpathSync(current)].concat(missing)); }
12
+ catch (error) {
13
+ if (error.code !== "ENOENT") throw error;
14
+ // A dangling symlink is an existing object, not a missing directory.
15
+ try { fs.lstatSync(current); throw new Error("A project path contains a dangling symlink."); }
16
+ catch (statError) { if (statError.code !== "ENOENT") throw statError; }
17
+ var parent = path.dirname(current);
18
+ if (parent === current) throw error;
19
+ missing.unshift(path.basename(current));
20
+ current = parent;
21
+ }
22
+ }
23
+ }
24
+
25
+ function inside(root, candidate) {
26
+ var relative = path.relative(root, candidate);
27
+ return relative !== ".." && relative.indexOf(".." + path.sep) !== 0 && !path.isAbsolute(relative);
28
+ }
29
+
30
+ function rejectIntersection(target, projects, userId) {
31
+ (projects || []).forEach(function (project) {
32
+ var root = canonical(project.path);
33
+ if (target === root || inside(target, root) || (project.ownerId !== userId && inside(root, target))) {
34
+ throw new Error("The destination intersects a registered project.");
35
+ }
36
+ });
37
+ }
38
+
39
+ function claim(options) {
40
+ var slug = options.slug;
41
+ if (!slug || slug === "." || slug === ".." || path.basename(slug) !== slug) throw new Error("Invalid project directory name.");
42
+ var base = canonical(options.baseDir);
43
+ var target = path.join(base, slug);
44
+ rejectIntersection(target, options.projects, options.userId);
45
+ fs.mkdirSync(base, { recursive: true });
46
+ if (fs.realpathSync(options.baseDir) !== base) throw new Error("The project base directory changed.");
47
+ rejectIntersection(target, options.projects, options.userId);
48
+ // EEXIST rejects directories, files, symlinks and concurrent claims alike.
49
+ fs.mkdirSync(target, { mode: 0o700 });
50
+ var identity = fs.lstatSync(target);
51
+ function assertOwned() {
52
+ var current = fs.lstatSync(target);
53
+ if (!current.isDirectory() || current.isSymbolicLink() || current.dev !== identity.dev || current.ino !== identity.ino || fs.realpathSync(base) !== base) {
54
+ throw new Error("The claimed project directory changed.");
55
+ }
56
+ }
57
+ function cleanup() {
58
+ try { assertOwned(); } catch (error) { return false; }
59
+ fs.rmSync(target, { recursive: true });
60
+ return true;
61
+ }
62
+ return { path: target, assertOwned: assertOwned, cleanup: cleanup };
63
+ }
64
+
65
+ module.exports = { claim: claim, canonical: canonical };
@@ -0,0 +1,61 @@
1
+ // Shared file-viewer effects. Authority and scope are derived by each caller.
2
+ var fs = require('fs'), path = require('path'), crypto = require('crypto');
3
+ var resolve = require('./project-file-path').resolveFilePath;
4
+ function hash(content) { return crypto.createHash('sha256').update(content).digest('hex'); }
5
+ function attachFileOperations(ctx) {
6
+ function list(scope, requested) {
7
+ var dir = resolve(ctx.cwd, requested || '.', scope);
8
+ var items = scope.identity ? ctx.fsAsUser('list', { dir: dir }, scope.identity) : fs.readdirSync(dir, { withFileTypes: true }).map(function (item) { return { name: item.name, isDir: item.isDirectory() }; });
9
+ return items.filter(function (item) { return !item.isDir || !ctx.IGNORED_DIRS.has(item.name); }).map(function (item) { return { name: item.name, type: item.isDir ? 'dir' : 'file', path: path.relative(ctx.cwd, path.join(dir, item.name)).split(path.sep).join('/') }; });
10
+ }
11
+ function read(scope, requested) {
12
+ var file = resolve(ctx.cwd, requested, scope), identity = scope.identity;
13
+ var stat = identity ? ctx.fsAsUser('stat', { file: file }, identity) : fs.statSync(file);
14
+ if (stat.size > ctx.FS_MAX_SIZE) return { binary: true, size: stat.size, error: 'File too large (' + (stat.size / 1024 / 1024).toFixed(1) + ' MB)' };
15
+ var ext = path.extname(file).toLowerCase();
16
+ if (ctx.BINARY_EXTS.has(ext)) {
17
+ var binary = { binary: true, size: stat.size };
18
+ if (ctx.IMAGE_EXTS.has(ext)) binary.imageUrl = 'api/file?path=' + encodeURIComponent(requested);
19
+ return binary;
20
+ }
21
+ return { content: identity ? ctx.fsAsUser('read', { file: file, readContent: true }, identity).content : fs.readFileSync(file, 'utf8'), size: stat.size };
22
+ }
23
+ function snapshot(scope, requested) {
24
+ var file = resolve(ctx.cwd, requested, scope), stat = fs.lstatSync(file);
25
+ if (!stat.isFile() || stat.isSymbolicLink() || stat.nlink !== 1 || stat.size > 65536) throw new Error('Select a regular, singly linked text file of at most 64 KB.');
26
+ var result = read(scope, requested);
27
+ if (result.binary || typeof result.content !== 'string' || result.content.indexOf('\0') >= 0) throw new Error('Binary files are unavailable for canonical editing.');
28
+ return { dev: stat.dev, ino: stat.ino, mode: stat.mode, uid: stat.uid, gid: stat.gid, size: stat.size, mtime: stat.mtimeMs, hash: hash(result.content) };
29
+ }
30
+ function write(scope, requested, content, control) {
31
+ var file = resolve(ctx.cwd, requested, scope), attempted = false;
32
+ if (!control) {
33
+ if (scope.identity) ctx.fsAsUser('write', { file: file, content: content }, scope.identity);
34
+ else fs.writeFileSync(file, content, 'utf8');
35
+ return { ok: true };
36
+ }
37
+ if (typeof fs.constants.O_NOFOLLOW !== 'number') throw new Error('Canonical editing requires no-follow file descriptors on this platform.');
38
+ control.revalidate();
39
+ if (JSON.stringify(snapshot(scope, requested)) !== JSON.stringify(control.expected)) throw new Error('The exact file revision changed.');
40
+ try {
41
+ attempted = true;
42
+ if (scope.identity) ctx.fsAsUser('write_exact', { file: file, content: content, expected: control.expected, root: fs.realpathSync(ctx.cwd) }, scope.identity);
43
+ else {
44
+ var fd = fs.openSync(file, fs.constants.O_RDWR | fs.constants.O_NOFOLLOW);
45
+ try {
46
+ var stat = fs.fstatSync(fd), expected = control.expected;
47
+ if (!stat.isFile() || stat.nlink !== 1 || stat.dev !== expected.dev || stat.ino !== expected.ino || stat.mode !== expected.mode || stat.uid !== expected.uid || stat.gid !== expected.gid || stat.size !== expected.size || stat.mtimeMs !== expected.mtime || hash(fs.readFileSync(fd, 'utf8')) !== expected.hash) throw new Error('The open file revision changed.');
48
+ control.revalidate();
49
+ fs.ftruncateSync(fd, 0);
50
+ var bytes = Buffer.from(content, 'utf8'), offset = 0;
51
+ while (offset < bytes.length) { var written = fs.writeSync(fd, bytes, offset, bytes.length - offset, offset); if (!written) throw new Error('File write made no progress.'); offset += written; }
52
+ fs.fsyncSync(fd);
53
+ } finally { fs.closeSync(fd); }
54
+ }
55
+ control.revalidate();
56
+ return { status: 'completed', persisted: true, path: requested };
57
+ } catch (error) { error.productControlResult = { persisted: attempted ? 'unknown' : false, retrySafe: false, error: error.message }; throw error; }
58
+ }
59
+ return { list: list, read: read, write: write, snapshot: snapshot };
60
+ }
61
+ module.exports = { attachFileOperations: attachFileOperations };
@@ -37,6 +37,7 @@ function attachFilesystem(ctx) {
37
37
 
38
38
  var access = ctx.requestAccess || attachRequestAccess(ctx);
39
39
  var fileHistory = attachFileHistory(ctx, access);
40
+ var operations = require("./project-file-operations").attachFileOperations(ctx);
40
41
 
41
42
  function handleFilesystemMessage(ws, msg) {
42
43
  var fileScope;
@@ -57,40 +58,11 @@ function attachFilesystem(ctx) {
57
58
 
58
59
  // --- fs_list ---
59
60
  if (msg.type === "fs_list") {
60
- var fsListIdentity = fileScope.identity;
61
- var fsDir = fileTarget;
62
61
  try {
63
- var fsListUserInfo = fsListIdentity;
64
- var entries = [];
65
- if (fsListUserInfo) {
66
- // Run as target OS user to respect Linux file permissions
67
- var rawEntries = fsAsUser("list", { dir: fsDir }, fsListUserInfo);
68
- for (var fi = 0; fi < rawEntries.length; fi++) {
69
- var re = rawEntries[fi];
70
- if (re.isDir && IGNORED_DIRS.has(re.name)) continue;
71
- entries.push({
72
- name: re.name,
73
- type: re.isDir ? "dir" : "file",
74
- path: path.relative(cwd, path.join(fsDir, re.name)).split(path.sep).join("/"),
75
- });
76
- }
77
- } else {
78
- var items = fs.readdirSync(fsDir, { withFileTypes: true });
79
- for (var fi = 0; fi < items.length; fi++) {
80
- var item = items[fi];
81
- if (item.isDirectory() && IGNORED_DIRS.has(item.name)) continue;
82
- entries.push({
83
- name: item.name,
84
- type: item.isDirectory() ? "dir" : "file",
85
- path: path.relative(cwd, path.join(fsDir, item.name)).split(path.sep).join("/"),
86
- });
87
- }
88
- }
89
- sendTo(ws, { type: "fs_list_result", path: msg.path || ".", entries: entries });
90
- // Auto-watch the directory for changes
91
- startDirWatch(ws, msg.path || ".");
62
+ sendTo(ws, { type: 'fs_list_result', path: msg.path || '.', entries: operations.list(fileScope, msg.path || '.') });
63
+ startDirWatch(ws, msg.path || '.');
92
64
  } catch (e) {
93
- sendTo(ws, { type: "fs_list_result", path: msg.path, entries: [], error: fileError(e).message, errorCode: fileError(e).code });
65
+ sendTo(ws, { type: 'fs_list_result', path: msg.path, entries: [], error: fileError(e).message, errorCode: fileError(e).code });
94
66
  }
95
67
  return true;
96
68
  }
@@ -143,62 +115,19 @@ function attachFilesystem(ctx) {
143
115
 
144
116
  // --- fs_read ---
145
117
  if (msg.type === "fs_read") {
146
- var fsReadIdentity = fileScope.identity;
147
- var fsFile = fileTarget;
118
+ var metadata = { type: 'fs_read_result', path: msg.path, requestId: msg.requestId, projectSlug: msg.projectSlug, sessionId: msg.sessionId, accountId: msg.accountId };
148
119
  try {
149
- var fsReadUserInfo = fsReadIdentity;
150
- var ext = path.extname(fsFile).toLowerCase();
151
- if (fsReadUserInfo) {
152
- // Run stat and read as target OS user
153
- var statResult = fsAsUser("stat", { file: fsFile }, fsReadUserInfo);
154
- if (statResult.size > FS_MAX_SIZE) {
155
- sendTo(ws, { type: "fs_read_result", path: msg.path, requestId: msg.requestId, projectSlug: msg.projectSlug, sessionId: msg.sessionId, accountId: msg.accountId, binary: true, size: statResult.size, error: "File too large (" + (statResult.size / 1024 / 1024).toFixed(1) + " MB)" });
156
- return true;
157
- }
158
- if (BINARY_EXTS.has(ext)) {
159
- var result = { type: "fs_read_result", path: msg.path, requestId: msg.requestId, projectSlug: msg.projectSlug, sessionId: msg.sessionId, accountId: msg.accountId, binary: true, size: statResult.size };
160
- if (IMAGE_EXTS.has(ext)) result.imageUrl = "api/file?path=" + encodeURIComponent(msg.path);
161
- sendTo(ws, result);
162
- return true;
163
- }
164
- var readResult = fsAsUser("read", { file: fsFile, readContent: true }, fsReadUserInfo);
165
- sendTo(ws, { type: "fs_read_result", path: msg.path, requestId: msg.requestId, projectSlug: msg.projectSlug, sessionId: msg.sessionId, accountId: msg.accountId, content: readResult.content, size: statResult.size });
166
- } else {
167
- var stat = fs.statSync(fsFile);
168
- if (stat.size > FS_MAX_SIZE) {
169
- sendTo(ws, { type: "fs_read_result", path: msg.path, requestId: msg.requestId, projectSlug: msg.projectSlug, sessionId: msg.sessionId, accountId: msg.accountId, binary: true, size: stat.size, error: "File too large (" + (stat.size / 1024 / 1024).toFixed(1) + " MB)" });
170
- return true;
171
- }
172
- if (BINARY_EXTS.has(ext)) {
173
- var result = { type: "fs_read_result", path: msg.path, requestId: msg.requestId, projectSlug: msg.projectSlug, sessionId: msg.sessionId, accountId: msg.accountId, binary: true, size: stat.size };
174
- if (IMAGE_EXTS.has(ext)) result.imageUrl = "api/file?path=" + encodeURIComponent(msg.path);
175
- sendTo(ws, result);
176
- return true;
177
- }
178
- var content = fs.readFileSync(fsFile, "utf8");
179
- sendTo(ws, { type: "fs_read_result", path: msg.path, requestId: msg.requestId, projectSlug: msg.projectSlug, sessionId: msg.sessionId, accountId: msg.accountId, content: content, size: stat.size });
180
- }
181
- } catch (e) {
182
- sendTo(ws, { type: "fs_read_result", path: msg.path, requestId: msg.requestId, projectSlug: msg.projectSlug, sessionId: msg.sessionId, accountId: msg.accountId, error: fileError(e).message, errorCode: fileError(e).code });
120
+ var readResult = operations.read(fileScope, msg.path);
121
+ try { var fileSnapshot = operations.snapshot(fileScope, msg.path); if (fileSnapshot.hash === require('crypto').createHash('sha256').update(readResult.content).digest('hex')) readResult.controlRevision = require('./clay-control-target-projection').digest(fileSnapshot); } catch (ignored) {}
122
+ sendTo(ws, Object.assign(metadata, readResult));
183
123
  }
124
+ catch (e) { sendTo(ws, Object.assign(metadata, { error: fileError(e).message, errorCode: fileError(e).code })); }
184
125
  return true;
185
126
  }
186
127
 
187
- // --- fs_write ---
188
- if (msg.type === "fs_write") {
189
- var fsWriteIdentity = fileScope.identity;
190
- var fsWriteFile = fileTarget;
191
- try {
192
- var fsWriteUserInfo = fsWriteIdentity;
193
- if (fsWriteUserInfo) {
194
- fsAsUser("write", { file: fsWriteFile, content: msg.content || "" }, fsWriteUserInfo);
195
- } else {
196
- fs.writeFileSync(fsWriteFile, msg.content || "", "utf8");
197
- }
198
- sendTo(ws, { type: "fs_write_result", path: msg.path, ok: true });
199
- } catch (e) {
200
- sendTo(ws, { type: "fs_write_result", path: msg.path, ok: false, error: fileError(e).message, errorCode: fileError(e).code });
201
- }
128
+ if (msg.type === 'fs_write') {
129
+ try { sendTo(ws, Object.assign({ type: 'fs_write_result', path: msg.path }, operations.write(fileScope, msg.path, msg.content || ''))); }
130
+ catch (e) { sendTo(ws, { type: 'fs_write_result', path: msg.path, ok: false, error: fileError(e).message, errorCode: fileError(e).code }); }
202
131
  return true;
203
132
  }
204
133
 
@@ -239,8 +168,8 @@ function attachFilesystem(ctx) {
239
168
  var setResult;
240
169
  try { setResult = opts.onSetProjectEnv(targetSlug, msg.envrc || ""); }
241
170
  catch (error) { sendTo(ws, { type: "set_project_env_result", slug: targetSlug, requestId: msg.requestId, ok: false, error: error.message }); return true; }
242
- if (setResult.ok) onEnvironmentChanged();
243
- sendTo(ws, { type: "set_project_env_result", ok: setResult.ok, slug: targetSlug, requestId: msg.requestId, error: setResult.error, timing: setResult.ok ? "Applies to newly created coding-agent processes. Active processes keep their current environment." : undefined });
171
+ if (setResult.ok && !setResult.runtimeRefreshed) onEnvironmentChanged();
172
+ sendTo(ws, Object.assign({}, setResult, { type: "set_project_env_result", slug: targetSlug, requestId: msg.requestId, timing: setResult.ok ? "Applies to newly created coding-agent processes. Active processes keep their current environment." : undefined }));
244
173
  } else {
245
174
  sendTo(ws, { type: "set_project_env_result", slug: targetSlug, requestId: msg.requestId, ok: false, error: "Not supported" });
246
175
  }
@@ -297,6 +226,7 @@ function attachFilesystem(ctx) {
297
226
 
298
227
  return {
299
228
  handleFilesystemMessage: handleFilesystemMessage,
229
+ productOperations: operations,
300
230
  };
301
231
  }
302
232