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,94 @@
1
+ var crypto = require('crypto');
2
+ function attachMateLifecycleService(ctx) {
3
+ var busy = new Set();
4
+ function owner(userId) {
5
+ if (ctx.users.isMultiUser() && (!userId || !ctx.users.findUserById(userId))) throw new Error('Mate owner is unavailable.');
6
+ return ctx.mates.buildMateCtx(ctx.users.isMultiUser() ? userId : null);
7
+ }
8
+ function read(userId, id) {
9
+ var mate = ctx.mates.getMate(owner(userId), id);
10
+ if (!mate || ctx.users.isMultiUser() && mate.createdBy !== userId) throw new Error('Mate is unavailable or belongs to another owner.');
11
+ return mate;
12
+ }
13
+ function validateReceipt(userId, id, allowMissing) {
14
+ var mate = ctx.mates.getMate(owner(userId), id);
15
+ if (!mate && allowMissing) return;
16
+ if (!mate || ctx.users.isMultiUser() && mate.createdBy !== userId) throw new Error('Mate receipt access changed.');
17
+ }
18
+ function list(userId) {
19
+ return ctx.mates.getAllMates(owner(userId)).filter(function (mate) { return !ctx.users.isMultiUser() || mate.createdBy === userId; });
20
+ }
21
+ function broadcast(userId, message, ws) {
22
+ var seen = new Set();
23
+ function send(socket) {
24
+ if (!socket || seen.has(socket) || socket.readyState !== 1) return;
25
+ if (ctx.users.isMultiUser() && (!socket._clayUser || socket._clayUser.id !== userId)) return;
26
+ seen.add(socket); socket.send(JSON.stringify(message));
27
+ }
28
+ send(ws);
29
+ ctx.projects.forEach(function (project) { project.forEachClient(send); });
30
+ }
31
+ function update(userId, id, updates, ws, requestId) {
32
+ var mate = read(userId, id);
33
+ if (Object.prototype.hasOwnProperty.call(updates, 'lifecycleDeletion')) throw new Error('Mate deletion state is server-managed.');
34
+ if (busy.has(id) || mate.lifecycleDeletion) throw new Error('Mate deletion is pending.');
35
+ var updated = ctx.mates.updateMate(owner(userId), id, updates);
36
+ if (!updated || updated.error) throw new Error(updated && updated.error || 'Mate not found');
37
+ try {
38
+ broadcast(userId, { type: 'mate_updated', mate: updated, requestId: requestId }, ws);
39
+ if (ctx.refresh) ctx.refresh(owner(userId));
40
+ } catch (error) { error.productControlResult = { persisted: true, retrySafe: false }; throw error; }
41
+ return updated;
42
+ }
43
+ function idle(project) {
44
+ if (!project) return;
45
+ if (project.inspectMateLifecycleRuntime && project.inspectMateLifecycleRuntime().some(function (t) { return !t.exited; })) throw new Error('Close the Mate terminals before deletion.');
46
+ project.getSessionManager().sessions.forEach(function (session) {
47
+ if (session.isProcessing || session._queryStarting || session.queryInstance || session.worker || session.terminalId || session.runtimeTerminalId || Object.keys(session._mentionSessions || {}).length) throw new Error('Wait for this Mate runtime to become idle before deletion.');
48
+ });
49
+ }
50
+ async function closeRuntime(slug, validate) {
51
+ var timer;
52
+ try {
53
+ return await Promise.race([Promise.resolve().then(function () { if (validate) validate(true); return ctx.removeProject(slug, true); }), new Promise(function (resolve, reject) {
54
+ timer = setTimeout(function () { reject(new Error('Mate runtime cleanup timed out; deletion remains pending.')); }, 10000);
55
+ })]);
56
+ } finally { clearTimeout(timer); }
57
+ }
58
+ async function remove(userId, id, validate, ws) {
59
+ var mate = read(userId, id), slug = 'mate-' + id, project = ctx.projects.get(slug);
60
+ if (mate.primary || mate.builtinKey === 'clay') throw new Error('Canonical Clay and primary Mates cannot be deleted.');
61
+ if (busy.has(id)) throw new Error('Another Mate lifecycle operation is applying.');
62
+ idle(project);
63
+ if (validate) validate();
64
+ busy.add(id);
65
+ var persisted = false, runtimeClosed = false;
66
+ try {
67
+ // Retain an owner-visible tombstone until every required cleanup is acknowledged.
68
+ if (!mate.lifecycleDeletion) mate = ctx.mates.updateMate(owner(userId), id, { lifecycleDeletion: { id: crypto.randomUUID(), runtimeRequired: !!project, runtimeClosed: !project } });
69
+ if (!mate || mate.error) throw new Error('Could not persist the pending Mate deletion.');
70
+ persisted = true;
71
+ if (mate.builtinKey) ctx.users.addDeletedBuiltinKey(userId, mate.builtinKey);
72
+ if (!project && mate.lifecycleDeletion.runtimeRequired && !mate.lifecycleDeletion.runtimeClosed) throw new Error('Runtime cleanup proof is unavailable; retain the pending deletion for operator recovery.');
73
+ if (project && await closeRuntime(slug, validate) !== true) throw new Error('Mate runtime cleanup was not acknowledged.');
74
+ runtimeClosed = true;
75
+ mate = ctx.mates.updateMate(owner(userId), id, { lifecycleDeletion: Object.assign({}, mate.lifecycleDeletion, { runtimeClosed: true }) });
76
+ if (!mate || mate.error) throw new Error('Runtime closure proof could not be saved.');
77
+ if (validate) validate(true);
78
+ var current = read(userId, id);
79
+ if (!current.lifecycleDeletion || current.lifecycleDeletion.id !== mate.lifecycleDeletion.id) throw new Error('Mate deletion identity changed.');
80
+ var result = ctx.mates.deleteMate(owner(userId), id, { strictCleanup: true });
81
+ if (!result || !result.ok) throw new Error(result && result.error || 'Mate deletion was not persisted.');
82
+ var definitions = require('./builtin-mates');
83
+ var available = ctx.mates.getMissingBuiltinKeys(owner(userId)).map(function (key) { var def = definitions.getBuiltinByKey(key); return def && { key: def.key, displayName: def.displayName, bio: def.bio, avatarCustom: def.avatarCustom || '', avatarStyle: def.avatarStyle || 'imprint', avatarColor: def.avatarColor || '' }; }).filter(Boolean);
84
+ broadcast(userId, { type: 'mate_deleted', mateId: id, availableBuiltins: available }, ws);
85
+ if (ctx.refresh) ctx.refresh(owner(userId));
86
+ return { persisted: true, runtimeClosed: true, directoryRemoved: true };
87
+ } catch (error) {
88
+ error.productControlResult = { persisted: persisted, runtimeClosed: runtimeClosed, deletionPending: !!ctx.mates.getMate(owner(userId), id), retrySafe: false };
89
+ throw error;
90
+ } finally { busy.delete(id); }
91
+ }
92
+ return { read: read, validateReceipt: validateReceipt, list: list, update: update, remove: remove };
93
+ }
94
+ module.exports = { attachMateLifecycleService: attachMateLifecycleService };
@@ -0,0 +1,43 @@
1
+ // Shared Home/Mate delivery. Canonical callers supply an exact live guard and
2
+ // persist delegated provenance before any provider input is admitted.
3
+ async function deliver(ctx, session, text, options) {
4
+ options = options || {};
5
+ var guarded = typeof options.revalidate === 'function';
6
+ function current() {
7
+ if (ctx.sm.sessions.get(session.localId) !== session) throw new Error('The conversation was deleted or replaced.');
8
+ if (guarded) {
9
+ options.revalidate();
10
+ if (session.taskStopRequested || session._pendingMessageDrainPaused || session._responseRecoveryStopPending || session.abortController && session.abortController.signal.aborted) throw new Error('The conversation is stopped; explicit owner resumption is required.');
11
+ }
12
+ return true;
13
+ }
14
+ if (guarded) current.exactProductDelivery = true;
15
+ current();
16
+ var event = options.event || { type: 'user_message', text: text };
17
+ if (guarded) {
18
+ if (ctx.sm.saveSessionFile(session) !== true) throw new Error('The conversation could not be saved; no provider delivery was attempted.');
19
+ if (ctx.sm.sendAndRecordDurably(session, event) !== true) throw new Error('The delegated message could not be saved; no provider delivery was attempted.');
20
+ session._mcpForeignInput = true;
21
+ } else ctx.sm.sendAndRecord(session, event);
22
+ var attempted = false;
23
+ try {
24
+ current();
25
+ var active = !!session.isProcessing;
26
+ session.isProcessing = true;
27
+ if (!active) session.sentToolResults = {};
28
+ if (options.onRecorded) options.onRecorded(session);
29
+ var queryText = options.queryText || text;
30
+ attempted = true;
31
+ if (session.queryInstance || session.worker && session.messageQueue === 'worker' || active) {
32
+ if (ctx.sdk.pushMessage(session, queryText, null) === true) return { status: 'queued', persisted: true, delivery: 'accepted', completion: 'pending' };
33
+ if (active) return { status: 'failed', persisted: true, delivery: 'rejected', retrySafe: false, error: 'The active provider did not accept the message. Inspect before another request.' };
34
+ }
35
+ var accepted = await ctx.sdk.startQuery(session, queryText, null, ctx.linuxUser || null, guarded ? current : undefined);
36
+ return { status: accepted === true ? 'started' : 'failed', persisted: true, delivery: accepted === true ? 'accepted' : 'not_confirmed', completion: 'pending', retrySafe: false };
37
+ } catch (error) {
38
+ if (!attempted) session.isProcessing = false;
39
+ error.productControlResult = { persisted: true, delivery: attempted ? 'unknown' : 'not_attempted', retrySafe: false };
40
+ throw error;
41
+ }
42
+ }
43
+ module.exports = { deliver: deliver };
@@ -4,6 +4,7 @@ var resolveMateIdentityFile = require('./mate-identity-file').resolveMateIdentit
4
4
 
5
5
  var fs = require("fs");
6
6
  var path = require("path");
7
+ var scheduleInput = require("./mate-schedule-input");
7
8
 
8
9
  function cleanLine(value, maxLength, field) {
9
10
  if (typeof value !== "string") throw new Error(field + " is required.");
@@ -27,6 +28,11 @@ function normalize(definition) {
27
28
  var identity = typeof definition.identityMarkdown === "string" ? definition.identityMarkdown.replace(/\u0000/g, "").trim() : "";
28
29
  if (identity.length < 200) throw new Error("The Mate identity is incomplete.");
29
30
  if (identity.length > 30000) throw new Error("The Mate identity is too long.");
31
+ var scheduleIntent = definition.scheduleIntent || null;
32
+ if (scheduleIntent) {
33
+ var scheduleChecked = scheduleInput.normalize(scheduleIntent);
34
+ scheduleIntent = { message: scheduleChecked.message, scheduleRule: scheduleChecked.rule };
35
+ }
30
36
  return {
31
37
  name: cleanLine(definition.name, 80, "Mate name"),
32
38
  bio: cleanLine(definition.bio, 280, "Mate bio"),
@@ -37,6 +43,7 @@ function normalize(definition) {
37
43
  identityMarkdown: identity,
38
44
  vendor: cleanLine(definition.vendor || "claude", 40, "Vendor"),
39
45
  model: cleanLine(definition.model, 240, "Mate model"),
46
+ scheduleIntent: scheduleIntent,
40
47
  };
41
48
  }
42
49
 
@@ -54,6 +61,7 @@ function createReadyMate(mates, mateCtx, definition) {
54
61
  vendor: ready.vendor,
55
62
  model: ready.model,
56
63
  autonomy: ready.autonomy,
64
+ scheduleIntent: ready.scheduleIntent,
57
65
  });
58
66
  var mateDir = mates.getMateDir(mateCtx, mate.id);
59
67
  var identityPath = resolveMateIdentityFile(mateDir);
@@ -80,7 +88,7 @@ function createReadyMate(mates, mateCtx, definition) {
80
88
  vendor: ready.vendor,
81
89
  model: ready.model,
82
90
  profile: Object.assign({}, mate.profile || {}, { displayName: ready.name, bio: ready.bio }),
83
- seedData: Object.assign({}, mate.seedData || {}, { relationship: ready.relationship, activity: ready.activities, communicationStyle: ready.communicationStyle, autonomy: ready.autonomy }),
91
+ seedData: Object.assign({}, mate.seedData || {}, { relationship: ready.relationship, activity: ready.activities, communicationStyle: ready.communicationStyle, autonomy: ready.autonomy, scheduleIntent: ready.scheduleIntent }),
84
92
  });
85
93
  mates.backupIdentity(mateDir, ready.identityMarkdown);
86
94
  mates.logIdentityChange(mateDir, "clay_interview", ready.identityMarkdown, "");
@@ -0,0 +1,29 @@
1
+ var rules = require('./schedule-rule');
2
+ function normalize(input, previous) {
3
+ input = input || {};
4
+ var message = input.message === undefined && previous ? previous.message : input.message;
5
+ if (typeof message !== 'string' || !message.trim() || message.length > 1200 || message.indexOf('\u0000') >= 0) throw new Error('A message of 1 to 1200 characters is required.');
6
+ var source = input.scheduleRule || (previous && previous.rule) || {
7
+ version: 1, timezone: 'server-local', anchorDate: input.date, time: input.time, recurrence: null,
8
+ };
9
+ source = Object.assign({}, source);
10
+ if (input.date !== undefined) source.anchorDate = input.date;
11
+ if (input.time !== undefined) source.time = input.time;
12
+ var checked = rules.validateScheduleRule(source);
13
+ if (!checked.ok) throw new Error(checked.error);
14
+ return { message: message.trim(), rule: checked.value };
15
+ }
16
+ function timezone() { return Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC'; }
17
+ function review(value) {
18
+ var rule = value.rule;
19
+ var repeat = rule.recurrence ? 'Every ' + rule.recurrence.every + ' ' + rule.recurrence.unit +
20
+ (rule.recurrence.weekdays ? ' on ' + rule.recurrence.weekdays.map(function (day) { return ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'][day]; }).join(', ') : '') : 'Once';
21
+ if (rule.recurrenceEnd) repeat += rule.recurrenceEnd.type === 'after' ? ', ' + rule.recurrenceEnd.count + ' deliveries' : ', through ' + rule.recurrenceEnd.date;
22
+ if (rule.interval) {
23
+ var ending = rule.interval.end;
24
+ repeat += '; every ' + rule.interval.every + ' ' + rule.interval.unit;
25
+ repeat += !ending ? ', until end of day' : ending.type === 'after' ? ', ' + ending.count + ' deliveries per day' : ', until ' + ending.time;
26
+ }
27
+ return value.message + '\n' + rule.anchorDate + ' at ' + rule.time + ' (' + timezone() + ')\n' + repeat;
28
+ }
29
+ module.exports = { normalize: normalize, timezone: timezone, review: review };
@@ -0,0 +1,151 @@
1
+ var crypto = require('crypto');
2
+ var rule = require('./schedule-rule');
3
+
4
+ function copy(value) { return JSON.parse(JSON.stringify(value)); }
5
+ function attachMateScheduleLifecycle(ctx) {
6
+ var now = ctx.now || Date.now;
7
+ function save(session, records) {
8
+ var previous = session.mateSchedules;
9
+ session.mateSchedules = records;
10
+ if (ctx.sm.saveSessionFile(session) !== true) {
11
+ session.mateSchedules = previous;
12
+ throw new Error('The Mate schedule could not be saved.');
13
+ }
14
+ }
15
+ function records(session) { return copy(session.mateSchedules || []); }
16
+ function find(session, id) {
17
+ return records(session).find(function (record) { return record.id === id; }) || null;
18
+ }
19
+ function replace(session, record) {
20
+ var all = records(session);
21
+ var index = all.findIndex(function (item) { return item.id === record.id; });
22
+ if (index < 0) all.push(record); else all[index] = record;
23
+ save(session, all);
24
+ }
25
+ function jobFor(record) {
26
+ return ctx.scheduler.listJobs().reverse().find(function (job) {
27
+ return job.projectId === ctx.slug && job.payload && job.payload.mateScheduleId === record.id &&
28
+ job.payload.logicalRevision === record.revision && job.payload.occurrence === record.occurrence;
29
+ });
30
+ }
31
+ function current(session, job) {
32
+ var record = job && job.payload && find(session, job.payload.mateScheduleId);
33
+ if (record && record.createdBy && record.createdBy.type === 'canonicalClay') { try { (ctx.authorizeRuntime || ctx.authorize)(session); } catch (error) { return false; } }
34
+ return !!(record && record.state === 'active' && record.revision === job.payload.logicalRevision &&
35
+ record.occurrence === job.payload.occurrence && record.ownerId === job.ownerId);
36
+ }
37
+ function admit(session, record) {
38
+ if (record.state !== 'active') return null;
39
+ var existing = jobFor(record);
40
+ if (existing && !(existing.state === 'cancelled' && existing.cancelledReason === 'queue_admission_failed')) {
41
+ if (['failed', 'cancelled', 'interrupted'].indexOf(existing.state) >= 0) throw new Error('The occurrence requires recovery; it was not scheduled.');
42
+ return existing;
43
+ }
44
+ var job = ctx.schedule(session, record.message, Math.max(record.runAt, now() + 1),
45
+ { id: record.ownerId }, null, null, 'mate-' + record.id + '-' + record.revision + '-' + record.occurrence,
46
+ null, { exact: true, scheduleRule: record.rule, mateScheduleId: record.id,
47
+ logicalScheduleId: record.id, logicalRevision: record.revision, occurrence: record.occurrence });
48
+ if (!job) throw new Error('The occurrence could not be admitted. The saved schedule needs retry.');
49
+ return job;
50
+ }
51
+ function create(session, value, id, control) {
52
+ var actor = ctx.authorize(session, control);
53
+ var existing = find(session, id);
54
+ if (existing) { if (existing.message !== value.message || JSON.stringify(existing.rule) !== JSON.stringify(value.rule)) throw new Error('Schedule request already names another intent.'); admit(session, existing); return existing; }
55
+ var runAt = rule.nextScheduleRuleTime(value.rule, now(), [], {});
56
+ if (!Number.isFinite(runAt)) throw new Error('The schedule time has expired.');
57
+ var record = { id: id, revision: crypto.randomUUID(), ownerId: ctx.owner(session), message: value.message,
58
+ createdBy: actor || null, updatedBy: actor || null, rule: value.rule, state: 'active', occurrence: 0, count: 0, dateCounts: {}, runAt: runAt };
59
+ replace(session, record);
60
+ admit(session, record);
61
+ return record;
62
+ }
63
+ function checked(session, args, control) {
64
+ ctx.authorize(session, control);
65
+ var record = find(session, args.id);
66
+ if (!record || record.ownerId !== ctx.owner(session)) throw new Error('Schedule not found.');
67
+ if (args.revision && args.revision !== record.revision) throw new Error('The schedule revision is stale.');
68
+ return record;
69
+ }
70
+ function cancel(session, args, control) {
71
+ var record = checked(session, args, control);
72
+ var actor = ctx.authorize(session, control);
73
+ if (record.state === 'cancelled') return record;
74
+ // Persist the tombstone before touching occurrences. Dispatch rechecks it.
75
+ record.state = 'cancelled';
76
+ if (actor) record.updatedBy = actor;
77
+ replace(session, record);
78
+ var job = jobFor(record);
79
+ if (job && job.state === 'queued' && !ctx.cancelJob(session, job)) throw new Error('Cancellation was saved, but occurrence cleanup failed.');
80
+ return record;
81
+ }
82
+ function update(session, args, value, control) {
83
+ var previous = checked(session, args, control);
84
+ var actor = ctx.authorize(session, control);
85
+ if (previous.state !== 'active') throw new Error('Only active schedules can be updated.');
86
+ var runAt = rule.nextScheduleRuleTime(value.rule, now(), [], {});
87
+ if (!Number.isFinite(runAt)) throw new Error('The schedule time has expired.');
88
+ var record = Object.assign({}, previous, { revision: crypto.randomUUID(), updatedBy: actor || previous.updatedBy || null, message: value.message,
89
+ rule: value.rule, count: 0, dateCounts: {}, occurrence: 0, runAt: runAt });
90
+ replace(session, record);
91
+ var oldJob = jobFor(previous);
92
+ if (oldJob && oldJob.state === 'queued' && !ctx.cancelJob(session, oldJob)) throw new Error('Update saved, but previous occurrence cleanup failed.');
93
+ admit(session, record);
94
+ return record;
95
+ }
96
+ function consumed(session, job) {
97
+ if (!current(session, job)) return false;
98
+ var record = find(session, job.payload.mateScheduleId);
99
+ record.count++;
100
+ var date = new Date(record.runAt);
101
+ var key = date.getFullYear() + '-' + String(date.getMonth() + 1).padStart(2, '0') + '-' + String(date.getDate()).padStart(2, '0');
102
+ record.dateCounts[key] = Number(record.dateCounts[key] || 0) + 1;
103
+ // Coalesce missed times: deliver the admitted occurrence once, then choose
104
+ // the next future time. Counts represent deliveries, not missed slots.
105
+ record.runAt = rule.nextScheduleRuleTime(record.rule, Math.max(record.runAt, now()), [],
106
+ { startCount: record.count, dateStartCounts: record.dateCounts });
107
+ record.occurrence++;
108
+ if (!Number.isFinite(record.runAt)) record.state = 'completed';
109
+ replace(session, record);
110
+ admit(session, record);
111
+ return true;
112
+ }
113
+ function reconcile(session) {
114
+ records(session).forEach(function (record) {
115
+ if (record.state !== 'active') return;
116
+ (ctx.authorizeRuntime || ctx.authorize)(session);
117
+ var job = jobFor(record);
118
+ var item = job && ctx.queueItem(session, job);
119
+ if (item && item.state === 'consumed') consumed(session, job);
120
+ else if (item && (item.state === 'cancelled' || item.state === 'failed')) {
121
+ record.state = item.state;
122
+ replace(session, record);
123
+ } else if (job && ['failed', 'cancelled', 'interrupted'].indexOf(job.state) >= 0 && (!item || item.state !== 'pending')) {
124
+ record.state = 'interrupted';
125
+ replace(session, record);
126
+ } else admit(session, record);
127
+ });
128
+ }
129
+ function onConsumed(session, job) {
130
+ try { return consumed(session, job); }
131
+ catch (error) {
132
+ // Queue consumption is already durable. Keep the accepted provider turn
133
+ // running; reconciliation repairs the saved next occurrence after restart.
134
+ ctx.sm.sendAndRecord(session, { type: 'error', text: 'The scheduled message was delivered, but its next occurrence needs recovery: ' + error.message });
135
+ return false;
136
+ }
137
+ }
138
+ return { create: create, read: checked, list: function (session, control) { ctx.authorize(session, control); return records(session); },
139
+ update: update, cancel: cancel, failed: function (session, job, error) {
140
+ if (!current(session, job)) return;
141
+ var record = find(session, job.payload.mateScheduleId);
142
+ record.state = 'failed'; record.error = error.message;
143
+ replace(session, record);
144
+ }, cancelled: function (session, job) {
145
+ if (!current(session, job)) return;
146
+ var record = find(session, job.payload.mateScheduleId);
147
+ record.state = 'cancelled';
148
+ replace(session, record);
149
+ }, current: current, consumed: onConsumed, reconcile: reconcile };
150
+ }
151
+ module.exports = { attachMateScheduleLifecycle: attachMateScheduleLifecycle };
@@ -0,0 +1,103 @@
1
+ var crypto = require('crypto');
2
+ var userInput = require('./yoke/user-input');
3
+ var input = require('./mate-schedule-input');
4
+ var personal = require('./mcp-session-access');
5
+
6
+ function attachMateScheduling(ctx) {
7
+ function owner(session) { return ctx.users.isMultiUser() ? session.ownerId : 'local-user'; }
8
+ function privateSession(session) {
9
+ if (!ctx.isMate || !session || ctx.sm.sessions.get(session.localId) !== session ||
10
+ session.destroying || session.mateCreationMode || session.taskStopRequested || session._mcpForeignInput ||
11
+ session.sessionVisibility === 'shared' || session.delegated || session._delegatedBy || session._pairDelegation) throw new Error('An active private Mate conversation is required.');
12
+ if (ctx.users.isMultiUser() && (session.ownerId !== ctx.projectOwnerId || !personal.canUsePersonalConnections(session, ctx.sm.sessions))) throw new Error('Only the Mate owner can schedule messages.');
13
+ if (!ctx.users.isMultiUser() && (session.ownerId || (session.history || []).some(function (event) { return event.type === 'user_message' && event.from && event.from !== 'default'; }))) throw new Error('Foreign input cannot authorize Mate schedules.');
14
+ ctx.authorizeAccess(session, owner(session));
15
+ }
16
+ function authorize(session) {
17
+ if (ctx.isHostAgent) throw new Error('Canonical Clay scheduling requires its separate owner-reviewed controls.');
18
+ privateSession(session);
19
+ }
20
+ function authorizeControl(session, control) {
21
+ privateSession(session);
22
+ return require('./canonical-clay-scheduling-authority').validate(control, ctx.slug, session);
23
+ }
24
+ function authorizeLifecycle(session) {
25
+ privateSession(session);
26
+ require('./canonical-clay-scheduling-authority').assertMate(ctx.projectOwnerId, ctx.mateId, ctx.users.isMultiUser());
27
+ if (ctx.isHostAgent) require('./canonical-clay-scheduling-authority').assertHost(ctx.projectOwnerId, ctx.mateId, ctx.users.isMultiUser());
28
+ }
29
+ function persistDraft(session, draft) {
30
+ var previous = session.mateScheduleDraft;
31
+ session.mateScheduleDraft = draft;
32
+ if (ctx.sm.saveSessionFile(session) !== true) {
33
+ session.mateScheduleDraft = previous;
34
+ throw new Error('The schedule draft could not be saved.');
35
+ }
36
+ }
37
+ function result(value) { return { content: [{ type: 'text', text: JSON.stringify(value) }] }; }
38
+ function getToolDefs(session) {
39
+ try { authorize(session); } catch (error) { return []; }
40
+ var generation = Number(session._sdkQueryGeneration || 0);
41
+ var originalOwner = owner(session);
42
+ function current() {
43
+ authorize(session);
44
+ if (generation !== Number(session._sdkQueryGeneration || 0) || owner(session) !== originalOwner) throw new Error('The schedule query is stale.');
45
+ }
46
+ function tool(name, description, properties, required, handler) {
47
+ return { name: name, queryBound: true, inputSchema: { type: 'object', properties: properties, required: required, additionalProperties: false },
48
+ description: description, handler: async function (args) {
49
+ try { current(); return result(await handler(args || {})); }
50
+ catch (error) { return result({ status: 'rejected', reason: error.message }); }
51
+ } };
52
+ }
53
+ var str = { type: 'string' };
54
+ var fields = { message: str, date: str, time: str, scheduleRule: { type: ['object', 'null'] } };
55
+ async function approve(draft, question, label, other) {
56
+ persistDraft(session, draft);
57
+ var reply = await userInput.dispatchUserInput(ctx.userInput(session), { questions: [{
58
+ id: 'approve', header: 'Mate schedule', question: question,
59
+ options: [{ label: label }, { label: other || 'Cancel' }], allowOther: false,
60
+ }] }, { source: 'mate-schedule', signal: session.abortController && session.abortController.signal });
61
+ current();
62
+ if (!session.mateScheduleDraft || session.mateScheduleDraft.id !== draft.id || session.mateScheduleDraft.revision !== draft.revision) throw new Error('The schedule draft is stale.');
63
+ var accepted = reply.status === 'submitted' && reply.answers.approve && reply.answers.approve.length === 1 && reply.answers.approve[0] === label;
64
+ if (!accepted) persistDraft(session, null);
65
+ return accepted;
66
+ }
67
+ async function review(args, update) {
68
+ var previous = update ? ctx.lifecycle.read(session, args) : null;
69
+ var value = input.normalize(args, previous);
70
+ var draft = { id: crypto.randomUUID(), revision: crypto.randomUUID(), value: value,
71
+ targetId: previous && previous.id, targetRevision: previous && previous.revision };
72
+ if (!await approve(draft, (update ? 'Update this schedule?\n' : 'Create this schedule?\n') + input.review(value), update ? 'Update schedule' : 'Create schedule')) return { status: 'cancelled' };
73
+ var record = update ? ctx.lifecycle.update(session, { id: draft.targetId, revision: draft.targetRevision }, value) : ctx.lifecycle.create(session, value, draft.id);
74
+ persistDraft(session, null);
75
+ return { status: update ? 'updated' : 'created', schedule: record, timezone: input.timezone() };
76
+ }
77
+ async function cancel(args) {
78
+ var record = ctx.lifecycle.read(session, args);
79
+ if (record.state === 'cancelled') return { status: 'cancelled', schedule: record };
80
+ var draft = { id: crypto.randomUUID(), revision: crypto.randomUUID(), targetId: record.id, targetRevision: record.revision };
81
+ if (!await approve(draft, 'Cancel this schedule?\n' + input.review({ message: record.message, rule: record.rule }), 'Cancel schedule', 'Keep schedule')) return { status: 'unchanged' };
82
+ record = ctx.lifecycle.cancel(session, { id: draft.targetId, revision: draft.targetRevision });
83
+ persistDraft(session, null);
84
+ return { status: 'cancelled', schedule: record };
85
+ }
86
+ var begin = tool('begin_mate_schedule_interview', 'Begin scheduling for this Mate. Ask exactly one structured question at a time. Never assume repetition.', {}, [], function () {
87
+ return { status: 'interview_started', timezone: input.timezone(), guidance: 'Collect the message, date/time, and explicitly requested repetition one question at a time. Then propose the complete schedule for the human approval card.' };
88
+ });
89
+ begin.structuredQuestionLimit = function () { try { current(); return 1; } catch (error) { return null; } };
90
+ return [begin,
91
+ tool('propose_mate_schedule', 'Review a complete message schedule in a server-owned approval card. Only authenticated human approval persists a schedule.', fields, ['message'], function (args) { return review(args, false); }),
92
+ tool('list_mate_schedules', 'List logical schedules and their stable IDs, revisions and lifecycle states in this private conversation.', {}, [], function () { return { schedules: ctx.lifecycle.list(session) }; }),
93
+ tool('read_mate_schedule', 'Read one logical schedule using its listed ID.', { id: str }, ['id'], function (args) { return { schedule: ctx.lifecycle.read(session, args) }; }),
94
+ tool('update_mate_schedule', 'Propose changes to an active schedule by listed ID and revision, with a fresh human approval card.', Object.assign({ id: str, revision: str }, fields), ['id', 'revision'], function (args) { return review(args, true); }),
95
+ tool('cancel_mate_schedule', 'Review cancellation by listed ID and revision in an authenticated human approval card. Cancellation persists across restart.', { id: str, revision: str }, ['id', 'revision'], cancel),
96
+ ];
97
+ }
98
+ return { authorize: authorize, authorizeControl: authorizeControl, authorizeLifecycle: authorizeLifecycle, owner: owner, getToolDefs: getToolDefs, getSystemPrompt: function (session) {
99
+ try { authorize(session); } catch (error) { return ''; }
100
+ return 'When scheduling for this Mate, call begin_mate_schedule_interview, ask exactly one structured question at a time, and clarify the message, local date/time in ' + input.timezone() + ', and explicitly requested repetition. Use scheduleRule for recurrence and count/until limits. propose_mate_schedule and update_mate_schedule present the exact authenticated human approval card. Chat text and model flags never approve schedules. Use listed logical IDs and revisions to read, update or cancel. Scheduled output uses this private Mate runtime and tools. Project Scheduled Tasks Driver/Worker tools remain unavailable.';
101
+ } };
102
+ }
103
+ module.exports = { attachMateScheduling: attachMateScheduling };
package/lib/mates.js CHANGED
@@ -4,6 +4,8 @@ var path = require("path");
4
4
  var crypto = require("crypto");
5
5
  var config = require("./config");
6
6
  var workspaceColor = require("./mate-workspace-color");
7
+ var avatarCatalog = require("./mate-avatar-catalog");
8
+ var avatarMigration = require("./mate-avatar-migration");
7
9
 
8
10
  var crisisSafety = require("./crisis-safety");
9
11
  var matesPrompts = require("./mates-prompts");
@@ -98,10 +100,12 @@ function loadMates(ctx) {
98
100
  var raw = fs.readFileSync(matesFilePath(ctx), "utf8");
99
101
  var data = JSON.parse(raw);
100
102
  if (!data.mates) data.mates = [];
101
- return data;
102
103
  } catch (e) {
103
104
  return defaultData();
104
105
  }
106
+ // Migration write failures must propagate, never masquerade as an empty registry.
107
+ if (avatarMigration.migrate(data)) saveMates(ctx, data);
108
+ return data;
105
109
  }
106
110
 
107
111
  function saveMates(ctx, data) {
@@ -135,6 +139,7 @@ function createMate(ctx, seedData) {
135
139
  displayName: null,
136
140
  avatarColor: "#5857fc",
137
141
  avatarStyle: "imprint",
142
+ avatarCatalogId: "B" + String(crypto.randomInt(1, 49)).padStart(2, "0"),
138
143
  avatarSeed: crypto.randomBytes(4).toString("hex"),
139
144
  },
140
145
  bio: null,
@@ -142,6 +147,7 @@ function createMate(ctx, seedData) {
142
147
  interviewProjectPath: null,
143
148
  };
144
149
 
150
+ avatarMigration.normalizeMate(mate);
145
151
  data.mates.push(mate);
146
152
  saveMates(ctx, data);
147
153
 
@@ -205,6 +211,8 @@ function updateMate(ctx, id, updates) {
205
211
  if (data.mates[i].primary || data.mates[i].builtinKey === "clay") delete updates.workspaceColor;
206
212
  else if (!workspaceColor.valid(updates.workspaceColor)) return { error: "Choose a supported workspace color." };
207
213
  }
214
+ var avatarError = avatarCatalog.validate(updates.profile, data.mates[i]);
215
+ if (avatarError) return { error: avatarError };
208
216
  // Primary mates: protect name and core identity fields
209
217
  if (data.mates[i].primary) {
210
218
  delete updates.name;
@@ -219,6 +227,7 @@ function updateMate(ctx, id, updates) {
219
227
  // Locked built-in artwork is server-managed. Preserve it even when a
220
228
  // stale or crafted client submits a replacement profile object.
221
229
  if (data.mates[i].profile && data.mates[i].profile.avatarLocked && updates.profile) {
230
+ updates.profile.avatarCatalogId = data.mates[i].profile.avatarCatalogId || "";
222
231
  updates.profile.avatarStyle = data.mates[i].profile.avatarStyle;
223
232
  updates.profile.avatarSeed = data.mates[i].profile.avatarSeed;
224
233
  updates.profile.avatarColor = data.mates[i].profile.avatarColor;
@@ -229,6 +238,7 @@ function updateMate(ctx, id, updates) {
229
238
  for (var j = 0; j < keys.length; j++) {
230
239
  data.mates[i][keys[j]] = updates[keys[j]];
231
240
  }
241
+ avatarMigration.normalizeMate(data.mates[i]);
232
242
  saveMates(ctx, data);
233
243
  return data.mates[i];
234
244
  }
@@ -236,7 +246,7 @@ function updateMate(ctx, id, updates) {
236
246
  return null;
237
247
  }
238
248
 
239
- function deleteMate(ctx, id) {
249
+ function deleteMate(ctx, id, options) {
240
250
  var data = loadMates(ctx);
241
251
  // Primary mates cannot be deleted (they are system infrastructure)
242
252
  for (var di = 0; di < data.mates.length; di++) {
@@ -249,15 +259,16 @@ function deleteMate(ctx, id) {
249
259
  return m.id !== id;
250
260
  });
251
261
  if (data.mates.length === before) return { error: "Mate not found" };
252
- saveMates(ctx, data);
262
+ if (!options || !options.strictCleanup) saveMates(ctx, data);
253
263
 
254
264
  // Remove mate directory
255
265
  var mateDir = getMateDir(ctx, id);
256
266
  try {
257
267
  fs.rmSync(mateDir, { recursive: true, force: true });
258
268
  } catch (e) {
259
- // Directory may not exist
269
+ if (options && options.strictCleanup) throw e;
260
270
  }
271
+ if (options && options.strictCleanup) saveMates(ctx, data);
261
272
 
262
273
  return { ok: true };
263
274
  }
@@ -506,6 +517,7 @@ function createBuiltinMate(ctx, builtinKey) {
506
517
  interviewProjectPath: null,
507
518
  };
508
519
 
520
+ avatarMigration.normalizeMate(mate);
509
521
  data.mates.push(mate);
510
522
  saveMates(ctx, data);
511
523
 
@@ -661,8 +673,8 @@ function syncPrimaryMates(ctx) {
661
673
  }
662
674
  // Locked avatars are code-managed — refresh them from the def so a
663
675
  // built-in's avatar can be updated by changing the source file
664
- // (e.g. Clay flipping from a stale Ally asset to the app icon).
665
- if (def.avatarLocked) {
676
+ // User uploads are preserved while canonical library artwork follows the definition.
677
+ if (def.avatarLocked && !avatarMigration.hasCustom(mate)) {
666
678
  if (def.avatarCustom != null && mate.profile.avatarCustom !== def.avatarCustom) {
667
679
  mate.profile.avatarCustom = def.avatarCustom; changed = true;
668
680
  }